llwindowmacosx.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:98k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llwindowmacosx.cpp
  3.  * @brief Platform-dependent implementation of llwindow
  4.  *
  5.  * $LicenseInfo:firstyear=2001&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2001-2010, Linden Research, Inc.
  8.  * 
  9.  * Second Life Viewer Source Code
  10.  * The source code in this file ("Source Code") is provided by Linden Lab
  11.  * to you under the terms of the GNU General Public License, version 2.0
  12.  * ("GPL"), unless you have obtained a separate licensing agreement
  13.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  14.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16.  * 
  17.  * There are special exceptions to the terms and conditions of the GPL as
  18.  * it is applied to this Source Code. View the full text of the exception
  19.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  20.  * online at
  21.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22.  * 
  23.  * By copying, modifying or distributing this software, you acknowledge
  24.  * that you have read and understood your obligations described above,
  25.  * and agree to abide by those obligations.
  26.  * 
  27.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29.  * COMPLETENESS OR PERFORMANCE.
  30.  * $/LicenseInfo$
  31.  */
  32. #include "linden_common.h"
  33. #include "llwindowmacosx.h"
  34. #include "llkeyboardmacosx.h"
  35. #include "llwindowcallbacks.h"
  36. #include "llwindowmacosx-objc.h"
  37. #include "llpreeditor.h"
  38. #include "llerror.h"
  39. #include "llgl.h"
  40. #include "llstring.h"
  41. #include "lldir.h"
  42. #include "indra_constants.h"
  43. #include <Carbon/Carbon.h>
  44. #include <OpenGL/OpenGL.h>
  45. extern BOOL gDebugWindowProc;
  46. // culled from winuser.h
  47. //const S32 WHEEL_DELTA = 120;     /* Value for rolling one detent */
  48. // On the Mac, the scroll wheel reports a delta of 1 for each detent.
  49. // There's also acceleration for faster scrolling, based on a slider in the system preferences.
  50. const S32 WHEEL_DELTA = 1;     /* Value for rolling one detent */
  51. const S32 BITS_PER_PIXEL = 32;
  52. const S32 MAX_NUM_RESOLUTIONS = 32;
  53. //
  54. // LLWindowMacOSX
  55. //
  56. BOOL LLWindowMacOSX::sUseMultGL = FALSE;
  57. WindowRef LLWindowMacOSX::sMediaWindow = NULL;
  58. // Cross-platform bits:
  59. BOOL check_for_card(const char* RENDERER, const char* bad_card)
  60. {
  61. if (!strnicmp(RENDERER, bad_card, strlen(bad_card)))
  62. {
  63. std::string buffer = llformat(
  64. "Your video card appears to be a %s, which Second Life does not support.n"
  65. "n"
  66. "Second Life requires a video card with 32 Mb of memory or more, as well asn"
  67. "multitexture support.  We explicitly support nVidia GeForce 2 or better, n"
  68. "and ATI Radeon 8500 or better.n"
  69. "n"
  70. "If you own a supported card and continue to receive this message, try n"
  71. "updating to the latest video card drivers. Otherwise look in then"
  72. "secondlife.com support section or e-mail technical supportn"
  73. "n"
  74. "You can try to run Second Life, but it will probably crash or runn"
  75. "very slowly.  Try anyway?",
  76. bad_card);
  77. S32 button = OSMessageBox(buffer.c_str(), "Unsupported video card", OSMB_YESNO);
  78. if (OSBTN_YES == button)
  79. {
  80. return FALSE;
  81. }
  82. else
  83. {
  84. return TRUE;
  85. }
  86. }
  87. return FALSE;
  88. }
  89. // Switch to determine whether we capture all displays, or just the main one.
  90. // We may want to base this on the setting of _DEBUG...
  91. #define CAPTURE_ALL_DISPLAYS 0
  92. static double getDictDouble (CFDictionaryRef refDict, CFStringRef key);
  93. static long getDictLong (CFDictionaryRef refDict, CFStringRef key);
  94. // CarbonEvents we're interested in.
  95. static EventTypeSpec WindowHandlerEventList[] =
  96. {
  97. // Window-related events
  98. // { kEventClassWindow, kEventWindowCollapsing },
  99. // { kEventClassWindow, kEventWindowCollapsed },
  100. // { kEventClassWindow, kEventWindowShown },
  101. { kEventClassWindow, kEventWindowActivated },
  102. { kEventClassWindow, kEventWindowDeactivated },
  103. { kEventClassWindow, kEventWindowShown },
  104. { kEventClassWindow, kEventWindowHidden },
  105. { kEventClassWindow, kEventWindowCollapsed },
  106. { kEventClassWindow, kEventWindowExpanded },
  107. { kEventClassWindow, kEventWindowGetClickActivation },
  108. { kEventClassWindow, kEventWindowClose },
  109. { kEventClassWindow, kEventWindowBoundsChanging },
  110. { kEventClassWindow, kEventWindowBoundsChanged },
  111. // { kEventClassWindow, kEventWindowZoomed },
  112. // { kEventClassWindow, kEventWindowDrawContent },
  113. // Mouse events
  114. { kEventClassMouse, kEventMouseDown },
  115. { kEventClassMouse, kEventMouseUp },
  116. { kEventClassMouse, kEventMouseDragged },
  117. { kEventClassMouse, kEventMouseWheelMoved },
  118. { kEventClassMouse, kEventMouseMoved },
  119. // Keyboard events
  120. // No longer handle raw key down events directly.
  121. // When text input events come in, extract the raw key events from them and process at that point.
  122. // This allows input methods to eat keystrokes the way they're supposed to.
  123. // { kEventClassKeyboard, kEventRawKeyDown },
  124. // { kEventClassKeyboard, kEventRawKeyRepeat },
  125. { kEventClassKeyboard, kEventRawKeyUp },
  126. { kEventClassKeyboard, kEventRawKeyModifiersChanged },
  127. // Text input events
  128. { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
  129. { kEventClassTextInput, kEventTextInputUpdateActiveInputArea },
  130. { kEventClassTextInput, kEventTextInputOffsetToPos },
  131. { kEventClassTextInput, kEventTextInputPosToOffset },
  132. { kEventClassTextInput, kEventTextInputShowHideBottomWindow },
  133. { kEventClassTextInput, kEventTextInputGetSelectedText },
  134. { kEventClassTextInput, kEventTextInputFilterText },
  135. // TSM Document Access events (advanced input method support)
  136. { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetLength },
  137. { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetSelectedRange },
  138. { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetCharacters },
  139. { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetFont },
  140. { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetGlyphInfo },
  141. { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessLockDocument },
  142. { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessUnlockDocument }
  143. };
  144. static EventTypeSpec GlobalHandlerEventList[] =
  145. {
  146. // Mouse events
  147. { kEventClassMouse, kEventMouseDown },
  148. { kEventClassMouse, kEventMouseUp },
  149. { kEventClassMouse, kEventMouseDragged },
  150. { kEventClassMouse, kEventMouseWheelMoved },
  151. { kEventClassMouse, kEventMouseMoved },
  152. // Keyboard events
  153. // No longer handle raw key down events directly.
  154. // When text input events come in, extract the raw key events from them and process at that point.
  155. // This allows input methods to eat keystrokes the way they're supposed to.
  156. // { kEventClassKeyboard, kEventRawKeyDown },
  157. // { kEventClassKeyboard, kEventRawKeyRepeat },
  158. { kEventClassKeyboard, kEventRawKeyUp },
  159. { kEventClassKeyboard, kEventRawKeyModifiersChanged },
  160. // Text input events
  161. { kEventClassTextInput, kEventTextInputUpdateActiveInputArea },
  162. { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
  163. { kEventClassTextInput, kEventTextInputOffsetToPos },
  164. { kEventClassTextInput, kEventTextInputPosToOffset },
  165. { kEventClassTextInput, kEventTextInputShowHideBottomWindow },
  166. { kEventClassTextInput, kEventTextInputGetSelectedText },
  167. { kEventClassTextInput, kEventTextInputFilterText },
  168. // TSM Document Access events (advanced input method support)
  169. { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetLength },
  170. { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetSelectedRange },
  171. { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetCharacters },
  172. { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetFont },
  173. { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetGlyphInfo },
  174. { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessLockDocument },
  175. { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessUnlockDocument }
  176. };
  177. static EventTypeSpec CommandHandlerEventList[] =
  178. {
  179. { kEventClassCommand, kEventCommandProcess }
  180. };
  181. // MBW -- HACK ALERT
  182. // On the Mac, to put up an OS dialog in full screen mode, we must first switch OUT of full screen mode.
  183. // The proper way to do this is to bracket the dialog with calls to beforeDialog() and afterDialog(), but these
  184. // require a pointer to the LLWindowMacOSX object.  Stash it here and maintain in the constructor and destructor.
  185. // This assumes that there will be only one object of this class at any time.  Hopefully this is true.
  186. static LLWindowMacOSX *gWindowImplementation = NULL;
  187. LLWindowMacOSX::LLWindowMacOSX(LLWindowCallbacks* callbacks,
  188.    const std::string& title, const std::string& name, S32 x, S32 y, S32 width,
  189.    S32 height, U32 flags,
  190.    BOOL fullscreen, BOOL clearBg,
  191.    BOOL disable_vsync, BOOL use_gl,
  192.    BOOL ignore_pixel_depth,
  193.    U32 fsaa_samples)
  194. : LLWindow(NULL, fullscreen, flags)
  195. {
  196. // *HACK: During window construction we get lots of OS events for window
  197. // reshape, activate, etc. that the viewer isn't ready to handle.
  198. // Route them to a dummy callback structure until the end of constructor.
  199. LLWindowCallbacks null_callbacks;
  200. mCallbacks = &null_callbacks;
  201. // Voodoo for calling cocoa from carbon (see llwindowmacosx-objc.mm).
  202. setupCocoa();
  203. // Initialize the keyboard
  204. gKeyboard = new LLKeyboardMacOSX();
  205. gKeyboard->setCallbacks(callbacks);
  206. // Ignore use_gl for now, only used for drones on PC
  207. mWindow = NULL;
  208. mContext = NULL;
  209. mPixelFormat = NULL;
  210. mDisplay = CGMainDisplayID();
  211. mOldDisplayMode = NULL;
  212. mTimer = NULL;
  213. mSimulatedRightClick = FALSE;
  214. mLastModifiers = 0;
  215. mHandsOffEvents = FALSE;
  216. mCursorDecoupled = FALSE;
  217. mCursorLastEventDeltaX = 0;
  218. mCursorLastEventDeltaY = 0;
  219. mCursorIgnoreNextDelta = FALSE;
  220. mNeedsResize = FALSE;
  221. mOverrideAspectRatio = 0.f;
  222. mMinimized = FALSE;
  223. mTSMDocument = NULL; // Just in case.
  224. mLanguageTextInputAllowed = FALSE;
  225. mTSMScriptCode = 0;
  226. mTSMLangCode = 0;
  227. mPreeditor = NULL;
  228. mRawKeyEvent = NULL;
  229. mFSAASamples = fsaa_samples;
  230. mForceRebuild = FALSE;
  231. // For reasons that aren't clear to me, LLTimers seem to be created in the "started" state.
  232. // 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.
  233. mBounceTimer.stop();
  234. // Get the original aspect ratio of the main device.
  235. mOriginalAspectRatio = (double)CGDisplayPixelsWide(mDisplay) / (double)CGDisplayPixelsHigh(mDisplay);
  236. // Stash the window title
  237. strcpy((char*)mWindowTitle + 1, title.c_str()); /* Flawfinder: ignore */
  238. mWindowTitle[0] = title.length();
  239. mEventHandlerUPP = NewEventHandlerUPP(staticEventHandler);
  240. mMoveEventCampartorUPP = NewEventComparatorUPP(staticMoveEventComparator);
  241. mGlobalHandlerRef = NULL;
  242. mWindowHandlerRef = NULL;
  243. mDragOverrideCursor = -1;
  244. // We're not clipping yet
  245. SetRect( &mOldMouseClip, 0, 0, 0, 0 );
  246. // Set up global event handlers (the fullscreen case needs this)
  247. InstallStandardEventHandler(GetApplicationEventTarget());
  248. // Stash an object pointer for OSMessageBox()
  249. gWindowImplementation = this;
  250. // Create the GL context and set it up for windowed or fullscreen, as appropriate.
  251. if(createContext(x, y, width, height, 32, fullscreen, disable_vsync))
  252. {
  253. if(mWindow != NULL)
  254. {
  255. // MBW -- XXX -- I think we can now do this here?
  256. // Constrain the window to the screen it's mostly on, resizing if necessary.
  257. ConstrainWindowToScreen(
  258. mWindow,
  259. kWindowStructureRgn,
  260. kWindowConstrainMayResize |
  261. // kWindowConstrainStandardOptions |
  262. 0,
  263. NULL,
  264. NULL);
  265. MacShowWindow(mWindow);
  266. BringToFront(mWindow);
  267. }
  268. if (!gGLManager.initGL())
  269. {
  270. setupFailure(
  271. "Second Life is unable to run because your video card driversn"
  272. "are out of date or unsupported. Please make sure you haven"
  273. "the latest video card drivers installed.n"
  274. "If you continue to receive this message, contact customer service.",
  275. "Error",
  276. OSMB_OK);
  277. return;
  278. }
  279. //start with arrow cursor
  280. initCursors();
  281. setCursor( UI_CURSOR_ARROW );
  282. }
  283. mCallbacks = callbacks;
  284. stop_glerror();
  285. }
  286. BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL disable_vsync)
  287. {
  288. OSStatus err;
  289. BOOL glNeedsInit = FALSE;
  290. if(mGlobalHandlerRef == NULL)
  291. {
  292. InstallApplicationEventHandler(mEventHandlerUPP, GetEventTypeCount (CommandHandlerEventList), CommandHandlerEventList, (void*)this, &mGlobalHandlerRef);
  293. }
  294. mFullscreen = fullscreen;
  295. if (mFullscreen && (mOldDisplayMode == NULL))
  296. {
  297. LL_INFOS("Window") << "createContext: setting up fullscreen " << width << "x" << height << LL_ENDL;
  298. // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays.  Plan accordingly.
  299. double refresh = getDictDouble (CGDisplayCurrentMode (mDisplay),  kCGDisplayRefreshRate);
  300. // If the requested width or height is 0, find the best default for the monitor.
  301. if((width == 0) || (height == 0))
  302. {
  303. // Scan through the list of modes, looking for one which has:
  304. // height between 700 and 800
  305. // aspect ratio closest to the user's original mode
  306. S32 resolutionCount = 0;
  307. LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount);
  308. if(resolutionList != NULL)
  309. {
  310. F32 closestAspect = 0;
  311. U32 closestHeight = 0;
  312. U32 closestWidth = 0;
  313. int i;
  314. LL_DEBUGS("Window") << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << LL_ENDL;
  315. for(i=0; i < resolutionCount; i++)
  316. {
  317. F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight;
  318. LL_DEBUGS("Window") << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << LL_ENDL;
  319. if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) &&
  320. (fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio)))
  321. {
  322. LL_DEBUGS("Window") << " (new closest mode) " << LL_ENDL;
  323. // This is the closest mode we've seen yet.
  324. closestWidth = resolutionList[i].mWidth;
  325. closestHeight = resolutionList[i].mHeight;
  326. closestAspect = aspect;
  327. }
  328. }
  329. width = closestWidth;
  330. height = closestHeight;
  331. }
  332. }
  333. if((width == 0) || (height == 0))
  334. {
  335. // Mode search failed for some reason.  Use the old-school default.
  336. width = 1024;
  337. height = 768;
  338. }
  339. if (true)
  340. {
  341. // Fullscreen support
  342. CFDictionaryRef refDisplayMode = 0;
  343. boolean_t exactMatch = false;
  344. #if CAPTURE_ALL_DISPLAYS
  345. // Capture all displays (may want to do this for final build)
  346. CGCaptureAllDisplays ();
  347. #else
  348. // Capture only the main display (useful for debugging)
  349. CGDisplayCapture (mDisplay);
  350. #endif
  351. // Switch the display to the desired resolution and refresh
  352. refDisplayMode = CGDisplayBestModeForParametersAndRefreshRate(
  353. mDisplay,
  354. BITS_PER_PIXEL,
  355. width,
  356. height,
  357. refresh,
  358. &exactMatch);
  359. if (refDisplayMode)
  360. {
  361. LL_DEBUGS("Window") << "createContext: switching display resolution" << LL_ENDL;
  362. mOldDisplayMode = CGDisplayCurrentMode (mDisplay);
  363. CGDisplaySwitchToMode (mDisplay, refDisplayMode);
  364. // CFRelease(refDisplayMode);
  365. AddEventTypesToHandler(mGlobalHandlerRef, GetEventTypeCount (GlobalHandlerEventList), GlobalHandlerEventList);
  366. }
  367. mFullscreen = TRUE;
  368. mFullscreenWidth   = CGDisplayPixelsWide(mDisplay);
  369. mFullscreenHeight  = CGDisplayPixelsHigh(mDisplay);
  370. mFullscreenBits    = CGDisplayBitsPerPixel(mDisplay);
  371. mFullscreenRefresh = llround(getDictDouble (CGDisplayCurrentMode (mDisplay),  kCGDisplayRefreshRate));
  372. LL_INFOS("Window") << "Running at " << mFullscreenWidth
  373. << "x"   << mFullscreenHeight
  374. << "x"   << mFullscreenBits
  375. << " @ " << mFullscreenRefresh
  376. << LL_ENDL;
  377. }
  378. else
  379. {
  380. // No fullscreen support
  381. mFullscreen = FALSE;
  382. mFullscreenWidth   = -1;
  383. mFullscreenHeight  = -1;
  384. mFullscreenBits    = -1;
  385. mFullscreenRefresh = -1;
  386. std::string error= llformat("Unable to run fullscreen at %d x %d.nRunning in window.", width, height);
  387. OSMessageBox(error, "Error", OSMB_OK);
  388. }
  389. }
  390. if(!mFullscreen && (mWindow == NULL))
  391. {
  392. Rect window_rect;
  393. //int displayWidth = CGDisplayPixelsWide(mDisplay);
  394. //int displayHeight = CGDisplayPixelsHigh(mDisplay);
  395. //const int menuBarPlusTitleBar = 44;   // Ugly magic number.
  396. LL_DEBUGS("Window") << "createContext: creating window" << LL_ENDL;
  397. window_rect.left = (long) x;
  398. window_rect.right = (long) x + width;
  399. window_rect.top = (long) y;
  400. window_rect.bottom = (long) y + height;
  401. //-----------------------------------------------------------------------
  402. // Create the window
  403. //-----------------------------------------------------------------------
  404. mWindow = NewCWindow(
  405. NULL,
  406. &window_rect,
  407. mWindowTitle,
  408. false, // Create the window invisible.  Whoever calls createContext() should show it after any moving/resizing.
  409. // noGrowDocProc, // Window with no grow box and no zoom box
  410. zoomDocProc, // Window with a grow box and a zoom box
  411. // zoomNoGrow, // Window with a zoom box but no grow box
  412. kFirstWindowOfClass,
  413. true,
  414. (long)this);
  415. if (!mWindow)
  416. {
  417. setupFailure("Window creation error", "Error", OSMB_OK);
  418. return FALSE;
  419. }
  420. // Turn on live resize.
  421. // For this to work correctly, we need to be able to call LLViewerWindow::draw from
  422. // the event handler for kEventWindowBoundsChanged.  It's not clear that we have access from here.
  423. // err = ChangeWindowAttributes(mWindow, kWindowLiveResizeAttribute, 0);
  424. // Set up window event handlers (some window-related events ONLY go to window handlers.)
  425. InstallStandardEventHandler(GetWindowEventTarget(mWindow));
  426. InstallWindowEventHandler(mWindow, mEventHandlerUPP, GetEventTypeCount (WindowHandlerEventList), WindowHandlerEventList, (void*)this, &mWindowHandlerRef); // add event handler
  427. #if LL_OS_DRAGDROP_ENABLED
  428. InstallTrackingHandler( dragTrackingHandler, mWindow, (void*)this );
  429. InstallReceiveHandler( dragReceiveHandler, mWindow, (void*)this );
  430. #endif // LL_OS_DRAGDROP_ENABLED
  431. }
  432. {
  433. // Create and initialize our TSM document for language text input.
  434. // If an error occured, we can do nothing better than simply ignore it.
  435. // mTSMDocument will be kept NULL in case.
  436. if (mTSMDocument)
  437. {
  438. DeactivateTSMDocument(mTSMDocument);
  439. DeleteTSMDocument(mTSMDocument);
  440. mTSMDocument = NULL;
  441. }
  442. static InterfaceTypeList types = { kUnicodeDocument };
  443. err = NewTSMDocument(1, types, &mTSMDocument, 0);
  444. if (err != noErr)
  445. {
  446. LL_WARNS("Window") << "createContext: couldn't create a TSMDocument (" << err << ")" << LL_ENDL;
  447. }
  448. if (mTSMDocument)
  449. {
  450. ActivateTSMDocument(mTSMDocument);
  451. allowLanguageTextInput(NULL, FALSE);
  452. }
  453. }
  454. if(mContext == NULL)
  455. {
  456. AGLRendererInfo rendererInfo = NULL;
  457. //-----------------------------------------------------------------------
  458. // Create GL drawing context
  459. //-----------------------------------------------------------------------
  460. if(mPixelFormat == NULL)
  461. {
  462. if(mFullscreen)
  463. {
  464. GLint fullscreenAttrib[] =
  465. {
  466. AGL_RGBA,
  467. AGL_FULLSCREEN,
  468. // AGL_NO_RECOVERY, // MBW -- XXX -- Not sure if we want this attribute
  469. AGL_SAMPLE_BUFFERS_ARB, mFSAASamples > 0 ? 1 : 0,
  470. AGL_SAMPLES_ARB, mFSAASamples,
  471. AGL_DOUBLEBUFFER,
  472. AGL_CLOSEST_POLICY,
  473. AGL_ACCELERATED,
  474. AGL_RED_SIZE, 8,
  475. AGL_GREEN_SIZE, 8,
  476. AGL_BLUE_SIZE, 8,
  477. AGL_ALPHA_SIZE, 8,
  478. AGL_DEPTH_SIZE, 24,
  479. AGL_STENCIL_SIZE, 8,
  480. AGL_NONE
  481. };
  482. LL_DEBUGS("Window") << "createContext: creating fullscreen pixelformat" << LL_ENDL;
  483. GDHandle gdhDisplay = NULL;
  484. err = DMGetGDeviceByDisplayID ((DisplayIDType)mDisplay, &gdhDisplay, false);
  485. mPixelFormat = aglChoosePixelFormat(&gdhDisplay, 1, fullscreenAttrib);
  486. rendererInfo = aglQueryRendererInfo(&gdhDisplay, 1);
  487. }
  488. else
  489. {
  490. GLint windowedAttrib[] =
  491. {
  492. AGL_RGBA,
  493. AGL_DOUBLEBUFFER,
  494. AGL_CLOSEST_POLICY,
  495. AGL_ACCELERATED,
  496. AGL_SAMPLE_BUFFERS_ARB, mFSAASamples > 0 ? 1 : 0,
  497. AGL_SAMPLES_ARB, mFSAASamples,
  498. AGL_RED_SIZE, 8,
  499. AGL_GREEN_SIZE, 8,
  500. AGL_BLUE_SIZE, 8,
  501. AGL_ALPHA_SIZE, 8,
  502. AGL_DEPTH_SIZE, 24,
  503. AGL_STENCIL_SIZE, 8,
  504. AGL_NONE
  505. };
  506. LL_DEBUGS("Window") << "createContext: creating windowed pixelformat" << LL_ENDL;
  507. mPixelFormat = aglChoosePixelFormat(NULL, 0, windowedAttrib);
  508. GDHandle gdhDisplay = GetMainDevice();
  509. rendererInfo = aglQueryRendererInfo(&gdhDisplay, 1);
  510. }
  511. // May want to get the real error text like this:
  512. // (char *) aglErrorString(aglGetError());
  513. if(aglGetError() != AGL_NO_ERROR)
  514. {
  515. setupFailure("Can't find suitable pixel format", "Error", OSMB_OK);
  516. return FALSE;
  517. }
  518. }
  519. if(mPixelFormat)
  520. {
  521. LL_DEBUGS("Window") << "createContext: creating GL context" << LL_ENDL;
  522. mContext = aglCreateContext(mPixelFormat, NULL);
  523. }
  524. if(mContext == NULL)
  525. {
  526. setupFailure("Can't make GL context", "Error", OSMB_OK);
  527. return FALSE;
  528. }
  529. gGLManager.mVRAM = 0;
  530. if(rendererInfo != NULL)
  531. {
  532. GLint result;
  533. if(aglDescribeRenderer(rendererInfo, AGL_VIDEO_MEMORY, &result))
  534. {
  535. // llinfos << "createContext: aglDescribeRenderer(AGL_VIDEO_MEMORY) returned " << result << llendl;
  536. gGLManager.mVRAM = result / (1024 * 1024);
  537. }
  538. else
  539. {
  540. // llinfos << "createContext: aglDescribeRenderer(AGL_VIDEO_MEMORY) failed." << llendl;
  541. }
  542. // This could be useful at some point, if it takes into account the memory already used by screen buffers, etc...
  543. if(aglDescribeRenderer(rendererInfo, AGL_TEXTURE_MEMORY, &result))
  544. {
  545. // llinfos << "createContext: aglDescribeRenderer(AGL_TEXTURE_MEMORY) returned " << result << llendl;
  546. }
  547. else
  548. {
  549. // llinfos << "createContext: aglDescribeRenderer(AGL_TEXTURE_MEMORY) failed." << llendl;
  550. }
  551. aglDestroyRendererInfo(rendererInfo);
  552. }
  553. // Since we just created the context, it needs to be set up.
  554. glNeedsInit = TRUE;
  555. }
  556. // Hook up the context to a drawable
  557. if (mFullscreen && (mOldDisplayMode != NULL))
  558. {
  559. // We successfully captured the display.  Use a fullscreen drawable
  560. LL_DEBUGS("Window") << "createContext: attaching fullscreen drawable" << LL_ENDL;
  561. #if CAPTURE_ALL_DISPLAYS
  562. // Capture all displays (may want to do this for final build)
  563. aglDisable (mContext, AGL_FS_CAPTURE_SINGLE);
  564. #else
  565. // Capture only the main display (useful for debugging)
  566. aglEnable (mContext, AGL_FS_CAPTURE_SINGLE);
  567. #endif
  568. if (!aglSetFullScreen (mContext, 0, 0, 0, 0))
  569. {
  570. setupFailure("Can't set GL fullscreen", "Error", OSMB_OK);
  571. return FALSE;
  572. }
  573. }
  574. else if(!mFullscreen && (mWindow != NULL))
  575. {
  576. LL_DEBUGS("Window") << "createContext: attaching windowed drawable" << LL_ENDL;
  577. // We created a window.  Use it as the drawable.
  578. if(!aglSetDrawable(mContext, GetWindowPort (mWindow)))
  579. {
  580. setupFailure("Can't set GL drawable", "Error", OSMB_OK);
  581. return FALSE;
  582. }
  583. }
  584. else
  585. {
  586. setupFailure("Can't get fullscreen or windowed drawable.", "Error", OSMB_OK);
  587. return FALSE;
  588. }
  589. if(mContext != NULL)
  590. {
  591. LL_DEBUGS("Window") << "createContext: setting current context" << LL_ENDL;
  592. if (!aglSetCurrentContext(mContext))
  593. {
  594. setupFailure("Can't activate GL rendering context", "Error", OSMB_OK);
  595. return FALSE;
  596. }
  597. }
  598. if(glNeedsInit)
  599. {
  600. // Check for some explicitly unsupported cards.
  601. const char* RENDERER = (const char*) glGetString(GL_RENDERER);
  602. const char* CARD_LIST[] =
  603. { "RAGE 128",
  604. "RIVA TNT2",
  605. "Intel 810",
  606. "3Dfx/Voodoo3",
  607. "Radeon 7000",
  608. "Radeon 7200",
  609. "Radeon 7500",
  610. "Radeon DDR",
  611. "Radeon VE",
  612. "GDI Generic" };
  613. const S32 CARD_COUNT = LL_ARRAY_SIZE(CARD_LIST);
  614. // Future candidates:
  615. // ProSavage/Twister
  616. // SuperSavage
  617. S32 i;
  618. for (i = 0; i < CARD_COUNT; i++)
  619. {
  620. if (check_for_card(RENDERER, CARD_LIST[i]))
  621. {
  622. close();
  623. return FALSE;
  624. }
  625. }
  626. }
  627. GLint colorBits, alphaBits, depthBits, stencilBits;
  628. if( !aglDescribePixelFormat(mPixelFormat, AGL_BUFFER_SIZE, &colorBits) ||
  629. !aglDescribePixelFormat(mPixelFormat, AGL_ALPHA_SIZE, &alphaBits) ||
  630. !aglDescribePixelFormat(mPixelFormat, AGL_DEPTH_SIZE, &depthBits) ||
  631. !aglDescribePixelFormat(mPixelFormat, AGL_STENCIL_SIZE, &stencilBits))
  632. {
  633. close();
  634. setupFailure("Can't get pixel format description", "Error", OSMB_OK);
  635. return FALSE;
  636. }
  637. LL_INFOS("GLInit") << "GL buffer: Color Bits " << S32(colorBits)
  638. << " Alpha Bits " << S32(alphaBits)
  639. << " Depth Bits " << S32(depthBits)
  640. << " Stencil Bits" << S32(stencilBits)
  641. << LL_ENDL;
  642. if (colorBits < 32)
  643. {
  644. close();
  645. setupFailure(
  646. "Second Life requires True Color (32-bit) to run in a window.n"
  647. "Please go to Control Panels -> Display -> Settings andn"
  648. "set the screen to 32-bit color.n"
  649. "Alternately, if you choose to run fullscreen, Second Lifen"
  650. "will automatically adjust the screen each time it runs.",
  651. "Error",
  652. OSMB_OK);
  653. return FALSE;
  654. }
  655. if (alphaBits < 8)
  656. {
  657. close();
  658. setupFailure(
  659. "Second Life is unable to run because it can't get an 8 bit alphan"
  660. "channel.  Usually this is due to video card driver issues.n"
  661. "Please make sure you have the latest video card drivers installed.n"
  662. "Also be sure your monitor is set to True Color (32-bit) inn"
  663. "Control Panels -> Display -> Settings.n"
  664. "If you continue to receive this message, contact customer service.",
  665. "Error",
  666. OSMB_OK);
  667. return FALSE;
  668. }
  669. // Disable vertical sync for swap
  670. GLint frames_per_swap = 0;
  671. if (disable_vsync)
  672. {
  673. LL_DEBUGS("GLInit") << "Disabling vertical sync" << LL_ENDL;
  674. frames_per_swap = 0;
  675. }
  676. else
  677. {
  678. LL_DEBUGS("GLinit") << "Keeping vertical sync" << LL_ENDL;
  679. frames_per_swap = 1;
  680. }
  681. aglSetInteger(mContext, AGL_SWAP_INTERVAL, &frames_per_swap);  
  682. //enable multi-threaded OpenGL
  683. if (sUseMultGL)
  684. {
  685. CGLError cgl_err;
  686. CGLContextObj ctx = CGLGetCurrentContext();
  687. cgl_err =  CGLEnable( ctx, kCGLCEMPEngine);
  688. if (cgl_err != kCGLNoError )
  689. {
  690. LL_DEBUGS("GLInit") << "Multi-threaded OpenGL not available." << LL_ENDL;
  691. }    
  692. else
  693. {
  694. LL_DEBUGS("GLInit") << "Multi-threaded OpenGL enabled." << LL_ENDL;
  695. }
  696. }
  697. // Don't need to get the current gamma, since there's a call that restores it to the system defaults.
  698. return TRUE;
  699. }
  700. // changing fullscreen resolution, or switching between windowed and fullscreen mode.
  701. BOOL LLWindowMacOSX::switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp)
  702. {
  703. BOOL needsRebuild = FALSE;
  704. BOOL result = true;
  705. if(fullscreen)
  706. {
  707. if(mFullscreen)
  708. {
  709. // Switching resolutions in fullscreen mode.  Don't need to rebuild for this.
  710. // Fullscreen support
  711. CFDictionaryRef refDisplayMode = 0;
  712. boolean_t exactMatch = false;
  713. // Switch the display to the desired resolution and refresh
  714. refDisplayMode = CGDisplayBestModeForParametersAndRefreshRate(
  715. mDisplay,
  716. BITS_PER_PIXEL,
  717. size.mX,
  718. size.mY,
  719. getDictDouble (CGDisplayCurrentMode (mDisplay),  kCGDisplayRefreshRate),
  720. &exactMatch);
  721. if (refDisplayMode)
  722. {
  723. CGDisplaySwitchToMode (mDisplay, refDisplayMode);
  724. // CFRelease(refDisplayMode);
  725. }
  726. mFullscreenWidth   = CGDisplayPixelsWide(mDisplay);
  727. mFullscreenHeight  = CGDisplayPixelsHigh(mDisplay);
  728. mFullscreenBits    = CGDisplayBitsPerPixel(mDisplay);
  729. mFullscreenRefresh = llround(getDictDouble (CGDisplayCurrentMode (mDisplay),  kCGDisplayRefreshRate));
  730. LL_INFOS("Window") << "Switched resolution to " << mFullscreenWidth
  731. << "x"   << mFullscreenHeight
  732. << "x"   << mFullscreenBits
  733. << " @ " << mFullscreenRefresh
  734. << LL_ENDL;
  735. // Update the GL context to the new screen size
  736. if (!aglUpdateContext(mContext))
  737. {
  738. setupFailure("Can't set GL fullscreen", "Error", OSMB_OK);
  739. result = FALSE;
  740. }
  741. }
  742. else
  743. {
  744. // Switching from windowed to fullscreen
  745. needsRebuild = TRUE;
  746. }
  747. }
  748. else
  749. {
  750. if(mFullscreen)
  751. {
  752. // Switching from fullscreen to windowed
  753. needsRebuild = TRUE;
  754. }
  755. else
  756. {
  757. // Windowed to windowed -- not sure why we would be called like this.  Just change the window size.
  758. // The bounds changed event handler will do the rest.
  759. if(mWindow != NULL)
  760. {
  761. ::SizeWindow(mWindow, size.mX, size.mY, true);
  762. }
  763. }
  764. }
  765. stop_glerror();
  766. if(needsRebuild || mForceRebuild)
  767. {
  768. mForceRebuild = FALSE;
  769. destroyContext();
  770. result = createContext(0, 0, size.mX, size.mY, 0, fullscreen, disable_vsync);
  771. if (result)
  772. {
  773. if(mWindow != NULL)
  774. {
  775. MacShowWindow(mWindow);
  776. BringToFront(mWindow);
  777. }
  778. llverify(gGLManager.initGL());
  779. //start with arrow cursor
  780. initCursors();
  781. setCursor( UI_CURSOR_ARROW );
  782. }
  783. }
  784. stop_glerror();
  785. return result;
  786. }
  787. void LLWindowMacOSX::destroyContext()
  788. {
  789. if (!mContext)
  790. {
  791. // We don't have a context
  792. return;
  793. }
  794. // Unhook the GL context from any drawable it may have
  795. if(mContext != NULL)
  796. {
  797. LL_DEBUGS("Window") << "destroyContext: unhooking drawable " << LL_ENDL;
  798. aglSetCurrentContext (NULL);
  799. aglSetDrawable(mContext, NULL);
  800. }
  801. // Make sure the display resolution gets restored
  802. if(mOldDisplayMode != NULL)
  803. {
  804. LL_DEBUGS("Window") << "destroyContext: restoring display resolution " << LL_ENDL;
  805. CGDisplaySwitchToMode (mDisplay, mOldDisplayMode);
  806. #if CAPTURE_ALL_DISPLAYS
  807. // Uncapture all displays (may want to do this for final build)
  808. CGReleaseAllDisplays ();
  809. #else
  810. // Uncapture only the main display (useful for debugging)
  811. CGDisplayRelease (mDisplay);
  812. #endif
  813. // CFRelease(mOldDisplayMode);
  814. mOldDisplayMode = NULL;
  815. // Remove the global event handlers the fullscreen case needed
  816. RemoveEventTypesFromHandler(mGlobalHandlerRef, GetEventTypeCount (GlobalHandlerEventList), GlobalHandlerEventList);
  817. }
  818. // Clean up remaining GL state before blowing away window
  819. gGLManager.shutdownGL();
  820. // Clean up the pixel format
  821. if(mPixelFormat != NULL)
  822. {
  823. LL_DEBUGS("Window") << "destroyContext: destroying pixel format " << LL_ENDL;
  824. aglDestroyPixelFormat(mPixelFormat);
  825. mPixelFormat = NULL;
  826. }
  827. // Remove any Carbon Event handlers we installed
  828. if(mGlobalHandlerRef != NULL)
  829. {
  830. LL_DEBUGS("Window") << "destroyContext: removing global event handler" << LL_ENDL;
  831. RemoveEventHandler(mGlobalHandlerRef);
  832. mGlobalHandlerRef = NULL;
  833. }
  834. if(mWindowHandlerRef != NULL)
  835. {
  836. LL_DEBUGS("Window") << "destroyContext: removing window event handler" << LL_ENDL;
  837. RemoveEventHandler(mWindowHandlerRef);
  838. mWindowHandlerRef = NULL;
  839. }
  840. // Cleanup any TSM document we created.
  841. if(mTSMDocument != NULL)
  842. {
  843. LL_DEBUGS("Window") << "destroyContext: deleting TSM document" << LL_ENDL;
  844. DeactivateTSMDocument(mTSMDocument);
  845. DeleteTSMDocument(mTSMDocument);
  846. mTSMDocument = NULL;
  847. }
  848. // Close the window
  849. if(mWindow != NULL)
  850. {
  851. LL_DEBUGS("Window") << "destroyContext: disposing window" << LL_ENDL;
  852. DisposeWindow(mWindow);
  853. mWindow = NULL;
  854. }
  855. // Clean up the GL context
  856. if(mContext != NULL)
  857. {
  858. LL_DEBUGS("Window") << "destroyContext: destroying GL context" << LL_ENDL;
  859. aglDestroyContext(mContext);
  860. mContext = NULL;
  861. }
  862. }
  863. LLWindowMacOSX::~LLWindowMacOSX()
  864. {
  865. destroyContext();
  866. if(mSupportedResolutions != NULL)
  867. {
  868. delete []mSupportedResolutions;
  869. }
  870. gWindowImplementation = NULL;
  871. }
  872. void LLWindowMacOSX::show()
  873. {
  874. if(IsWindowCollapsed(mWindow))
  875. CollapseWindow(mWindow, false);
  876. MacShowWindow(mWindow);
  877. BringToFront(mWindow);
  878. }
  879. void LLWindowMacOSX::hide()
  880. {
  881. setMouseClipping(FALSE);
  882. HideWindow(mWindow);
  883. }
  884. //virtual
  885. void LLWindowMacOSX::minimize()
  886. {
  887. setMouseClipping(FALSE);
  888. showCursor();
  889. CollapseWindow(mWindow, true);
  890. }
  891. //virtual
  892. void LLWindowMacOSX::restore()
  893. {
  894. show();
  895. }
  896. // close() destroys all OS-specific code associated with a window.
  897. // Usually called from LLWindowManager::destroyWindow()
  898. void LLWindowMacOSX::close()
  899. {
  900. // Is window is already closed?
  901. // if (!mWindow)
  902. // {
  903. // return;
  904. // }
  905. // Make sure cursor is visible and we haven't mangled the clipping state.
  906. setMouseClipping(FALSE);
  907. showCursor();
  908. destroyContext();
  909. }
  910. BOOL LLWindowMacOSX::isValid()
  911. {
  912. if(mFullscreen)
  913. {
  914. return(TRUE);
  915. }
  916. return (mWindow != NULL);
  917. }
  918. BOOL LLWindowMacOSX::getVisible()
  919. {
  920. BOOL result = FALSE;
  921. if(mFullscreen)
  922. {
  923. result = TRUE;
  924. }if (mWindow)
  925. {
  926. if(MacIsWindowVisible(mWindow))
  927. result = TRUE;
  928. }
  929. return(result);
  930. }
  931. BOOL LLWindowMacOSX::getMinimized()
  932. {
  933. BOOL result = FALSE;
  934. // Since the set of states where we want to act "minimized" is non-trivial, it's easier to
  935. // track things locally than to try and retrieve the state from the window manager.
  936. result = mMinimized;
  937. return(result);
  938. }
  939. BOOL LLWindowMacOSX::getMaximized()
  940. {
  941. BOOL result = FALSE;
  942. if (mWindow)
  943. {
  944. // TODO
  945. }
  946. return(result);
  947. }
  948. BOOL LLWindowMacOSX::maximize()
  949. {
  950. // TODO
  951. return FALSE;
  952. }
  953. BOOL LLWindowMacOSX::getFullscreen()
  954. {
  955. return mFullscreen;
  956. }
  957. void LLWindowMacOSX::gatherInput()
  958. {
  959. // stop bouncing icon after fixed period of time
  960. if (mBounceTimer.getStarted() && mBounceTimer.getElapsedTimeF32() > mBounceTime)
  961. {
  962. stopDockTileBounce();
  963. }
  964. // Use the old-school version so we get AppleEvent handler dispatch and menuselect handling.
  965. // Anything that has an event handler will get processed inside WaitNextEvent, so we only need to handle
  966. // the odd stuff here.
  967. EventRecord evt;
  968. while(WaitNextEvent(everyEvent, &evt, 0, NULL))
  969. {
  970. // printf("WaitNextEvent returned true, event is %d.n", evt.what);
  971. switch(evt.what)
  972. {
  973. case mouseDown:
  974. {
  975. short part;
  976. WindowRef window;
  977. long selectResult;
  978. part = FindWindow(evt.where, &window);
  979. switch ( part )
  980. {
  981. case inMenuBar:
  982. selectResult = MenuSelect(evt.where);
  983. HiliteMenu(0);
  984. break;
  985. }
  986. }
  987. break;
  988. case kHighLevelEvent:
  989. AEProcessAppleEvent (&evt);
  990. break;
  991. case updateEvt:
  992. // We shouldn't be getting these regularly (since our window will be buffered), but we need to handle them correctly...
  993. BeginUpdate((WindowRef)evt.message);
  994. EndUpdate((WindowRef)evt.message);
  995. break;
  996. }
  997. }
  998. }
  999. BOOL LLWindowMacOSX::getPosition(LLCoordScreen *position)
  1000. {
  1001. Rect window_rect;
  1002. OSStatus err = -1;
  1003. if(mFullscreen)
  1004. {
  1005. position->mX = 0;
  1006. position->mY = 0;
  1007. err = noErr;
  1008. }
  1009. else if(mWindow)
  1010. {
  1011. err = GetWindowBounds(mWindow, kWindowContentRgn, &window_rect);
  1012. position->mX = window_rect.left;
  1013. position->mY = window_rect.top;
  1014. }
  1015. else
  1016. {
  1017. llerrs << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << llendl;
  1018. }
  1019. return (err == noErr);
  1020. }
  1021. BOOL LLWindowMacOSX::getSize(LLCoordScreen *size)
  1022. {
  1023. Rect window_rect;
  1024. OSStatus err = -1;
  1025. if(mFullscreen)
  1026. {
  1027. size->mX = mFullscreenWidth;
  1028. size->mY = mFullscreenHeight;
  1029. err = noErr;
  1030. }
  1031. else if(mWindow)
  1032. {
  1033. err = GetWindowBounds(mWindow, kWindowContentRgn, &window_rect);
  1034. size->mX = window_rect.right - window_rect.left;
  1035. size->mY = window_rect.bottom - window_rect.top;
  1036. }
  1037. else
  1038. {
  1039. llerrs << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << llendl;
  1040. }
  1041. return (err == noErr);
  1042. }
  1043. BOOL LLWindowMacOSX::getSize(LLCoordWindow *size)
  1044. {
  1045. Rect window_rect;
  1046. OSStatus err = -1;
  1047. if(mFullscreen)
  1048. {
  1049. size->mX = mFullscreenWidth;
  1050. size->mY = mFullscreenHeight;
  1051. err = noErr;
  1052. }
  1053. else if(mWindow)
  1054. {
  1055. err = GetWindowBounds(mWindow, kWindowContentRgn, &window_rect);
  1056. size->mX = window_rect.right - window_rect.left;
  1057. size->mY = window_rect.bottom - window_rect.top;
  1058. }
  1059. else
  1060. {
  1061. llerrs << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << llendl;
  1062. }
  1063. return (err == noErr);
  1064. }
  1065. BOOL LLWindowMacOSX::setPosition(const LLCoordScreen position)
  1066. {
  1067. if(mWindow)
  1068. {
  1069. MacMoveWindow(mWindow, position.mX, position.mY, false);
  1070. }
  1071. return TRUE;
  1072. }
  1073. BOOL LLWindowMacOSX::setSize(const LLCoordScreen size)
  1074. {
  1075. if(mWindow)
  1076. {
  1077. SizeWindow(mWindow, size.mX, size.mY, true);
  1078. }
  1079. return TRUE;
  1080. }
  1081. void LLWindowMacOSX::swapBuffers()
  1082. {
  1083. aglSwapBuffers(mContext);
  1084. }
  1085. F32 LLWindowMacOSX::getGamma()
  1086. {
  1087. F32 result = 1.8; // Default to something sane
  1088. CGGammaValue redMin;
  1089. CGGammaValue redMax;
  1090. CGGammaValue redGamma;
  1091. CGGammaValue greenMin;
  1092. CGGammaValue greenMax;
  1093. CGGammaValue greenGamma;
  1094. CGGammaValue blueMin;
  1095. CGGammaValue blueMax;
  1096. CGGammaValue blueGamma;
  1097. if(CGGetDisplayTransferByFormula(
  1098. mDisplay,
  1099. &redMin,
  1100. &redMax,
  1101. &redGamma,
  1102. &greenMin,
  1103. &greenMax,
  1104. &greenGamma,
  1105. &blueMin,
  1106. &blueMax,
  1107. &blueGamma) == noErr)
  1108. {
  1109. // So many choices...
  1110. // Let's just return the green channel gamma for now.
  1111. result = greenGamma;
  1112. }
  1113. return result;
  1114. }
  1115. U32 LLWindowMacOSX::getFSAASamples()
  1116. {
  1117. return mFSAASamples;
  1118. }
  1119. void LLWindowMacOSX::setFSAASamples(const U32 samples)
  1120. {
  1121. mFSAASamples = samples;
  1122. mForceRebuild = TRUE;
  1123. }
  1124. BOOL LLWindowMacOSX::restoreGamma()
  1125. {
  1126. CGDisplayRestoreColorSyncSettings();
  1127. return true;
  1128. }
  1129. BOOL LLWindowMacOSX::setGamma(const F32 gamma)
  1130. {
  1131. CGGammaValue redMin;
  1132. CGGammaValue redMax;
  1133. CGGammaValue redGamma;
  1134. CGGammaValue greenMin;
  1135. CGGammaValue greenMax;
  1136. CGGammaValue greenGamma;
  1137. CGGammaValue blueMin;
  1138. CGGammaValue blueMax;
  1139. CGGammaValue blueGamma;
  1140. // MBW -- XXX -- Should we allow this in windowed mode?
  1141. if(CGGetDisplayTransferByFormula(
  1142. mDisplay,
  1143. &redMin,
  1144. &redMax,
  1145. &redGamma,
  1146. &greenMin,
  1147. &greenMax,
  1148. &greenGamma,
  1149. &blueMin,
  1150. &blueMax,
  1151. &blueGamma) != noErr)
  1152. {
  1153. return false;
  1154. }
  1155. if(CGSetDisplayTransferByFormula(
  1156. mDisplay,
  1157. redMin,
  1158. redMax,
  1159. gamma,
  1160. greenMin,
  1161. greenMax,
  1162. gamma,
  1163. blueMin,
  1164. blueMax,
  1165. gamma) != noErr)
  1166. {
  1167. return false;
  1168. }
  1169. return true;
  1170. }
  1171. BOOL LLWindowMacOSX::isCursorHidden()
  1172. {
  1173. return mCursorHidden;
  1174. }
  1175. // Constrains the mouse to the window.
  1176. void LLWindowMacOSX::setMouseClipping( BOOL b )
  1177. {
  1178. // Just stash the requested state.  We'll simulate this when the cursor is hidden by decoupling.
  1179. mIsMouseClipping = b;
  1180. if(b)
  1181. {
  1182. // llinfos << "setMouseClipping(TRUE)" << llendl;
  1183. }
  1184. else
  1185. {
  1186. // llinfos << "setMouseClipping(FALSE)" << llendl;
  1187. }
  1188. adjustCursorDecouple();
  1189. }
  1190. BOOL LLWindowMacOSX::setCursorPosition(const LLCoordWindow position)
  1191. {
  1192. BOOL result = FALSE;
  1193. LLCoordScreen screen_pos;
  1194. if (!convertCoords(position, &screen_pos))
  1195. {
  1196. return FALSE;
  1197. }
  1198. CGPoint newPosition;
  1199. // llinfos << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << llendl;
  1200. newPosition.x = screen_pos.mX;
  1201. newPosition.y = screen_pos.mY;
  1202. CGSetLocalEventsSuppressionInterval(0.0);
  1203. if(CGWarpMouseCursorPosition(newPosition) == noErr)
  1204. {
  1205. result = TRUE;
  1206. }
  1207. // Under certain circumstances, this will trigger us to decouple the cursor.
  1208. adjustCursorDecouple(true);
  1209. // trigger mouse move callback
  1210. LLCoordGL gl_pos;
  1211. convertCoords(position, &gl_pos);
  1212. mCallbacks->handleMouseMove(this, gl_pos, (MASK)0);
  1213. return result;
  1214. }
  1215. static void fixOrigin(void)
  1216. {
  1217. GrafPtr port;
  1218. Rect portrect;
  1219. ::GetPort(&port);
  1220. ::GetPortBounds(port, &portrect);
  1221. if((portrect.left != 0) || (portrect.top != 0))
  1222. {
  1223. // Mozilla sometimes changes our port origin.
  1224. ::SetOrigin(0,0);
  1225. }
  1226. }
  1227. BOOL LLWindowMacOSX::getCursorPosition(LLCoordWindow *position)
  1228. {
  1229. Point cursor_point;
  1230. LLCoordScreen screen_pos;
  1231. GrafPtr save;
  1232. if(mWindow == NULL)
  1233. return FALSE;
  1234. ::GetPort(&save);
  1235. ::SetPort(GetWindowPort(mWindow));
  1236. fixOrigin();
  1237. // gets the mouse location in local coordinates
  1238. ::GetMouse(&cursor_point);
  1239. // lldebugs << "getCursorPosition(): cursor is at " << cursor_point.h << ", " << cursor_point.v << "  port origin: " << portrect.left << ", " << portrect.top << llendl;
  1240. ::SetPort(save);
  1241. if(mCursorDecoupled)
  1242. {
  1243. // CGMouseDelta x, y;
  1244. // If the cursor's decoupled, we need to read the latest movement delta as well.
  1245. // CGGetLastMouseDelta( &x, &y );
  1246. // cursor_point.h += x;
  1247. // cursor_point.v += y;
  1248. // CGGetLastMouseDelta may behave strangely when the cursor's first captured.
  1249. // Stash in the event handler instead.
  1250. cursor_point.h += mCursorLastEventDeltaX;
  1251. cursor_point.v += mCursorLastEventDeltaY;
  1252. }
  1253. position->mX = cursor_point.h;
  1254. position->mY = cursor_point.v;
  1255. return TRUE;
  1256. }
  1257. void LLWindowMacOSX::adjustCursorDecouple(bool warpingMouse)
  1258. {
  1259. if(mIsMouseClipping && mCursorHidden)
  1260. {
  1261. if(warpingMouse)
  1262. {
  1263. // The cursor should be decoupled.  Make sure it is.
  1264. if(!mCursorDecoupled)
  1265. {
  1266. // llinfos << "adjustCursorDecouple: decoupling cursor" << llendl;
  1267. CGAssociateMouseAndMouseCursorPosition(false);
  1268. mCursorDecoupled = true;
  1269. FlushSpecificEventsFromQueue(GetCurrentEventQueue(), mMoveEventCampartorUPP, NULL);
  1270. mCursorIgnoreNextDelta = TRUE;
  1271. }
  1272. }
  1273. }
  1274. else
  1275. {
  1276. // The cursor should not be decoupled.  Make sure it isn't.
  1277. if(mCursorDecoupled)
  1278. {
  1279. // llinfos << "adjustCursorDecouple: recoupling cursor" << llendl;
  1280. CGAssociateMouseAndMouseCursorPosition(true);
  1281. mCursorDecoupled = false;
  1282. }
  1283. }
  1284. }
  1285. F32 LLWindowMacOSX::getNativeAspectRatio()
  1286. {
  1287. if (mFullscreen)
  1288. {
  1289. return (F32)mFullscreenWidth / (F32)mFullscreenHeight;
  1290. }
  1291. else
  1292. {
  1293. // The constructor for this class grabs the aspect ratio of the monitor before doing any resolution
  1294. // switching, and stashes it in mOriginalAspectRatio.  Here, we just return it.
  1295. if (mOverrideAspectRatio > 0.f)
  1296. {
  1297. return mOverrideAspectRatio;
  1298. }
  1299. return mOriginalAspectRatio;
  1300. }
  1301. }
  1302. F32 LLWindowMacOSX::getPixelAspectRatio()
  1303. {
  1304. //OS X always enforces a 1:1 pixel aspect ratio, regardless of video mode
  1305. return 1.f;
  1306. }
  1307. //static SInt32 oldWindowLevel;
  1308. // MBW -- XXX -- There's got to be a better way than this.  Find it, please...
  1309. void LLWindowMacOSX::beforeDialog()
  1310. {
  1311. if(mFullscreen)
  1312. {
  1313. #if CAPTURE_ALL_DISPLAYS
  1314. // Uncapture all displays (may want to do this for final build)
  1315. CGReleaseAllDisplays ();
  1316. #else
  1317. // Uncapture only the main display (useful for debugging)
  1318. CGDisplayRelease (mDisplay);
  1319. #endif
  1320. // kDocumentWindowClass
  1321. // kMovableModalWindowClass
  1322. // kAllWindowClasses
  1323. // GLint order = 0;
  1324. // aglSetInteger(mContext, AGL_ORDER_CONTEXT_TO_FRONT, &order);
  1325. aglSetDrawable(mContext, NULL);
  1326. // GetWindowGroupLevel(GetWindowGroupOfClass(kAllWindowClasses), &oldWindowLevel);
  1327. // SetWindowGroupLevel(GetWindowGroupOfClass(kAllWindowClasses), CGShieldingWindowLevel());
  1328. mHandsOffEvents = TRUE;
  1329. }
  1330. }
  1331. void LLWindowMacOSX::afterDialog()
  1332. {
  1333. if(mFullscreen)
  1334. {
  1335. mHandsOffEvents = FALSE;
  1336. // SetWindowGroupLevel(GetWindowGroupOfClass(kAllWindowClasses), oldWindowLevel);
  1337. aglSetFullScreen(mContext, 0, 0, 0, 0);
  1338. // GLint order = 1;
  1339. // aglSetInteger(mContext, AGL_ORDER_CONTEXT_TO_FRONT, &order);
  1340. #if CAPTURE_ALL_DISPLAYS
  1341. // Capture all displays (may want to do this for final build)
  1342. CGCaptureAllDisplays ();
  1343. #else
  1344. // Capture only the main display (useful for debugging)
  1345. CGDisplayCapture (mDisplay);
  1346. #endif
  1347. }
  1348. }
  1349. void LLWindowMacOSX::flashIcon(F32 seconds)
  1350. {
  1351. // Don't do this if we're already started, since this would try to install the NMRec twice.
  1352. if(!mBounceTimer.getStarted())
  1353. {
  1354. OSErr err;
  1355. mBounceTime = seconds;
  1356. memset(&mBounceRec, 0, sizeof(mBounceRec));
  1357. mBounceRec.qType = nmType;
  1358. mBounceRec.nmMark = 1;
  1359. err = NMInstall(&mBounceRec);
  1360. if(err == noErr)
  1361. {
  1362. mBounceTimer.start();
  1363. }
  1364. else
  1365. {
  1366. // This is very not-fatal (only problem is the icon will not bounce), but we'd like to find out about it somehow...
  1367. llinfos << "NMInstall failed with error code " << err << llendl;
  1368. }
  1369. }
  1370. }
  1371. BOOL LLWindowMacOSX::isClipboardTextAvailable()
  1372. {
  1373. OSStatus err;
  1374. ScrapRef scrap;
  1375. ScrapFlavorFlags flags;
  1376. BOOL result = false;
  1377. err = GetCurrentScrap(&scrap);
  1378. if(err == noErr)
  1379. {
  1380. err = GetScrapFlavorFlags(scrap, kScrapFlavorTypeUnicode, &flags);
  1381. }
  1382. if(err == noErr)
  1383. result = true;
  1384. return result;
  1385. }
  1386. BOOL LLWindowMacOSX::pasteTextFromClipboard(LLWString &dst)
  1387. {
  1388. OSStatus err;
  1389. ScrapRef scrap;
  1390. Size len;
  1391. BOOL result = false;
  1392. err = GetCurrentScrap(&scrap);
  1393. if(err == noErr)
  1394. {
  1395. err = GetScrapFlavorSize(scrap, kScrapFlavorTypeUnicode, &len);
  1396. }
  1397. if((err == noErr) && (len > 0))
  1398. {
  1399. int u16len = len / sizeof(U16);
  1400. U16 *temp = new U16[u16len + 1];
  1401. if (temp)
  1402. {
  1403. memset(temp, 0, (u16len + 1) * sizeof(temp[0]));
  1404. err = GetScrapFlavorData(scrap, kScrapFlavorTypeUnicode, &len, temp);
  1405. if (err == noErr)
  1406. {
  1407. // convert rn to n and r to n in the incoming text.
  1408. U16 *s, *d;
  1409. for(s = d = temp; s[0] != ''; s++, d++)
  1410. {
  1411. if(s[0] == 'r')
  1412. {
  1413. if(s[1] == 'n')
  1414. {
  1415. // CRLF, a.k.a. DOS newline.  Collapse to a single 'n'.
  1416. s++;
  1417. }
  1418. d[0] = 'n';
  1419. }
  1420. else
  1421. {
  1422. d[0] = s[0];
  1423. }
  1424. }
  1425. d[0] = '';
  1426. dst = utf16str_to_wstring(temp);
  1427. result = true;
  1428. }
  1429. delete[] temp;
  1430. }
  1431. }
  1432. return result;
  1433. }
  1434. BOOL LLWindowMacOSX::copyTextToClipboard(const LLWString &s)
  1435. {
  1436. OSStatus err;
  1437. ScrapRef scrap;
  1438. //Size len;
  1439. //char *temp;
  1440. BOOL result = false;
  1441. if (!s.empty())
  1442. {
  1443. err = GetCurrentScrap(&scrap);
  1444. if (err == noErr)
  1445. err = ClearScrap(&scrap);
  1446. if (err == noErr)
  1447. {
  1448. llutf16string utf16str = wstring_to_utf16str(s);
  1449. size_t u16len = utf16str.length() * sizeof(U16);
  1450. err = PutScrapFlavor(scrap, kScrapFlavorTypeUnicode, kScrapFlavorMaskNone, u16len, utf16str.data());
  1451. if (err == noErr)
  1452. result = true;
  1453. }
  1454. }
  1455. return result;
  1456. }
  1457. // protected
  1458. BOOL LLWindowMacOSX::resetDisplayResolution()
  1459. {
  1460. // This is only called from elsewhere in this class, and it's not used by the Mac implementation.
  1461. return true;
  1462. }
  1463. LLWindow::LLWindowResolution* LLWindowMacOSX::getSupportedResolutions(S32 &num_resolutions)
  1464. {
  1465. if (!mSupportedResolutions)
  1466. {
  1467. CFArrayRef modes = CGDisplayAvailableModes(mDisplay);
  1468. if(modes != NULL)
  1469. {
  1470. CFIndex index, cnt;
  1471. mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
  1472. mNumSupportedResolutions = 0;
  1473. //  Examine each mode
  1474. cnt = CFArrayGetCount( modes );
  1475. for ( index = 0; (index < cnt) && (mNumSupportedResolutions < MAX_NUM_RESOLUTIONS); index++ )
  1476. {
  1477. //  Pull the mode dictionary out of the CFArray
  1478. CFDictionaryRef mode = (CFDictionaryRef)CFArrayGetValueAtIndex( modes, index );
  1479. long width = getDictLong(mode, kCGDisplayWidth);
  1480. long height = getDictLong(mode, kCGDisplayHeight);
  1481. long bits = getDictLong(mode, kCGDisplayBitsPerPixel);
  1482. if(bits == BITS_PER_PIXEL && width >= 800 && height >= 600)
  1483. {
  1484. BOOL resolution_exists = FALSE;
  1485. for(S32 i = 0; i < mNumSupportedResolutions; i++)
  1486. {
  1487. if (mSupportedResolutions[i].mWidth == width &&
  1488. mSupportedResolutions[i].mHeight == height)
  1489. {
  1490. resolution_exists = TRUE;
  1491. }
  1492. }
  1493. if (!resolution_exists)
  1494. {
  1495. mSupportedResolutions[mNumSupportedResolutions].mWidth = width;
  1496. mSupportedResolutions[mNumSupportedResolutions].mHeight = height;
  1497. mNumSupportedResolutions++;
  1498. }
  1499. }
  1500. }
  1501. }
  1502. }
  1503. num_resolutions = mNumSupportedResolutions;
  1504. return mSupportedResolutions;
  1505. }
  1506. BOOL LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordWindow *to)
  1507. {
  1508. S32 client_height;
  1509. Rect client_rect;
  1510. if(mFullscreen)
  1511. {
  1512. // In the fullscreen case, the "window" is the entire screen.
  1513. client_rect.left = 0;
  1514. client_rect.top = 0;
  1515. client_rect.right = mFullscreenWidth;
  1516. client_rect.bottom = mFullscreenHeight;
  1517. }
  1518. else if (!mWindow ||
  1519. (GetWindowBounds(mWindow, kWindowContentRgn, &client_rect) != noErr) ||
  1520. NULL == to)
  1521. {
  1522. return FALSE;
  1523. }
  1524. to->mX = from.mX;
  1525. client_height = client_rect.bottom - client_rect.top;
  1526. to->mY = client_height - from.mY - 1;
  1527. return TRUE;
  1528. }
  1529. BOOL LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordGL* to)
  1530. {
  1531. S32 client_height;
  1532. Rect client_rect;
  1533. if(mFullscreen)
  1534. {
  1535. // In the fullscreen case, the "window" is the entire screen.
  1536. client_rect.left = 0;
  1537. client_rect.top = 0;
  1538. client_rect.right = mFullscreenWidth;
  1539. client_rect.bottom = mFullscreenHeight;
  1540. }
  1541. else if (!mWindow ||
  1542. (GetWindowBounds(mWindow, kWindowContentRgn, &client_rect) != noErr) ||
  1543. NULL == to)
  1544. {
  1545. return FALSE;
  1546. }
  1547. to->mX = from.mX;
  1548. client_height = client_rect.bottom - client_rect.top;
  1549. to->mY = client_height - from.mY - 1;
  1550. return TRUE;
  1551. }
  1552. BOOL LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordWindow* to)
  1553. {
  1554. if(mFullscreen)
  1555. {
  1556. // In the fullscreen case, window and screen coordinates are the same.
  1557. to->mX = from.mX;
  1558. to->mY = from.mY;
  1559. return TRUE;
  1560. }
  1561. else if(mWindow)
  1562. {
  1563. GrafPtr save;
  1564. Point mouse_point;
  1565. mouse_point.h = from.mX;
  1566. mouse_point.v = from.mY;
  1567. ::GetPort(&save);
  1568. ::SetPort(GetWindowPort(mWindow));
  1569. fixOrigin();
  1570. ::GlobalToLocal(&mouse_point);
  1571. to->mX = mouse_point.h;
  1572. to->mY = mouse_point.v;
  1573. ::SetPort(save);
  1574. return TRUE;
  1575. }
  1576. return FALSE;
  1577. }
  1578. BOOL LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordScreen *to)
  1579. {
  1580. if(mFullscreen)
  1581. {
  1582. // In the fullscreen case, window and screen coordinates are the same.
  1583. to->mX = from.mX;
  1584. to->mY = from.mY;
  1585. return TRUE;
  1586. }
  1587. else if(mWindow)
  1588. {
  1589. GrafPtr save;
  1590. Point mouse_point;
  1591. mouse_point.h = from.mX;
  1592. mouse_point.v = from.mY;
  1593. ::GetPort(&save);
  1594. ::SetPort(GetWindowPort(mWindow));
  1595. fixOrigin();
  1596. LocalToGlobal(&mouse_point);
  1597. to->mX = mouse_point.h;
  1598. to->mY = mouse_point.v;
  1599. ::SetPort(save);
  1600. return TRUE;
  1601. }
  1602. return FALSE;
  1603. }
  1604. BOOL LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordGL *to)
  1605. {
  1606. LLCoordWindow window_coord;
  1607. return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
  1608. }
  1609. BOOL LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordScreen *to)
  1610. {
  1611. LLCoordWindow window_coord;
  1612. return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
  1613. }
  1614. void LLWindowMacOSX::setupFailure(const std::string& text, const std::string& caption, U32 type)
  1615. {
  1616. destroyContext();
  1617. OSMessageBox(text, caption, type);
  1618. }
  1619. pascal Boolean LLWindowMacOSX::staticMoveEventComparator( EventRef event, void* data)
  1620. {
  1621. UInt32  evtClass = GetEventClass (event);
  1622. UInt32  evtKind = GetEventKind (event);
  1623. if ((evtClass == kEventClassMouse) && ((evtKind == kEventMouseDragged) || (evtKind == kEventMouseMoved)))
  1624. {
  1625. return true;
  1626. }
  1627. else
  1628. {
  1629. return false;
  1630. }
  1631. }
  1632. pascal OSStatus LLWindowMacOSX::staticEventHandler(EventHandlerCallRef myHandler, EventRef event, void* userData)
  1633. {
  1634. LLWindowMacOSX *self = (LLWindowMacOSX*)userData;
  1635. return(self->eventHandler(myHandler, event));
  1636. }
  1637. OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef event)
  1638. {
  1639. OSStatus result = eventNotHandledErr;
  1640. UInt32  evtClass = GetEventClass (event);
  1641. UInt32  evtKind = GetEventKind (event);
  1642. // Always handle command events, even in hands-off mode.
  1643. if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess))
  1644. {
  1645. HICommand command;
  1646. GetEventParameter (event, kEventParamDirectObject, typeHICommand, NULL, sizeof(command), NULL, &command);
  1647. switch(command.commandID)
  1648. {
  1649. case kHICommandQuit:
  1650. if(mCallbacks->handleCloseRequest(this))
  1651. {
  1652. // Get the app to initiate cleanup.
  1653. mCallbacks->handleQuit(this);
  1654. // The app is responsible for calling destroyWindow when done with GL
  1655. }
  1656. result = noErr;
  1657. break;
  1658. default:
  1659. // MBW -- XXX -- Should we handle other events here?
  1660. break;
  1661. }
  1662. }
  1663. if(mHandsOffEvents)
  1664. {
  1665. return(result);
  1666. }
  1667. switch (evtClass)
  1668. {
  1669. case kEventClassTextInput:
  1670. {
  1671. switch (evtKind)
  1672. {
  1673. case kEventTextInputUpdateActiveInputArea:
  1674. {
  1675. EventParamType param_type;
  1676. long fix_len;
  1677. UInt32 text_len;
  1678. if (mPreeditor
  1679. && (result = GetEventParameter(event, kEventParamTextInputSendFixLen,
  1680. typeLongInteger, &param_type, sizeof(fix_len), NULL, &fix_len)) == noErr
  1681. && typeLongInteger == param_type 
  1682. && (result = GetEventParameter(event, kEventParamTextInputSendText,
  1683. typeUnicodeText, &param_type, 0, &text_len, NULL)) == noErr
  1684. && typeUnicodeText == param_type)
  1685. {
  1686. // Handle an optional (but essential to facilitate TSMDA) ReplaceRange param.
  1687. CFRange range;
  1688. if (GetEventParameter(event, kEventParamTextInputSendReplaceRange,
  1689. typeCFRange, &param_type, sizeof(range), NULL, &range) == noErr
  1690. && typeCFRange == param_type)
  1691. {
  1692. // Although the spec. is unclear, replace range should
  1693. // not present when there is an active preedit.  We just
  1694. // ignore the case.  markAsPreedit will detect the case and warn it.
  1695. const LLWString & text = mPreeditor->getPreeditString();
  1696. const S32 location = wstring_wstring_length_from_utf16_length(text, 0, range.location);
  1697. const S32 length = wstring_wstring_length_from_utf16_length(text, location, range.length);
  1698. mPreeditor->markAsPreedit(location, length);
  1699. }
  1700. mPreeditor->resetPreedit();
  1701. // Receive the text from input method.
  1702. U16 *const text = new U16[text_len / sizeof(U16)];
  1703. GetEventParameter(event, kEventParamTextInputSendText, typeUnicodeText, NULL, text_len, NULL, text);
  1704. if (fix_len < 0)
  1705. {
  1706. // Do we still need this?  Seems obsolete...
  1707. fix_len = text_len;
  1708. }
  1709. const LLWString fix_string
  1710. = utf16str_to_wstring(llutf16string(text, fix_len / sizeof(U16)));
  1711. const LLWString preedit_string
  1712. = utf16str_to_wstring(llutf16string(text + fix_len / sizeof(U16), (text_len - fix_len) / sizeof(U16)));
  1713. delete[] text;
  1714. // Handle fixed (comitted) string.
  1715. if (fix_string.length() > 0)
  1716. {
  1717. for (LLWString::const_iterator i = fix_string.begin(); i != fix_string.end(); i++)
  1718. {
  1719. mPreeditor->handleUnicodeCharHere(*i);
  1720. }
  1721. }
  1722. // Receive the segment info and caret position.
  1723. LLPreeditor::segment_lengths_t preedit_segment_lengths;
  1724. LLPreeditor::standouts_t preedit_standouts;
  1725. S32 caret_position = preedit_string.length();
  1726. UInt32 text_range_array_size;
  1727. if (GetEventParameter(event, kEventParamTextInputSendHiliteRng, typeTextRangeArray,
  1728. &param_type, 0, &text_range_array_size, NULL) == noErr
  1729. && typeTextRangeArray == param_type
  1730. && text_range_array_size > sizeof(TextRangeArray))
  1731. {
  1732. // TextRangeArray is a variable-length struct.
  1733. TextRangeArray * const text_range_array = (TextRangeArray *) new char[text_range_array_size];
  1734. GetEventParameter(event, kEventParamTextInputSendHiliteRng, typeTextRangeArray,
  1735. NULL, text_range_array_size, NULL, text_range_array);
  1736. // WARNING: We assume ranges are in ascending order, 
  1737. // although the condition is undocumented.  It seems
  1738. // OK to assume this.  I also assumed
  1739. // the ranges are contiguous in previous versions, but I
  1740. // have heard a rumore that older versions os ATOK may 
  1741. // return ranges with some _gap_.  I don't know whether
  1742. // it is true, but I'm preparing my code for the case.
  1743. const S32 ranges = text_range_array->fNumOfRanges;
  1744. preedit_segment_lengths.reserve(ranges);
  1745. preedit_standouts.reserve(ranges);
  1746. S32 last_bytes = 0;
  1747. S32 last_utf32 = 0;
  1748. for (S32 i = 0; i < ranges; i++)
  1749. {
  1750. const TextRange &range = text_range_array->fRange[i];
  1751. if (range.fStart > last_bytes)
  1752. {
  1753. const S32 length_utf16 = (range.fStart - last_bytes) / sizeof(U16);
  1754. const S32 length_utf32 = wstring_wstring_length_from_utf16_length(preedit_string, last_utf32, length_utf16);
  1755. preedit_segment_lengths.push_back(length_utf32);
  1756. preedit_standouts.push_back(FALSE);
  1757. last_utf32 += length_utf32;
  1758. }
  1759. if (range.fEnd > range.fStart)
  1760. {
  1761. const S32 length_utf16 = (range.fEnd - range.fStart) / sizeof(U16);
  1762. const S32 length_utf32 = wstring_wstring_length_from_utf16_length(preedit_string, last_utf32, length_utf16);
  1763. preedit_segment_lengths.push_back(length_utf32);
  1764. preedit_standouts.push_back(
  1765. kTSMHiliteSelectedRawText == range.fHiliteStyle
  1766. || kTSMHiliteSelectedConvertedText ==  range.fHiliteStyle
  1767. || kTSMHiliteSelectedText == range.fHiliteStyle);
  1768. last_utf32 += length_utf32;
  1769. }
  1770. if (kTSMHiliteCaretPosition == range.fHiliteStyle)
  1771. {
  1772. caret_position = last_utf32;
  1773. }
  1774. last_bytes = range.fEnd;
  1775. }
  1776. if (preedit_string.length() > last_utf32)
  1777. {
  1778. preedit_segment_lengths.push_back(preedit_string.length() - last_utf32);
  1779. preedit_standouts.push_back(FALSE);
  1780. }
  1781. delete[] (char *) text_range_array;
  1782. }
  1783. // Handle preedit string.
  1784. if (preedit_string.length() == 0)
  1785. {
  1786. preedit_segment_lengths.clear();
  1787. preedit_standouts.clear();
  1788. }
  1789. else if (preedit_segment_lengths.size() == 0)
  1790. {
  1791. preedit_segment_lengths.push_back(preedit_string.length());
  1792. preedit_standouts.push_back(FALSE);
  1793. }
  1794. mPreeditor->updatePreedit(preedit_string, preedit_segment_lengths, preedit_standouts, caret_position);
  1795. result = noErr;
  1796. }
  1797. }
  1798. break;
  1799. case kEventTextInputUnicodeForKeyEvent:
  1800. {
  1801. UInt32 modifiers = 0;
  1802. // First, process the raw event.
  1803. {
  1804. EventRef rawEvent = NULL;
  1805. // Get the original event and extract the modifier keys, so we can ignore command-key events.
  1806. if (GetEventParameter(event, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(rawEvent), NULL, &rawEvent) == noErr)
  1807. {
  1808. // Grab the modifiers for later use in this function...
  1809. GetEventParameter (rawEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
  1810. // and call this function recursively to handle the raw key event.
  1811. eventHandler (myHandler, rawEvent);
  1812. // save the raw event until we're done processing the unicode input as well.
  1813. mRawKeyEvent = rawEvent;
  1814. }
  1815. }
  1816. OSStatus err = noErr;
  1817. EventParamType actualType = typeUnicodeText;
  1818. UInt32 actualSize = 0;
  1819. size_t actualCount = 0;
  1820. U16 *buffer = NULL;
  1821. // Get the size of the unicode data
  1822. err = GetEventParameter (event, kEventParamTextInputSendText, typeUnicodeText, &actualType, 0, &actualSize, NULL);
  1823. if(err == noErr)
  1824. {
  1825. // allocate a buffer and get the actual data.
  1826. actualCount = actualSize / sizeof(U16);
  1827. buffer = new U16[actualCount];
  1828. err = GetEventParameter (event, kEventParamTextInputSendText, typeUnicodeText, &actualType, actualSize, &actualSize, buffer);
  1829. }
  1830. if(err == noErr)
  1831. {
  1832. if(modifiers & (cmdKey | controlKey))
  1833. {
  1834. // This was a menu key equivalent.  Ignore it.
  1835. }
  1836. else
  1837. {
  1838. MASK mask = LLWindowMacOSX::modifiersToMask(modifiers);
  1839. llassert( actualType == typeUnicodeText );
  1840. // The result is a UTF16 buffer.  Pass the characters in turn to handleUnicodeChar.
  1841. // Convert to UTF32 and go character-by-character.
  1842. llutf16string utf16(buffer, actualCount);
  1843. LLWString utf32 = utf16str_to_wstring(utf16);
  1844. LLWString::iterator iter;
  1845. for(iter = utf32.begin(); iter != utf32.end(); iter++)
  1846. {
  1847. mCallbacks->handleUnicodeChar(*iter, mask);
  1848. }
  1849. }
  1850. }
  1851. if(buffer != NULL)
  1852. {
  1853. delete[] buffer;
  1854. }
  1855. mRawKeyEvent = NULL;
  1856. result = err;
  1857. }
  1858. break;
  1859. case kEventTextInputOffsetToPos:
  1860. {
  1861. EventParamType param_type;
  1862. long offset;
  1863. if (mPreeditor
  1864. && GetEventParameter(event, kEventParamTextInputSendTextOffset, typeLongInteger,
  1865. &param_type, sizeof(offset), NULL, &offset) == noErr
  1866. && typeLongInteger == param_type)
  1867. {
  1868. S32 preedit, preedit_length;
  1869. mPreeditor->getPreeditRange(&preedit, &preedit_length);
  1870. const LLWString & text = mPreeditor->getPreeditString();
  1871.  
  1872. LLCoordGL caret_coord;
  1873. LLRect preedit_bounds;
  1874. if (0 <= offset
  1875. && mPreeditor->getPreeditLocation(wstring_wstring_length_from_utf16_length(text, preedit, offset / sizeof(U16)),
  1876.   &caret_coord, &preedit_bounds, NULL))
  1877. {
  1878. LLCoordGL caret_base_coord(caret_coord.mX, preedit_bounds.mBottom);
  1879. LLCoordScreen caret_base_coord_screen;
  1880. convertCoords(caret_base_coord, &caret_base_coord_screen);
  1881. Point qd_point;
  1882. qd_point.h = caret_base_coord_screen.mX;
  1883. qd_point.v = caret_base_coord_screen.mY;
  1884. SetEventParameter(event, kEventParamTextInputReplyPoint, typeQDPoint, sizeof(qd_point), &qd_point);
  1885. short line_height = (short) preedit_bounds.getHeight();
  1886. SetEventParameter(event, kEventParamTextInputReplyLineHeight, typeShortInteger, sizeof(line_height), &line_height);
  1887. result = noErr;
  1888. }
  1889. else
  1890. {
  1891. result = errOffsetInvalid;
  1892. }
  1893. }
  1894. }
  1895. break;
  1896. case kEventTextInputGetSelectedText:
  1897. {
  1898. if (mPreeditor)
  1899. {
  1900. S32 selection, selection_length;
  1901. mPreeditor->getSelectionRange(&selection, &selection_length);
  1902. if (selection_length)
  1903. {
  1904. const LLWString text = mPreeditor->getPreeditString().substr(selection, selection_length);
  1905. const llutf16string text_utf16 = wstring_to_utf16str(text);
  1906. result = SetEventParameter(event, kEventParamTextInputReplyText, typeUnicodeText,
  1907. text_utf16.length() * sizeof(U16), text_utf16.c_str());
  1908. }
  1909. }
  1910. }
  1911. break;
  1912. }
  1913. }
  1914. break;
  1915. case kEventClassKeyboard:
  1916. {
  1917. UInt32 keyCode = 0;
  1918. char charCode = 0;
  1919. UInt32 modifiers = 0;
  1920. // Some of these may fail for some event types.  That's fine.
  1921. GetEventParameter (event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode);
  1922. GetEventParameter (event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
  1923. // save the raw event so getNativeKeyData can use it.
  1924. mRawKeyEvent = event;
  1925. // printf("key event, key code = 0x%08x, char code = 0x%02x (%c), modifiers = 0x%08xn", keyCode, charCode, (char)charCode, modifiers);
  1926. // fflush(stdout);
  1927. switch (evtKind)
  1928. {
  1929. case kEventRawKeyDown:
  1930. case kEventRawKeyRepeat:
  1931. if (gDebugWindowProc)
  1932. {
  1933. printf("key down, key code = 0x%08x, char code = 0x%02x (%c), modifiers = 0x%08xn", 
  1934. (unsigned int)keyCode, charCode, (char)charCode, (unsigned int)modifiers);
  1935. fflush(stdout);
  1936. }
  1937. gKeyboard->handleKeyDown(keyCode, modifiers);
  1938. result = eventNotHandledErr;
  1939. break;
  1940. case kEventRawKeyUp:
  1941. if (gDebugWindowProc)
  1942. {
  1943. printf("key up,   key code = 0x%08x, char code = 0x%02x (%c), modifiers = 0x%08xn", 
  1944. (unsigned int)keyCode, charCode, (char)charCode, (unsigned int)modifiers);
  1945. fflush(stdout);
  1946. }
  1947. gKeyboard->handleKeyUp(keyCode, modifiers);
  1948. result = eventNotHandledErr;
  1949. break;
  1950. case kEventRawKeyModifiersChanged:
  1951. // The keyboard input system wants key up/down events for modifier keys.
  1952. // Mac OS doesn't supply these directly, but can supply events when the collective modifier state changes.
  1953. // Use these events to generate up/down events for the modifiers.
  1954. if((modifiers & shiftKey) && !(mLastModifiers & shiftKey))
  1955. {
  1956. if (gDebugWindowProc) printf("Shift key down eventn");
  1957. gKeyboard->handleKeyDown(0x38, (modifiers & 0x00FFFFFF) | ((0x38 << 24) & 0xFF000000));
  1958. }
  1959. else if(!(modifiers & shiftKey) && (mLastModifiers & shiftKey))
  1960. {
  1961. if (gDebugWindowProc) printf("Shift key up eventn");
  1962. gKeyboard->handleKeyUp(0x38, (modifiers & 0x00FFFFFF) | ((0x38 << 24) & 0xFF000000));
  1963. }
  1964. if((modifiers & alphaLock) && !(mLastModifiers & alphaLock))
  1965. {
  1966. if (gDebugWindowProc) printf("Caps lock down eventn");
  1967. gKeyboard->handleKeyDown(0x39, (modifiers & 0x00FFFFFF) | ((0x39 << 24) & 0xFF000000));
  1968. }
  1969. else if(!(modifiers & alphaLock) && (mLastModifiers & alphaLock))
  1970. {
  1971. if (gDebugWindowProc) printf("Caps lock up eventn");
  1972. gKeyboard->handleKeyUp(0x39, (modifiers & 0x00FFFFFF) | ((0x39 << 24) & 0xFF000000));
  1973. }
  1974. if((modifiers & controlKey) && !(mLastModifiers & controlKey))
  1975. {
  1976. if (gDebugWindowProc) printf("Control key down eventn");
  1977. gKeyboard->handleKeyDown(0x3b, (modifiers & 0x00FFFFFF) | ((0x3b << 24) & 0xFF000000));
  1978. }
  1979. else if(!(modifiers & controlKey) && (mLastModifiers & controlKey))
  1980. {
  1981. if (gDebugWindowProc) printf("Control key up eventn");
  1982. gKeyboard->handleKeyUp(0x3b, (modifiers & 0x00FFFFFF) | ((0x3b << 24) & 0xFF000000));
  1983. }
  1984. if((modifiers & optionKey) && !(mLastModifiers & optionKey))
  1985. {
  1986. if (gDebugWindowProc) printf("Option key down eventn");
  1987. gKeyboard->handleKeyDown(0x3a, (modifiers & 0x00FFFFFF) | ((0x3a << 24) & 0xFF000000));
  1988. }
  1989. else if(!(modifiers & optionKey) && (mLastModifiers & optionKey))
  1990. {
  1991. if (gDebugWindowProc) printf("Option key up eventn");
  1992. gKeyboard->handleKeyUp(0x3a, (modifiers & 0x00FFFFFF) | ((0x3a << 24) & 0xFF000000));
  1993. }
  1994. // When the state of the 'Fn' key (the one that changes some of the mappings on a powerbook/macbook keyboard
  1995. // to an embedded keypad) changes, it may subsequently cause a key up event to be lost, which may lead to 
  1996. // a movement key getting "stuck" down.  This is bad.
  1997. // This is an OS bug -- even the GetKeys() API doesn't tell you the key has been released.
  1998. // This workaround causes all held-down keys to be reset whenever the state of the Fn key changes.  This isn't
  1999. // exactly what we want, but it does avoid the case where you get stuck running forward.
  2000. if((modifiers & kEventKeyModifierFnMask) != (mLastModifiers & kEventKeyModifierFnMask))
  2001. {
  2002. if (gDebugWindowProc) printf("Fn key state change eventn");
  2003. gKeyboard->resetKeys();
  2004. }
  2005. if (gDebugWindowProc) fflush(stdout);
  2006. mLastModifiers = modifiers;
  2007. result = eventNotHandledErr;
  2008. break;
  2009. }
  2010. mRawKeyEvent = NULL;
  2011. }
  2012. break;
  2013. case kEventClassMouse:
  2014. {
  2015. result = CallNextEventHandler(myHandler, event);
  2016. if (eventNotHandledErr == result)
  2017. { // only handle events not already handled (prevents wierd resize interaction)
  2018. EventMouseButton button = kEventMouseButtonPrimary;
  2019. HIPoint location = {0.0f, 0.0f};
  2020. UInt32 modifiers = 0;
  2021. UInt32 clickCount = 1;
  2022. long wheelDelta = 0;
  2023. LLCoordScreen inCoords;
  2024. LLCoordGL outCoords;
  2025. MASK mask = 0;
  2026. GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
  2027. GetEventParameter(event, kEventParamMouseLocation, typeHIPoint, NULL, sizeof(location), NULL, &location);
  2028. GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
  2029. GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(wheelDelta), NULL, &wheelDelta);
  2030. GetEventParameter(event, kEventParamClickCount, typeUInt32, NULL, sizeof(clickCount), NULL, &clickCount);
  2031. inCoords.mX = llround(location.x);
  2032. inCoords.mY = llround(location.y);
  2033. if(modifiers & shiftKey) { mask |= MASK_SHIFT; }
  2034. if(modifiers & controlKey) { mask |= MASK_CONTROL; }
  2035. if(modifiers & optionKey) { mask |= MASK_ALT; }
  2036. if(mCursorDecoupled)
  2037. {
  2038. CGMouseDelta x, y;
  2039. // If the cursor's decoupled, we need to read the latest movement delta as well.
  2040. CGGetLastMouseDelta( &x, &y );
  2041. mCursorLastEventDeltaX = x;
  2042. mCursorLastEventDeltaY = y;
  2043. if(mCursorIgnoreNextDelta)
  2044. {
  2045. mCursorLastEventDeltaX = 0;
  2046. mCursorLastEventDeltaY = 0;
  2047. mCursorIgnoreNextDelta = FALSE;
  2048. }
  2049. }
  2050. else
  2051. {
  2052. mCursorLastEventDeltaX = 0;
  2053. mCursorLastEventDeltaY = 0;
  2054. }
  2055. inCoords.mX += mCursorLastEventDeltaX;
  2056. inCoords.mY += mCursorLastEventDeltaY;
  2057. convertCoords(inCoords, &outCoords);
  2058. // printf("coords in: %d, %d; coords out: %d, %dn", inCoords.mX, inCoords.mY, outCoords.mX, outCoords.mY);
  2059. // fflush(stdout);
  2060. switch (evtKind)
  2061. {
  2062. case kEventMouseDown:
  2063. if (mLanguageTextInputAllowed)
  2064. {
  2065. // We need to interrupt before handling mouse events,
  2066. // so that the fixed string from IM are delivered to
  2067. // the currently focused UI component.
  2068. interruptLanguageTextInput();
  2069. }
  2070. switch(button)
  2071. {
  2072. case kEventMouseButtonPrimary:
  2073. if(modifiers & cmdKey)
  2074. {
  2075. // Simulate a right click
  2076. mSimulatedRightClick = true;
  2077. mCallbacks->handleRightMouseDown(this, outCoords, mask);
  2078. }
  2079. else if(clickCount == 2)
  2080. {
  2081. // Windows double-click events replace the second mousedown event in a double-click.
  2082. mCallbacks->handleDoubleClick(this, outCoords, mask);
  2083. }
  2084. else
  2085. {
  2086. mCallbacks->handleMouseDown(this, outCoords, mask);
  2087. }
  2088. break;
  2089. case kEventMouseButtonSecondary:
  2090. mCallbacks->handleRightMouseDown(this, outCoords, mask);
  2091. break;
  2092. case kEventMouseButtonTertiary:
  2093. mCallbacks->handleMiddleMouseDown(this, outCoords, mask);
  2094. break;
  2095. }
  2096. result = noErr;
  2097. break;
  2098. case kEventMouseUp:
  2099. switch(button)
  2100. {
  2101. case kEventMouseButtonPrimary:
  2102. if(mSimulatedRightClick)
  2103. {
  2104. // End of simulated right click
  2105. mSimulatedRightClick = false;
  2106. mCallbacks->handleRightMouseUp(this, outCoords, mask);
  2107. }
  2108. else
  2109. {
  2110. mCallbacks->handleMouseUp(this, outCoords, mask);
  2111. }
  2112. break;
  2113. case kEventMouseButtonSecondary:
  2114. mCallbacks->handleRightMouseUp(this, outCoords, mask);
  2115. break;
  2116. case kEventMouseButtonTertiary:
  2117. mCallbacks->handleMiddleMouseUp(this, outCoords, mask);
  2118. break;
  2119. }
  2120. result = noErr;
  2121. break;
  2122. case kEventMouseWheelMoved:
  2123. {
  2124. static S32  z_delta = 0;
  2125. z_delta += wheelDelta;
  2126. if (z_delta <= -WHEEL_DELTA || WHEEL_DELTA <= z_delta)
  2127. {
  2128. mCallbacks->handleScrollWheel(this, -z_delta / WHEEL_DELTA);
  2129. z_delta = 0;
  2130. }
  2131. }
  2132. result = noErr;
  2133. break;
  2134. case kEventMouseDragged:
  2135. case kEventMouseMoved:
  2136. mCallbacks->handleMouseMove(this, outCoords, mask);
  2137. result = noErr;
  2138. break;
  2139. }
  2140. }
  2141. }
  2142. break;
  2143. case kEventClassWindow:
  2144. switch(evtKind)
  2145. {
  2146. case kEventWindowActivated:
  2147. if (mTSMDocument)
  2148. {
  2149. ActivateTSMDocument(mTSMDocument);
  2150. }
  2151. mCallbacks->handleFocus(this);
  2152. break;
  2153. case kEventWindowDeactivated:
  2154. if (mTSMDocument)
  2155. {
  2156. DeactivateTSMDocument(mTSMDocument);
  2157. }
  2158. mCallbacks->handleFocusLost(this);
  2159. break;
  2160. case kEventWindowBoundsChanging:
  2161. {
  2162. // This is where we would constrain move/resize to a particular screen
  2163. const S32 MIN_WIDTH  = 320;
  2164. const S32 MIN_HEIGHT = 240;
  2165. Rect currentBounds;
  2166. Rect previousBounds;
  2167. GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &currentBounds);
  2168. GetEventParameter(event, kEventParamPreviousBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &previousBounds);
  2169. if ((currentBounds.right - currentBounds.left) < MIN_WIDTH)
  2170. {
  2171. currentBounds.right = currentBounds.left + MIN_WIDTH;
  2172. }
  2173. if ((currentBounds.bottom - currentBounds.top) < MIN_HEIGHT)
  2174. {
  2175. currentBounds.bottom = currentBounds.top + MIN_HEIGHT;
  2176. }
  2177. SetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, sizeof(Rect), &currentBounds);
  2178. result = noErr;
  2179. }
  2180. break;
  2181. case kEventWindowBoundsChanged:
  2182. {
  2183. Rect newBounds;
  2184. GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &newBounds);
  2185. aglUpdateContext(mContext);
  2186. mCallbacks->handleResize(this, newBounds.right - newBounds.left, newBounds.bottom - newBounds.top);
  2187. }
  2188. break;
  2189. case kEventWindowClose:
  2190. if(mCallbacks->handleCloseRequest(this))
  2191. {
  2192. // Get the app to initiate cleanup.
  2193. mCallbacks->handleQuit(this);
  2194. // The app is responsible for calling destroyWindow when done with GL
  2195. }
  2196. result = noErr;
  2197. break;
  2198. case kEventWindowHidden:
  2199. // llinfos << "LLWindowMacOSX: Deactivating on hide" << llendl;
  2200. mMinimized = TRUE;
  2201. mCallbacks->handleActivate(this, false);
  2202. // result = noErr;
  2203. break;
  2204. case kEventWindowShown:
  2205. // llinfos << "LLWindowMacOSX: Activating on show" << llendl;
  2206. mMinimized = FALSE;
  2207. mCallbacks->handleActivate(this, true);
  2208. // result = noErr;
  2209. break;
  2210. case kEventWindowCollapsed:
  2211. // llinfos << "LLWindowMacOSX: Deactivating on collapse" << llendl;
  2212. mMinimized = TRUE;
  2213. mCallbacks->handleActivate(this, false);
  2214. // result = noErr;
  2215. break;
  2216. case kEventWindowExpanded:
  2217. // llinfos << "LLWindowMacOSX: Activating on expand" << llendl;
  2218. mMinimized = FALSE;
  2219. mCallbacks->handleActivate(this, true);
  2220. // result = noErr;
  2221. break;
  2222. case kEventWindowGetClickActivation:
  2223. // BringToFront(mWindow);
  2224. // result = noErr;
  2225. break;
  2226. }
  2227. break;
  2228. case kEventClassTSMDocumentAccess:
  2229. if (mPreeditor)
  2230. {
  2231. switch(evtKind)
  2232. {
  2233. case kEventTSMDocumentAccessGetLength:
  2234. {
  2235. // Return the number of UTF-16 units in the text, excluding those for preedit.
  2236. S32 preedit, preedit_length;
  2237. mPreeditor->getPreeditRange(&preedit, &preedit_length);
  2238. const LLWString & text = mPreeditor->getPreeditString();
  2239. const CFIndex length = wstring_utf16_length(text, 0, preedit)
  2240. + wstring_utf16_length(text, preedit + preedit_length, text.length());
  2241. result = SetEventParameter(event, kEventParamTSMDocAccessCharacterCount, typeCFIndex, sizeof(length), &length);
  2242. }
  2243. break;
  2244. case kEventTSMDocumentAccessGetSelectedRange:
  2245. {
  2246. // Return the selected range, excluding preedit.
  2247. // In our preeditor, preedit and selection are exclusive, so,
  2248. // when it has a preedit, there is no selection and the 
  2249. // insertion point is on the preedit that corrupses into the
  2250. // beginning of the preedit when the preedit was removed.
  2251. S32 preedit, preedit_length;
  2252. mPreeditor->getPreeditRange(&preedit, &preedit_length);
  2253. const LLWString & text = mPreeditor->getPreeditString();
  2254. CFRange range;
  2255. if (preedit_length)
  2256. {
  2257. range.location = wstring_utf16_length(text, 0, preedit);
  2258. range.length = 0;
  2259. }
  2260. else
  2261. {
  2262. S32 selection, selection_length;
  2263. mPreeditor->getSelectionRange(&selection, &selection_length);
  2264. range.location = wstring_utf16_length(text, 0, selection);
  2265. range.length = wstring_utf16_length(text, selection, selection_length);
  2266. }
  2267. result = SetEventParameter(event, kEventParamTSMDocAccessReplyCharacterRange, typeCFRange, sizeof(range), &range);
  2268. }
  2269. break;
  2270. case kEventTSMDocumentAccessGetCharacters:
  2271. {
  2272. UniChar *target_pointer;
  2273. CFRange range;
  2274. EventParamType param_type;
  2275. if ((result = GetEventParameter(event, kEventParamTSMDocAccessSendCharacterRange,
  2276. typeCFRange, &param_type, sizeof(range), NULL, &range)) == noErr
  2277. && typeCFRange == param_type
  2278. && (result = GetEventParameter(event, kEventParamTSMDocAccessSendCharactersPtr,
  2279. typePtr, &param_type, sizeof(target_pointer), NULL, &target_pointer)) == noErr
  2280. && typePtr == param_type)
  2281. {
  2282. S32 preedit, preedit_length;
  2283. mPreeditor->getPreeditRange(&preedit, &preedit_length);
  2284. const LLWString & text = mPreeditor->getPreeditString();
  2285. // The GetCharacters event of TSMDA has a fundamental flaw;
  2286. // An input method need to decide the starting offset and length
  2287. // *before* it actually see the contents, so it is impossible
  2288. // to guarantee the character-aligned access.  The event reply
  2289. // has no way to indicate a condition something like "Request
  2290. // was not fulfilled due to unaligned access.  Please retry."
  2291. // Any error sent back to the input method stops use of TSMDA
  2292. // entirely during the session...
  2293. // We need to simulate very strictly the behaviour as if the
  2294. // underlying *text engine* holds the contents in UTF-16.
  2295. // I guess this is the reason why Apple repeats saying "all
  2296. // text handling application should use UTF-16."  They are
  2297. // trying to _fix_ the flaw by changing the appliations...
  2298. // ... or, domination of UTF-16 in the industry may be a part
  2299. // of the company vision, and Apple is trying to force third
  2300. // party developers to obey their vision.  Remember that use
  2301. // of 16 bits per _a_character_ was one of the very fundamental
  2302. // Unicode design policy on its early days (during late 80s)
  2303. // and the original Unicode design was by two Apple employees...
  2304. const llutf16string text_utf16
  2305. = wstring_to_utf16str(text, preedit)
  2306. + wstring_to_utf16str(text.substr(preedit + preedit_length));
  2307. llassert_always(sizeof(U16) == sizeof(UniChar));
  2308. llassert(0 <= range.location && 0 <= range.length && range.location + range.length <= text_utf16.length());
  2309. memcpy(target_pointer, text_utf16.c_str() + range.location, range.length * sizeof(UniChar));
  2310. // Note that result has already been set above.
  2311. }
  2312. }
  2313. break;
  2314. }
  2315. }
  2316. break;
  2317. }
  2318. return result;
  2319. }
  2320. const char* cursorIDToName(int id)
  2321. {
  2322. switch (id)
  2323. {
  2324. case UI_CURSOR_ARROW: return "UI_CURSOR_ARROW";
  2325. case UI_CURSOR_WAIT: return "UI_CURSOR_WAIT";
  2326. case UI_CURSOR_HAND: return "UI_CURSOR_HAND";
  2327. case UI_CURSOR_IBEAM: return "UI_CURSOR_IBEAM";
  2328. case UI_CURSOR_CROSS: return "UI_CURSOR_CROSS";
  2329. case UI_CURSOR_SIZENWSE: return "UI_CURSOR_SIZENWSE";
  2330. case UI_CURSOR_SIZENESW: return "UI_CURSOR_SIZENESW";
  2331. case UI_CURSOR_SIZEWE: return "UI_CURSOR_SIZEWE";
  2332. case UI_CURSOR_SIZENS: return "UI_CURSOR_SIZENS";
  2333. case UI_CURSOR_NO: return "UI_CURSOR_NO";
  2334. case UI_CURSOR_WORKING: return "UI_CURSOR_WORKING";
  2335. case UI_CURSOR_TOOLGRAB: return "UI_CURSOR_TOOLGRAB";
  2336. case UI_CURSOR_TOOLLAND: return "UI_CURSOR_TOOLLAND";
  2337. case UI_CURSOR_TOOLFOCUS: return "UI_CURSOR_TOOLFOCUS";
  2338. case UI_CURSOR_TOOLCREATE: return "UI_CURSOR_TOOLCREATE";
  2339. case UI_CURSOR_ARROWDRAG: return "UI_CURSOR_ARROWDRAG";
  2340. case UI_CURSOR_ARROWCOPY: return "UI_CURSOR_ARROWCOPY";
  2341. case UI_CURSOR_ARROWDRAGMULTI: return "UI_CURSOR_ARROWDRAGMULTI";
  2342. case UI_CURSOR_ARROWCOPYMULTI: return "UI_CURSOR_ARROWCOPYMULTI";
  2343. case UI_CURSOR_NOLOCKED: return "UI_CURSOR_NOLOCKED";
  2344. case UI_CURSOR_ARROWLOCKED: return "UI_CURSOR_ARROWLOCKED";
  2345. case UI_CURSOR_GRABLOCKED: return "UI_CURSOR_GRABLOCKED";
  2346. case UI_CURSOR_TOOLTRANSLATE: return "UI_CURSOR_TOOLTRANSLATE";
  2347. case UI_CURSOR_TOOLROTATE: return "UI_CURSOR_TOOLROTATE";
  2348. case UI_CURSOR_TOOLSCALE: return "UI_CURSOR_TOOLSCALE";
  2349. case UI_CURSOR_TOOLCAMERA: return "UI_CURSOR_TOOLCAMERA";
  2350. case UI_CURSOR_TOOLPAN: return "UI_CURSOR_TOOLPAN";
  2351. case UI_CURSOR_TOOLZOOMIN: return "UI_CURSOR_TOOLZOOMIN";
  2352. case UI_CURSOR_TOOLPICKOBJECT3: return "UI_CURSOR_TOOLPICKOBJECT3";
  2353. case UI_CURSOR_TOOLPLAY: return "UI_CURSOR_TOOLPLAY";
  2354. case UI_CURSOR_TOOLPAUSE: return "UI_CURSOR_TOOLPAUSE";
  2355. case UI_CURSOR_TOOLMEDIAOPEN: return "UI_CURSOR_TOOLMEDIAOPEN";
  2356. case UI_CURSOR_PIPETTE: return "UI_CURSOR_PIPETTE";
  2357. }
  2358. llerrs << "cursorIDToName: unknown cursor id" << id << llendl;
  2359. return "UI_CURSOR_ARROW";
  2360. }
  2361. static CursorRef gCursors[UI_CURSOR_COUNT];
  2362. static void initPixmapCursor(int cursorid, int hotspotX, int hotspotY)
  2363. {
  2364. // cursors are in <Application Bundle>/Contents/Resources/cursors_mac/UI_CURSOR_FOO.tif
  2365. std::string fullpath = gDirUtilp->getAppRODataDir();
  2366. fullpath += gDirUtilp->getDirDelimiter();
  2367. fullpath += "cursors_mac";
  2368. fullpath += gDirUtilp->getDirDelimiter();
  2369. fullpath += cursorIDToName(cursorid);
  2370. fullpath += ".tif";
  2371. gCursors[cursorid] = createImageCursor(fullpath.c_str(), hotspotX, hotspotY);
  2372. }
  2373. void LLWindowMacOSX::setCursor(ECursorType cursor)
  2374. {
  2375. OSStatus result = noErr;
  2376. if (mDragOverrideCursor != -1) 
  2377. {
  2378. // A drag is in progress...remember the requested cursor and we'll
  2379. // restore it when it is done
  2380. mCurrentCursor = cursor;
  2381. return;
  2382. }
  2383. if (cursor == UI_CURSOR_ARROW
  2384. && mBusyCount > 0)
  2385. {
  2386. cursor = UI_CURSOR_WORKING;
  2387. }
  2388. if(mCurrentCursor == cursor)
  2389. return;
  2390. // RN: replace multi-drag cursors with single versions
  2391. if (cursor == UI_CURSOR_ARROWDRAGMULTI)
  2392. {
  2393. cursor = UI_CURSOR_ARROWDRAG;
  2394. }
  2395. else if (cursor == UI_CURSOR_ARROWCOPYMULTI)
  2396. {
  2397. cursor = UI_CURSOR_ARROWCOPY;
  2398. }
  2399. switch(cursor)
  2400. {
  2401. default:
  2402. case UI_CURSOR_ARROW:
  2403. InitCursor();
  2404. if(mCursorHidden)
  2405. {
  2406. // Since InitCursor resets the hide level, correct for it here.
  2407. ::HideCursor();
  2408. }
  2409. break;
  2410. // MBW -- XXX -- Some of the standard Windows cursors have no standard Mac equivalents.
  2411. //    Find out what they look like and replicate them.
  2412. // These are essentially correct
  2413. case UI_CURSOR_WAIT: SetThemeCursor(kThemeWatchCursor); break;
  2414. case UI_CURSOR_IBEAM: SetThemeCursor(kThemeIBeamCursor); break;
  2415. case UI_CURSOR_CROSS: SetThemeCursor(kThemeCrossCursor); break;
  2416. case UI_CURSOR_HAND: SetThemeCursor(kThemePointingHandCursor); break;
  2417. // case UI_CURSOR_NO: SetThemeCursor(kThemeNotAllowedCursor); break;
  2418. case UI_CURSOR_ARROWCOPY:   SetThemeCursor(kThemeCopyArrowCursor); break;
  2419. // Double-check these
  2420. case UI_CURSOR_NO:
  2421. case UI_CURSOR_SIZEWE:
  2422. case UI_CURSOR_SIZENS:
  2423. case UI_CURSOR_SIZENWSE:
  2424. case UI_CURSOR_SIZENESW:
  2425. case UI_CURSOR_WORKING:
  2426. case UI_CURSOR_TOOLGRAB:
  2427. case UI_CURSOR_TOOLLAND:
  2428. case UI_CURSOR_TOOLFOCUS:
  2429. case UI_CURSOR_TOOLCREATE:
  2430. case UI_CURSOR_ARROWDRAG:
  2431. case UI_CURSOR_NOLOCKED:
  2432. case UI_CURSOR_ARROWLOCKED:
  2433. case UI_CURSOR_GRABLOCKED:
  2434. case UI_CURSOR_TOOLTRANSLATE:
  2435. case UI_CURSOR_TOOLROTATE:
  2436. case UI_CURSOR_TOOLSCALE:
  2437. case UI_CURSOR_TOOLCAMERA:
  2438. case UI_CURSOR_TOOLPAN:
  2439. case UI_CURSOR_TOOLZOOMIN:
  2440. case UI_CURSOR_TOOLPICKOBJECT3:
  2441. case UI_CURSOR_TOOLPLAY:
  2442. case UI_CURSOR_TOOLPAUSE:
  2443. case UI_CURSOR_TOOLMEDIAOPEN:
  2444. result = setImageCursor(gCursors[cursor]);
  2445. break;
  2446. }
  2447. if(result != noErr)
  2448. {
  2449. InitCursor();
  2450. }
  2451. mCurrentCursor = cursor;
  2452. }
  2453. ECursorType LLWindowMacOSX::getCursor() const
  2454. {
  2455. return mCurrentCursor;
  2456. }
  2457. void LLWindowMacOSX::initCursors()
  2458. {
  2459. initPixmapCursor(UI_CURSOR_NO, 8, 8);
  2460. initPixmapCursor(UI_CURSOR_WORKING, 1, 1);
  2461. initPixmapCursor(UI_CURSOR_TOOLGRAB, 2, 14);
  2462. initPixmapCursor(UI_CURSOR_TOOLLAND, 13, 8);
  2463. initPixmapCursor(UI_CURSOR_TOOLFOCUS, 7, 6);
  2464. initPixmapCursor(UI_CURSOR_TOOLCREATE, 7, 7);
  2465. initPixmapCursor(UI_CURSOR_ARROWDRAG, 1, 1);
  2466. initPixmapCursor(UI_CURSOR_ARROWCOPY, 1, 1);
  2467. initPixmapCursor(UI_CURSOR_NOLOCKED, 8, 8);
  2468. initPixmapCursor(UI_CURSOR_ARROWLOCKED, 1, 1);
  2469. initPixmapCursor(UI_CURSOR_GRABLOCKED, 2, 14);
  2470. initPixmapCursor(UI_CURSOR_TOOLTRANSLATE, 1, 1);
  2471. initPixmapCursor(UI_CURSOR_TOOLROTATE, 1, 1);
  2472. initPixmapCursor(UI_CURSOR_TOOLSCALE, 1, 1);
  2473. initPixmapCursor(UI_CURSOR_TOOLCAMERA, 7, 6);
  2474. initPixmapCursor(UI_CURSOR_TOOLPAN, 7, 6);
  2475. initPixmapCursor(UI_CURSOR_TOOLZOOMIN, 7, 6);
  2476. initPixmapCursor(UI_CURSOR_TOOLPICKOBJECT3, 1, 1);
  2477. initPixmapCursor(UI_CURSOR_TOOLPLAY, 1, 1);
  2478. initPixmapCursor(UI_CURSOR_TOOLPAUSE, 1, 1);
  2479. initPixmapCursor(UI_CURSOR_TOOLMEDIAOPEN, 1, 1);
  2480. initPixmapCursor(UI_CURSOR_SIZENWSE, 10, 10);
  2481. initPixmapCursor(UI_CURSOR_SIZENESW, 10, 10);
  2482. initPixmapCursor(UI_CURSOR_SIZEWE, 10, 10);
  2483. initPixmapCursor(UI_CURSOR_SIZENS, 10, 10);
  2484. }
  2485. void LLWindowMacOSX::captureMouse()
  2486. {
  2487. // By registering a global CarbonEvent handler for mouse move events, we ensure that
  2488. // mouse events are always processed.  Thus, capture and release are unnecessary.
  2489. }
  2490. void LLWindowMacOSX::releaseMouse()
  2491. {
  2492. // By registering a global CarbonEvent handler for mouse move events, we ensure that
  2493. // mouse events are always processed.  Thus, capture and release are unnecessary.
  2494. }
  2495. void LLWindowMacOSX::hideCursor()
  2496. {
  2497. if(!mCursorHidden)
  2498. {
  2499. // llinfos << "hideCursor: hiding" << llendl;
  2500. mCursorHidden = TRUE;
  2501. mHideCursorPermanent = TRUE;
  2502. ::HideCursor();
  2503. }
  2504. else
  2505. {
  2506. // llinfos << "hideCursor: already hidden" << llendl;
  2507. }
  2508. adjustCursorDecouple();
  2509. }
  2510. void LLWindowMacOSX::showCursor()
  2511. {
  2512. if(mCursorHidden)
  2513. {
  2514. // llinfos << "showCursor: showing" << llendl;
  2515. mCursorHidden = FALSE;
  2516. mHideCursorPermanent = FALSE;
  2517. ::ShowCursor();
  2518. }
  2519. else
  2520. {
  2521. // llinfos << "showCursor: already visible" << llendl;
  2522. }
  2523. adjustCursorDecouple();
  2524. }
  2525. void LLWindowMacOSX::showCursorFromMouseMove()
  2526. {
  2527. if (!mHideCursorPermanent)
  2528. {
  2529. showCursor();
  2530. }
  2531. }
  2532. void LLWindowMacOSX::hideCursorUntilMouseMove()
  2533. {
  2534. if (!mHideCursorPermanent)
  2535. {
  2536. hideCursor();
  2537. mHideCursorPermanent = FALSE;
  2538. }
  2539. }
  2540. //
  2541. // LLSplashScreenMacOSX
  2542. //
  2543. LLSplashScreenMacOSX::LLSplashScreenMacOSX()
  2544. {
  2545. mWindow = NULL;
  2546. }
  2547. LLSplashScreenMacOSX::~LLSplashScreenMacOSX()
  2548. {
  2549. }
  2550. void LLSplashScreenMacOSX::showImpl()
  2551. {
  2552. // This code _could_ be used to display a spash screen...
  2553. #if 0
  2554. IBNibRef nib = NULL;
  2555. OSStatus err;
  2556. err = CreateNibReference(CFSTR("SecondLife"), &nib);
  2557. if(err == noErr)
  2558. {
  2559. CreateWindowFromNib(nib, CFSTR("Splash Screen"), &mWindow);
  2560. DisposeNibReference(nib);
  2561. }
  2562. if(mWindow != NULL)
  2563. {
  2564. ShowWindow(mWindow);
  2565. }
  2566. #endif
  2567. }
  2568. void LLSplashScreenMacOSX::updateImpl(const std::string& mesg)
  2569. {
  2570. if(mWindow != NULL)
  2571. {
  2572. CFStringRef string = NULL;
  2573. string = CFStringCreateWithCString(NULL, mesg.c_str(), kCFStringEncodingUTF8);
  2574. if(string != NULL)
  2575. {
  2576. ControlRef progressText = NULL;
  2577. ControlID id;
  2578. OSStatus err;
  2579. id.signature = 'what';
  2580. id.id = 0;
  2581. err = GetControlByID(mWindow, &id, &progressText);
  2582. if(err == noErr)
  2583. {
  2584. err = SetControlData(progressText, kControlEntireControl, kControlStaticTextCFStringTag, sizeof(CFStringRef), (Ptr)&string);
  2585. Draw1Control(progressText);
  2586. }
  2587. CFRelease(string);
  2588. }
  2589. }
  2590. }
  2591. void LLSplashScreenMacOSX::hideImpl()
  2592. {
  2593. if(mWindow != NULL)
  2594. {
  2595. DisposeWindow(mWindow);
  2596. mWindow = NULL;
  2597. }
  2598. }
  2599. S32 OSMessageBoxMacOSX(const std::string& text, const std::string& caption, U32 type)
  2600. {
  2601. S32 result = OSBTN_CANCEL;
  2602. SInt16 retval_mac = 1;
  2603. AlertStdCFStringAlertParamRec params;
  2604. CFStringRef errorString = NULL;
  2605. CFStringRef explanationString = NULL;
  2606. DialogRef alert = NULL;
  2607. AlertType alertType = kAlertCautionAlert;
  2608. OSStatus err;
  2609. explanationString = CFStringCreateWithCString(NULL, text.c_str(), kCFStringEncodingUTF8);
  2610. errorString = CFStringCreateWithCString(NULL, caption.c_str(), kCFStringEncodingUTF8);
  2611. params.version = kStdCFStringAlertVersionOne;
  2612. params.movable = false;
  2613. params.helpButton = false;
  2614. params.defaultText = (CFStringRef)kAlertDefaultOKText;
  2615. params.cancelText = 0;
  2616. params.otherText = 0;
  2617. params.defaultButton = 1;
  2618. params.cancelButton = 0;
  2619. params.position = kWindowDefaultPosition;
  2620. params.flags = 0;
  2621. switch(type)
  2622. {
  2623. case OSMB_OK:
  2624. default:
  2625. break;
  2626. case OSMB_OKCANCEL:
  2627. params.cancelText = (CFStringRef)kAlertDefaultCancelText;
  2628. params.cancelButton = 2;
  2629. break;
  2630. case OSMB_YESNO:
  2631. alertType = kAlertNoteAlert;
  2632. params.defaultText = CFSTR("Yes");
  2633. params.cancelText = CFSTR("No");
  2634. params.cancelButton = 2;
  2635. break;
  2636. }
  2637. if(gWindowImplementation != NULL)
  2638. gWindowImplementation->beforeDialog();
  2639. err = CreateStandardAlert(
  2640. alertType,
  2641. errorString,
  2642. explanationString,
  2643. &params,
  2644. &alert);
  2645. if(err == noErr)
  2646. {
  2647. err = RunStandardAlert(
  2648. alert,
  2649. NULL,
  2650. &retval_mac);
  2651. }
  2652. if(gWindowImplementation != NULL)
  2653. gWindowImplementation->afterDialog();
  2654. switch(type)
  2655. {
  2656. case OSMB_OK:
  2657. case OSMB_OKCANCEL:
  2658. default:
  2659. if(retval_mac == 1)
  2660. result = OSBTN_OK;
  2661. else
  2662. result = OSBTN_CANCEL;
  2663. break;
  2664. case OSMB_YESNO:
  2665. if(retval_mac == 1)
  2666. result = OSBTN_YES;
  2667. else
  2668. result = OSBTN_NO;
  2669. break;
  2670. }
  2671. if(errorString != NULL)
  2672. {
  2673. CFRelease(errorString);
  2674. }
  2675. if(explanationString != NULL)
  2676. {
  2677. CFRelease(explanationString);
  2678. }
  2679. return result;
  2680. }
  2681. // Open a URL with the user's default web browser.
  2682. // Must begin with protocol identifier.
  2683. void LLWindowMacOSX::spawnWebBrowser(const std::string& escaped_url)
  2684. {
  2685. bool found = false;
  2686. S32 i;
  2687. for (i = 0; i < gURLProtocolWhitelistCount; i++)
  2688. {
  2689. if (escaped_url.find(gURLProtocolWhitelist[i]) != std::string::npos)
  2690. {
  2691. found = true;
  2692. break;
  2693. }
  2694. }
  2695. if (!found)
  2696. {
  2697. llwarns << "spawn_web_browser called for url with protocol not on whitelist: " << escaped_url << llendl;
  2698. return;
  2699. }
  2700. OSStatus result = noErr;
  2701. CFURLRef urlRef = NULL;
  2702. llinfos << "Opening URL " << escaped_url << llendl;
  2703. CFStringRef stringRef = CFStringCreateWithCString(NULL, escaped_url.c_str(), kCFStringEncodingUTF8);
  2704. if (stringRef)
  2705. {
  2706. // This will succeed if the string is a full URL, including the http://
  2707. // Note that URLs specified this way need to be properly percent-escaped.
  2708. urlRef = CFURLCreateWithString(NULL, stringRef, NULL);
  2709. // Don't use CRURLCreateWithFileSystemPath -- only want valid URLs
  2710. CFRelease(stringRef);
  2711. }
  2712. if (urlRef)
  2713. {
  2714. result = LSOpenCFURLRef(urlRef, NULL);
  2715. if (result != noErr)
  2716. {
  2717. llinfos << "Error " << result << " on open." << llendl;
  2718. }
  2719. CFRelease(urlRef);
  2720. }
  2721. else
  2722. {
  2723. llinfos << "Error: couldn't create URL." << llendl;
  2724. }
  2725. }
  2726. LLSD LLWindowMacOSX::getNativeKeyData()
  2727. {
  2728. LLSD result = LLSD::emptyMap();
  2729. if(mRawKeyEvent)
  2730. {
  2731. char char_code = 0;
  2732. UInt32 key_code = 0;
  2733. UInt32 modifiers = 0;
  2734. UInt32 keyboard_type = 0;
  2735. GetEventParameter (mRawKeyEvent, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &char_code);
  2736. GetEventParameter (mRawKeyEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &key_code);
  2737. GetEventParameter (mRawKeyEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
  2738. GetEventParameter (mRawKeyEvent, kEventParamKeyboardType, typeUInt32, NULL, sizeof(UInt32), NULL, &keyboard_type);
  2739. result["char_code"] = (S32)char_code;
  2740. result["key_code"] = (S32)key_code;
  2741. result["modifiers"] = (S32)modifiers;
  2742. result["keyboard_type"] = (S32)keyboard_type;
  2743. #if 0
  2744. // This causes trouble for control characters -- apparently character codes less than 32 (escape, control-A, etc)
  2745. // cause llsd serialization to create XML that the llsd deserializer won't parse!
  2746. std::string unicode;
  2747. OSStatus err = noErr;
  2748. EventParamType actualType = typeUTF8Text;
  2749. UInt32 actualSize = 0;
  2750. char *buffer = NULL;
  2751. err = GetEventParameter (mRawKeyEvent, kEventParamKeyUnicodes, typeUTF8Text, &actualType, 0, &actualSize, NULL);
  2752. if(err == noErr)
  2753. {
  2754. // allocate a buffer and get the actual data.
  2755. buffer = new char[actualSize];
  2756. err = GetEventParameter (mRawKeyEvent, kEventParamKeyUnicodes, typeUTF8Text, &actualType, actualSize, &actualSize, buffer);
  2757. if(err == noErr)
  2758. {
  2759. unicode.assign(buffer, actualSize);
  2760. }
  2761. delete[] buffer;
  2762. }
  2763. result["unicode"] = unicode;
  2764. #endif
  2765. }
  2766. lldebugs << "native key data is: " << result << llendl;
  2767. return result;
  2768. }
  2769. BOOL LLWindowMacOSX::dialogColorPicker( F32 *r, F32 *g, F32 *b)
  2770. {
  2771. BOOL retval = FALSE;
  2772. OSErr error = noErr;
  2773. NColorPickerInfo info;
  2774. memset(&info, 0, sizeof(info));
  2775. info.theColor.color.rgb.red = (UInt16)(*r * 65535.f);
  2776. info.theColor.color.rgb.green = (UInt16)(*g * 65535.f);
  2777. info.theColor.color.rgb.blue = (UInt16)(*b * 65535.f);
  2778. info.placeWhere = kCenterOnMainScreen;
  2779. if(gWindowImplementation != NULL)
  2780. gWindowImplementation->beforeDialog();
  2781. error = NPickColor(&info);
  2782. if(gWindowImplementation != NULL)
  2783. gWindowImplementation->afterDialog();
  2784. if (error == noErr)
  2785. {
  2786. retval = info.newColorChosen;
  2787. if (info.newColorChosen)
  2788. {
  2789. *r = ((float) info.theColor.color.rgb.red) / 65535.0;
  2790. *g = ((float) info.theColor.color.rgb.green) / 65535.0;
  2791. *b = ((float) info.theColor.color.rgb.blue) / 65535.0;
  2792. }
  2793. }
  2794. return (retval);
  2795. }
  2796. void *LLWindowMacOSX::getPlatformWindow()
  2797. {
  2798. // NOTE: this will be NULL in fullscreen mode.  Plan accordingly.
  2799. return (void*)mWindow;
  2800. }
  2801. void *LLWindowMacOSX::getMediaWindow()
  2802. {
  2803. /* 
  2804. Mozilla needs to be initialized with a WindowRef to function properly.  
  2805. (There's no good reason for this, since it shouldn't be interacting with our window in any way, but that's another issue.)
  2806. If we're in windowed mode, we _could_ hand it our actual window pointer, but a subsequent switch to fullscreen will destroy that window, 
  2807. which trips up Mozilla.
  2808. Instead of using our actual window, we create an invisible window which will persist for the lifetime of the application and pass that to Mozilla.
  2809. This satisfies its deep-seated need to latch onto a WindowRef and solves the issue with switching between fullscreen and windowed modes.
  2810. Note that we will never destroy this window (by design!), but since only one will ever be created per run of the application, that's okay.
  2811. */
  2812. if(sMediaWindow == NULL)
  2813. {
  2814. Rect window_rect = {100, 100, 200, 200};
  2815. sMediaWindow = NewCWindow(
  2816. NULL,
  2817. &window_rect,
  2818. (ConstStr255Param) "p",
  2819. false, // Create the window invisible.  
  2820. zoomDocProc, // Window with a grow box and a zoom box
  2821. kLastWindowOfClass, // create it behind other windows
  2822. false, // no close box
  2823. 0);
  2824. }
  2825. return (void*)sMediaWindow;
  2826. }
  2827. void LLWindowMacOSX::stopDockTileBounce()
  2828. {
  2829. NMRemove(&mBounceRec);
  2830. mBounceTimer.stop();
  2831. }
  2832. // get a double value from a dictionary
  2833. static double getDictDouble (CFDictionaryRef refDict, CFStringRef key)
  2834. {
  2835. double double_value;
  2836. CFNumberRef number_value = (CFNumberRef) CFDictionaryGetValue(refDict, key);
  2837. if (!number_value) // if can't get a number for the dictionary
  2838. return -1;  // fail
  2839. if (!CFNumberGetValue(number_value, kCFNumberDoubleType, &double_value)) // or if cant convert it
  2840. return -1; // fail
  2841. return double_value; // otherwise return the long value
  2842. }
  2843. // get a long value from a dictionary
  2844. static long getDictLong (CFDictionaryRef refDict, CFStringRef key)
  2845. {
  2846. long int_value;
  2847. CFNumberRef number_value = (CFNumberRef) CFDictionaryGetValue(refDict, key);
  2848. if (!number_value) // if can't get a number for the dictionary
  2849. return -1;  // fail
  2850. if (!CFNumberGetValue(number_value, kCFNumberLongType, &int_value)) // or if cant convert it
  2851. return -1; // fail
  2852. return int_value; // otherwise return the long value
  2853. }
  2854. void LLWindowMacOSX::allowLanguageTextInput(LLPreeditor *preeditor, BOOL b)
  2855. {
  2856. ScriptLanguageRecord script_language;
  2857. if (preeditor != mPreeditor && !b)
  2858. {
  2859. // This condition may occur by a call to
  2860. // setEnabled(BOOL) against LLTextEditor or LLLineEditor
  2861. // when the control is not focused.
  2862. // We need to silently ignore the case so that
  2863. // the language input status of the focused control
  2864. // is not disturbed.
  2865. return;
  2866. }
  2867. UseInputWindow(mTSMDocument, !b);
  2868. // Take care of old and new preeditors.
  2869. if (preeditor != mPreeditor || !b)
  2870. {
  2871. // We need to interrupt before updating mPreeditor,
  2872. // so that the fix string from input method goes to
  2873. // the old preeditor.
  2874. if (mLanguageTextInputAllowed)
  2875. {
  2876. interruptLanguageTextInput();
  2877. }
  2878. mPreeditor = (b ? preeditor : NULL);
  2879. }
  2880. if (b == mLanguageTextInputAllowed)
  2881. {
  2882. return;
  2883. }
  2884. mLanguageTextInputAllowed = b;
  2885. if (b)
  2886. {
  2887. if (mTSMScriptCode != smRoman)
  2888. {
  2889. script_language.fScript = mTSMScriptCode;
  2890. script_language.fLanguage = mTSMLangCode;
  2891. SetTextServiceLanguage(&script_language);
  2892. }
  2893. }
  2894. else
  2895. {
  2896. GetTextServiceLanguage(&script_language);
  2897. mTSMScriptCode = script_language.fScript;
  2898. mTSMLangCode = script_language.fLanguage;
  2899. if (mTSMScriptCode != smRoman)
  2900. {
  2901. script_language.fScript = smRoman;
  2902. script_language.fLanguage = langEnglish;
  2903. SetTextServiceLanguage(&script_language);
  2904. }
  2905. }
  2906. }
  2907. void LLWindowMacOSX::interruptLanguageTextInput()
  2908. {
  2909. if (mTSMDocument)
  2910. {
  2911. FixTSMDocument(mTSMDocument);
  2912. }
  2913. // Don't we need to call resetPreedit here?
  2914. // Well, if Apple's TSM document is correct, we don't.
  2915. }
  2916. //static
  2917. std::vector<std::string> LLWindowMacOSX::getDynamicFallbackFontList()
  2918. {
  2919. // Fonts previously in getFontListSans() have moved to fonts.xml.
  2920. return std::vector<std::string>();
  2921. }
  2922. // static
  2923. MASK LLWindowMacOSX::modifiersToMask(SInt16 modifiers)
  2924. {
  2925. MASK mask = 0;
  2926. if(modifiers & shiftKey) { mask |= MASK_SHIFT; }
  2927. if(modifiers & (cmdKey | controlKey)) { mask |= MASK_CONTROL; }
  2928. if(modifiers & optionKey) { mask |= MASK_ALT; }
  2929. return mask;
  2930. }
  2931. #if LL_OS_DRAGDROP_ENABLED
  2932. OSErr LLWindowMacOSX::dragTrackingHandler(DragTrackingMessage message, WindowRef theWindow,
  2933.   void * handlerRefCon, DragRef drag)
  2934. {
  2935. OSErr result = noErr;
  2936. LLWindowMacOSX *self = (LLWindowMacOSX*)handlerRefCon;
  2937. lldebugs << "drag tracking handler, message = " << message << llendl;
  2938. switch(message)
  2939. {
  2940. case kDragTrackingInWindow:
  2941. result = self->handleDragNDrop(drag, LLWindowCallbacks::DNDA_TRACK);
  2942. break;
  2943. case kDragTrackingEnterHandler:
  2944. result = self->handleDragNDrop(drag, LLWindowCallbacks::DNDA_START_TRACKING);
  2945. break;
  2946. case kDragTrackingLeaveHandler:
  2947. result = self->handleDragNDrop(drag, LLWindowCallbacks::DNDA_STOP_TRACKING);
  2948. break;
  2949. default:
  2950. break;
  2951. }
  2952. return result;
  2953. }
  2954. OSErr LLWindowMacOSX::dragReceiveHandler(WindowRef theWindow, void * handlerRefCon,
  2955.  DragRef drag)
  2956. {
  2957. LLWindowMacOSX *self = (LLWindowMacOSX*)handlerRefCon;
  2958. return self->handleDragNDrop(drag, LLWindowCallbacks::DNDA_DROPPED);
  2959. }
  2960. OSErr LLWindowMacOSX::handleDragNDrop(DragRef drag, LLWindowCallbacks::DragNDropAction action)
  2961. {
  2962. OSErr result = dragNotAcceptedErr; // overall function result
  2963. OSErr err = noErr; // for local error handling
  2964. // Get the mouse position and modifiers of this drag.
  2965. SInt16 modifiers, mouseDownModifiers, mouseUpModifiers;
  2966. ::GetDragModifiers(drag, &modifiers, &mouseDownModifiers, &mouseUpModifiers);
  2967. MASK mask = LLWindowMacOSX::modifiersToMask(modifiers);
  2968. Point mouse_point;
  2969. // This will return the mouse point in global screen coords
  2970. ::GetDragMouse(drag, &mouse_point, NULL);
  2971. LLCoordScreen screen_coords(mouse_point.h, mouse_point.v);
  2972. LLCoordGL gl_pos;
  2973. convertCoords(screen_coords, &gl_pos);
  2974. // Look at the pasteboard and try to extract an URL from it
  2975. PasteboardRef   pasteboard;
  2976. if(GetDragPasteboard(drag, &pasteboard) == noErr)
  2977. {
  2978. ItemCount num_items = 0;
  2979. // Treat an error here as an item count of 0
  2980. (void)PasteboardGetItemCount(pasteboard, &num_items);
  2981. // Only deal with single-item drags.
  2982. if(num_items == 1)
  2983. {
  2984. PasteboardItemID item_id = NULL;
  2985. CFArrayRef flavors = NULL;
  2986. CFDataRef data = NULL;
  2987. err = PasteboardGetItemIdentifier(pasteboard, 1, &item_id); // Yes, this really is 1-based.
  2988. // Try to extract an URL from the pasteboard
  2989. if(err == noErr)
  2990. {
  2991. err = PasteboardCopyItemFlavors( pasteboard, item_id, &flavors);
  2992. }
  2993. if(err == noErr)
  2994. {
  2995. if(CFArrayContainsValue(flavors, CFRangeMake(0, CFArrayGetCount(flavors)), kUTTypeURL))
  2996. {
  2997. // This is an URL.
  2998. err = PasteboardCopyItemFlavorData(pasteboard, item_id, kUTTypeURL, &data);
  2999. }
  3000. else if(CFArrayContainsValue(flavors, CFRangeMake(0, CFArrayGetCount(flavors)), kUTTypeUTF8PlainText))
  3001. {
  3002. // This is a string that might be an URL.
  3003. err = PasteboardCopyItemFlavorData(pasteboard, item_id, kUTTypeUTF8PlainText, &data);
  3004. }
  3005. }
  3006. if(flavors != NULL)
  3007. {
  3008. CFRelease(flavors);
  3009. }
  3010. if(data != NULL)
  3011. {
  3012. std::string url;
  3013. url.assign((char*)CFDataGetBytePtr(data), CFDataGetLength(data));
  3014. CFRelease(data);
  3015. if(!url.empty())
  3016. {
  3017. LLWindowCallbacks::DragNDropResult res = 
  3018. mCallbacks->handleDragNDrop(this, gl_pos, mask, action, url);
  3019. switch (res) {
  3020. case LLWindowCallbacks::DND_NONE: // No drop allowed
  3021. if (action == LLWindowCallbacks::DNDA_TRACK)
  3022. {
  3023. mDragOverrideCursor = kThemeNotAllowedCursor;
  3024. }
  3025. else {
  3026. mDragOverrideCursor = -1;
  3027. }
  3028. break;
  3029. case LLWindowCallbacks::DND_MOVE: // Drop accepted would result in a "move" operation
  3030. mDragOverrideCursor = kThemePointingHandCursor;
  3031. result = noErr;
  3032. break;
  3033. case LLWindowCallbacks::DND_COPY: // Drop accepted would result in a "copy" operation
  3034. mDragOverrideCursor = kThemeCopyArrowCursor;
  3035. result = noErr;
  3036. break;
  3037. case LLWindowCallbacks::DND_LINK: // Drop accepted would result in a "link" operation:
  3038. mDragOverrideCursor = kThemeAliasArrowCursor;
  3039. result = noErr;
  3040. break;
  3041. default:
  3042. mDragOverrideCursor = -1;
  3043. break;
  3044. }
  3045. // This overrides the cursor being set by setCursor.
  3046. // This is a bit of a hack workaround because lots of areas
  3047. // within the viewer just blindly set the cursor.
  3048. if (mDragOverrideCursor == -1)
  3049. {
  3050. // Restore the cursor
  3051. ECursorType temp_cursor = mCurrentCursor;
  3052. // get around the "setting the same cursor" code in setCursor()
  3053. mCurrentCursor = UI_CURSOR_COUNT; 
  3054.   setCursor(temp_cursor);
  3055. }
  3056. else {
  3057. // Override the cursor
  3058. SetThemeCursor(mDragOverrideCursor);
  3059. }
  3060. }
  3061. }
  3062. }
  3063. }
  3064. return result;
  3065. }
  3066. #endif // LL_OS_DRAGDROP_ENABLED