PageRenderTime 62ms CodeModel.GetById 24ms RepoModel.GetById 0ms 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
Possible License(s): LGPL-2.1

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

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