PageRenderTime 153ms CodeModel.GetById 16ms app.highlight 123ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/llwindow/llwindowmacosx.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2391 lines | 1740 code | 375 blank | 276 comment | 322 complexity | 344ddd084fdb6cbaf077b053b7b08935 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/**
   2 * @file llwindowmacosx.cpp
   3 * @brief Platform-dependent implementation of llwindow
   4 *
   5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 *
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 *
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 *
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#include "linden_common.h"
  28
  29#include "llwindowmacosx.h"
  30
  31#include "llkeyboardmacosx.h"
  32#include "llwindowcallbacks.h"
  33#include "llwindowmacosx-objc.h"
  34#include "llpreeditor.h"
  35
  36#include "llerror.h"
  37#include "llgl.h"
  38#include "llstring.h"
  39#include "lldir.h"
  40#include "indra_constants.h"
  41
  42#include <Carbon/Carbon.h>
  43#include <OpenGL/OpenGL.h>
  44
  45extern BOOL gDebugWindowProc;
  46
  47// culled from winuser.h
  48//const S32	WHEEL_DELTA = 120;     /* Value for rolling one detent */
  49// On the Mac, the scroll wheel reports a delta of 1 for each detent.
  50// There's also acceleration for faster scrolling, based on a slider in the system preferences.
  51const S32	WHEEL_DELTA = 1;     /* Value for rolling one detent */
  52const S32	BITS_PER_PIXEL = 32;
  53const S32	MAX_NUM_RESOLUTIONS = 32;
  54
  55
  56//
  57// LLWindowMacOSX
  58//
  59
  60BOOL LLWindowMacOSX::sUseMultGL = FALSE;
  61WindowRef LLWindowMacOSX::sMediaWindow = NULL;
  62
  63// Cross-platform bits:
  64
  65BOOL check_for_card(const char* RENDERER, const char* bad_card)
  66{
  67	if (!strnicmp(RENDERER, bad_card, strlen(bad_card)))
  68	{
  69		std::string buffer = llformat(
  70			"Your video card appears to be a %s, which Second Life does not support.\n"
  71			"\n"
  72			"Second Life requires a video card with 32 Mb of memory or more, as well as\n"
  73			"multitexture support.  We explicitly support nVidia GeForce 2 or better, \n"
  74			"and ATI Radeon 8500 or better.\n"
  75			"\n"
  76			"If you own a supported card and continue to receive this message, try \n"
  77			"updating to the latest video card drivers. Otherwise look in the\n"
  78			"secondlife.com support section or e-mail technical support\n"
  79			"\n"
  80			"You can try to run Second Life, but it will probably crash or run\n"
  81			"very slowly.  Try anyway?",
  82			bad_card);
  83		S32 button = OSMessageBox(buffer.c_str(), "Unsupported video card", OSMB_YESNO);
  84		if (OSBTN_YES == button)
  85		{
  86			return FALSE;
  87		}
  88		else
  89		{
  90			return TRUE;
  91		}
  92	}
  93
  94	return FALSE;
  95}
  96
  97// Switch to determine whether we capture all displays, or just the main one.
  98// We may want to base this on the setting of _DEBUG...
  99
 100#define CAPTURE_ALL_DISPLAYS 0
 101static double getDictDouble (CFDictionaryRef refDict, CFStringRef key);
 102static long getDictLong (CFDictionaryRef refDict, CFStringRef key);
 103
 104
 105
 106
 107// CarbonEvents we're interested in.
 108static EventTypeSpec WindowHandlerEventList[] =
 109{
 110	// Window-related events
 111	{ kEventClassWindow, kEventWindowActivated },
 112	{ kEventClassWindow, kEventWindowDeactivated },
 113	{ kEventClassWindow, kEventWindowShown },
 114	{ kEventClassWindow, kEventWindowHidden },
 115	{ kEventClassWindow, kEventWindowCollapsed },
 116	{ kEventClassWindow, kEventWindowExpanded },
 117	{ kEventClassWindow, kEventWindowGetClickActivation },
 118	{ kEventClassWindow, kEventWindowClose },
 119	{ kEventClassWindow, kEventWindowBoundsChanging },
 120	{ kEventClassWindow, kEventWindowBoundsChanged },
 121	{ kEventClassWindow, kEventWindowGetIdealSize },
 122
 123	// Mouse events
 124	{ kEventClassMouse, kEventMouseDown },
 125	{ kEventClassMouse, kEventMouseUp },
 126	{ kEventClassMouse, kEventMouseDragged },
 127	{ kEventClassMouse, kEventMouseWheelMoved },
 128	{ kEventClassMouse, kEventMouseMoved },
 129
 130	// Keyboard events
 131	// No longer handle raw key down events directly.
 132	// When text input events come in, extract the raw key events from them and process at that point.
 133	// This allows input methods to eat keystrokes the way they're supposed to.
 134//	{ kEventClassKeyboard, kEventRawKeyDown },
 135//	{ kEventClassKeyboard, kEventRawKeyRepeat },
 136	{ kEventClassKeyboard, kEventRawKeyUp },
 137	{ kEventClassKeyboard, kEventRawKeyModifiersChanged },
 138
 139	// Text input events
 140	{ kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
 141	{ kEventClassTextInput, kEventTextInputUpdateActiveInputArea },
 142	{ kEventClassTextInput, kEventTextInputOffsetToPos },
 143	{ kEventClassTextInput, kEventTextInputPosToOffset },
 144	{ kEventClassTextInput, kEventTextInputShowHideBottomWindow },
 145	{ kEventClassTextInput, kEventTextInputGetSelectedText },
 146	{ kEventClassTextInput, kEventTextInputFilterText },
 147
 148	// TSM Document Access events (advanced input method support)
 149	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetLength },
 150	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetSelectedRange },
 151	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetCharacters },
 152	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetFont },
 153	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetGlyphInfo },
 154	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessLockDocument },
 155	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessUnlockDocument }
 156};
 157
 158static EventTypeSpec GlobalHandlerEventList[] =
 159{
 160	// Mouse events
 161	{ kEventClassMouse, kEventMouseDown },
 162	{ kEventClassMouse, kEventMouseUp },
 163	{ kEventClassMouse, kEventMouseDragged },
 164	{ kEventClassMouse, kEventMouseWheelMoved },
 165	{ kEventClassMouse, kEventMouseMoved },
 166
 167	// Keyboard events
 168	// No longer handle raw key down events directly.
 169	// When text input events come in, extract the raw key events from them and process at that point.
 170	// This allows input methods to eat keystrokes the way they're supposed to.
 171//	{ kEventClassKeyboard, kEventRawKeyDown },
 172//	{ kEventClassKeyboard, kEventRawKeyRepeat },
 173	{ kEventClassKeyboard, kEventRawKeyUp },
 174	{ kEventClassKeyboard, kEventRawKeyModifiersChanged },
 175
 176	// Text input events
 177	{ kEventClassTextInput, kEventTextInputUpdateActiveInputArea },
 178	{ kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
 179	{ kEventClassTextInput, kEventTextInputOffsetToPos },
 180	{ kEventClassTextInput, kEventTextInputPosToOffset },
 181	{ kEventClassTextInput, kEventTextInputShowHideBottomWindow },
 182	{ kEventClassTextInput, kEventTextInputGetSelectedText },
 183	{ kEventClassTextInput, kEventTextInputFilterText },
 184
 185	// TSM Document Access events (advanced input method support)
 186	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetLength },
 187	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetSelectedRange },
 188	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetCharacters },
 189	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetFont },
 190	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetGlyphInfo },
 191	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessLockDocument },
 192	{ kEventClassTSMDocumentAccess, kEventTSMDocumentAccessUnlockDocument }
 193};
 194
 195static EventTypeSpec CommandHandlerEventList[] =
 196{
 197	{ kEventClassCommand, kEventCommandProcess }
 198};
 199
 200// MBW -- HACK ALERT
 201// On the Mac, to put up an OS dialog in full screen mode, we must first switch OUT of full screen mode.
 202// The proper way to do this is to bracket the dialog with calls to beforeDialog() and afterDialog(), but these
 203// require a pointer to the LLWindowMacOSX object.  Stash it here and maintain in the constructor and destructor.
 204// This assumes that there will be only one object of this class at any time.  Hopefully this is true.
 205static LLWindowMacOSX *gWindowImplementation = NULL;
 206
 207
 208
 209LLWindowMacOSX::LLWindowMacOSX(LLWindowCallbacks* callbacks,
 210							   const std::string& title, const std::string& name, S32 x, S32 y, S32 width,
 211							   S32 height, U32 flags,
 212							   BOOL fullscreen, BOOL clearBg,
 213							   BOOL disable_vsync, BOOL use_gl,
 214							   BOOL ignore_pixel_depth,
 215							   U32 fsaa_samples)
 216	: LLWindow(NULL, fullscreen, flags)
 217{
 218	// *HACK: During window construction we get lots of OS events for window
 219	// reshape, activate, etc. that the viewer isn't ready to handle.
 220	// Route them to a dummy callback structure until the end of constructor.
 221	LLWindowCallbacks null_callbacks;
 222	mCallbacks = &null_callbacks;
 223
 224	// Voodoo for calling cocoa from carbon (see llwindowmacosx-objc.mm).
 225	setupCocoa();
 226
 227	// Initialize the keyboard
 228	gKeyboard = new LLKeyboardMacOSX();
 229	gKeyboard->setCallbacks(callbacks);
 230
 231	// Ignore use_gl for now, only used for drones on PC
 232	mWindow = NULL;
 233	mContext = NULL;
 234	mPixelFormat = NULL;
 235	mDisplay = CGMainDisplayID();
 236	mOldDisplayMode = NULL;
 237	mTimer = NULL;
 238	mSimulatedRightClick = FALSE;
 239	mLastModifiers = 0;
 240	mHandsOffEvents = FALSE;
 241	mCursorDecoupled = FALSE;
 242	mCursorLastEventDeltaX = 0;
 243	mCursorLastEventDeltaY = 0;
 244	mCursorIgnoreNextDelta = FALSE;
 245	mNeedsResize = FALSE;
 246	mOverrideAspectRatio = 0.f;
 247	mMaximized = FALSE;
 248	mMinimized = FALSE;
 249	mTSMDocument = NULL; // Just in case.
 250	mLanguageTextInputAllowed = FALSE;
 251	mTSMScriptCode = 0;
 252	mTSMLangCode = 0;
 253	mPreeditor = NULL;
 254	mRawKeyEvent = NULL;
 255	mFSAASamples = fsaa_samples;
 256	mForceRebuild = FALSE;
 257
 258	// For reasons that aren't clear to me, LLTimers seem to be created in the "started" state.
 259	// Since the started state of this one is used to track whether the NMRec has been installed, it wants to start out in the "stopped" state.
 260	mBounceTimer.stop();
 261
 262	// Get the original aspect ratio of the main device.
 263	mOriginalAspectRatio = (double)CGDisplayPixelsWide(mDisplay) / (double)CGDisplayPixelsHigh(mDisplay);
 264
 265	// Stash the window title
 266	strcpy((char*)mWindowTitle + 1, title.c_str()); /* Flawfinder: ignore */
 267	mWindowTitle[0] = title.length();
 268
 269	mEventHandlerUPP = NewEventHandlerUPP(staticEventHandler);
 270	mMoveEventCampartorUPP = NewEventComparatorUPP(staticMoveEventComparator);
 271	mGlobalHandlerRef = NULL;
 272	mWindowHandlerRef = NULL;
 273
 274	mDragOverrideCursor = -1;
 275
 276	// We're not clipping yet
 277	SetRect( &mOldMouseClip, 0, 0, 0, 0 );
 278
 279	// Set up global event handlers (the fullscreen case needs this)
 280	InstallStandardEventHandler(GetApplicationEventTarget());
 281
 282	// Stash an object pointer for OSMessageBox()
 283	gWindowImplementation = this;
 284
 285	// Create the GL context and set it up for windowed or fullscreen, as appropriate.
 286	if(createContext(x, y, width, height, 32, fullscreen, disable_vsync))
 287	{
 288		if(mWindow != NULL)
 289		{
 290			// MBW -- XXX -- I think we can now do this here?
 291			// Constrain the window to the screen it's mostly on, resizing if necessary.
 292			ConstrainWindowToScreen(
 293				mWindow,
 294				kWindowStructureRgn,
 295				kWindowConstrainMayResize |
 296				//				kWindowConstrainStandardOptions |
 297				0,
 298				NULL,
 299				NULL);
 300
 301			MacShowWindow(mWindow);
 302			BringToFront(mWindow);
 303		}
 304
 305		if (!gGLManager.initGL())
 306		{
 307			setupFailure(
 308				"Second Life is unable to run because your video card drivers\n"
 309				"are out of date or unsupported. Please make sure you have\n"
 310				"the latest video card drivers installed.\n"
 311				"If you continue to receive this message, contact customer service.",
 312				"Error",
 313				OSMB_OK);
 314			return;
 315		}
 316
 317		//start with arrow cursor
 318		initCursors();
 319		setCursor( UI_CURSOR_ARROW );
 320	}
 321
 322	mCallbacks = callbacks;
 323	stop_glerror();
 324}
 325
 326BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL disable_vsync)
 327{
 328	OSStatus		err;
 329	BOOL			glNeedsInit = FALSE;
 330
 331	if(mGlobalHandlerRef == NULL)
 332	{
 333		InstallApplicationEventHandler(mEventHandlerUPP, GetEventTypeCount (CommandHandlerEventList), CommandHandlerEventList, (void*)this, &mGlobalHandlerRef);
 334	}
 335
 336	mFullscreen = fullscreen;
 337
 338	if (mFullscreen && (mOldDisplayMode == NULL))
 339	{
 340		LL_INFOS("Window") << "createContext: setting up fullscreen " << width << "x" << height << LL_ENDL;
 341
 342		// NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays.  Plan accordingly.
 343		double refresh = getDictDouble (CGDisplayCurrentMode (mDisplay),  kCGDisplayRefreshRate);
 344
 345		// If the requested width or height is 0, find the best default for the monitor.
 346		if((width == 0) || (height == 0))
 347		{
 348			// Scan through the list of modes, looking for one which has:
 349			//		height between 700 and 800
 350			//		aspect ratio closest to the user's original mode
 351			S32 resolutionCount = 0;
 352			LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount);
 353
 354			if(resolutionList != NULL)
 355			{
 356				F32 closestAspect = 0;
 357				U32 closestHeight = 0;
 358				U32 closestWidth = 0;
 359				int i;
 360
 361				LL_DEBUGS("Window") << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << LL_ENDL;
 362
 363				for(i=0; i < resolutionCount; i++)
 364				{
 365					F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight;
 366
 367					LL_DEBUGS("Window") << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << LL_ENDL;
 368
 369					if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) &&
 370						(fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio)))
 371					{
 372						LL_DEBUGS("Window") << " (new closest mode) " << LL_ENDL;
 373
 374						// This is the closest mode we've seen yet.
 375						closestWidth = resolutionList[i].mWidth;
 376						closestHeight = resolutionList[i].mHeight;
 377						closestAspect = aspect;
 378					}
 379				}
 380
 381				width = closestWidth;
 382				height = closestHeight;
 383			}
 384		}
 385
 386		if((width == 0) || (height == 0))
 387		{
 388			// Mode search failed for some reason.  Use the old-school default.
 389			width = 1024;
 390			height = 768;
 391		}
 392
 393		if (true)
 394		{
 395			// Fullscreen support
 396			CFDictionaryRef refDisplayMode = 0;
 397			boolean_t exactMatch = false;
 398
 399#if CAPTURE_ALL_DISPLAYS
 400			// Capture all displays (may want to do this for final build)
 401			CGCaptureAllDisplays ();
 402#else
 403			// Capture only the main display (useful for debugging)
 404			CGDisplayCapture (mDisplay);
 405#endif
 406
 407			// Switch the display to the desired resolution and refresh
 408			refDisplayMode = CGDisplayBestModeForParametersAndRefreshRate(
 409				mDisplay,
 410				BITS_PER_PIXEL,
 411				width,
 412				height,
 413				refresh,
 414				&exactMatch);
 415
 416			if (refDisplayMode)
 417			{
 418				LL_DEBUGS("Window") << "createContext: switching display resolution" << LL_ENDL;
 419				mOldDisplayMode = CGDisplayCurrentMode (mDisplay);
 420				CGDisplaySwitchToMode (mDisplay, refDisplayMode);
 421				//				CFRelease(refDisplayMode);
 422
 423				AddEventTypesToHandler(mGlobalHandlerRef, GetEventTypeCount (GlobalHandlerEventList), GlobalHandlerEventList);
 424			}
 425
 426
 427			mFullscreen = TRUE;
 428			mFullscreenWidth   = CGDisplayPixelsWide(mDisplay);
 429			mFullscreenHeight  = CGDisplayPixelsHigh(mDisplay);
 430			mFullscreenBits    = CGDisplayBitsPerPixel(mDisplay);
 431			mFullscreenRefresh = llround(getDictDouble (CGDisplayCurrentMode (mDisplay),  kCGDisplayRefreshRate));
 432
 433			LL_INFOS("Window") << "Running at " << mFullscreenWidth
 434				<< "x"   << mFullscreenHeight
 435				<< "x"   << mFullscreenBits
 436				<< " @ " << mFullscreenRefresh
 437				<< LL_ENDL;
 438		}
 439		else
 440		{
 441			// No fullscreen support
 442			mFullscreen = FALSE;
 443			mFullscreenWidth   = -1;
 444			mFullscreenHeight  = -1;
 445			mFullscreenBits    = -1;
 446			mFullscreenRefresh = -1;
 447
 448			std::string error= llformat("Unable to run fullscreen at %d x %d.\nRunning in window.", width, height);
 449			OSMessageBox(error, "Error", OSMB_OK);
 450		}
 451	}
 452
 453	if(!mFullscreen && (mWindow == NULL))
 454	{
 455		//int				displayWidth = CGDisplayPixelsWide(mDisplay);
 456		//int				displayHeight = CGDisplayPixelsHigh(mDisplay);
 457		//const int		menuBarPlusTitleBar = 44;   // Ugly magic number.
 458
 459		LL_DEBUGS("Window") << "createContext: creating window" << LL_ENDL;
 460
 461		mPreviousWindowRect.left = (long) x;
 462		mPreviousWindowRect.right = (long) x + width;
 463		mPreviousWindowRect.top = (long) y;
 464		mPreviousWindowRect.bottom = (long) y + height;
 465
 466		//-----------------------------------------------------------------------
 467		// Create the window
 468		//-----------------------------------------------------------------------
 469		mWindow = NewCWindow(
 470			NULL,
 471			&mPreviousWindowRect,
 472			mWindowTitle,
 473			false,				// Create the window invisible.  Whoever calls createContext() should show it after any moving/resizing.
 474			//		noGrowDocProc,		// Window with no grow box and no zoom box
 475			zoomDocProc,		// Window with a grow box and a zoom box
 476			//		zoomNoGrow,			// Window with a zoom box but no grow box
 477			kFirstWindowOfClass,
 478			true,
 479			(long)this);
 480
 481		if (!mWindow)
 482		{
 483			setupFailure("Window creation error", "Error", OSMB_OK);
 484			return FALSE;
 485		}
 486
 487		// Turn on live resize.
 488		// For this to work correctly, we need to be able to call LLViewerWindow::draw from
 489		// the event handler for kEventWindowBoundsChanged.  It's not clear that we have access from here.
 490		//	err = ChangeWindowAttributes(mWindow, kWindowLiveResizeAttribute, 0);
 491
 492		// Set up window event handlers (some window-related events ONLY go to window handlers.)
 493		InstallStandardEventHandler(GetWindowEventTarget(mWindow));
 494		InstallWindowEventHandler(mWindow, mEventHandlerUPP, GetEventTypeCount (WindowHandlerEventList), WindowHandlerEventList, (void*)this, &mWindowHandlerRef); // add event handler
 495#if LL_OS_DRAGDROP_ENABLED
 496		InstallTrackingHandler( dragTrackingHandler, mWindow, (void*)this );
 497		InstallReceiveHandler( dragReceiveHandler, mWindow, (void*)this );
 498#endif // LL_OS_DRAGDROP_ENABLED
 499	}
 500
 501	{
 502		// Create and initialize our TSM document for language text input.
 503		// If an error occured, we can do nothing better than simply ignore it.
 504		// mTSMDocument will be kept NULL in case.
 505		if (mTSMDocument)
 506		{
 507			DeactivateTSMDocument(mTSMDocument);
 508			DeleteTSMDocument(mTSMDocument);
 509			mTSMDocument = NULL;
 510		}
 511		static InterfaceTypeList types = { kUnicodeDocument };
 512		err = NewTSMDocument(1, types, &mTSMDocument, 0);
 513		if (err != noErr)
 514		{
 515			LL_WARNS("Window") << "createContext: couldn't create a TSMDocument (" << err << ")" << LL_ENDL;
 516		}
 517		if (mTSMDocument)
 518		{
 519			ActivateTSMDocument(mTSMDocument);
 520			allowLanguageTextInput(NULL, FALSE);
 521		}
 522	}
 523
 524	if(mContext == NULL)
 525	{
 526		AGLRendererInfo rendererInfo = NULL;
 527
 528		//-----------------------------------------------------------------------
 529		// Create GL drawing context
 530		//-----------------------------------------------------------------------
 531
 532		if(mPixelFormat == NULL)
 533		{
 534			if(mFullscreen)
 535			{
 536				GLint fullscreenAttrib[] =
 537				{
 538					AGL_RGBA,
 539					AGL_FULLSCREEN,
 540					AGL_NO_RECOVERY,
 541					AGL_SAMPLE_BUFFERS_ARB, mFSAASamples > 0 ? 1 : 0,
 542					AGL_SAMPLES_ARB, mFSAASamples,
 543					AGL_DOUBLEBUFFER,
 544					AGL_CLOSEST_POLICY,
 545					AGL_ACCELERATED,
 546					AGL_RED_SIZE, 8,
 547					AGL_GREEN_SIZE, 8,
 548					AGL_BLUE_SIZE, 8,
 549					AGL_ALPHA_SIZE, 8,
 550					AGL_DEPTH_SIZE, 24,
 551					AGL_STENCIL_SIZE, 8,
 552					AGL_NONE
 553				};
 554
 555				LL_DEBUGS("Window") << "createContext: creating fullscreen pixelformat" << LL_ENDL;
 556
 557				GDHandle gdhDisplay = NULL;
 558				err = DMGetGDeviceByDisplayID ((DisplayIDType)mDisplay, &gdhDisplay, false);
 559
 560				mPixelFormat = aglChoosePixelFormat(&gdhDisplay, 1, fullscreenAttrib);
 561				rendererInfo = aglQueryRendererInfo(&gdhDisplay, 1);
 562			}
 563			else
 564			{
 565				// NOTE from Leslie:
 566				//
 567				// AGL_NO_RECOVERY, when combined with AGL_ACCELERATED prevents software rendering
 568				// fallback which means we won't hvae shaders that compile and link but then don't
 569				// work.  The drawback is that our shader compilation will be a bit more finicky though.
 570
 571				GLint windowedAttrib[] =
 572				{
 573					AGL_RGBA,
 574					AGL_NO_RECOVERY,
 575					AGL_DOUBLEBUFFER,
 576					AGL_CLOSEST_POLICY,
 577					AGL_ACCELERATED,
 578					AGL_SAMPLE_BUFFERS_ARB, mFSAASamples > 0 ? 1 : 0,
 579					AGL_SAMPLES_ARB, mFSAASamples,
 580					AGL_RED_SIZE, 8,
 581					AGL_GREEN_SIZE, 8,
 582					AGL_BLUE_SIZE, 8,
 583					AGL_ALPHA_SIZE, 8,
 584					AGL_DEPTH_SIZE, 24,
 585					AGL_STENCIL_SIZE, 8,
 586					AGL_NONE
 587				};
 588
 589				LL_DEBUGS("Window") << "createContext: creating windowed pixelformat" << LL_ENDL;
 590
 591				mPixelFormat = aglChoosePixelFormat(NULL, 0, windowedAttrib);
 592
 593				GDHandle gdhDisplay = GetMainDevice();
 594				rendererInfo = aglQueryRendererInfo(&gdhDisplay, 1);
 595			}
 596
 597			// May want to get the real error text like this:
 598			// (char *) aglErrorString(aglGetError());
 599
 600			if(aglGetError() != AGL_NO_ERROR)
 601			{
 602				setupFailure("Can't find suitable pixel format", "Error", OSMB_OK);
 603				return FALSE;
 604			}
 605		}
 606
 607		if(mPixelFormat)
 608		{
 609			LL_DEBUGS("Window") << "createContext: creating GL context" << LL_ENDL;
 610			mContext = aglCreateContext(mPixelFormat, NULL);
 611		}
 612
 613		if(mContext == NULL)
 614		{
 615			setupFailure("Can't make GL context", "Error", OSMB_OK);
 616			return FALSE;
 617		}
 618
 619		gGLManager.mVRAM = 0;
 620
 621		if(rendererInfo != NULL)
 622		{
 623			GLint result;
 624
 625			if(aglDescribeRenderer(rendererInfo, AGL_VIDEO_MEMORY, &result))
 626			{
 627				//				llinfos << "createContext: aglDescribeRenderer(AGL_VIDEO_MEMORY) returned " << result << llendl;
 628				gGLManager.mVRAM = result / (1024 * 1024);
 629			}
 630			else
 631			{
 632				//				llinfos << "createContext: aglDescribeRenderer(AGL_VIDEO_MEMORY) failed." << llendl;
 633			}
 634
 635			// This could be useful at some point, if it takes into account the memory already used by screen buffers, etc...
 636			if(aglDescribeRenderer(rendererInfo, AGL_TEXTURE_MEMORY, &result))
 637			{
 638				//				llinfos << "createContext: aglDescribeRenderer(AGL_TEXTURE_MEMORY) returned " << result << llendl;
 639			}
 640			else
 641			{
 642				//				llinfos << "createContext: aglDescribeRenderer(AGL_TEXTURE_MEMORY) failed." << llendl;
 643			}
 644
 645			aglDestroyRendererInfo(rendererInfo);
 646		}
 647
 648		// Since we just created the context, it needs to be set up.
 649		glNeedsInit = TRUE;
 650	}
 651
 652	// Hook up the context to a drawable
 653	if (mFullscreen && (mOldDisplayMode != NULL))
 654	{
 655		// We successfully captured the display.  Use a fullscreen drawable
 656
 657		LL_DEBUGS("Window") << "createContext: attaching fullscreen drawable" << LL_ENDL;
 658
 659#if CAPTURE_ALL_DISPLAYS
 660		// Capture all displays (may want to do this for final build)
 661		aglDisable (mContext, AGL_FS_CAPTURE_SINGLE);
 662#else
 663		// Capture only the main display (useful for debugging)
 664		aglEnable (mContext, AGL_FS_CAPTURE_SINGLE);
 665#endif
 666
 667		if (!aglSetFullScreen (mContext, 0, 0, 0, 0))
 668		{
 669			setupFailure("Can't set GL fullscreen", "Error", OSMB_OK);
 670			return FALSE;
 671		}
 672	}
 673	else if(!mFullscreen && (mWindow != NULL))
 674	{
 675		LL_DEBUGS("Window") << "createContext: attaching windowed drawable" << LL_ENDL;
 676
 677		// We created a window.  Use it as the drawable.
 678		if(!aglSetDrawable(mContext, GetWindowPort (mWindow)))
 679		{
 680			setupFailure("Can't set GL drawable", "Error", OSMB_OK);
 681			return FALSE;
 682		}
 683	}
 684	else
 685	{
 686		setupFailure("Can't get fullscreen or windowed drawable.", "Error", OSMB_OK);
 687		return FALSE;
 688	}
 689
 690	if(mContext != NULL)
 691	{
 692		LL_DEBUGS("Window") << "createContext: setting current context" << LL_ENDL;
 693
 694		if (!aglSetCurrentContext(mContext))
 695		{
 696			setupFailure("Can't activate GL rendering context", "Error", OSMB_OK);
 697			return FALSE;
 698		}
 699	}
 700
 701	if(glNeedsInit)
 702	{
 703		// Check for some explicitly unsupported cards.
 704		const char* RENDERER = (const char*) glGetString(GL_RENDERER);
 705
 706		const char* CARD_LIST[] =
 707		{	"RAGE 128",
 708		"RIVA TNT2",
 709		"Intel 810",
 710		"3Dfx/Voodoo3",
 711		"Radeon 7000",
 712		"Radeon 7200",
 713		"Radeon 7500",
 714		"Radeon DDR",
 715		"Radeon VE",
 716		"GDI Generic" };
 717		const S32 CARD_COUNT = LL_ARRAY_SIZE(CARD_LIST);
 718
 719		// Future candidates:
 720		// ProSavage/Twister
 721		// SuperSavage
 722
 723		S32 i;
 724		for (i = 0; i < CARD_COUNT; i++)
 725		{
 726			if (check_for_card(RENDERER, CARD_LIST[i]))
 727			{
 728				close();
 729				return FALSE;
 730			}
 731		}
 732	}
 733
 734	GLint colorBits, alphaBits, depthBits, stencilBits;
 735
 736	if(	!aglDescribePixelFormat(mPixelFormat, AGL_BUFFER_SIZE, &colorBits) ||
 737		!aglDescribePixelFormat(mPixelFormat, AGL_ALPHA_SIZE, &alphaBits) ||
 738		!aglDescribePixelFormat(mPixelFormat, AGL_DEPTH_SIZE, &depthBits) ||
 739		!aglDescribePixelFormat(mPixelFormat, AGL_STENCIL_SIZE, &stencilBits))
 740	{
 741		close();
 742		setupFailure("Can't get pixel format description", "Error", OSMB_OK);
 743		return FALSE;
 744	}
 745
 746	LL_INFOS("GLInit") << "GL buffer: Color Bits " << S32(colorBits)
 747		<< " Alpha Bits " << S32(alphaBits)
 748		<< " Depth Bits " << S32(depthBits)
 749		<< " Stencil Bits" << S32(stencilBits)
 750		<< LL_ENDL;
 751
 752	if (colorBits < 32)
 753	{
 754		close();
 755		setupFailure(
 756			"Second Life requires True Color (32-bit) to run in a window.\n"
 757			"Please go to Control Panels -> Display -> Settings and\n"
 758			"set the screen to 32-bit color.\n"
 759			"Alternately, if you choose to run fullscreen, Second Life\n"
 760			"will automatically adjust the screen each time it runs.",
 761			"Error",
 762			OSMB_OK);
 763		return FALSE;
 764	}
 765
 766	if (alphaBits < 8)
 767	{
 768		close();
 769		setupFailure(
 770			"Second Life is unable to run because it can't get an 8 bit alpha\n"
 771			"channel.  Usually this is due to video card driver issues.\n"
 772			"Please make sure you have the latest video card drivers installed.\n"
 773			"Also be sure your monitor is set to True Color (32-bit) in\n"
 774			"Control Panels -> Display -> Settings.\n"
 775			"If you continue to receive this message, contact customer service.",
 776			"Error",
 777			OSMB_OK);
 778		return FALSE;
 779	}
 780
 781	// Disable vertical sync for swap
 782	GLint frames_per_swap = 0;
 783	if (disable_vsync)
 784	{
 785		LL_DEBUGS("GLInit") << "Disabling vertical sync" << LL_ENDL;
 786		frames_per_swap = 0;
 787	}
 788	else
 789	{
 790		LL_DEBUGS("GLinit") << "Keeping vertical sync" << LL_ENDL;
 791		frames_per_swap = 1;
 792	}
 793	aglSetInteger(mContext, AGL_SWAP_INTERVAL, &frames_per_swap);
 794
 795	//enable multi-threaded OpenGL
 796	if (sUseMultGL)
 797	{
 798		CGLError cgl_err;
 799		CGLContextObj ctx = CGLGetCurrentContext();
 800
 801		cgl_err =  CGLEnable( ctx, kCGLCEMPEngine);
 802
 803		if (cgl_err != kCGLNoError )
 804		{
 805			LL_DEBUGS("GLInit") << "Multi-threaded OpenGL not available." << LL_ENDL;
 806		}
 807		else
 808		{
 809			LL_DEBUGS("GLInit") << "Multi-threaded OpenGL enabled." << LL_ENDL;
 810		}
 811	}
 812
 813	// Don't need to get the current gamma, since there's a call that restores it to the system defaults.
 814	return TRUE;
 815}
 816
 817
 818// changing fullscreen resolution, or switching between windowed and fullscreen mode.
 819BOOL LLWindowMacOSX::switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp)
 820{
 821	BOOL needsRebuild = FALSE;
 822	BOOL result = true;
 823
 824	if(fullscreen)
 825	{
 826		if(mFullscreen)
 827		{
 828			// Switching resolutions in fullscreen mode.  Don't need to rebuild for this.
 829			// Fullscreen support
 830			CFDictionaryRef refDisplayMode = 0;
 831			boolean_t exactMatch = false;
 832
 833			// Switch the display to the desired resolution and refresh
 834			refDisplayMode = CGDisplayBestModeForParametersAndRefreshRate(
 835				mDisplay,
 836				BITS_PER_PIXEL,
 837				size.mX,
 838				size.mY,
 839				getDictDouble (CGDisplayCurrentMode (mDisplay),  kCGDisplayRefreshRate),
 840				&exactMatch);
 841
 842			if (refDisplayMode)
 843			{
 844				CGDisplaySwitchToMode (mDisplay, refDisplayMode);
 845				//				CFRelease(refDisplayMode);
 846			}
 847
 848			mFullscreenWidth   = CGDisplayPixelsWide(mDisplay);
 849			mFullscreenHeight  = CGDisplayPixelsHigh(mDisplay);
 850			mFullscreenBits    = CGDisplayBitsPerPixel(mDisplay);
 851			mFullscreenRefresh = llround(getDictDouble (CGDisplayCurrentMode (mDisplay),  kCGDisplayRefreshRate));
 852
 853			LL_INFOS("Window") << "Switched resolution to " << mFullscreenWidth
 854				<< "x"   << mFullscreenHeight
 855				<< "x"   << mFullscreenBits
 856				<< " @ " << mFullscreenRefresh
 857				<< LL_ENDL;
 858
 859			// Update the GL context to the new screen size
 860			if (!aglUpdateContext(mContext))
 861			{
 862				setupFailure("Can't set GL fullscreen", "Error", OSMB_OK);
 863				result = FALSE;
 864			}
 865		}
 866		else
 867		{
 868			// Switching from windowed to fullscreen
 869			needsRebuild = TRUE;
 870		}
 871	}
 872	else
 873	{
 874		if(mFullscreen)
 875		{
 876			// Switching from fullscreen to windowed
 877			needsRebuild = TRUE;
 878		}
 879		else
 880		{
 881			// Windowed to windowed -- not sure why we would be called like this.  Just change the window size.
 882			// The bounds changed event handler will do the rest.
 883			if(mWindow != NULL)
 884			{
 885				::SizeWindow(mWindow, size.mX, size.mY, true);
 886			}
 887		}
 888	}
 889
 890	stop_glerror();
 891	if(needsRebuild || mForceRebuild)
 892	{
 893		mForceRebuild = FALSE;
 894		destroyContext();
 895		result = createContext(0, 0, size.mX, size.mY, 0, fullscreen, disable_vsync);
 896		if (result)
 897		{
 898			if(mWindow != NULL)
 899			{
 900				MacShowWindow(mWindow);
 901				BringToFront(mWindow);
 902			}
 903
 904			llverify(gGLManager.initGL());
 905
 906			//start with arrow cursor
 907			initCursors();
 908			setCursor( UI_CURSOR_ARROW );
 909		}
 910	}
 911
 912	stop_glerror();
 913
 914	return result;
 915}
 916
 917void LLWindowMacOSX::destroyContext()
 918{
 919	if (!mContext)
 920	{
 921		// We don't have a context
 922		return;
 923	}
 924	// Unhook the GL context from any drawable it may have
 925	if(mContext != NULL)
 926	{
 927		LL_DEBUGS("Window") << "destroyContext: unhooking drawable " << LL_ENDL;
 928
 929		aglSetCurrentContext (NULL);
 930		aglSetDrawable(mContext, NULL);
 931	}
 932
 933	// Make sure the display resolution gets restored
 934	if(mOldDisplayMode != NULL)
 935	{
 936		LL_DEBUGS("Window") << "destroyContext: restoring display resolution " << LL_ENDL;
 937
 938		CGDisplaySwitchToMode (mDisplay, mOldDisplayMode);
 939
 940#if CAPTURE_ALL_DISPLAYS
 941		// Uncapture all displays (may want to do this for final build)
 942		CGReleaseAllDisplays ();
 943#else
 944		// Uncapture only the main display (useful for debugging)
 945		CGDisplayRelease (mDisplay);
 946#endif
 947
 948		//		CFRelease(mOldDisplayMode);
 949
 950		mOldDisplayMode = NULL;
 951
 952		// Remove the global event handlers the fullscreen case needed
 953		RemoveEventTypesFromHandler(mGlobalHandlerRef, GetEventTypeCount (GlobalHandlerEventList), GlobalHandlerEventList);
 954	}
 955
 956	// Clean up remaining GL state before blowing away window
 957	gGLManager.shutdownGL();
 958
 959	// Clean up the pixel format
 960	if(mPixelFormat != NULL)
 961	{
 962		LL_DEBUGS("Window") << "destroyContext: destroying pixel format " << LL_ENDL;
 963		aglDestroyPixelFormat(mPixelFormat);
 964		mPixelFormat = NULL;
 965	}
 966
 967	// Remove any Carbon Event handlers we installed
 968	if(mGlobalHandlerRef != NULL)
 969	{
 970		LL_DEBUGS("Window") << "destroyContext: removing global event handler" << LL_ENDL;
 971		RemoveEventHandler(mGlobalHandlerRef);
 972		mGlobalHandlerRef = NULL;
 973	}
 974
 975	if(mWindowHandlerRef != NULL)
 976	{
 977		LL_DEBUGS("Window") << "destroyContext: removing window event handler" << LL_ENDL;
 978		RemoveEventHandler(mWindowHandlerRef);
 979		mWindowHandlerRef = NULL;
 980	}
 981
 982	// Cleanup any TSM document we created.
 983	if(mTSMDocument != NULL)
 984	{
 985		LL_DEBUGS("Window") << "destroyContext: deleting TSM document" << LL_ENDL;
 986		DeactivateTSMDocument(mTSMDocument);
 987		DeleteTSMDocument(mTSMDocument);
 988		mTSMDocument = NULL;
 989	}
 990
 991	// Close the window
 992	if(mWindow != NULL)
 993	{
 994		LL_DEBUGS("Window") << "destroyContext: disposing window" << LL_ENDL;
 995		DisposeWindow(mWindow);
 996		mWindow = NULL;
 997	}
 998
 999	// Clean up the GL context
1000	if(mContext != NULL)
1001	{
1002		LL_DEBUGS("Window") << "destroyContext: destroying GL context" << LL_ENDL;
1003		aglDestroyContext(mContext);
1004		mContext = NULL;
1005	}
1006
1007}
1008
1009LLWindowMacOSX::~LLWindowMacOSX()
1010{
1011	destroyContext();
1012
1013	if(mSupportedResolutions != NULL)
1014	{
1015		delete []mSupportedResolutions;
1016	}
1017
1018	gWindowImplementation = NULL;
1019
1020}
1021
1022
1023void LLWindowMacOSX::show()
1024{
1025	if(IsWindowCollapsed(mWindow))
1026		CollapseWindow(mWindow, false);
1027
1028	MacShowWindow(mWindow);
1029	BringToFront(mWindow);
1030}
1031
1032void LLWindowMacOSX::hide()
1033{
1034	setMouseClipping(FALSE);
1035	HideWindow(mWindow);
1036}
1037
1038//virtual
1039void LLWindowMacOSX::minimize()
1040{
1041	setMouseClipping(FALSE);
1042	showCursor();
1043	CollapseWindow(mWindow, true);
1044}
1045
1046//virtual
1047void LLWindowMacOSX::restore()
1048{
1049	show();
1050}
1051
1052
1053// close() destroys all OS-specific code associated with a window.
1054// Usually called from LLWindowManager::destroyWindow()
1055void LLWindowMacOSX::close()
1056{
1057	// Is window is already closed?
1058	//	if (!mWindow)
1059	//	{
1060	//		return;
1061	//	}
1062
1063	// Make sure cursor is visible and we haven't mangled the clipping state.
1064	setMouseClipping(FALSE);
1065	showCursor();
1066
1067	destroyContext();
1068}
1069
1070BOOL LLWindowMacOSX::isValid()
1071{
1072	if(mFullscreen)
1073	{
1074		return(TRUE);
1075	}
1076
1077	return (mWindow != NULL);
1078}
1079
1080BOOL LLWindowMacOSX::getVisible()
1081{
1082	BOOL result = FALSE;
1083
1084	if(mFullscreen)
1085	{
1086		result = TRUE;
1087	}if (mWindow)
1088	{
1089		if(MacIsWindowVisible(mWindow))
1090			result = TRUE;
1091	}
1092
1093	return(result);
1094}
1095
1096BOOL LLWindowMacOSX::getMinimized()
1097{
1098	return mMinimized;
1099}
1100
1101BOOL LLWindowMacOSX::getMaximized()
1102{
1103	return mMaximized;
1104}
1105
1106BOOL LLWindowMacOSX::maximize()
1107{
1108	if (mWindow && !mMaximized)
1109	{
1110		ZoomWindow(mWindow, inContent, true);
1111	}
1112
1113	return mMaximized;
1114}
1115
1116BOOL LLWindowMacOSX::getFullscreen()
1117{
1118	return mFullscreen;
1119}
1120
1121void LLWindowMacOSX::gatherInput()
1122{
1123	// stop bouncing icon after fixed period of time
1124	if (mBounceTimer.getStarted() && mBounceTimer.getElapsedTimeF32() > mBounceTime)
1125	{
1126		stopDockTileBounce();
1127	}
1128
1129	// Use the old-school version so we get AppleEvent handler dispatch and menuselect handling.
1130	// Anything that has an event handler will get processed inside WaitNextEvent, so we only need to handle
1131	// the odd stuff here.
1132	EventRecord evt;
1133	while(WaitNextEvent(everyEvent, &evt, 0, NULL))
1134	{
1135		//			printf("WaitNextEvent returned true, event is %d.\n", evt.what);
1136		switch(evt.what)
1137		{
1138		case mouseDown:
1139			{
1140				short part;
1141				WindowRef window;
1142				long selectResult;
1143				part = FindWindow(evt.where, &window);
1144				switch ( part )
1145				{
1146				case inMenuBar:
1147					selectResult = MenuSelect(evt.where);
1148
1149					HiliteMenu(0);
1150					break;
1151				}
1152			}
1153			break;
1154
1155		case kHighLevelEvent:
1156			AEProcessAppleEvent (&evt);
1157			break;
1158
1159		case updateEvt:
1160			// We shouldn't be getting these regularly (since our window will be buffered), but we need to handle them correctly...
1161			BeginUpdate((WindowRef)evt.message);
1162			EndUpdate((WindowRef)evt.message);
1163			break;
1164
1165		}
1166	}
1167	
1168	updateCursor();
1169}
1170
1171BOOL LLWindowMacOSX::getPosition(LLCoordScreen *position)
1172{
1173	Rect window_rect;
1174	OSStatus err = -1;
1175
1176	if(mFullscreen)
1177	{
1178		position->mX = 0;
1179		position->mY = 0;
1180		err = noErr;
1181	}
1182	else if(mWindow)
1183	{
1184		err = GetWindowBounds(mWindow, kWindowContentRgn, &window_rect);
1185
1186		position->mX = window_rect.left;
1187		position->mY = window_rect.top;
1188	}
1189	else
1190	{
1191		llerrs << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << llendl;
1192	}
1193
1194	return (err == noErr);
1195}
1196
1197BOOL LLWindowMacOSX::getSize(LLCoordScreen *size)
1198{
1199	Rect window_rect;
1200	OSStatus err = -1;
1201
1202	if(mFullscreen)
1203	{
1204		size->mX = mFullscreenWidth;
1205		size->mY = mFullscreenHeight;
1206		err = noErr;
1207	}
1208	else if(mWindow)
1209	{
1210		err = GetWindowBounds(mWindow, kWindowContentRgn, &window_rect);
1211
1212		size->mX = window_rect.right - window_rect.left;
1213		size->mY = window_rect.bottom - window_rect.top;
1214	}
1215	else
1216	{
1217		llerrs << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << llendl;
1218	}
1219
1220	return (err == noErr);
1221}
1222
1223BOOL LLWindowMacOSX::getSize(LLCoordWindow *size)
1224{
1225	Rect window_rect;
1226	OSStatus err = -1;
1227
1228	if(mFullscreen)
1229	{
1230		size->mX = mFullscreenWidth;
1231		size->mY = mFullscreenHeight;
1232		err = noErr;
1233	}
1234	else if(mWindow)
1235	{
1236		err = GetWindowBounds(mWindow, kWindowContentRgn, &window_rect);
1237
1238		size->mX = window_rect.right - window_rect.left;
1239		size->mY = window_rect.bottom - window_rect.top;
1240	}
1241	else
1242	{
1243		llerrs << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << llendl;
1244	}
1245
1246	return (err == noErr);
1247}
1248
1249BOOL LLWindowMacOSX::setPosition(const LLCoordScreen position)
1250{
1251	if(mWindow)
1252	{
1253		MacMoveWindow(mWindow, position.mX, position.mY, false);
1254	}
1255
1256	return TRUE;
1257}
1258
1259BOOL LLWindowMacOSX::setSizeImpl(const LLCoordScreen size)
1260{
1261	if(mWindow)
1262	{
1263		SizeWindow(mWindow, size.mX, size.mY, true);
1264	}
1265
1266	return TRUE;
1267}
1268
1269void LLWindowMacOSX::swapBuffers()
1270{
1271	aglSwapBuffers(mContext);
1272}
1273
1274F32 LLWindowMacOSX::getGamma()
1275{
1276	F32 result = 1.8;	// Default to something sane
1277
1278	CGGammaValue redMin;
1279	CGGammaValue redMax;
1280	CGGammaValue redGamma;
1281	CGGammaValue greenMin;
1282	CGGammaValue greenMax;
1283	CGGammaValue greenGamma;
1284	CGGammaValue blueMin;
1285	CGGammaValue blueMax;
1286	CGGammaValue blueGamma;
1287
1288	if(CGGetDisplayTransferByFormula(
1289		mDisplay,
1290		&redMin,
1291		&redMax,
1292		&redGamma,
1293		&greenMin,
1294		&greenMax,
1295		&greenGamma,
1296		&blueMin,
1297		&blueMax,
1298		&blueGamma) == noErr)
1299	{
1300		// So many choices...
1301		// Let's just return the green channel gamma for now.
1302		result = greenGamma;
1303	}
1304
1305	return result;
1306}
1307
1308U32 LLWindowMacOSX::getFSAASamples()
1309{
1310	return mFSAASamples;
1311}
1312
1313void LLWindowMacOSX::setFSAASamples(const U32 samples)
1314{
1315	mFSAASamples = samples;
1316	mForceRebuild = TRUE;
1317}
1318
1319BOOL LLWindowMacOSX::restoreGamma()
1320{
1321	CGDisplayRestoreColorSyncSettings();
1322	return true;
1323}
1324
1325BOOL LLWindowMacOSX::setGamma(const F32 gamma)
1326{
1327	CGGammaValue redMin;
1328	CGGammaValue redMax;
1329	CGGammaValue redGamma;
1330	CGGammaValue greenMin;
1331	CGGammaValue greenMax;
1332	CGGammaValue greenGamma;
1333	CGGammaValue blueMin;
1334	CGGammaValue blueMax;
1335	CGGammaValue blueGamma;
1336
1337	// MBW -- XXX -- Should we allow this in windowed mode?
1338
1339	if(CGGetDisplayTransferByFormula(
1340		mDisplay,
1341		&redMin,
1342		&redMax,
1343		&redGamma,
1344		&greenMin,
1345		&greenMax,
1346		&greenGamma,
1347		&blueMin,
1348		&blueMax,
1349		&blueGamma) != noErr)
1350	{
1351		return false;
1352	}
1353
1354	if(CGSetDisplayTransferByFormula(
1355		mDisplay,
1356		redMin,
1357		redMax,
1358		gamma,
1359		greenMin,
1360		greenMax,
1361		gamma,
1362		blueMin,
1363		blueMax,
1364		gamma) != noErr)
1365	{
1366		return false;
1367	}
1368
1369
1370	return true;
1371}
1372
1373BOOL LLWindowMacOSX::isCursorHidden()
1374{
1375	return mCursorHidden;
1376}
1377
1378
1379
1380// Constrains the mouse to the window.
1381void LLWindowMacOSX::setMouseClipping( BOOL b )
1382{
1383	// Just stash the requested state.  We'll simulate this when the cursor is hidden by decoupling.
1384	mIsMouseClipping = b;
1385
1386	if(b)
1387	{
1388		//		llinfos << "setMouseClipping(TRUE)" << llendl;
1389	}
1390	else
1391	{
1392		//		llinfos << "setMouseClipping(FALSE)" << llendl;
1393	}
1394
1395	adjustCursorDecouple();
1396}
1397
1398BOOL LLWindowMacOSX::setCursorPosition(const LLCoordWindow position)
1399{
1400	BOOL result = FALSE;
1401	LLCoordScreen screen_pos;
1402
1403	if (!convertCoords(position, &screen_pos))
1404	{
1405		return FALSE;
1406	}
1407
1408	CGPoint newPosition;
1409
1410	//	llinfos << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << llendl;
1411
1412	newPosition.x = screen_pos.mX;
1413	newPosition.y = screen_pos.mY;
1414
1415	CGSetLocalEventsSuppressionInterval(0.0);
1416	if(CGWarpMouseCursorPosition(newPosition) == noErr)
1417	{
1418		result = TRUE;
1419	}
1420
1421	// Under certain circumstances, this will trigger us to decouple the cursor.
1422	adjustCursorDecouple(true);
1423
1424	// trigger mouse move callback
1425	LLCoordGL gl_pos;
1426	convertCoords(position, &gl_pos);
1427	mCallbacks->handleMouseMove(this, gl_pos, (MASK)0);
1428
1429	return result;
1430}
1431
1432static void fixOrigin(void)
1433{
1434	GrafPtr port;
1435	Rect portrect;
1436
1437	::GetPort(&port);
1438	::GetPortBounds(port, &portrect);
1439	if((portrect.left != 0) || (portrect.top != 0))
1440	{
1441		// Mozilla sometimes changes our port origin.
1442		::SetOrigin(0,0);
1443	}
1444}
1445
1446BOOL LLWindowMacOSX::getCursorPosition(LLCoordWindow *position)
1447{
1448	Point cursor_point;
1449	LLCoordScreen screen_pos;
1450	GrafPtr save;
1451
1452	if(mWindow == NULL)
1453		return FALSE;
1454
1455	::GetPort(&save);
1456	::SetPort(GetWindowPort(mWindow));
1457	fixOrigin();
1458
1459	// gets the mouse location in local coordinates
1460	::GetMouse(&cursor_point);
1461
1462//	lldebugs << "getCursorPosition(): cursor is at " << cursor_point.h << ", " << cursor_point.v << "  port origin: " << portrect.left << ", " << portrect.top << llendl;
1463
1464	::SetPort(save);
1465
1466	if(mCursorDecoupled)
1467	{
1468		//		CGMouseDelta x, y;
1469
1470		// If the cursor's decoupled, we need to read the latest movement delta as well.
1471		//		CGGetLastMouseDelta( &x, &y );
1472		//		cursor_point.h += x;
1473		//		cursor_point.v += y;
1474
1475		// CGGetLastMouseDelta may behave strangely when the cursor's first captured.
1476		// Stash in the event handler instead.
1477		cursor_point.h += mCursorLastEventDeltaX;
1478		cursor_point.v += mCursorLastEventDeltaY;
1479	}
1480
1481	position->mX = cursor_point.h;
1482	position->mY = cursor_point.v;
1483
1484	return TRUE;
1485}
1486
1487void LLWindowMacOSX::adjustCursorDecouple(bool warpingMouse)
1488{
1489	if(mIsMouseClipping && mCursorHidden)
1490	{
1491		if(warpingMouse)
1492		{
1493			// The cursor should be decoupled.  Make sure it is.
1494			if(!mCursorDecoupled)
1495			{
1496				//			llinfos << "adjustCursorDecouple: decoupling cursor" << llendl;
1497				CGAssociateMouseAndMouseCursorPosition(false);
1498				mCursorDecoupled = true;
1499				FlushSpecificEventsFromQueue(GetCurrentEventQueue(), mMoveEventCampartorUPP, NULL);
1500				mCursorIgnoreNextDelta = TRUE;
1501			}
1502		}
1503	}
1504	else
1505	{
1506		// The cursor should not be decoupled.  Make sure it isn't.
1507		if(mCursorDecoupled)
1508		{
1509			//			llinfos << "adjustCursorDecouple: recoupling cursor" << llendl;
1510			CGAssociateMouseAndMouseCursorPosition(true);
1511			mCursorDecoupled = false;
1512		}
1513	}
1514}
1515
1516F32 LLWindowMacOSX::getNativeAspectRatio()
1517{
1518	if (mFullscreen)
1519	{
1520		return (F32)mFullscreenWidth / (F32)mFullscreenHeight;
1521	}
1522	else
1523	{
1524		// The constructor for this class grabs the aspect ratio of the monitor before doing any resolution
1525		// switching, and stashes it in mOriginalAspectRatio.  Here, we just return it.
1526
1527		if (mOverrideAspectRatio > 0.f)
1528		{
1529			return mOverrideAspectRatio;
1530		}
1531
1532		return mOriginalAspectRatio;
1533	}
1534}
1535
1536F32 LLWindowMacOSX::getPixelAspectRatio()
1537{
1538	//OS X always enforces a 1:1 pixel aspect ratio, regardless of video mode
1539	return 1.f;
1540}
1541
1542//static SInt32 oldWindowLevel;
1543
1544// MBW -- XXX -- There's got to be a better way than this.  Find it, please...
1545
1546void LLWindowMacOSX::beforeDialog()
1547{
1548	if(mFullscreen)
1549	{
1550
1551#if CAPTURE_ALL_DISPLAYS
1552		// Uncapture all displays (may want to do this for final build)
1553		CGReleaseAllDisplays ();
1554#else
1555		// Uncapture only the main display (useful for debugging)
1556		CGDisplayRelease (mDisplay);
1557#endif
1558		// kDocumentWindowClass
1559		// kMovableModalWindowClass
1560		// kAllWindowClasses
1561
1562		//		GLint order = 0;
1563		//		aglSetInteger(mContext, AGL_ORDER_CONTEXT_TO_FRONT, &order);
1564		aglSetDrawable(mContext, NULL);
1565		//		GetWindowGroupLevel(GetWindowGroupOfClass(kAllWindowClasses), &oldWindowLevel);
1566		//		SetWindowGroupLevel(GetWindowGroupOfClass(kAllWindowClasses), CGShieldingWindowLevel());
1567
1568		mHandsOffEvents = TRUE;
1569
1570	}
1571}
1572
1573void LLWindowMacOSX::afterDialog()
1574{
1575	if(mFullscreen)
1576	{
1577		mHandsOffEvents = FALSE;
1578
1579		//		SetWindowGroupLevel(GetWindowGroupOfClass(kAllWindowClasses), oldWindowLevel);
1580		aglSetFullScreen(mContext, 0, 0, 0, 0);
1581		//		GLint order = 1;
1582		//		aglSetInteger(mContext, AGL_ORDER_CONTEXT_TO_FRONT, &order);
1583
1584#if CAPTURE_ALL_DISPLAYS
1585		// Capture all displays (may want to do this for final build)
1586		CGCaptureAllDisplays ();
1587#else
1588		// Capture only the main display (useful for debugging)
1589		CGDisplayCapture (mDisplay);
1590#endif
1591	}
1592}
1593
1594
1595void LLWindowMacOSX::flashIcon(F32 seconds)
1596{
1597	// Don't do this if we're already started, since this would try to install the NMRec twice.
1598	if(!mBounceTimer.getStarted())
1599	{
1600		OSErr err;
1601
1602		mBounceTime = seconds;
1603		memset(&mBounceRec, 0, sizeof(mBounceRec));
1604		mBounceRec.qType = nmType;
1605		mBounceRec.nmMark = 1;
1606		err = NMInstall(&mBounceRec);
1607		if(err == noErr)
1608		{
1609			mBounceTimer.start();
1610		}
1611		else
1612		{
1613			// This is very not-fatal (only problem is the icon will not bounce), but we'd like to find out about it somehow...
1614			llinfos << "NMInstall failed with error code " << err << llendl;
1615		}
1616	}
1617}
1618
1619BOOL LLWindowMacOSX::isClipboardTextAvailable()
1620{
1621	OSStatus err;
1622	ScrapRef scrap;
1623	ScrapFlavorFlags flags;
1624	BOOL result = false;
1625
1626	err = GetCurrentScrap(&scrap);
1627
1628	if(err == noErr)
1629	{
1630		err = GetScrapFlavorFlags(scrap, kScrapFlavorTypeUnicode, &flags);
1631	}
1632
1633	if(err == noErr)
1634		result = true;
1635
1636	return result;
1637}
1638
1639BOOL LLWindowMacOSX::pasteTextFromClipboard(LLWString &dst)
1640{
1641	OSStatus err;
1642	ScrapRef scrap;
1643	Size len;
1644	BOOL result = false;
1645
1646	err = GetCurrentScrap(&scrap);
1647
1648	if(err == noErr)
1649	{
1650		err = GetScrapFlavorSize(scrap, kScrapFlavorTypeUnicode, &len);
1651	}
1652
1653	if((err == noErr) && (len > 0))
1654	{
1655		int u16len = len / sizeof(U16);
1656		U16 *temp = new U16[u16len + 1];
1657		if (temp)
1658		{
1659			memset(temp, 0, (u16len + 1) * sizeof(temp[0]));
1660			err = GetScrapFlavorData(scrap, kScrapFlavorTypeUnicode, &len, temp);
1661			if (err == noErr)
1662			{
1663				// convert \r\n to \n and \r to \n in the incoming text.
1664				U16 *s, *d;
1665				for(s = d = temp; s[0] != '\0'; s++, d++)
1666				{
1667					if(s[0] == '\r')
1668					{
1669						if(s[1] == '\n')
1670						{
1671							// CRLF, a.k.a. DOS newline.  Collapse to a single '\n'.
1672							s++;
1673						}
1674
1675						d[0] = '\n';
1676					}
1677					else
1678					{
1679						d[0] = s[0];
1680					}
1681				}
1682
1683				d[0] = '\0';
1684
1685				dst = utf16str_to_wstring(temp);
1686
1687				result = true;
1688			}
1689			delete[] temp;
1690		}
1691	}
1692
1693	return result;
1694}
1695
1696BOOL LLWindowMacOSX::copyTextToClipboard(const LLWString &s)
1697{
1698	OSStatus err;
1699	ScrapRef scrap;
1700	//Size len;
1701	//char *temp;
1702	BOOL result = false;
1703
1704	if (!s.empty())
1705	{
1706		err = GetCurrentScrap(&scrap);
1707		if (err == noErr)
1708			err = ClearScrap(&scrap);
1709
1710		if (err == noErr)
1711		{
1712			llutf16string utf16str = wstring_to_utf16str(s);
1713			size_t u16len = utf16str.length() * sizeof(U16);
1714			err = PutScrapFlavor(scrap, kScrapFlavorTypeUnicode, kScrapFlavorMaskNone, u16len, utf16str.data());
1715			if (err == noErr)
1716				result = true;
1717		}
1718	}
1719
1720	return result;
1721}
1722
1723
1724// protected
1725BOOL LLWindowMacOSX::resetDisplayResolution()
1726{
1727	// This is only called from elsewhere in this class, and it's not used by the Mac implementation.
1728	return true;
1729}
1730
1731
1732LLWindow::LLWindowResolution* LLWindowMacOSX::getSupportedResolutions(S32 &num_resolutions)
1733{
1734	if (!mSupportedResolutions)
1735	{
1736		CFArrayRef modes = CGDisplayAvailableModes(mDisplay);
1737
1738		if(modes != NULL)
1739		{
1740			CFIndex index, cnt;
1741
1742			mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
1743			mNumSupportedResolutions = 0;
1744
1745			//  Examine each mode
1746			cnt = CFArrayGetCount( modes );
1747
1748			for ( index = 0; (index < cnt) && (mNumSupportedResolutions < MAX_NUM_RESOLUTIONS); index++ )
1749			{
1750				//  Pull the mode dictionary out of the CFArray
1751				CFDictionaryRef mode = (CFDictionaryRef)CFArrayGetValueAtIndex( modes, index );
1752				long width = getDictLong(mode, kCGDisplayWidth);
1753				long height = getDictLong(mode, kCGDisplayHeight);
1754				long bits = getDictLong(mode, kCGDisplayBitsPerPixel);
1755
1756				if(bits == BITS_PER_PIXEL && width >= 800 && height >= 600)
1757				{
1758					BOOL resolution_exists = FALSE;
1759					for(S32 i = 0; i < mNumSupportedResolutions; i++)
1760					{
1761						if (mSupportedResolutions[i].mWidth == width &&
1762							mSupportedResolutions[i].mHeight == height)
1763						{
1764							resolution_exists = TRUE;
1765						}
1766					}
1767					if (!resolution_exists)
1768					{
1769						mSupportedResolutions[mNumSupportedResolutions].mWidth = width;
1770						mSupportedResolutions[mNumSupportedResolutions].mHeight = height;
1771						mNumSupportedResolutions++;
1772					}
1773				}
1774			}
1775		}
1776	}
1777
1778	num_resolutions = mNumSupportedResolutions;
1779	return mSupportedResolutions;
1780}
1781
1782BOOL LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordWindow *to)
1783{
1784	S32		client_height;
1785	Rect	client_rect;
1786
1787	if(mFullscreen)
1788	{
1789		// In the fullscreen case, the "window" is the entire screen.
1790		client_rect.left = 0;
1791		client_rect.top = 0;
1792		client_rect.right = mFullscreenWidth;
1793		client_rect.bottom = mFullscreenHeight;
1794	}
1795	else if (!mWindow ||
1796		(GetWindowBounds(mWindow, kWindowContentRgn, &client_rect) != noErr) ||
1797		NULL == to)
1798	{
1799		return FALSE;
1800	}
1801
1802	to->mX = from.mX;
1803	client_height = client_rect.bottom - client_rect.top;
1804	to->mY = client_height - from.mY - 1;
1805
1806	return TRUE;
1807}
1808
1809BOOL LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordGL* to)
1810{
1811	S32		client_height;
1812	Rect	client_rect;
1813
1814	if(mFullscreen)
1815	{
1816		// In the fullscreen case, the "window" is the entire screen.
1817		client_rect.left = 0;
1818		client_rect.top = 0;
1819		client_rect.right = mFullscreenWidth;
1820		client_rect.bottom = mFullscreenHeight;
1821	}
1822	else if (!mWindow ||
1823		(GetWindowBounds(mWindow, kWindowContentRgn, &client_rect) != noErr) ||
1824		NULL == to)
1825	{
1826		return FALSE;
1827	}
1828
1829	to->mX = from.mX;
1830	client_height = client_rect.bottom - client_rect.top;
1831	to->mY = client_height - from.mY - 1;
1832
1833	return TRUE;
1834}
1835
1836BOOL LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordWindow* to)
1837{
1838	if(mFullscreen)
1839	{
1840		// In the fullscreen case, window and screen coordinates are the same.
1841		to->mX = from.mX;
1842		to->mY = from.mY;
1843		return TRUE;
1844	}
1845	else if(mWindow)
1846	{
1847		GrafPtr save;
1848		Point mouse_point;
1849
1850		mouse_point.h = from.mX;
1851		mouse_point.v = from.mY;
1852
1853		::GetPort(&save);
1854		::SetPort(GetWindowPort(mWindow));
1855		fixOrigin();
1856
1857		::GlobalToLocal(&mouse_point);
1858
1859		to->mX = mouse_point.h;
1860		to->mY = mouse_point.v;
1861
1862		::SetPort(save);
1863
1864		return TRUE;
1865	}
1866
1867	return FALSE;
1868}
1869
1870BOOL LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordScreen *to)
1871{
1872	if(mFullscreen)
1873	{
1874		// In the fullscreen case, window and screen coordinates are the same.
1875		to->mX = from.mX;
1876		to->mY = from.mY;
1877		return TRUE;
1878	}
1879	else if(mWindow)
1880	{
1881		GrafPtr save;
1882		Point mouse_point;
1883
1884		mouse_point.h = from.mX;
1885		mouse_point.v = from.mY;
1886		::GetPort(&save);
1887		::SetPort(GetWindowPort(mWindow));
1888		fixOrigin();
1889
1890		LocalToGlobal(&mouse_point);
1891
1892		to->mX = mouse_point.h;
1893		to->mY = mouse_point.v;
1894
1895		::SetPort(save);
1896
1897		return TRUE;
1898	}
1899
1900	return FALSE;
1901}
1902
1903BOOL LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordGL *to)
1904{
1905	LLCoordWindow window_coord;
1906
1907	return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
1908}
1909
1910BOOL LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordScreen *to)
1911{
1912	LLCoordWindow window_coord;
1913
1914	return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
1915}
1916
1917
1918
1919
1920void LLWindowMacOSX::setupFailure(const std::string& text, const std::string& caption, U32 type)
1921{
1922	destroyContext();
1923
1924	OSMessageBox(text, caption, type);
1925}
1926
1927pascal Boolean LLWindowMacOSX::staticMoveEventComparator( EventRef event, void* data)
1928{
1929	UInt32 				evtClass = GetEventClass (event);
1930	UInt32 				evtKind = GetEventKind (event);
1931
1932	if ((evtClass == kEventClassMouse) && ((evtKind == kEventMouseDragged) || (evtKind == kEventMouseMoved)))
1933	{
1934		return true;
1935	}
1936
1937	else
1938	{
1939		return false;
1940	}
1941}
1942
1943
1944pascal OSStatus LLWindowMacOSX::staticEventHandler(EventHandlerCallRef myHandler, EventRef event, void* userData)
1945{
1946	LLWindowMacOSX *self = (LLWindowMacOSX*)userData;
1947
1948	return(self->eventHandler(myHandler, event));
1949}
1950
1951OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef event)
1952{
1953	OSStatus			result = eventNotHandledErr;
1954	UInt32 				evtClass = GetEventClass (event);
1955	UInt32 				evtKind = GetEventKind (event);
1956
1957	// Always handle command events, even in hands-off mode.
1958	if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess))
1959	{
1960		HICommand command;
1961		GetEventParameter (event, kEventParamDirec

Large files files are truncated, but you can click here to view the full file