llwindowmacosx.cpp

Go to the documentation of this file.
00001 
00032 #if LL_DARWIN
00033 
00034 #include "linden_common.h"
00035 
00036 #include <Carbon/Carbon.h>
00037 #include <OpenGL/OpenGL.h>
00038 
00039 #include "llwindowmacosx.h"
00040 #include "llkeyboardmacosx.h"
00041 #include "llerror.h"
00042 #include "llgl.h"
00043 #include "llstring.h"
00044 #include "lldir.h"
00045 #include "llviewercontrol.h"
00046 
00047 #include "llglheaders.h"
00048 
00049 #include "indra_constants.h"
00050 
00051 #include "llwindowmacosx-objc.h"
00052 #include "llpreeditor.h"
00053 
00054 extern BOOL gDebugWindowProc;
00055 
00056 // culled from winuser.h
00057 //const S32     WHEEL_DELTA = 120;     /* Value for rolling one detent */
00058 // On the Mac, the scroll wheel reports a delta of 1 for each detent.
00059 // There's also acceleration for faster scrolling, based on a slider in the system preferences.
00060 const S32       WHEEL_DELTA = 1;     /* Value for rolling one detent */
00061 const S32       BITS_PER_PIXEL = 32;
00062 const S32       MAX_NUM_RESOLUTIONS = 32;
00063 
00064 
00065 //
00066 // LLWindowMacOSX
00067 //
00068 
00069 // Cross-platform bits:
00070 
00071 void show_window_creation_error(const char* title)
00072 {
00073         llwarns << title << llendl;
00074         /*
00075         OSMessageBox(
00076         "Second Life is unable to run because it can't set up your display.\n"
00077         "We need to be able to make a 32-bit color window at 1024x768, with\n"
00078         "an 8 bit alpha channel.\n"
00079         "\n"
00080         "First, be sure your monitor is set to True Color (32-bit) in\n"
00081         "Start -> Control Panels -> Display -> Settings.\n"
00082         "\n"
00083         "Otherwise, this may be due to video card driver issues.\n"
00084         "Please make sure you have the latest video card drivers installed.\n"
00085         "ATI drivers are available at http://www.ati.com/\n"
00086         "nVidia drivers are available at http://www.nvidia.com/\n"
00087         "\n"
00088         "If you continue to receive this message, contact customer service.",
00089         title,
00090         OSMB_OK);
00091         */
00092 }
00093 
00094 BOOL check_for_card(const char* RENDERER, const char* bad_card)
00095 {
00096         if (!strnicmp(RENDERER, bad_card, strlen(bad_card)))
00097         {
00098                 char buffer[1024];/* Flawfinder: ignore */
00099                 snprintf(buffer, sizeof(buffer), 
00100                         "Your video card appears to be a %s, which Second Life does not support.\n"
00101                         "\n"
00102                         "Second Life requires a video card with 32 Mb of memory or more, as well as\n"
00103                         "multitexture support.  We explicitly support nVidia GeForce 2 or better, \n"
00104                         "and ATI Radeon 8500 or better.\n"
00105                         "\n"
00106                         "If you own a supported card and continue to receive this message, try \n"
00107                         "updating to the latest video card drivers. Otherwise look in the\n"
00108                         "secondlife.com support section or e-mail technical support\n"
00109                         "\n"
00110                         "You can try to run Second Life, but it will probably crash or run\n"
00111                         "very slowly.  Try anyway?",
00112                         bad_card);
00113                 S32 button = OSMessageBox(buffer, "Unsupported video card", OSMB_YESNO);
00114                 if (OSBTN_YES == button)
00115                 {
00116                         return FALSE;
00117                 }
00118                 else
00119                 {
00120                         return TRUE;
00121                 }
00122         }
00123 
00124         return FALSE;
00125 }
00126 
00127 
00128 
00129 // Switch to determine whether we capture all displays, or just the main one.
00130 // We may want to base this on the setting of _DEBUG...
00131 
00132 #define CAPTURE_ALL_DISPLAYS 0
00133 static double getDictDouble (CFDictionaryRef refDict, CFStringRef key);
00134 static long getDictLong (CFDictionaryRef refDict, CFStringRef key);
00135 
00136 
00137 
00138 
00139 // CarbonEvents we're interested in.
00140 static EventTypeSpec WindowHandlerEventList[] =
00141 {
00142         // Window-related events
00143         //      { kEventClassWindow, kEventWindowCollapsing },
00144         //      { kEventClassWindow, kEventWindowCollapsed },
00145         //      { kEventClassWindow, kEventWindowShown },
00146         { kEventClassWindow, kEventWindowActivated },
00147         { kEventClassWindow, kEventWindowDeactivated },
00148         { kEventClassWindow, kEventWindowShown },
00149         { kEventClassWindow, kEventWindowHidden },
00150         { kEventClassWindow, kEventWindowCollapsed },
00151         { kEventClassWindow, kEventWindowExpanded },
00152         { kEventClassWindow, kEventWindowGetClickActivation },
00153         { kEventClassWindow, kEventWindowClose },
00154         { kEventClassWindow, kEventWindowBoundsChanging },
00155         { kEventClassWindow, kEventWindowBoundsChanged },
00156         //      { kEventClassWindow, kEventWindowZoomed },
00157         //      { kEventClassWindow, kEventWindowDrawContent },
00158 
00159         // Mouse events
00160         { kEventClassMouse, kEventMouseDown },
00161         { kEventClassMouse, kEventMouseUp },
00162         { kEventClassMouse, kEventMouseDragged },
00163         { kEventClassMouse, kEventMouseWheelMoved },
00164         { kEventClassMouse, kEventMouseMoved },
00165 
00166         // Keyboard events
00167         // No longer handle raw key down events directly.
00168         // When text input events come in, extract the raw key events from them and process at that point.
00169         // This allows input methods to eat keystrokes the way they're supposed to.
00170 //      { kEventClassKeyboard, kEventRawKeyDown },
00171 //      { kEventClassKeyboard, kEventRawKeyRepeat },
00172         { kEventClassKeyboard, kEventRawKeyUp },
00173         { kEventClassKeyboard, kEventRawKeyModifiersChanged },
00174 
00175         // Text input events
00176         { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
00177         { kEventClassTextInput, kEventTextInputUpdateActiveInputArea },
00178         { kEventClassTextInput, kEventTextInputOffsetToPos },
00179         { kEventClassTextInput, kEventTextInputPosToOffset },
00180         { kEventClassTextInput, kEventTextInputShowHideBottomWindow },
00181         { kEventClassTextInput, kEventTextInputGetSelectedText },
00182         { kEventClassTextInput, kEventTextInputFilterText },
00183 
00184         // TSM Document Access events (advanced input method support)
00185         { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetLength },
00186         { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetSelectedRange },
00187         { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetCharacters },
00188         { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetFont },
00189         { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetGlyphInfo },
00190         { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessLockDocument },
00191         { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessUnlockDocument }
00192 };
00193 
00194 static EventTypeSpec GlobalHandlerEventList[] =
00195 {
00196         // Mouse events
00197         { kEventClassMouse, kEventMouseDown },
00198         { kEventClassMouse, kEventMouseUp },
00199         { kEventClassMouse, kEventMouseDragged },
00200         { kEventClassMouse, kEventMouseWheelMoved },
00201         { kEventClassMouse, kEventMouseMoved },
00202 
00203         // Keyboard events
00204         // No longer handle raw key down events directly.
00205         // When text input events come in, extract the raw key events from them and process at that point.
00206         // This allows input methods to eat keystrokes the way they're supposed to.
00207 //      { kEventClassKeyboard, kEventRawKeyDown },
00208 //      { kEventClassKeyboard, kEventRawKeyRepeat },
00209         { kEventClassKeyboard, kEventRawKeyUp },
00210         { kEventClassKeyboard, kEventRawKeyModifiersChanged },
00211 
00212         // Text input events
00213         { kEventClassTextInput, kEventTextInputUpdateActiveInputArea },
00214         { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
00215         { kEventClassTextInput, kEventTextInputOffsetToPos },
00216         { kEventClassTextInput, kEventTextInputPosToOffset },
00217         { kEventClassTextInput, kEventTextInputShowHideBottomWindow },
00218         { kEventClassTextInput, kEventTextInputGetSelectedText },
00219         { kEventClassTextInput, kEventTextInputFilterText },
00220 
00221         // TSM Document Access events (advanced input method support)
00222         { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetLength },
00223         { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetSelectedRange },
00224         { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetCharacters },
00225         { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetFont },
00226         { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetGlyphInfo },
00227         { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessLockDocument },
00228         { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessUnlockDocument }
00229 };
00230 
00231 static EventTypeSpec CommandHandlerEventList[] =
00232 {
00233         { kEventClassCommand, kEventCommandProcess }
00234 };
00235 
00236 // MBW -- HACK ALERT
00237 // On the Mac, to put up an OS dialog in full screen mode, we must first switch OUT of full screen mode.
00238 // The proper way to do this is to bracket the dialog with calls to beforeDialog() and afterDialog(), but these
00239 // require a pointer to the LLWindowMacOSX object.  Stash it here and maintain in the constructor and destructor.
00240 // This assumes that there will be only one object of this class at any time.  Hopefully this is true.
00241 static LLWindowMacOSX *gWindowImplementation = NULL;
00242 
00243 
00244 
00245 LLWindowMacOSX::LLWindowMacOSX(char *title, char *name, S32 x, S32 y, S32 width,
00246                                                            S32 height, U32 flags,
00247                                                            BOOL fullscreen, BOOL clearBg,
00248                                                            BOOL disable_vsync, BOOL use_gl,
00249                                                            BOOL ignore_pixel_depth,
00250                                                            U32 fsaa_samples)
00251         : LLWindow(fullscreen, flags)
00252 {
00253         // Voodoo for calling cocoa from carbon (see llwindowmacosx-objc.mm).
00254         setupCocoa();
00255         
00256         // Initialize the keyboard
00257         gKeyboard = new LLKeyboardMacOSX();
00258 
00259         // Ignore use_gl for now, only used for drones on PC
00260         mWindow = NULL;
00261         mContext = NULL;
00262         mPixelFormat = NULL;
00263         mDisplay = CGMainDisplayID();
00264         mOldDisplayMode = NULL;
00265         mTimer = NULL;
00266         mSimulatedRightClick = FALSE;
00267         mLastModifiers = 0;
00268         mHandsOffEvents = FALSE;
00269         mCursorDecoupled = FALSE;
00270         mCursorLastEventDeltaX = 0;
00271         mCursorLastEventDeltaY = 0;
00272         mCursorIgnoreNextDelta = FALSE;
00273         mNeedsResize = FALSE;
00274         mOverrideAspectRatio = 0.f;
00275         mMinimized = FALSE;
00276         mTSMDocument = NULL; // Just in case.
00277         mLanguageTextInputAllowed = FALSE;
00278         mTSMScriptCode = 0;
00279         mTSMLangCode = 0;
00280         mPreeditor = NULL;
00281         mFSAASamples = fsaa_samples;
00282         mForceRebuild = FALSE;
00283         
00284         // For reasons that aren't clear to me, LLTimers seem to be created in the "started" state.
00285         // 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.
00286         mBounceTimer.stop();    
00287 
00288         // Get the original aspect ratio of the main device.
00289         mOriginalAspectRatio = (double)CGDisplayPixelsWide(mDisplay) / (double)CGDisplayPixelsHigh(mDisplay);
00290 
00291         // Stash the window title
00292         strcpy((char*)mWindowTitle + 1, title); /* Flawfinder: ignore */
00293         mWindowTitle[0] = strlen(title);        /* Flawfinder: ignore */
00294 
00295         mEventHandlerUPP = NewEventHandlerUPP(staticEventHandler);
00296         mGlobalHandlerRef = NULL;
00297         mWindowHandlerRef = NULL;
00298 
00299         // We're not clipping yet
00300         SetRect( &mOldMouseClip, 0, 0, 0, 0 );
00301 
00302         // Set up global event handlers (the fullscreen case needs this)
00303         InstallStandardEventHandler(GetApplicationEventTarget());
00304 
00305         // Stash an object pointer for OSMessageBox()
00306         gWindowImplementation = this;
00307 
00308         // Create the GL context and set it up for windowed or fullscreen, as appropriate.
00309         if(createContext(x, y, width, height, 32, fullscreen, disable_vsync))
00310         {
00311                 if(mWindow != NULL)
00312                 {
00313                         // MBW -- XXX -- I think we can now do this here?
00314                         // Constrain the window to the screen it's mostly on, resizing if necessary.
00315                         ConstrainWindowToScreen(
00316                                 mWindow,
00317                                 kWindowStructureRgn,
00318                                 kWindowConstrainMayResize |
00319                                 //                              kWindowConstrainStandardOptions |
00320                                 0,
00321                                 NULL,
00322                                 NULL);
00323 
00324                         MacShowWindow(mWindow);
00325                         BringToFront(mWindow);
00326                 }
00327 
00328                 if (!gGLManager.initGL())
00329                 {
00330                         setupFailure(
00331                                 "Second Life is unable to run because your video card drivers\n"
00332                                 "are out of date or unsupported. Please make sure you have\n"
00333                                 "the latest video card drivers installed.\n"
00334                                 "If you continue to receive this message, contact customer service.",
00335                                 "Error",
00336                                 OSMB_OK);
00337                         return;
00338                 }
00339 
00340                 //start with arrow cursor
00341                 initCursors();
00342                 setCursor( UI_CURSOR_ARROW );
00343         }
00344 
00345         stop_glerror();
00346 }
00347 
00348 BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL disable_vsync)
00349 {
00350         OSStatus                err;
00351         BOOL                    glNeedsInit = FALSE;
00352 
00353         if(mGlobalHandlerRef == NULL)
00354         {
00355                 InstallApplicationEventHandler(mEventHandlerUPP, GetEventTypeCount (CommandHandlerEventList), CommandHandlerEventList, (void*)this, &mGlobalHandlerRef);
00356         }
00357 
00358         mFullscreen = fullscreen;
00359 
00360         if (mFullscreen && (mOldDisplayMode == NULL))
00361         {
00362                 LL_INFOS("Window") << "createContext: setting up fullscreen " << width << "x" << height << LL_ENDL;
00363 
00364                 // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays.  Plan accordingly.
00365                 double refresh = getDictDouble (CGDisplayCurrentMode (mDisplay),  kCGDisplayRefreshRate);
00366 
00367                 // If the requested width or height is 0, find the best default for the monitor.
00368                 if((width == 0) || (height == 0))
00369                 {
00370                         // Scan through the list of modes, looking for one which has:
00371                         //              height between 700 and 800
00372                         //              aspect ratio closest to the user's original mode
00373                         S32 resolutionCount = 0;
00374                         LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount);
00375 
00376                         if(resolutionList != NULL)
00377                         {
00378                                 F32 closestAspect = 0;
00379                                 U32 closestHeight = 0;
00380                                 U32 closestWidth = 0;
00381                                 int i;
00382 
00383                                 LL_DEBUGS("Window") << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << LL_ENDL;
00384 
00385                                 for(i=0; i < resolutionCount; i++)
00386                                 {
00387                                         F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight;
00388 
00389                                         LL_DEBUGS("Window") << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << LL_ENDL;
00390 
00391                                         if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) &&
00392                                                 (fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio)))
00393                                         {
00394                                                 LL_DEBUGS("Window") << " (new closest mode) " << LL_ENDL;
00395 
00396                                                 // This is the closest mode we've seen yet.
00397                                                 closestWidth = resolutionList[i].mWidth;
00398                                                 closestHeight = resolutionList[i].mHeight;
00399                                                 closestAspect = aspect;
00400                                         }
00401                                 }
00402 
00403                                 width = closestWidth;
00404                                 height = closestHeight;
00405                         }
00406                 }
00407 
00408                 if((width == 0) || (height == 0))
00409                 {
00410                         // Mode search failed for some reason.  Use the old-school default.
00411                         width = 1024;
00412                         height = 768;
00413                 }
00414 
00415                 if (true)
00416                 {
00417                         // Fullscreen support
00418                         CFDictionaryRef refDisplayMode = 0;
00419                         boolean_t exactMatch = false;
00420 
00421 #if CAPTURE_ALL_DISPLAYS
00422                         // Capture all displays (may want to do this for final build)
00423                         CGCaptureAllDisplays ();
00424 #else
00425                         // Capture only the main display (useful for debugging)
00426                         CGDisplayCapture (mDisplay);
00427 #endif
00428 
00429                         // Switch the display to the desired resolution and refresh
00430                         refDisplayMode = CGDisplayBestModeForParametersAndRefreshRate(
00431                                 mDisplay,
00432                                 BITS_PER_PIXEL,
00433                                 width,
00434                                 height,
00435                                 refresh,
00436                                 &exactMatch);
00437 
00438                         if (refDisplayMode)
00439                         {
00440                                 LL_DEBUGS("Window") << "createContext: switching display resolution" << LL_ENDL;
00441                                 mOldDisplayMode = CGDisplayCurrentMode (mDisplay);
00442                                 CGDisplaySwitchToMode (mDisplay, refDisplayMode);
00443                                 //                              CFRelease(refDisplayMode);
00444 
00445                                 AddEventTypesToHandler(mGlobalHandlerRef, GetEventTypeCount (GlobalHandlerEventList), GlobalHandlerEventList);
00446                         }
00447 
00448 
00449                         mFullscreen = TRUE;
00450                         mFullscreenWidth   = CGDisplayPixelsWide(mDisplay);
00451                         mFullscreenHeight  = CGDisplayPixelsHigh(mDisplay);
00452                         mFullscreenBits    = CGDisplayBitsPerPixel(mDisplay);
00453                         mFullscreenRefresh = llround(getDictDouble (CGDisplayCurrentMode (mDisplay),  kCGDisplayRefreshRate));
00454 
00455                         LL_INFOS("Window") << "Running at " << mFullscreenWidth
00456                                 << "x"   << mFullscreenHeight
00457                                 << "x"   << mFullscreenBits
00458                                 << " @ " << mFullscreenRefresh
00459                                 << LL_ENDL;
00460                 }
00461                 else
00462                 {
00463                         // No fullscreen support
00464                         mFullscreen = FALSE;
00465                         mFullscreenWidth   = -1;
00466                         mFullscreenHeight  = -1;
00467                         mFullscreenBits    = -1;
00468                         mFullscreenRefresh = -1;
00469 
00470                         char error[256];        /* Flawfinder: ignore */
00471                         snprintf(error, sizeof(error), "Unable to run fullscreen at %d x %d.\nRunning in window.", width, height);      
00472                         OSMessageBox(error, "Error", OSMB_OK);
00473                 }
00474         }
00475 
00476         if(!mFullscreen && (mWindow == NULL))
00477         {
00478                 Rect                    window_rect;
00479                 //int                           displayWidth = CGDisplayPixelsWide(mDisplay);
00480                 //int                           displayHeight = CGDisplayPixelsHigh(mDisplay);
00481                 //const int             menuBarPlusTitleBar = 44;   // Ugly magic number.
00482 
00483                 LL_DEBUGS("Window") << "createContext: creating window" << LL_ENDL;
00484 
00485                 window_rect.left = (long) x;
00486                 window_rect.right = (long) x + width;
00487                 window_rect.top = (long) y;
00488                 window_rect.bottom = (long) y + height;
00489 
00490                 //-----------------------------------------------------------------------
00491                 // Create the window
00492                 //-----------------------------------------------------------------------
00493                 mWindow = NewCWindow(
00494                         NULL,
00495                         &window_rect,
00496                         mWindowTitle,
00497                         false,                          // Create the window invisible.  Whoever calls createContext() should show it after any moving/resizing.
00498                         //              noGrowDocProc,          // Window with no grow box and no zoom box
00499                         zoomDocProc,            // Window with a grow box and a zoom box
00500                         //              zoomNoGrow,                     // Window with a zoom box but no grow box
00501                         kFirstWindowOfClass,
00502                         true,
00503                         (long)this);
00504 
00505 
00506                 if (!mWindow)
00507                 {
00508                         setupFailure("Window creation error", "Error", OSMB_OK);
00509                         return FALSE;
00510                 }
00511 
00512                 // Turn on live resize.
00513                 // For this to work correctly, we need to be able to call LLViewerWindow::draw from
00514                 // the event handler for kEventWindowBoundsChanged.  It's not clear that we have access from here.
00515                 //      err = ChangeWindowAttributes(mWindow, kWindowLiveResizeAttribute, 0);
00516 
00517                 // Set up window event handlers (some window-related events ONLY go to window handlers.)
00518                 InstallStandardEventHandler(GetWindowEventTarget(mWindow));
00519                 InstallWindowEventHandler (mWindow, mEventHandlerUPP, GetEventTypeCount (WindowHandlerEventList), WindowHandlerEventList, (void*)this, &mWindowHandlerRef); // add event handler
00520 
00521         }
00522 
00523         {
00524                 // Create and initialize our TSM document for language text input.
00525                 // If an error occured, we can do nothing better than simply ignore it.
00526                 // mTSMDocument will be kept NULL in case.
00527                 if (mTSMDocument)
00528                 {
00529                         DeactivateTSMDocument(mTSMDocument);
00530                         DeleteTSMDocument(mTSMDocument);
00531                         mTSMDocument = NULL;
00532                 }
00533                 static InterfaceTypeList types = { kUnicodeDocument };
00534                 err = NewTSMDocument(1, types, &mTSMDocument, 0);
00535                 if (err != noErr)
00536                 {
00537                         LL_WARNS("Window") << "createContext: couldn't create a TSMDocument (" << err << ")" << LL_ENDL;
00538                 }
00539                 if (mTSMDocument)
00540                 {
00541                         ActivateTSMDocument(mTSMDocument);
00542                         UseInputWindow(mTSMDocument, FALSE);
00543                         allowLanguageTextInput(NULL, FALSE);
00544                 }
00545         }
00546 
00547         if(mContext == NULL)
00548         {
00549                 AGLRendererInfo rendererInfo = NULL;
00550 
00551                 //-----------------------------------------------------------------------
00552                 // Create GL drawing context
00553                 //-----------------------------------------------------------------------
00554 
00555                 if(mPixelFormat == NULL)
00556                 {
00557                         if(mFullscreen)
00558                         {
00559                                 GLint fullscreenAttrib[] =
00560                                 {
00561                                         AGL_RGBA,
00562                                                 AGL_FULLSCREEN,
00563                                                 //                      AGL_NO_RECOVERY,        // MBW -- XXX -- Not sure if we want this attribute
00564                                                 AGL_SAMPLE_BUFFERS_ARB, mFSAASamples > 0 ? 1 : 0,
00565                                                 AGL_SAMPLES_ARB, mFSAASamples,
00566                                                 AGL_DOUBLEBUFFER,
00567                                                 AGL_CLOSEST_POLICY,
00568                                                 AGL_ACCELERATED,
00569                                                 AGL_RED_SIZE, 8,
00570                                                 AGL_GREEN_SIZE, 8,
00571                                                 AGL_BLUE_SIZE, 8,
00572                                                 AGL_ALPHA_SIZE, 8,
00573                                                 AGL_DEPTH_SIZE, 24,
00574                                                 AGL_STENCIL_SIZE, 8,
00575                                                 AGL_NONE
00576                                 };
00577 
00578                                 LL_DEBUGS("Window") << "createContext: creating fullscreen pixelformat" << LL_ENDL;
00579 
00580                                 GDHandle gdhDisplay = NULL;
00581                                 err = DMGetGDeviceByDisplayID ((DisplayIDType)mDisplay, &gdhDisplay, false);
00582 
00583                                 mPixelFormat = aglChoosePixelFormat(&gdhDisplay, 1, fullscreenAttrib);
00584                                 rendererInfo = aglQueryRendererInfo(&gdhDisplay, 1);
00585                         }
00586                         else
00587                         {
00588                                 GLint windowedAttrib[] =
00589                                 {
00590                                         AGL_RGBA,
00591                                                 AGL_DOUBLEBUFFER,
00592                                                 AGL_CLOSEST_POLICY,
00593                                                 AGL_ACCELERATED,
00594                                                 AGL_SAMPLE_BUFFERS_ARB, mFSAASamples > 0 ? 1 : 0,
00595                                                 AGL_SAMPLES_ARB, mFSAASamples,
00596                                                 AGL_RED_SIZE, 8,
00597                                                 AGL_GREEN_SIZE, 8,
00598                                                 AGL_BLUE_SIZE, 8,
00599                                                 AGL_ALPHA_SIZE, 8,
00600                                                 AGL_DEPTH_SIZE, 24,
00601                                                 AGL_STENCIL_SIZE, 8,
00602                                                 AGL_NONE
00603                                 };
00604 
00605                                 LL_DEBUGS("Window") << "createContext: creating windowed pixelformat" << LL_ENDL;
00606 
00607                                 mPixelFormat = aglChoosePixelFormat(NULL, 0, windowedAttrib);
00608 
00609                                 GDHandle gdhDisplay = GetMainDevice();
00610                                 rendererInfo = aglQueryRendererInfo(&gdhDisplay, 1);
00611                         }
00612 
00613                         // May want to get the real error text like this:
00614                         // (char *) aglErrorString(aglGetError());
00615 
00616                         if(aglGetError() != AGL_NO_ERROR)
00617                         {
00618                                 setupFailure("Can't find suitable pixel format", "Error", OSMB_OK);
00619                                 return FALSE;
00620                         }
00621                 }
00622 
00623                 if(mPixelFormat)
00624                 {
00625                         LL_DEBUGS("Window") << "createContext: creating GL context" << LL_ENDL;
00626                         mContext = aglCreateContext(mPixelFormat, NULL);
00627                 }
00628 
00629                 if(mContext == NULL)
00630                 {
00631                         setupFailure("Can't make GL context", "Error", OSMB_OK);
00632                         return FALSE;
00633                 }
00634 
00635                 gGLManager.mVRAM = 0;
00636 
00637                 if(rendererInfo != NULL)
00638                 {
00639                         GLint result;
00640 
00641                         if(aglDescribeRenderer(rendererInfo, AGL_VIDEO_MEMORY, &result))
00642                         {
00643                                 //                              llinfos << "createContext: aglDescribeRenderer(AGL_VIDEO_MEMORY) returned " << result << llendl;
00644                                 gGLManager.mVRAM = result / (1024 * 1024);
00645                         }
00646                         else
00647                         {
00648                                 //                              llinfos << "createContext: aglDescribeRenderer(AGL_VIDEO_MEMORY) failed." << llendl;
00649                         }
00650 
00651                         // This could be useful at some point, if it takes into account the memory already used by screen buffers, etc...
00652                         if(aglDescribeRenderer(rendererInfo, AGL_TEXTURE_MEMORY, &result))
00653                         {
00654                                 //                              llinfos << "createContext: aglDescribeRenderer(AGL_TEXTURE_MEMORY) returned " << result << llendl;
00655                         }
00656                         else
00657                         {
00658                                 //                              llinfos << "createContext: aglDescribeRenderer(AGL_TEXTURE_MEMORY) failed." << llendl;
00659                         }
00660 
00661                         aglDestroyRendererInfo(rendererInfo);
00662                 }
00663 
00664                 // Since we just created the context, it needs to be set up.
00665                 glNeedsInit = TRUE;
00666         }
00667 
00668         // Hook up the context to a drawable
00669         if (mFullscreen && (mOldDisplayMode != NULL))
00670         {
00671                 // We successfully captured the display.  Use a fullscreen drawable
00672 
00673                 LL_DEBUGS("Window") << "createContext: attaching fullscreen drawable" << LL_ENDL;
00674 
00675 #if CAPTURE_ALL_DISPLAYS
00676                 // Capture all displays (may want to do this for final build)
00677                 aglDisable (mContext, AGL_FS_CAPTURE_SINGLE);
00678 #else
00679                 // Capture only the main display (useful for debugging)
00680                 aglEnable (mContext, AGL_FS_CAPTURE_SINGLE);
00681 #endif
00682 
00683                 if (!aglSetFullScreen (mContext, 0, 0, 0, 0))
00684                 {
00685                         setupFailure("Can't set GL fullscreen", "Error", OSMB_OK);
00686                         return FALSE;
00687                 }
00688         }
00689         else if(!mFullscreen && (mWindow != NULL))
00690         {
00691                 LL_DEBUGS("Window") << "createContext: attaching windowed drawable" << LL_ENDL;
00692 
00693                 // We created a window.  Use it as the drawable.
00694                 if(!aglSetDrawable(mContext, GetWindowPort (mWindow)))
00695                 {
00696                         setupFailure("Can't set GL drawable", "Error", OSMB_OK);
00697                         return FALSE;
00698                 }
00699         }
00700         else
00701         {
00702                 setupFailure("Can't get fullscreen or windowed drawable.", "Error", OSMB_OK);
00703                 return FALSE;
00704         }
00705 
00706         if(mContext != NULL)
00707         {
00708                 LL_DEBUGS("Window") << "createContext: setting current context" << LL_ENDL;
00709 
00710                 if (!aglSetCurrentContext(mContext))
00711                 {
00712                         setupFailure("Can't activate GL rendering context", "Error", OSMB_OK);
00713                         return FALSE;
00714                 }
00715         }
00716 
00717         if(glNeedsInit)
00718         {
00719                 // Check for some explicitly unsupported cards.
00720                 const char* RENDERER = (const char*) glGetString(GL_RENDERER);
00721 
00722                 const char* CARD_LIST[] =
00723                 {       "RAGE 128",
00724                 "RIVA TNT2",
00725                 "Intel 810",
00726                 "3Dfx/Voodoo3",
00727                 "Radeon 7000",
00728                 "Radeon 7200",
00729                 "Radeon 7500",
00730                 "Radeon DDR",
00731                 "Radeon VE",
00732                 "GDI Generic" };
00733                 const S32 CARD_COUNT = sizeof(CARD_LIST)/sizeof(char*);
00734 
00735                 // Future candidates:
00736                 // ProSavage/Twister
00737                 // SuperSavage
00738 
00739                 S32 i;
00740                 for (i = 0; i < CARD_COUNT; i++)
00741                 {
00742                         if (check_for_card(RENDERER, CARD_LIST[i]))
00743                         {
00744                                 close();
00745                                 return FALSE;
00746                         }
00747                 }
00748         }
00749 
00750         GLint colorBits, alphaBits, depthBits, stencilBits;
00751 
00752         if(     !aglDescribePixelFormat(mPixelFormat, AGL_BUFFER_SIZE, &colorBits) ||
00753                 !aglDescribePixelFormat(mPixelFormat, AGL_ALPHA_SIZE, &alphaBits) ||
00754                 !aglDescribePixelFormat(mPixelFormat, AGL_DEPTH_SIZE, &depthBits) ||
00755                 !aglDescribePixelFormat(mPixelFormat, AGL_STENCIL_SIZE, &stencilBits))
00756         {
00757                 close();
00758                 setupFailure("Can't get pixel format description", "Error", OSMB_OK);
00759                 return FALSE;
00760         }
00761 
00762         LL_INFOS("GLInit") << "GL buffer: Color Bits " << S32(colorBits)
00763                 << " Alpha Bits " << S32(alphaBits)
00764                 << " Depth Bits " << S32(depthBits)
00765                 << " Stencil Bits" << S32(stencilBits)
00766                 << LL_ENDL;
00767 
00768         if (colorBits < 32)
00769         {
00770                 close();
00771                 setupFailure(
00772                         "Second Life requires True Color (32-bit) to run in a window.\n"
00773                         "Please go to Control Panels -> Display -> Settings and\n"
00774                         "set the screen to 32-bit color.\n"
00775                         "Alternately, if you choose to run fullscreen, Second Life\n"
00776                         "will automatically adjust the screen each time it runs.",
00777                         "Error",
00778                         OSMB_OK);
00779                 return FALSE;
00780         }
00781 
00782         if (alphaBits < 8)
00783         {
00784                 close();
00785                 setupFailure(
00786                         "Second Life is unable to run because it can't get an 8 bit alpha\n"
00787                         "channel.  Usually this is due to video card driver issues.\n"
00788                         "Please make sure you have the latest video card drivers installed.\n"
00789                         "Also be sure your monitor is set to True Color (32-bit) in\n"
00790                         "Control Panels -> Display -> Settings.\n"
00791                         "If you continue to receive this message, contact customer service.",
00792                         "Error",
00793                         OSMB_OK);
00794                 return FALSE;
00795         }
00796 
00797         // Disable vertical sync for swap
00798         GLint frames_per_swap = 0;
00799         if (disable_vsync)
00800         {
00801                 LL_DEBUGS("GLInit") << "Disabling vertical sync" << LL_ENDL;
00802                 frames_per_swap = 0;
00803         }
00804         else
00805         {
00806                 LL_DEBUGS("GLinit") << "Keeping vertical sync" << LL_ENDL;
00807                 frames_per_swap = 1;
00808         }
00809         aglSetInteger(mContext, AGL_SWAP_INTERVAL, &frames_per_swap);  
00810 
00811         //enable multi-threaded OpenGL
00812         if (gSavedSettings.getBOOL("RenderAppleUseMultGL"))
00813         {
00814                 CGLError cgl_err;
00815                 CGLContextObj ctx = CGLGetCurrentContext();
00816 
00817                 cgl_err =  CGLEnable( ctx, kCGLCEMPEngine);
00818 
00819                 if (cgl_err != kCGLNoError )
00820                 {
00821                         LL_DEBUGS("GLInit") << "Multi-threaded OpenGL not available." << LL_ENDL;
00822                 }    
00823                 else
00824                 {
00825                         LL_DEBUGS("GLInit") << "Multi-threaded OpenGL enabled." << LL_ENDL;
00826                 }
00827         }
00828 
00829         //make sure multisample starts off disabled
00830         glDisable(GL_MULTISAMPLE_ARB);
00831         
00832         // Don't need to get the current gamma, since there's a call that restores it to the system defaults.
00833         return TRUE;
00834 }
00835 
00836 
00837 // changing fullscreen resolution, or switching between windowed and fullscreen mode.
00838 BOOL LLWindowMacOSX::switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp)
00839 {
00840         BOOL needsRebuild = FALSE;
00841         BOOL result = true;
00842 
00843         if(fullscreen)
00844         {
00845                 if(mFullscreen)
00846                 {
00847                         // Switching resolutions in fullscreen mode.  Don't need to rebuild for this.
00848                         // Fullscreen support
00849                         CFDictionaryRef refDisplayMode = 0;
00850                         boolean_t exactMatch = false;
00851 
00852                         // Switch the display to the desired resolution and refresh
00853                         refDisplayMode = CGDisplayBestModeForParametersAndRefreshRate(
00854                                 mDisplay,
00855                                 BITS_PER_PIXEL,
00856                                 size.mX,
00857                                 size.mY,
00858                                 getDictDouble (CGDisplayCurrentMode (mDisplay),  kCGDisplayRefreshRate),
00859                                 &exactMatch);
00860 
00861                         if (refDisplayMode)
00862                         {
00863                                 CGDisplaySwitchToMode (mDisplay, refDisplayMode);
00864                                 //                              CFRelease(refDisplayMode);
00865                         }
00866 
00867                         mFullscreenWidth   = CGDisplayPixelsWide(mDisplay);
00868                         mFullscreenHeight  = CGDisplayPixelsHigh(mDisplay);
00869                         mFullscreenBits    = CGDisplayBitsPerPixel(mDisplay);
00870                         mFullscreenRefresh = llround(getDictDouble (CGDisplayCurrentMode (mDisplay),  kCGDisplayRefreshRate));
00871 
00872                         LL_INFOS("Window") << "Switched resolution to " << mFullscreenWidth
00873                                 << "x"   << mFullscreenHeight
00874                                 << "x"   << mFullscreenBits
00875                                 << " @ " << mFullscreenRefresh
00876                                 << LL_ENDL;
00877 
00878                         // Update the GL context to the new screen size
00879                         if (!aglUpdateContext(mContext))
00880                         {
00881                                 setupFailure("Can't set GL fullscreen", "Error", OSMB_OK);
00882                                 result = FALSE;
00883                         }
00884                 }
00885                 else
00886                 {
00887                         // Switching from windowed to fullscreen
00888                         needsRebuild = TRUE;
00889                 }
00890         }
00891         else
00892         {
00893                 if(mFullscreen)
00894                 {
00895                         // Switching from fullscreen to windowed
00896                         needsRebuild = TRUE;
00897                 }
00898                 else
00899                 {
00900                         // Windowed to windowed -- not sure why we would be called like this.  Just change the window size.
00901                         // The bounds changed event handler will do the rest.
00902                         if(mWindow != NULL)
00903                         {
00904                                 ::SizeWindow(mWindow, size.mX, size.mY, true);
00905                         }
00906                 }
00907         }
00908 
00909         stop_glerror();
00910         if(needsRebuild || mForceRebuild)
00911         {
00912                 mForceRebuild = FALSE;
00913                 destroyContext();
00914                 result = createContext(0, 0, size.mX, size.mY, 0, fullscreen, disable_vsync);
00915                 if (result)
00916                 {
00917                         if(mWindow != NULL)
00918                         {
00919                                 MacShowWindow(mWindow);
00920                                 BringToFront(mWindow);
00921                         }
00922 
00923                         llverify(gGLManager.initGL());
00924 
00925                         //start with arrow cursor
00926                         initCursors();
00927                         setCursor( UI_CURSOR_ARROW );
00928                 }
00929         }
00930 
00931         stop_glerror();
00932 
00933         return result;
00934 }
00935 
00936 void LLWindowMacOSX::destroyContext()
00937 {
00938         if (!mContext)
00939         {
00940                 // We don't have a context
00941                 return;
00942         }
00943         // Unhook the GL context from any drawable it may have
00944         if(mContext != NULL)
00945         {
00946                 LL_DEBUGS("Window") << "destroyContext: unhooking drawable " << LL_ENDL;
00947 
00948                 aglSetCurrentContext (NULL);
00949                 aglSetDrawable(mContext, NULL);
00950         }
00951 
00952         // Make sure the display resolution gets restored
00953         if(mOldDisplayMode != NULL)
00954         {
00955                 LL_DEBUGS("Window") << "destroyContext: restoring display resolution " << LL_ENDL;
00956 
00957                 CGDisplaySwitchToMode (mDisplay, mOldDisplayMode);
00958 
00959 #if CAPTURE_ALL_DISPLAYS
00960                 // Uncapture all displays (may want to do this for final build)
00961                 CGReleaseAllDisplays ();
00962 #else
00963                 // Uncapture only the main display (useful for debugging)
00964                 CGDisplayRelease (mDisplay);
00965 #endif
00966 
00967                 //              CFRelease(mOldDisplayMode);
00968 
00969                 mOldDisplayMode = NULL;
00970 
00971                 // Remove the global event handlers the fullscreen case needed
00972                 RemoveEventTypesFromHandler(mGlobalHandlerRef, GetEventTypeCount (GlobalHandlerEventList), GlobalHandlerEventList);
00973         }
00974 
00975         // Clean up remaining GL state before blowing away window
00976         gGLManager.shutdownGL();
00977 
00978         // Clean up the pixel format
00979         if(mPixelFormat != NULL)
00980         {
00981                 LL_DEBUGS("Window") << "destroyContext: destroying pixel format " << LL_ENDL;
00982                 aglDestroyPixelFormat(mPixelFormat);
00983                 mPixelFormat = NULL;
00984         }
00985 
00986         // Remove any Carbon Event handlers we installed
00987         if(mGlobalHandlerRef != NULL)
00988         {
00989                 LL_DEBUGS("Window") << "destroyContext: removing global event handler" << LL_ENDL;
00990                 RemoveEventHandler(mGlobalHandlerRef);
00991                 mGlobalHandlerRef = NULL;
00992         }
00993 
00994         if(mWindowHandlerRef != NULL)
00995         {
00996                 LL_DEBUGS("Window") << "destroyContext: removing window event handler" << LL_ENDL;
00997                 RemoveEventHandler(mWindowHandlerRef);
00998                 mWindowHandlerRef = NULL;
00999         }
01000 
01001         // Cleanup any TSM document we created.
01002         if(mTSMDocument != NULL)
01003         {
01004                 LL_DEBUGS("Window") << "destroyContext: deleting TSM document" << LL_ENDL;
01005                 DeactivateTSMDocument(mTSMDocument);
01006                 DeleteTSMDocument(mTSMDocument);
01007                 mTSMDocument = NULL;
01008         }
01009 
01010         // Close the window
01011         if(mWindow != NULL)
01012         {
01013                 LL_DEBUGS("Window") << "destroyContext: disposing window" << LL_ENDL;
01014                 DisposeWindow(mWindow);
01015                 mWindow = NULL;
01016         }
01017 
01018         // Clean up the GL context
01019         if(mContext != NULL)
01020         {
01021                 LL_DEBUGS("Window") << "destroyContext: destroying GL context" << LL_ENDL;
01022                 aglDestroyContext(mContext);
01023                 mContext = NULL;
01024         }
01025 
01026 }
01027 
01028 LLWindowMacOSX::~LLWindowMacOSX()
01029 {
01030         destroyContext();
01031 
01032         if(mSupportedResolutions != NULL)
01033         {
01034                 delete []mSupportedResolutions;
01035         }
01036 
01037         gWindowImplementation = NULL;
01038 
01039 }
01040 
01041 
01042 void LLWindowMacOSX::show()
01043 {
01044         if(IsWindowCollapsed(mWindow))
01045                 CollapseWindow(mWindow, false);
01046 
01047         MacShowWindow(mWindow);
01048         BringToFront(mWindow);
01049 }
01050 
01051 void LLWindowMacOSX::hide()
01052 {
01053         setMouseClipping(FALSE);
01054         HideWindow(mWindow);
01055 }
01056 
01057 void LLWindowMacOSX::minimize()
01058 {
01059         setMouseClipping(FALSE);
01060         showCursor();
01061         CollapseWindow(mWindow, true);
01062 }
01063 
01064 void LLWindowMacOSX::restore()
01065 {
01066         show();
01067 }
01068 
01069 
01070 // close() destroys all OS-specific code associated with a window.
01071 // Usually called from LLWindowManager::destroyWindow()
01072 void LLWindowMacOSX::close()
01073 {
01074         // Is window is already closed?
01075         //      if (!mWindow)
01076         //      {
01077         //              return;
01078         //      }
01079 
01080         // Make sure cursor is visible and we haven't mangled the clipping state.
01081         setMouseClipping(FALSE);
01082         showCursor();
01083 
01084         destroyContext();
01085 }
01086 
01087 BOOL LLWindowMacOSX::isValid()
01088 {
01089         if(mFullscreen)
01090         {
01091                 return(TRUE);
01092         }
01093 
01094         return (mWindow != NULL);
01095 }
01096 
01097 BOOL LLWindowMacOSX::getVisible()
01098 {
01099         BOOL result = FALSE;
01100 
01101         if(mFullscreen)
01102         {
01103                 result = TRUE;
01104         }if (mWindow)
01105         {
01106                 if(MacIsWindowVisible(mWindow))
01107                         result = TRUE;
01108         }
01109 
01110         return(result);
01111 }
01112 
01113 BOOL LLWindowMacOSX::getMinimized()
01114 {
01115         BOOL result = FALSE;
01116         
01117         // Since the set of states where we want to act "minimized" is non-trivial, it's easier to
01118         // track things locally than to try and retrieve the state from the window manager.
01119         result = mMinimized;
01120 
01121         return(result);
01122 }
01123 
01124 BOOL LLWindowMacOSX::getMaximized()
01125 {
01126         BOOL result = FALSE;
01127 
01128         if (mWindow)
01129         {
01130                 // TODO
01131         }
01132 
01133         return(result);
01134 }
01135 
01136 BOOL LLWindowMacOSX::maximize()
01137 {
01138         // TODO
01139         return FALSE;
01140 }
01141 
01142 BOOL LLWindowMacOSX::getFullscreen()
01143 {
01144         return mFullscreen;
01145 }
01146 
01147 void LLWindowMacOSX::gatherInput()
01148 {
01149         // stop bouncing icon after fixed period of time
01150         if (mBounceTimer.getStarted() && mBounceTimer.getElapsedTimeF32() > mBounceTime)
01151         {
01152                 stopDockTileBounce();
01153         }
01154 
01155         // Use the old-school version so we get AppleEvent handler dispatch and menuselect handling.
01156         // Anything that has an event handler will get processed inside WaitNextEvent, so we only need to handle
01157         // the odd stuff here.
01158         EventRecord evt;
01159         while(WaitNextEvent(everyEvent, &evt, 0, NULL))
01160         {
01161                 //                      printf("WaitNextEvent returned true, event is %d.\n", evt.what);
01162                 switch(evt.what)
01163                 {
01164                 case mouseDown:
01165                         {
01166                                 short part;
01167                                 WindowRef window;
01168                                 long selectResult;
01169                                 part = FindWindow(evt.where, &window);
01170                                 switch ( part )
01171                                 {
01172                                 case inMenuBar:
01173                                         selectResult = MenuSelect(evt.where);
01174 
01175                                         HiliteMenu(0);
01176                                         break;
01177                                 }
01178                         }
01179                         break;
01180 
01181                 case kHighLevelEvent:
01182                         AEProcessAppleEvent (&evt);
01183                         break;
01184 
01185                 case updateEvt:
01186                         // We shouldn't be getting these regularly (since our window will be buffered), but we need to handle them correctly...
01187                         BeginUpdate((WindowRef)evt.message);
01188                         EndUpdate((WindowRef)evt.message);
01189                         break;
01190 
01191                 }
01192         }
01193 }
01194 
01195 BOOL LLWindowMacOSX::getPosition(LLCoordScreen *position)
01196 {
01197         Rect window_rect;
01198         OSStatus err = -1;
01199 
01200         if(mFullscreen)
01201         {
01202                 position->mX = 0;
01203                 position->mY = 0;
01204                 err = noErr;
01205         }
01206         else if(mWindow)
01207         {
01208                 err = GetWindowBounds(mWindow, kWindowContentRgn, &window_rect);
01209 
01210                 position->mX = window_rect.left;
01211                 position->mY = window_rect.top;
01212         }
01213         else
01214         {
01215                 llerrs << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << llendl;
01216         }
01217 
01218         return (err == noErr);
01219 }
01220 
01221 BOOL LLWindowMacOSX::getSize(LLCoordScreen *size)
01222 {
01223         Rect window_rect;
01224         OSStatus err = -1;
01225 
01226         if(mFullscreen)
01227         {
01228                 size->mX = mFullscreenWidth;
01229                 size->mY = mFullscreenHeight;
01230                 err = noErr;
01231         }
01232         else if(mWindow)
01233         {
01234                 err = GetWindowBounds(mWindow, kWindowContentRgn, &window_rect);
01235 
01236                 size->mX = window_rect.right - window_rect.left;
01237                 size->mY = window_rect.bottom - window_rect.top;
01238         }
01239         else
01240         {
01241                 llerrs << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << llendl;
01242         }
01243 
01244         return (err == noErr);
01245 }
01246 
01247 BOOL LLWindowMacOSX::getSize(LLCoordWindow *size)
01248 {
01249         Rect window_rect;
01250         OSStatus err = -1;
01251 
01252         if(mFullscreen)
01253         {
01254                 size->mX = mFullscreenWidth;
01255                 size->mY = mFullscreenHeight;
01256                 err = noErr;
01257         }
01258         else if(mWindow)
01259         {
01260                 err = GetWindowBounds(mWindow, kWindowContentRgn, &window_rect);
01261 
01262                 size->mX = window_rect.right - window_rect.left;
01263                 size->mY = window_rect.bottom - window_rect.top;
01264         }
01265         else
01266         {
01267                 llerrs << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << llendl;
01268         }
01269 
01270         return (err == noErr);
01271 }
01272 
01273 BOOL LLWindowMacOSX::setPosition(const LLCoordScreen position)
01274 {
01275         if(mWindow)
01276         {
01277                 MacMoveWindow(mWindow, position.mX, position.mY, false);
01278         }
01279 
01280         return TRUE;
01281 }
01282 
01283 BOOL LLWindowMacOSX::setSize(const LLCoordScreen size)
01284 {
01285         if(mWindow)
01286         {
01287                 SizeWindow(mWindow, size.mX, size.mY, true);
01288         }
01289 
01290         return TRUE;
01291 }
01292 
01293 void LLWindowMacOSX::swapBuffers()
01294 {
01295         aglSwapBuffers(mContext);
01296 }
01297 
01298 F32 LLWindowMacOSX::getGamma()
01299 {
01300         F32 result = 1.8;       // Default to something sane
01301 
01302         CGGammaValue redMin;
01303         CGGammaValue redMax;
01304         CGGammaValue redGamma;
01305         CGGammaValue greenMin;
01306         CGGammaValue greenMax;
01307         CGGammaValue greenGamma;
01308         CGGammaValue blueMin;
01309         CGGammaValue blueMax;
01310         CGGammaValue blueGamma;
01311 
01312         if(CGGetDisplayTransferByFormula(
01313                 mDisplay,
01314                 &redMin,
01315                 &redMax,
01316                 &redGamma,
01317                 &greenMin,
01318                 &greenMax,
01319                 &greenGamma,
01320                 &blueMin,
01321                 &blueMax,
01322                 &blueGamma) == noErr)
01323         {
01324                 // So many choices...
01325                 // Let's just return the green channel gamma for now.
01326                 result = greenGamma;
01327         }
01328 
01329         return result;
01330 }
01331 
01332 U32 LLWindowMacOSX::getFSAASamples()
01333 {
01334         return mFSAASamples;
01335 }
01336 
01337 void LLWindowMacOSX::setFSAASamples(const U32 samples)
01338 {
01339         mFSAASamples = samples;
01340         mForceRebuild = TRUE;
01341 }
01342 
01343 BOOL LLWindowMacOSX::restoreGamma()
01344 {
01345         CGDisplayRestoreColorSyncSettings();
01346         return true;
01347 }
01348 
01349 BOOL LLWindowMacOSX::setGamma(const F32 gamma)
01350 {
01351         CGGammaValue redMin;
01352         CGGammaValue redMax;
01353         CGGammaValue redGamma;
01354         CGGammaValue greenMin;
01355         CGGammaValue greenMax;
01356         CGGammaValue greenGamma;
01357         CGGammaValue blueMin;
01358         CGGammaValue blueMax;
01359         CGGammaValue blueGamma;
01360 
01361         // MBW -- XXX -- Should we allow this in windowed mode?
01362 
01363         if(CGGetDisplayTransferByFormula(
01364                 mDisplay,
01365                 &redMin,
01366                 &redMax,
01367                 &redGamma,
01368                 &greenMin,
01369                 &greenMax,
01370                 &greenGamma,
01371                 &blueMin,
01372                 &blueMax,
01373                 &blueGamma) != noErr)
01374         {
01375                 return false;
01376         }
01377 
01378         if(CGSetDisplayTransferByFormula(
01379                 mDisplay,
01380                 redMin,
01381                 redMax,
01382                 gamma,
01383                 greenMin,
01384                 greenMax,
01385                 gamma,
01386                 blueMin,
01387                 blueMax,
01388                 gamma) != noErr)
01389         {
01390                 return false;
01391         }
01392 
01393 
01394         return true;
01395 }
01396 
01397 BOOL LLWindowMacOSX::isCursorHidden()
01398 {
01399         return mCursorHidden;
01400 }
01401 
01402 
01403 
01404 // Constrains the mouse to the window.
01405 void LLWindowMacOSX::setMouseClipping( BOOL b )
01406 {
01407         // Just stash the requested state.  We'll simulate this when the cursor is hidden by decoupling.
01408         mIsMouseClipping = b;
01409 
01410         if(b)
01411         {
01412                 //              llinfos << "setMouseClipping(TRUE)" << llendl
01413         }
01414         else
01415         {
01416                 //              llinfos << "setMouseClipping(FALSE)" << llendl
01417         }
01418 
01419         adjustCursorDecouple();
01420 }
01421 
01422 BOOL LLWindowMacOSX::setCursorPosition(const LLCoordWindow position)
01423 {
01424         BOOL result = FALSE;
01425         LLCoordScreen screen_pos;
01426 
01427         if (!convertCoords(position, &screen_pos))
01428         {
01429                 return FALSE;
01430         }
01431 
01432         CGPoint newPosition;
01433 
01434         //      llinfos << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << llendl
01435 
01436         newPosition.x = screen_pos.mX;
01437         newPosition.y = screen_pos.mY;
01438 
01439         CGSetLocalEventsSuppressionInterval(0.0);
01440         if(CGWarpMouseCursorPosition(newPosition) == noErr)
01441         {
01442                 result = TRUE;
01443         }
01444 
01445         // Under certain circumstances, this will trigger us to decouple the cursor.
01446         adjustCursorDecouple(true);
01447 
01448         return result;
01449 }
01450 
01451 static void fixOrigin(void)
01452 {
01453         GrafPtr port;
01454         Rect portrect;
01455 
01456         ::GetPort(&port);       
01457         ::GetPortBounds(port, &portrect);
01458         if((portrect.left != 0) || (portrect.top != 0))
01459         {
01460                 // Mozilla sometimes changes our port origin.  Fuckers.
01461                 ::SetOrigin(0,0);
01462         }
01463 }
01464 
01465 BOOL LLWindowMacOSX::getCursorPosition(LLCoordWindow *position)
01466 {
01467         Point cursor_point;
01468         LLCoordScreen screen_pos;
01469         GrafPtr save;
01470         
01471         if(mWindow == NULL)
01472                 return FALSE;
01473                 
01474         ::GetPort(&save);
01475         ::SetPort(GetWindowPort(mWindow));
01476         fixOrigin();
01477 
01478         // gets the mouse location in local coordinates
01479         ::GetMouse(&cursor_point);
01480         
01481 //      lldebugs << "getCursorPosition(): cursor is at " << cursor_point.h << ", " << cursor_point.v << "  port origin: " << portrect.left << ", " << portrect.top << llendl;
01482 
01483         ::SetPort(save);
01484 
01485         if(mCursorDecoupled)
01486         {
01487                 //              CGMouseDelta x, y;
01488 
01489                 // If the cursor's decoupled, we need to read the latest movement delta as well.
01490                 //              CGGetLastMouseDelta( &x, &y );
01491                 //              cursor_point.h += x;
01492                 //              cursor_point.v += y;
01493 
01494                 // CGGetLastMouseDelta may behave strangely when the cursor's first captured.
01495                 // Stash in the event handler instead.
01496                 cursor_point.h += mCursorLastEventDeltaX;
01497                 cursor_point.v += mCursorLastEventDeltaY;
01498         }
01499 
01500         position->mX = cursor_point.h;
01501         position->mY = cursor_point.v;
01502 
01503         return TRUE;
01504 }
01505 
01506 void LLWindowMacOSX::adjustCursorDecouple(bool warpingMouse)
01507 {
01508         if(mIsMouseClipping && mCursorHidden)
01509         {
01510                 if(warpingMouse)
01511                 {
01512                         // The cursor should be decoupled.  Make sure it is.
01513                         if(!mCursorDecoupled)
01514                         {
01515                                 //                      llinfos << "adjustCursorDecouple: decoupling cursor" << llendl;
01516                                 CGAssociateMouseAndMouseCursorPosition(false);
01517                                 mCursorDecoupled = true;
01518                                 mCursorIgnoreNextDelta = TRUE;
01519                         }
01520                 }
01521         }
01522         else
01523         {
01524                 // The cursor should not be decoupled.  Make sure it isn't.
01525                 if(mCursorDecoupled)
01526                 {
01527                         //                      llinfos << "adjustCursorDecouple: recoupling cursor" << llendl;
01528                         CGAssociateMouseAndMouseCursorPosition(true);
01529                         mCursorDecoupled = false;
01530                 }
01531         }
01532 }
01533 
01534 F32 LLWindowMacOSX::getNativeAspectRatio()
01535 {
01536         if (mFullscreen)
01537         {
01538                 return (F32)mFullscreenWidth / (F32)mFullscreenHeight;
01539         }
01540         else
01541         {
01542                 // The constructor for this class grabs the aspect ratio of the monitor before doing any resolution
01543                 // switching, and stashes it in mOriginalAspectRatio.  Here, we just return it.
01544         
01545                 if (mOverrideAspectRatio > 0.f)
01546                 {
01547                         return mOverrideAspectRatio;
01548                 }
01549 
01550                 return mOriginalAspectRatio;
01551         }
01552 }
01553 
01554 F32 LLWindowMacOSX::getPixelAspectRatio()
01555 {
01556         //OS X always enforces a 1:1 pixel aspect ratio, regardless of video mode
01557         return 1.f;
01558 }
01559 
01560 //static SInt32 oldWindowLevel;
01561 
01562 // MBW -- XXX -- There's got to be a better way than this.  Find it, please...
01563 
01564 void LLWindowMacOSX::beforeDialog()
01565 {
01566         if(mFullscreen)
01567         {
01568 
01569 #if CAPTURE_ALL_DISPLAYS
01570                 // Uncapture all displays (may want to do this for final build)
01571                 CGReleaseAllDisplays ();
01572 #else
01573                 // Uncapture only the main display (useful for debugging)
01574                 CGDisplayRelease (mDisplay);
01575 #endif
01576                 // kDocumentWindowClass
01577                 // kMovableModalWindowClass
01578                 // kAllWindowClasses
01579 
01580                 //              GLint order = 0;
01581                 //              aglSetInteger(mContext, AGL_ORDER_CONTEXT_TO_FRONT, &order);
01582                 aglSetDrawable(mContext, NULL);
01583                 //              GetWindowGroupLevel(GetWindowGroupOfClass(kAllWindowClasses), &oldWindowLevel);
01584                 //              SetWindowGroupLevel(GetWindowGroupOfClass(kAllWindowClasses), CGShieldingWindowLevel());
01585 
01586                 mHandsOffEvents = TRUE;
01587 
01588         }
01589 }
01590 
01591 void LLWindowMacOSX::afterDialog()
01592 {
01593         if(mFullscreen)
01594         {
01595                 mHandsOffEvents = FALSE;
01596 
01597                 //              SetWindowGroupLevel(GetWindowGroupOfClass(kAllWindowClasses), oldWindowLevel);
01598                 aglSetFullScreen(mContext, 0, 0, 0, 0);
01599                 //              GLint order = 1;
01600                 //              aglSetInteger(mContext, AGL_ORDER_CONTEXT_TO_FRONT, &order);
01601 
01602 #if CAPTURE_ALL_DISPLAYS
01603                 // Capture all displays (may want to do this for final build)
01604                 CGCaptureAllDisplays ();
01605 #else
01606                 // Capture only the main display (useful for debugging)
01607                 CGDisplayCapture (mDisplay);
01608 #endif
01609         }
01610 }
01611 
01612 
01613 S32 LLWindowMacOSX::stat(const char* file_name, struct stat* stat_info)
01614 {
01615         return ::stat( file_name, stat_info );
01616 }
01617 
01618 void LLWindowMacOSX::flashIcon(F32 seconds)
01619 {
01620         // Don't do this if we're already started, since this would try to install the NMRec twice.
01621         if(!mBounceTimer.getStarted())
01622         {
01623                 OSErr err;
01624 
01625                 mBounceTime = seconds;
01626                 memset(&mBounceRec, 0, sizeof(mBounceRec));
01627                 mBounceRec.qType = nmType;
01628                 mBounceRec.nmMark = 1;
01629                 err = NMInstall(&mBounceRec);
01630                 if(err == noErr)
01631                 {
01632                         mBounceTimer.start();
01633                 }
01634                 else
01635                 {
01636                         // This is very not-fatal (only problem is the icon will not bounce), but we'd like to find out about it somehow...
01637                         llinfos << "NMInstall failed with error code " << err << llendl;
01638                 }
01639         }
01640 }
01641 
01642 BOOL LLWindowMacOSX::isClipboardTextAvailable()
01643 {
01644         OSStatus err;
01645         ScrapRef scrap;
01646         ScrapFlavorFlags flags;
01647         BOOL result = false;
01648 
01649         err = GetCurrentScrap(&scrap);
01650 
01651         if(err == noErr)
01652         {
01653                 err = GetScrapFlavorFlags(scrap, kScrapFlavorTypeUnicode, &flags);
01654         }
01655 
01656         if(err == noErr)
01657                 result = true;
01658 
01659         return result;
01660 }
01661 
01662 BOOL LLWindowMacOSX::pasteTextFromClipboard(LLWString &dst)
01663 {
01664         OSStatus err;
01665         ScrapRef scrap;
01666         Size len;
01667         BOOL result = false;
01668 
01669         err = GetCurrentScrap(&scrap);
01670 
01671         if(err == noErr)
01672         {
01673                 err = GetScrapFlavorSize(scrap, kScrapFlavorTypeUnicode, &len);
01674         }
01675 
01676         if((err == noErr) && (len > 0))
01677         {
01678                 int u16len = len / sizeof(U16);
01679                 U16 *temp = new U16[u16len + 1];
01680                 if (temp)
01681                 {
01682                         memset(temp, 0, (u16len + 1) * sizeof(temp[0]));
01683                         err = GetScrapFlavorData(scrap, kScrapFlavorTypeUnicode, &len, temp);
01684                         if (err == noErr)
01685                         {
01686                                 // convert \r\n to \n and \r to \n in the incoming text.
01687                                 U16 *s, *d;
01688                                 for(s = d = temp; s[0] != '\0'; s++, d++)
01689                                 {
01690                                         if(s[0] == '\r')
01691                                         {
01692                                                 if(s[1] == '\n')
01693                                                 {
01694                                                         // CRLF, a.k.a. DOS newline.  Collapse to a single '\n'.
01695                                                         s++;
01696                                                 }
01697 
01698                                                 d[0] = '\n';
01699                                         }
01700                                         else
01701                                         {
01702                                                 d[0] = s[0];
01703                                         }
01704                                 }
01705 
01706                                 d[0] = '\0';
01707 
01708                                 dst = utf16str_to_wstring(temp);
01709 
01710                                 result = true;
01711                         }
01712                         delete[] temp;
01713                 }
01714         }
01715 
01716         return result;
01717 }
01718 
01719 BOOL LLWindowMacOSX::copyTextToClipboard(const LLWString &s)
01720 {
01721         OSStatus err;
01722         ScrapRef scrap;
01723         //Size len;
01724         //char *temp;
01725         BOOL result = false;
01726 
01727         if (!s.empty())
01728         {
01729                 err = GetCurrentScrap(&scrap);
01730                 if (err == noErr)
01731                         err = ClearScrap(&scrap);
01732 
01733                 if (err == noErr)
01734                 {
01735                         llutf16string utf16str = wstring_to_utf16str(s);
01736                         size_t u16len = utf16str.length() * sizeof(U16);
01737                         err = PutScrapFlavor(scrap, kScrapFlavorTypeUnicode, kScrapFlavorMaskNone, u16len, utf16str.data());
01738                         if (err == noErr)
01739                                 result = true;
01740                 }
01741         }
01742 
01743         return result;
01744 }
01745 
01746 
01747 BOOL LLWindowMacOSX::sendEmail(const char* address, const char* subject, const char* body_text,
01748                                                                            const char* attachment, const char* attachment_displayed_name )
01749 {
01750         // MBW -- XXX -- Um... yeah.  I'll get to this later.
01751 
01752         return false;
01753 }
01754 
01755 
01756 // protected
01757 BOOL LLWindowMacOSX::resetDisplayResolution()
01758 {
01759         // This is only called from elsewhere in this class, and it's not used by the Mac implementation.
01760         return true;
01761 }
01762 
01763 
01764 LLWindow::LLWindowResolution* LLWindowMacOSX::getSupportedResolutions(S32 &num_resolutions)
01765 {
01766         if (!mSupportedResolutions)
01767         {
01768                 CFArrayRef modes = CGDisplayAvailableModes(mDisplay);
01769 
01770                 if(modes != NULL)
01771                 {
01772                         CFIndex index, cnt;
01773 
01774                         mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
01775                         mNumSupportedResolutions = 0;
01776 
01777                         //  Examine each mode
01778                         cnt = CFArrayGetCount( modes );
01779 
01780                         for ( index = 0; (index < cnt) && (mNumSupportedResolutions < MAX_NUM_RESOLUTIONS); index++ )
01781                         {
01782                                 //  Pull the mode dictionary out of the CFArray
01783                                 CFDictionaryRef mode = (CFDictionaryRef)CFArrayGetValueAtIndex( modes, index );
01784                                 long width = getDictLong(mode, kCGDisplayWidth);
01785                                 long height = getDictLong(mode, kCGDisplayHeight);
01786                                 long bits = getDictLong(mode, kCGDisplayBitsPerPixel);
01787 
01788                                 if(bits == BITS_PER_PIXEL && width >= 800 && height >= 600)
01789                                 {
01790                                         BOOL resolution_exists = FALSE;
01791                                         for(S32 i = 0; i < mNumSupportedResolutions; i++)
01792                                         {
01793                                                 if (mSupportedResolutions[i].mWidth == width &&
01794                                                         mSupportedResolutions[i].mHeight == height)
01795                                                 {
01796                                                         resolution_exists = TRUE;
01797                                                 }
01798                                         }
01799                                         if (!resolution_exists)
01800                                         {
01801                                                 mSupportedResolutions[mNumSupportedResolutions].mWidth = width;
01802                                                 mSupportedResolutions[mNumSupportedResolutions].mHeight = height;
01803                                                 mNumSupportedResolutions++;
01804                                         }
01805                                 }
01806                         }
01807                 }
01808         }
01809 
01810         num_resolutions = mNumSupportedResolutions;
01811         return mSupportedResolutions;
01812 }
01813 
01814 BOOL LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordWindow *to)
01815 {
01816         S32             client_height;
01817         Rect    client_rect;
01818 
01819         if(mFullscreen)
01820         {
01821                 // In the fullscreen case, the "window" is the entire screen.
01822                 client_rect.left = 0;
01823                 client_rect.top = 0;
01824                 client_rect.right = mFullscreenWidth;
01825                 client_rect.bottom = mFullscreenHeight;
01826         }
01827         else if (!mWindow ||
01828                 (GetWindowBounds(mWindow, kWindowContentRgn, &client_rect) != noErr) ||
01829                 NULL == to)
01830         {
01831                 return FALSE;
01832         }
01833 
01834         to->mX = from.mX;
01835         client_height = client_rect.bottom - client_rect.top;
01836         to->mY = client_height - from.mY - 1;
01837 
01838         return TRUE;
01839 }
01840 
01841 BOOL LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordGL* to)
01842 {
01843         S32             client_height;
01844         Rect    client_rect;
01845 
01846         if(mFullscreen)
01847         {
01848                 // In the fullscreen case, the "window" is the entire screen.
01849                 client_rect.left = 0;
01850                 client_rect.top = 0;
01851                 client_rect.right = mFullscreenWidth;
01852                 client_rect.bottom = mFullscreenHeight;
01853         }
01854         else if (!mWindow ||
01855                 (GetWindowBounds(mWindow, kWindowContentRgn, &client_rect) != noErr) ||
01856                 NULL == to)
01857         {
01858                 return FALSE;
01859         }
01860 
01861         to->mX = from.mX;
01862         client_height = client_rect.bottom - client_rect.top;
01863         to->mY = client_height - from.mY - 1;
01864 
01865         return TRUE;
01866 }
01867 
01868 BOOL LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordWindow* to)
01869 {
01870         if(mFullscreen)
01871         {
01872                 // In the fullscreen case, window and screen coordinates are the same.
01873                 to->mX = from.mX;
01874                 to->mY = from.mY;
01875                 return TRUE;
01876         }
01877         else if(mWindow)
01878         {
01879                 GrafPtr save;
01880                 Point mouse_point;
01881 
01882                 mouse_point.h = from.mX;
01883                 mouse_point.v = from.mY;
01884 
01885                 ::GetPort(&save);
01886                 ::SetPort(GetWindowPort(mWindow));
01887                 fixOrigin();
01888 
01889                 ::GlobalToLocal(&mouse_point);
01890 
01891                 to->mX = mouse_point.h;
01892                 to->mY = mouse_point.v;
01893 
01894                 ::SetPort(save);
01895 
01896                 return TRUE;
01897         }
01898 
01899         return FALSE;
01900 }
01901 
01902 BOOL LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordScreen *to)
01903 {
01904         if(mFullscreen)
01905         {
01906                 // In the fullscreen case, window and screen coordinates are the same.
01907                 to->mX = from.mX;
01908                 to->mY = from.mY;
01909                 return TRUE;
01910         }
01911         else if(mWindow)
01912         {
01913                 GrafPtr save;
01914                 Point mouse_point;
01915 
01916                 mouse_point.h = from.mX;
01917                 mouse_point.v = from.mY;
01918                 ::GetPort(&save);
01919                 ::SetPort(GetWindowPort(mWindow));
01920                 fixOrigin();
01921 
01922                 LocalToGlobal(&mouse_point);
01923 
01924                 to->mX = mouse_point.h;
01925                 to->mY = mouse_point.v;
01926 
01927                 ::SetPort(save);
01928 
01929                 return TRUE;
01930         }
01931 
01932         return FALSE;
01933 }
01934 
01935 BOOL LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordGL *to)
01936 {
01937         LLCoordWindow window_coord;
01938 
01939         return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
01940 }
01941 
01942 BOOL LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordScreen *to)
01943 {
01944         LLCoordWindow window_coord;
01945 
01946         return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
01947 }
01948 
01949 
01950 
01951 
01952 void LLWindowMacOSX::setupFailure(const char* text, const char* caption, U32 type)
01953 {
01954         destroyContext();
01955 
01956         OSMessageBox(text, caption, type);
01957 }
01958 
01959 pascal OSStatus LLWindowMacOSX::staticEventHandler(EventHandlerCallRef myHandler, EventRef event, void* userData)
01960 {
01961         LLWindowMacOSX *self = (LLWindowMacOSX*)userData;
01962 
01963         return(self->eventHandler(myHandler, event));
01964 }
01965 
01966 OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef event)
01967 {
01968         OSStatus                        result = eventNotHandledErr;
01969         UInt32                          evtClass = GetEventClass (event);
01970         UInt32                          evtKind = GetEventKind (event);
01971 
01972         // Always handle command events, even in hands-off mode.
01973         if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess))
01974         {
01975                 HICommand command;
01976                 GetEventParameter (event, kEventParamDirectObject, typeHICommand, NULL, sizeof(command), NULL, &command);
01977 
01978                 switch(command.commandID)
01979                 {
01980                 case kHICommandQuit:
01981                         if(mCallbacks->handleCloseRequest(this))
01982                         {
01983                                 // Get the app to initiate cleanup.
01984                                 mCallbacks->handleQuit(this);
01985                                 // The app is responsible for calling destroyWindow when done with GL
01986                         }
01987                         result = noErr;
01988                         break;
01989 
01990                 default:
01991                         // MBW -- XXX -- Should we handle other events here?
01992                         break;
01993                 }
01994         }
01995 
01996         if(mHandsOffEvents)
01997         {
01998                 return(result);
01999         }
02000 
02001         switch (evtClass)
02002         {
02003         case kEventClassTextInput:
02004                 {
02005                         switch (evtKind)
02006                         {
02007                         case kEventTextInputUpdateActiveInputArea:
02008                                 {
02009                                         EventParamType param_type;
02010 
02011                                         long fix_len;
02012                                         UInt32 text_len;
02013                                         if (mPreeditor
02014                                                 && (result = GetEventParameter(event, kEventParamTextInputSendFixLen,
02015                                                                                 typeLongInteger, &param_type, sizeof(fix_len), NULL, &fix_len)) == noErr
02016                                                 && typeLongInteger == param_type 
02017                                                 && (result = GetEventParameter(event, kEventParamTextInputSendText,
02018                                                                                 typeUnicodeText, &param_type, 0, &text_len, NULL)) == noErr
02019                                                 && typeUnicodeText == param_type)
02020                                         {
02021                                                 // Handle an optional (but essential to facilitate TSMDA) ReplaceRange param.
02022                                                 CFRange range;
02023                                                 if (GetEventParameter(event, kEventParamTextInputSendReplaceRange,
02024                                                                 typeCFRange, &param_type, sizeof(range), NULL, &range) == noErr
02025                                                         && typeCFRange == param_type)
02026                                                 {
02027                                                         // Although the spec. is unclear, replace range should
02028                                                         // not present when there is an active preedit.  We just
02029                                                         // ignore the case.  markAsPreedit will detect the case and warn it.
02030                                                         const LLWString & text = mPreeditor->getWText();
02031                                                         const S32 location = wstring_wstring_length_from_utf16_length(text, 0, range.location);
02032                                                         const S32 length = wstring_wstring_length_from_utf16_length(text, location, range.length);
02033                                                         mPreeditor->markAsPreedit(location, length);
02034                                                 }
02035                                                 mPreeditor->resetPreedit();
02036                                                 
02037                                                 // Receive the text from input method.
02038                                                 U16 *const text = new U16[text_len / sizeof(U16)];
02039                                                 GetEventParameter(event, kEventParamTextInputSendText, typeUnicodeText, NULL, text_len, NULL, text);
02040                                                 if (fix_len < 0)
02041                                                 {
02042                                                         // Do we still need this?  Seems obsolete...
02043                                                         fix_len = text_len;
02044                                                 }
02045                                                 const LLWString fix_string
02046                                                                 = utf16str_to_wstring(llutf16string(text, fix_len / sizeof(U16)));
02047                                                 const LLWString preedit_string
02048                                                                 = utf16str_to_wstring(llutf16string(text + fix_len / sizeof(U16), (text_len - fix_len) / sizeof(U16)));
02049                                                 delete[] text;
02050 
02051                                                 // Handle fixed (comitted) string.
02052                                                 if (fix_string.length() > 0)
02053                                                 {
02054                                                         for (LLWString::const_iterator i = fix_string.begin(); i != fix_string.end(); i++)
02055                                                         {
02056                                                                 mPreeditor->handleUnicodeCharHere(*i);
02057                                                         }
02058                                                 }
02059 
02060                                                 // Receive the segment info and caret position.
02061                                                 LLPreeditor::segment_lengths_t preedit_segment_lengths;
02062                                                 LLPreeditor::standouts_t preedit_standouts;
02063                                                 S32 caret_position = preedit_string.length();
02064                                                 UInt32 text_range_array_size;
02065                                                 if (GetEventParameter(event, kEventParamTextInputSendHiliteRng, typeTextRangeArray,
02066                                                                 &param_type, 0, &text_range_array_size, NULL) == noErr
02067                                                         && typeTextRangeArray == param_type
02068                                                         && text_range_array_size > sizeof(TextRangeArray))
02069                                                 {
02070                                                         // TextRangeArray is a variable-length struct.
02071                                                         TextRangeArray * const text_range_array = (TextRangeArray *) new char[text_range_array_size];
02072                                                         GetEventParameter(event, kEventParamTextInputSendHiliteRng, typeTextRangeArray,
02073                                                                         NULL, text_range_array_size, NULL, text_range_array);
02074 
02075                                                         // WARNING: We assume ranges are in ascending order, 
02076                                                         // although the condition is undocumented.  It seems
02077                                                         // OK to assume this.  I also assumed
02078                                                         // the ranges are contiguous in previous versions, but I
02079                                                         // have heard a rumore that older versions os ATOK may 
02080                                                         // return ranges with some _gap_.  I don't know whether
02081                                                         // it is true, but I'm preparing my code for the case.
02082 
02083                                                         const S32 ranges = text_range_array->fNumOfRanges;
02084                                                         preedit_segment_lengths.reserve(ranges);
02085                                                         preedit_standouts.reserve(ranges);
02086 
02087                                                         S32 last_bytes = 0;
02088                                                         S32 last_utf32 = 0;
02089                                                         for (S32 i = 0; i < ranges; i++)
02090                                                         {
02091                                                                 const TextRange &range = text_range_array->fRange[i];
02092                                                                 if (range.fStart > last_bytes)
02093                                                                 {
02094                                                                         const S32 length_utf16 = (range.fStart - last_bytes) / sizeof(U16);
02095                                                                         const S32 length_utf32 = wstring_wstring_length_from_utf16_length(preedit_string, last_utf32, length_utf16);
02096                                                                         preedit_segment_lengths.push_back(length_utf32);
02097                                                                         preedit_standouts.push_back(FALSE);
02098                                                                         last_utf32 += length_utf32;
02099                                                                 }
02100                                                                 if (range.fEnd > range.fStart)
02101                                                                 {
02102                                                                         const S32 length_utf16 = (range.fEnd - range.fStart) / sizeof(U16);
02103                                                                         const S32 length_utf32 = wstring_wstring_length_from_utf16_length(preedit_string, last_utf32, length_utf16);
02104                                                                         preedit_segment_lengths.push_back(length_utf32);
02105                                                                         preedit_standouts.push_back(
02106                                                                                 kTSMHiliteSelectedRawText == range.fHiliteStyle
02107                                                                                 || kTSMHiliteSelectedConvertedText ==  range.fHiliteStyle
02108                                                                                 || kTSMHiliteSelectedText == range.fHiliteStyle);
02109                                                                         last_utf32 += length_utf32;
02110                                                                 }
02111                                                                 if (kTSMHiliteCaretPosition == range.fHiliteStyle)
02112                                                                 {
02113                                                                         caret_position = last_utf32;
02114                                                                 }
02115                                                                 last_bytes = range.fEnd;
02116                                                         }
02117                                                         if (preedit_string.length() > last_utf32)
02118                                                         {
02119                                                                 preedit_segment_lengths.push_back(preedit_string.length() - last_utf32);
02120                                                                 preedit_standouts.push_back(FALSE);
02121                                                         }
02122 
02123                                                         delete[] (char *) text_range_array;
02124                                                 }
02125 
02126                                                 // Handle preedit string.
02127                                                 if (preedit_string.length() == 0)
02128                                                 {
02129                                                         preedit_segment_lengths.clear();
02130                                                         preedit_standouts.clear();
02131                                                 }
02132                                                 else if (preedit_segment_lengths.size() == 0)
02133                                                 {
02134                                                         preedit_segment_lengths.push_back(preedit_string.length());
02135                                                         preedit_standouts.push_back(FALSE);
02136                                                 }
02137                                                 mPreeditor->updatePreedit(preedit_string, preedit_segment_lengths, preedit_standouts, caret_position);
02138 
02139                                                 result = noErr;
02140                                         }
02141                                 }
02142                                 break;
02143                                 
02144                         case kEventTextInputUnicodeForKeyEvent:
02145                                 {
02146                                         UInt32 modifiers = 0;
02147 
02148                                         // First, process the raw event.
02149                                         {
02150                                                 EventRef rawEvent;
02151 
02152                                                 // Get the original event and extract the modifier keys, so we can ignore command-key events.
02153                                                 if (GetEventParameter(event, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(rawEvent), NULL, &rawEvent) == noErr)
02154                                                 {
02155                                                         // Grab the modifiers for later use in this function...
02156                                                         GetEventParameter (rawEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
02157 
02158                                                         // and call this function recursively to handle the raw key event.
02159                                                         eventHandler (myHandler, rawEvent);
02160                                                 }
02161                                         }
02162 
02163                                         OSStatus err = noErr;
02164                                         EventParamType actualType = typeUnicodeText;
02165                                         UInt32 actualSize = 0;
02166                                         size_t actualCount = 0;
02167                                         U16 *buffer = NULL;
02168 
02169                                         // Get the size of the unicode data
02170                                         err = GetEventParameter (event, kEventParamTextInputSendText, typeUnicodeText, &actualType, 0, &actualSize, NULL);
02171                                         if(err == noErr)
02172                                         {
02173                                                 // allocate a buffer and get the actual data.
02174                                                 actualCount = actualSize / sizeof(U16);
02175                                                 buffer = new U16[actualCount];
02176                                                 err = GetEventParameter (event, kEventParamTextInputSendText, typeUnicodeText, &actualType, actualSize, &actualSize, buffer);
02177                                         }
02178 
02179                                         if(err == noErr)
02180                                         {
02181                                                 if(modifiers & (cmdKey | controlKey))
02182                                                 {
02183                                                         // This was a menu key equivalent.  Ignore it.
02184                                                 }
02185                                                 else
02186                                                 {
02187                                                         MASK mask = 0;
02188                                                         if(modifiers & shiftKey) { mask |= MASK_SHIFT; }
02189                                                         if(modifiers & (cmdKey | controlKey)) { mask |= MASK_CONTROL; }
02190                                                         if(modifiers & optionKey) { mask |= MASK_ALT; }
02191 
02192                                                         llassert( actualType == typeUnicodeText );
02193 
02194                                                         // The result is a UTF16 buffer.  Pass the characters in turn to handleUnicodeChar.
02195 
02196                                                         // Convert to UTF32 and go character-by-character.
02197                                                         llutf16string utf16(buffer, actualCount);
02198                                                         LLWString utf32 = utf16str_to_wstring(utf16);
02199                                                         LLWString::iterator iter;
02200 
02201                                                         for(iter = utf32.begin(); iter != utf32.end(); iter++)
02202                                                         {
02203                                                                 mCallbacks->handleUnicodeChar(*iter, mask);
02204                                                         }
02205                                                 }
02206                                         }
02207 
02208                                         if(buffer != NULL)
02209                                         {
02210                                                 delete[] buffer;
02211                                         }
02212 
02213                                         result = err;
02214                                 }
02215                                 break;
02216                                 
02217                         case kEventTextInputOffsetToPos:
02218                                 {
02219                                         EventParamType param_type;
02220                                         long offset;
02221                                         if (mPreeditor
02222                                                 && GetEventParameter(event, kEventParamTextInputSendTextOffset, typeLongInteger,
02223                                                                 &param_type, sizeof(offset), NULL, &offset) == noErr
02224                                                 && typeLongInteger == param_type)
02225                                         {
02226                                                 S32 preedit, preedit_length;
02227                                                 mPreeditor->getPreeditRange(&preedit, &preedit_length);
02228                                                 const LLWString & text = mPreeditor->getWText();
02229                                                  
02230                                                 LLCoordGL caret_coord;
02231                                                 LLRect preedit_bounds;
02232                                                 if (0 <= offset
02233                                                         && mPreeditor->getPreeditLocation(wstring_wstring_length_from_utf16_length(text, preedit, offset / sizeof(U16)),
02234                                                                                                                           &caret_coord, &preedit_bounds, NULL))
02235                                                 {
02236                                                         LLCoordGL caret_base_coord(caret_coord.mX, preedit_bounds.mBottom);
02237                                                         LLCoordScreen caret_base_coord_screen;
02238                                                         convertCoords(caret_base_coord, &caret_base_coord_screen);
02239                                                         Point qd_point;
02240                                                         qd_point.h = caret_base_coord_screen.mX;
02241                                                         qd_point.v = caret_base_coord_screen.mY;
02242                                                         SetEventParameter(event, kEventParamTextInputReplyPoint, typeQDPoint, sizeof(qd_point), &qd_point);
02243                                                         
02244                                                         short line_height = (short) preedit_bounds.getHeight();
02245                                                         SetEventParameter(event, kEventParamTextInputReplyLineHeight, typeShortInteger, sizeof(line_height), &line_height);
02246                                                         
02247                                                         result = noErr;
02248                                                 }
02249                                                 else
02250                                                 {
02251                                                         result = errOffsetInvalid;
02252                                                 }
02253                                         }
02254                                 }
02255                                 break;
02256 
02257                         case kEventTextInputGetSelectedText:
02258                                 {
02259                                         if (mPreeditor)
02260                                         {
02261                                                 S32 selection, selection_length;
02262                                                 mPreeditor->getSelectionRange(&selection, &selection_length);
02263                                                 if (selection_length)
02264                                                 {
02265                                                         const LLWString text = mPreeditor->getWText().substr(selection, selection_length);
02266                                                         const llutf16string text_utf16 = wstring_to_utf16str(text);
02267                                                         result = SetEventParameter(event, kEventParamTextInputReplyText, typeUnicodeText,
02268                                                                                 text_utf16.length() * sizeof(U16), text_utf16.c_str());
02269                                                 }
02270                                         }
02271                                 }
02272                                 break;
02273                         }
02274                 }
02275                 break;
02276 
02277         case kEventClassKeyboard:
02278                 {
02279                         UInt32 keyCode = 0;
02280                         char charCode = 0;
02281                         UInt32 modifiers = 0;
02282 
02283                         // Some of these may fail for some event types.  That's fine.
02284                         GetEventParameter (event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode);
02285                         GetEventParameter (event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
02286 
02287                         //                      printf("key event, key code = 0x%08x, char code = 0x%02x (%c), modifiers = 0x%08x\n", keyCode, charCode, (char)charCode, modifiers);
02288                         //                      fflush(stdout);
02289 
02290                         switch (evtKind)
02291                         {
02292                         case kEventRawKeyDown:
02293                         case kEventRawKeyRepeat:
02294                                 if (gDebugWindowProc)
02295                                 {
02296                                         printf("key down, key code = 0x%08x, char code = 0x%02x (%c), modifiers = 0x%08x\n", 
02297                                                         (unsigned int)keyCode, charCode, (char)charCode, (unsigned int)modifiers);
02298                                         fflush(stdout);
02299                                 }
02300                                 gKeyboard->handleKeyDown(keyCode, modifiers);
02301                                 result = eventNotHandledErr;
02302                                 break;
02303 
02304                         case kEventRawKeyUp:
02305                                 if (gDebugWindowProc)
02306                                 {
02307                                         printf("key up,   key code = 0x%08x, char code = 0x%02x (%c), modifiers = 0x%08x\n", 
02308                                                         (unsigned int)keyCode, charCode, (char)charCode, (unsigned int)modifiers);
02309                                         fflush(stdout);
02310                                 }
02311                                 gKeyboard->handleKeyUp(keyCode, modifiers);
02312                                 result = eventNotHandledErr;
02313                                 break;
02314 
02315                         case kEventRawKeyModifiersChanged:
02316                                 // The keyboard input system wants key up/down events for modifier keys.
02317                                 // Mac OS doesn't supply these directly, but can supply events when the collective modifier state changes.
02318                                 // Use these events to generate up/down events for the modifiers.
02319 
02320                                 if((modifiers & shiftKey) && !(mLastModifiers & shiftKey))
02321                                 {
02322                                         if (gDebugWindowProc) printf("Shift key down event\n");
02323                                         gKeyboard->handleKeyDown(0x38, (modifiers & 0x00FFFFFF) | ((0x38 << 24) & 0xFF000000));
02324                                 }
02325                                 else if(!(modifiers & shiftKey) && (mLastModifiers & shiftKey))
02326                                 {
02327                                         if (gDebugWindowProc) printf("Shift key up event\n");
02328                                         gKeyboard->handleKeyUp(0x38, (modifiers & 0x00FFFFFF) | ((0x38 << 24) & 0xFF000000));
02329                                 }
02330 
02331                                 if((modifiers & alphaLock) && !(mLastModifiers & alphaLock))
02332                                 {
02333                                         if (gDebugWindowProc) printf("Caps lock down event\n");
02334                                         gKeyboard->handleKeyDown(0x39, (modifiers & 0x00FFFFFF) | ((0x39 << 24) & 0xFF000000));
02335                                 }
02336                                 else if(!(modifiers & alphaLock) && (mLastModifiers & alphaLock))
02337                                 {
02338                                         if (gDebugWindowProc) printf("Caps lock up event\n");
02339                                         gKeyboard->handleKeyUp(0x39, (modifiers & 0x00FFFFFF) | ((0x39 << 24) & 0xFF000000));
02340                                 }
02341 
02342                                 if((modifiers & controlKey) && !(mLastModifiers & controlKey))
02343                                 {
02344                                         if (gDebugWindowProc) printf("Control key down event\n");
02345                                         gKeyboard->handleKeyDown(0x3b, (modifiers & 0x00FFFFFF) | ((0x3b << 24) & 0xFF000000));
02346                                 }
02347                                 else if(!(modifiers & controlKey) && (mLastModifiers & controlKey))
02348                                 {
02349                                         if (gDebugWindowProc) printf("Control key up event\n");
02350                                         gKeyboard->handleKeyUp(0x3b, (modifiers & 0x00FFFFFF) | ((0x3b << 24) & 0xFF000000));
02351                                 }
02352 
02353                                 if((modifiers & optionKey) && !(mLastModifiers & optionKey))
02354                                 {
02355                                         if (gDebugWindowProc) printf("Option key down event\n");
02356                                         gKeyboard->handleKeyDown(0x3a, (modifiers & 0x00FFFFFF) | ((0x3a << 24) & 0xFF000000));
02357                                 }
02358                                 else if(!(modifiers & optionKey) && (mLastModifiers & optionKey))
02359                                 {
02360                                         if (gDebugWindowProc) printf("Option key up event\n");
02361                                         gKeyboard->handleKeyUp(0x3a, (modifiers & 0x00FFFFFF) | ((0x3a << 24) & 0xFF000000));
02362                                 }
02363 
02364                                 // When the state of the 'Fn' key (the one that changes some of the mappings on a powerbook/macbook keyboard
02365                                 // to an embedded keypad) changes, it may subsequently cause a key up event to be lost, which may lead to 
02366                                 // a movement key getting "stuck" down.  This is bad.
02367                                 // This is an OS bug -- even the GetKeys() API doesn't tell you the key has been released.
02368                                 // This workaround causes all held-down keys to be reset whenever the state of the Fn key changes.  This isn't
02369                                 // exactly what we want, but it does avoid the case where you get stuck running forward.
02370                                 if((modifiers & kEventKeyModifierFnMask) != (mLastModifiers & kEventKeyModifierFnMask))
02371                                 {
02372                                         if (gDebugWindowProc) printf("Fn key state change event\n");
02373                                         gKeyboard->resetKeys();
02374                                 }
02375                                 
02376                                 if (gDebugWindowProc) fflush(stdout);
02377 
02378                                 mLastModifiers = modifiers;
02379                                 result = eventNotHandledErr;
02380                                 break;
02381                         }
02382                 }
02383                 break;
02384 
02385         case kEventClassMouse:
02386                 {
02387                         result = CallNextEventHandler(myHandler, event);
02388                         if (eventNotHandledErr == result)
02389                         { // only handle events not already handled (prevents wierd resize interaction)
02390                                 EventMouseButton        button = kEventMouseButtonPrimary;
02391                                 HIPoint                         location = {0.0f, 0.0f};
02392                                 UInt32                          modifiers = 0;
02393                                 UInt32                          clickCount = 1;
02394                                 long                            wheelDelta = 0;
02395                                 LLCoordScreen           inCoords;
02396                                 LLCoordGL                       outCoords;
02397                                 MASK                            mask = 0;
02398 
02399                                 GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
02400                                 GetEventParameter(event, kEventParamMouseLocation, typeHIPoint, NULL, sizeof(location), NULL, &location);
02401                                 GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
02402                                 GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(wheelDelta), NULL, &wheelDelta);
02403                                 GetEventParameter(event, kEventParamClickCount, typeUInt32, NULL, sizeof(clickCount), NULL, &clickCount);
02404 
02405                                 inCoords.mX = llround(location.x);
02406                                 inCoords.mY = llround(location.y);
02407 
02408                                 if(modifiers & shiftKey) { mask |= MASK_SHIFT; }
02409                                 if(modifiers & controlKey) { mask |= MASK_CONTROL; }
02410                                 if(modifiers & optionKey) { mask |= MASK_ALT; }
02411 
02412                                 if(mCursorDecoupled)
02413                                 {
02414                                         CGMouseDelta x, y;
02415 
02416                                         // If the cursor's decoupled, we need to read the latest movement delta as well.
02417                                         CGGetLastMouseDelta( &x, &y );
02418                                         mCursorLastEventDeltaX = x;
02419                                         mCursorLastEventDeltaY = y;
02420 
02421                                         if(mCursorIgnoreNextDelta)
02422                                         {
02423                                                 mCursorLastEventDeltaX = 0;
02424                                                 mCursorLastEventDeltaY = 0;
02425                                                 mCursorIgnoreNextDelta = FALSE;
02426                                         }
02427                                 }
02428                                 else
02429                                 {
02430                                         mCursorLastEventDeltaX = 0;
02431                                         mCursorLastEventDeltaY = 0;
02432                                 }
02433 
02434                                 inCoords.mX += mCursorLastEventDeltaX;
02435                                 inCoords.mY += mCursorLastEventDeltaY;
02436 
02437                                 convertCoords(inCoords, &outCoords);
02438 
02439                                 //                              printf("coords in: %d, %d; coords out: %d, %d\n", inCoords.mX, inCoords.mY, outCoords.mX, outCoords.mY);
02440                                 //                              fflush(stdout);
02441 
02442 
02443                                 switch (evtKind)
02444                                 {
02445                                 case kEventMouseDown:
02446                                         if (mLanguageTextInputAllowed)
02447                                         {
02448                                                 // We need to interrupt before handling mouse events,
02449                                                 // so that the fixed string from IM are delivered to
02450                                                 // the currently focused UI component.
02451                                                 interruptLanguageTextInput();
02452                                         }
02453                                         switch(button)
02454                                         {
02455                                         case kEventMouseButtonPrimary:
02456                                                 if(modifiers & cmdKey)
02457                                                 {
02458                                                         // Simulate a right click
02459                                                         mSimulatedRightClick = true;
02460                                                         mCallbacks->handleRightMouseDown(this, outCoords, mask);
02461                                                 }
02462                                                 else if(clickCount == 2)
02463                                                 {
02464                                                         // Windows double-click events replace the second mousedown event in a double-click.
02465                                                         mCallbacks->handleDoubleClick(this, outCoords, mask);
02466                                                 }
02467                                                 else
02468                                                 {
02469                                                         mCallbacks->handleMouseDown(this, outCoords, mask);
02470                                                 }
02471                                                 break;
02472                                         case kEventMouseButtonSecondary:
02473                                                 mCallbacks->handleRightMouseDown(this, outCoords, mask);
02474                                                 break;
02475                                         
02476                                         case kEventMouseButtonTertiary:
02477                                                 mCallbacks->handleMiddleMouseDown(this, outCoords, mask);
02478                                                 break;
02479                                         }
02480                                         result = noErr;
02481                                         break;
02482                                 case kEventMouseUp:
02483 
02484                                         switch(button)
02485                                         {
02486                                         case kEventMouseButtonPrimary:
02487                                                 if(mSimulatedRightClick)
02488                                                 {
02489                                                         // End of simulated right click
02490                                                         mSimulatedRightClick = false;
02491                                                         mCallbacks->handleRightMouseUp(this, outCoords, mask);
02492                                                 }
02493                                                 else
02494                                                 {
02495                                                         mCallbacks->handleMouseUp(this, outCoords, mask);
02496                                                 }
02497                                                 break;
02498                                         case kEventMouseButtonSecondary:
02499                                                 mCallbacks->handleRightMouseUp(this, outCoords, mask);
02500                                                 break;
02501 
02502                                         case kEventMouseButtonTertiary:
02503                                                 mCallbacks->handleMiddleMouseUp(this, outCoords, mask);
02504                                                 break;
02505                                         }
02506                                         result = noErr;
02507                                         break;
02508 
02509                                 case kEventMouseWheelMoved:
02510                                         {
02511                                                 static S32  z_delta = 0;
02512 
02513                                                 z_delta += wheelDelta;
02514 
02515                                                 if (z_delta <= -WHEEL_DELTA || WHEEL_DELTA <= z_delta)
02516                                                 {
02517                                                         mCallbacks->handleScrollWheel(this, -z_delta / WHEEL_DELTA);
02518                                                         z_delta = 0;
02519                                                 }
02520                                         }
02521                                         result = noErr;
02522                                         break;
02523 
02524                                 case kEventMouseDragged:
02525                                 case kEventMouseMoved:
02526                                         mCallbacks->handleMouseMove(this, outCoords, mask);
02527                                         result = noErr;
02528                                         break;
02529 
02530                                 }
02531                         }
02532                 }
02533                 break;
02534 
02535         case kEventClassWindow:
02536                 switch(evtKind)
02537                 {               
02538                 case kEventWindowActivated:
02539                         if (mTSMDocument)
02540                         {
02541                                 ActivateTSMDocument(mTSMDocument);
02542                         }
02543                         mCallbacks->handleFocus(this);
02544                         break;
02545                 case kEventWindowDeactivated:
02546                         if (mTSMDocument)
02547                         {
02548                                 DeactivateTSMDocument(mTSMDocument);
02549                         }
02550                         mCallbacks->handleFocusLost(this);
02551                         break;
02552                 case kEventWindowBoundsChanging:
02553                         {
02554                                 Rect currentBounds;
02555                                 Rect previousBounds;
02556 
02557                                 GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &currentBounds);
02558                                 GetEventParameter(event, kEventParamPreviousBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &previousBounds);
02559 
02560                                 // This is where we would constrain move/resize to a particular screen
02561                                 if(0)
02562                                 {
02563                                         SetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, sizeof(Rect), &currentBounds);
02564                                 }
02565                         }
02566                         break;
02567 
02568                 case kEventWindowBoundsChanged:
02569                         {
02570                                 Rect newBounds;
02571 
02572                                 GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &newBounds);
02573                                 aglUpdateContext(mContext);
02574                                 mCallbacks->handleResize(this, newBounds.right - newBounds.left, newBounds.bottom - newBounds.top);
02575 
02576 
02577                         }
02578                         break;
02579 
02580                 case kEventWindowClose:
02581                         if(mCallbacks->handleCloseRequest(this))
02582                         {
02583                                 // Get the app to initiate cleanup.
02584                                 mCallbacks->handleQuit(this);
02585                                 // The app is responsible for calling destroyWindow when done with GL
02586                         }
02587                         result = noErr;
02588                         break;
02589 
02590                 case kEventWindowHidden:
02591                         //                                      llinfos << "LLWindowMacOSX: Deactivating on hide" << llendl;
02592                         mMinimized = TRUE;
02593                         mCallbacks->handleActivate(this, false);
02594                         //                                      result = noErr;
02595                         break;
02596 
02597                 case kEventWindowShown:
02598                         //                                      llinfos << "LLWindowMacOSX: Activating on show" << llendl;
02599                         mMinimized = FALSE;
02600                         mCallbacks->handleActivate(this, true);
02601                         //                                      result = noErr;
02602                         break;
02603 
02604                 case kEventWindowCollapsed:
02605                         //                                      llinfos << "LLWindowMacOSX: Deactivating on collapse" << llendl;
02606                         mMinimized = TRUE;
02607                         mCallbacks->handleActivate(this, false);
02608                         //                                      result = noErr;
02609                         break;
02610 
02611                 case kEventWindowExpanded:
02612                         //                                      llinfos << "LLWindowMacOSX: Activating on expand" << llendl;
02613                         mMinimized = FALSE;
02614                         mCallbacks->handleActivate(this, true);
02615                         //                                      result = noErr;
02616                         break;
02617 
02618                 case kEventWindowGetClickActivation:
02619                         //                                      BringToFront(mWindow);
02620                         //                                      result = noErr;
02621                         break;
02622                 
02623                 }
02624                 break;
02625 
02626         case kEventClassTSMDocumentAccess:
02627                 if (mPreeditor)
02628                 {
02629                         switch(evtKind)
02630                         {               
02631 
02632                         case kEventTSMDocumentAccessGetLength:
02633                                 {
02634                                         // Return the number of UTF-16 units in the text, excluding those for preedit.
02635 
02636                                         S32 preedit, preedit_length;
02637                                         mPreeditor->getPreeditRange(&preedit, &preedit_length);
02638                                         const LLWString & text = mPreeditor->getWText();
02639                                         const CFIndex length = wstring_utf16_length(text, 0, preedit)
02640                                                 + wstring_utf16_length(text, preedit + preedit_length, text.length());
02641                                         result = SetEventParameter(event, kEventParamTSMDocAccessCharacterCount, typeCFIndex, sizeof(length), &length);
02642                                 }
02643                                 break;
02644 
02645                         case kEventTSMDocumentAccessGetSelectedRange:
02646                                 {
02647                                         // Return the selected range, excluding preedit.
02648                                         // In our preeditor, preedit and selection are exclusive, so,
02649                                         // when it has a preedit, there is no selection and the 
02650                                         // insertion point is on the preedit that corrupses into the
02651                                         // beginning of the preedit when the preedit was removed.
02652 
02653                                         S32 preedit, preedit_length;
02654                                         mPreeditor->getPreeditRange(&preedit, &preedit_length);
02655                                         const LLWString & text = mPreeditor->getWText();
02656                                         
02657                                         CFRange range;
02658                                         if (preedit_length)
02659                                         {
02660                                                 range.location = wstring_utf16_length(text, 0, preedit);
02661                                                 range.length = 0;
02662                                         }
02663                                         else
02664                                         {
02665                                                 S32 selection, selection_length;
02666                                                 mPreeditor->getSelectionRange(&selection, &selection_length);
02667                                                 range.location = wstring_utf16_length(text, 0, selection);
02668                                                 range.length = wstring_utf16_length(text, selection, selection_length);
02669                                         }
02670 
02671                                         result = SetEventParameter(event, kEventParamTSMDocAccessReplyCharacterRange, typeCFRange, sizeof(range), &range);
02672                                 }
02673                                 break;
02674 
02675                         case kEventTSMDocumentAccessGetCharacters:
02676                                 {
02677                                         UniChar *target_pointer;
02678                                         CFRange range;
02679                                         EventParamType param_type;
02680                                         if ((result = GetEventParameter(event, kEventParamTSMDocAccessSendCharacterRange,
02681                                                                                 typeCFRange, &param_type, sizeof(range), NULL, &range)) == noErr
02682                                                 && typeCFRange == param_type
02683                                                 && (result = GetEventParameter(event, kEventParamTSMDocAccessSendCharactersPtr,
02684                                                                                 typePtr, &param_type, sizeof(target_pointer), NULL, &target_pointer)) == noErr
02685                                                 && typePtr == param_type)
02686                                         {
02687                                                 S32 preedit, preedit_length;
02688                                                 mPreeditor->getPreeditRange(&preedit, &preedit_length);
02689                                                 const LLWString & text = mPreeditor->getWText();
02690 
02691                                                 // The GetCharacters event of TSMDA has a fundamental flaw;
02692                                                 // An input method need to decide the starting offset and length
02693                                                 // *before* it actually see the contents, so it is impossible
02694                                                 // to guarantee the character-aligned access.  The event reply
02695                                                 // has no way to indicate a condition something like "Request
02696                                                 // was not fulfilled due to unaligned access.  Please retry."
02697                                                 // Any error sent back to the input method stops use of TSMDA
02698                                                 // entirely during the session...
02699                                                 // We need to simulate very strictly the behaviour as if the
02700                                                 // underlying *text engine* holds the contents in UTF-16.
02701                                                 // I guess this is the reason why Apple repeats saying "all
02702                                                 // text handling application should use UTF-16."  They are
02703                                                 // trying to _fix_ the flaw by changing the appliations...
02704                                                 // ... or, domination of UTF-16 in the industry may be a part
02705                                                 // of the company vision, and Apple is trying to force third
02706                                                 // party developers to obey their vision.  Remember that use
02707                                                 // of 16 bits per _a_character_ was one of the very fundamental
02708                                                 // Unicode design policy on its early days (during late 80s)
02709                                                 // and the original Unicode design was by two Apple employees...
02710 
02711                                                 const llutf16string text_utf16
02712                                                         = wstring_to_utf16str(text, preedit)
02713                                                         + wstring_to_utf16str(text.substr(preedit + preedit_length));
02714 
02715                                                 llassert_always(sizeof(U16) == sizeof(UniChar));
02716                                                 llassert(0 <= range.location && 0 <= range.length && range.location + range.length <= text_utf16.length());
02717                                                 memcpy(target_pointer, text_utf16.c_str() + range.location, range.length * sizeof(UniChar));
02718 
02719                                                 // Note that result has already been set above.
02720                                         }                                       
02721                                 }
02722                                 break;
02723 
02724                         }
02725                 }
02726                 break;
02727         }
02728         return result;
02729 }
02730 
02731 const char* cursorIDToName(int id)
02732 {
02733         switch (id)
02734         {
02735                 case UI_CURSOR_ARROW:                   return "UI_CURSOR_ARROW";
02736                 case UI_CURSOR_WAIT:                    return "UI_CURSOR_WAIT";
02737                 case UI_CURSOR_HAND:                    return "UI_CURSOR_HAND";
02738                 case UI_CURSOR_IBEAM:                   return "UI_CURSOR_IBEAM";
02739                 case UI_CURSOR_CROSS:                   return "UI_CURSOR_CROSS";
02740                 case UI_CURSOR_SIZENWSE:                return "UI_CURSOR_SIZENWSE";
02741                 case UI_CURSOR_SIZENESW:                return "UI_CURSOR_SIZENESW";
02742                 case UI_CURSOR_SIZEWE:                  return "UI_CURSOR_SIZEWE";
02743                 case UI_CURSOR_SIZENS:                  return "UI_CURSOR_SIZENS";
02744                 case UI_CURSOR_NO:                              return "UI_CURSOR_NO";
02745                 case UI_CURSOR_WORKING:                 return "UI_CURSOR_WORKING";
02746                 case UI_CURSOR_TOOLGRAB:                return "UI_CURSOR_TOOLGRAB";
02747                 case UI_CURSOR_TOOLLAND:                return "UI_CURSOR_TOOLLAND";
02748                 case UI_CURSOR_TOOLFOCUS:               return "UI_CURSOR_TOOLFOCUS";
02749                 case UI_CURSOR_TOOLCREATE:              return "UI_CURSOR_TOOLCREATE";
02750                 case UI_CURSOR_ARROWDRAG:               return "UI_CURSOR_ARROWDRAG";
02751                 case UI_CURSOR_ARROWCOPY:               return "UI_CURSOR_ARROWCOPY";
02752                 case UI_CURSOR_ARROWDRAGMULTI:  return "UI_CURSOR_ARROWDRAGMULTI";
02753                 case UI_CURSOR_ARROWCOPYMULTI:  return "UI_CURSOR_ARROWCOPYMULTI";
02754                 case UI_CURSOR_NOLOCKED:                return "UI_CURSOR_NOLOCKED";
02755                 case UI_CURSOR_ARROWLOCKED:             return "UI_CURSOR_ARROWLOCKED";
02756                 case UI_CURSOR_GRABLOCKED:              return "UI_CURSOR_GRABLOCKED";
02757                 case UI_CURSOR_TOOLTRANSLATE:   return "UI_CURSOR_TOOLTRANSLATE";
02758                 case UI_CURSOR_TOOLROTATE:              return "UI_CURSOR_TOOLROTATE";
02759                 case UI_CURSOR_TOOLSCALE:               return "UI_CURSOR_TOOLSCALE";
02760                 case UI_CURSOR_TOOLCAMERA:              return "UI_CURSOR_TOOLCAMERA";
02761                 case UI_CURSOR_TOOLPAN:                 return "UI_CURSOR_TOOLPAN";
02762                 case UI_CURSOR_TOOLZOOMIN:              return "UI_CURSOR_TOOLZOOMIN";
02763                 case UI_CURSOR_TOOLPICKOBJECT3: return "UI_CURSOR_TOOLPICKOBJECT3";
02764                 case UI_CURSOR_TOOLSIT:                 return "UI_CURSOR_TOOLSIT";
02765                 case UI_CURSOR_TOOLBUY:                 return "UI_CURSOR_TOOLBUY";
02766                 case UI_CURSOR_TOOLPAY:                 return "UI_CURSOR_TOOLPAY";
02767                 case UI_CURSOR_TOOLOPEN:                return "UI_CURSOR_TOOLOPEN";
02768                 case UI_CURSOR_TOOLPLAY:                return "UI_CURSOR_TOOLPLAY";
02769                 case UI_CURSOR_TOOLPAUSE:               return "UI_CURSOR_TOOLPAUSE";
02770                 case UI_CURSOR_TOOLMEDIAOPEN:   return "UI_CURSOR_TOOLMEDIAOPEN";
02771                 case UI_CURSOR_PIPETTE:                 return "UI_CURSOR_PIPETTE";             
02772         }
02773 
02774         llerrs << "cursorIDToName: unknown cursor id" << id << llendl;
02775         
02776         return "UI_CURSOR_ARROW";
02777 }
02778 
02779 static CursorRef gCursors[UI_CURSOR_COUNT];
02780 
02781 
02782 static void initPixmapCursor(int cursorid, int hotspotX, int hotspotY)
02783 {
02784         // cursors are in <Application Bundle>/Contents/Resources/cursors_mac/UI_CURSOR_FOO.tif
02785         std::string fullpath = gDirUtilp->getAppRODataDir();
02786         fullpath += gDirUtilp->getDirDelimiter();
02787         fullpath += "cursors_mac";
02788         fullpath += gDirUtilp->getDirDelimiter();
02789         fullpath += cursorIDToName(cursorid);
02790         fullpath += ".tif";
02791         
02792         gCursors[cursorid] = createImageCursor(fullpath.c_str(), hotspotX, hotspotY);
02793 }
02794 
02795 void LLWindowMacOSX::setCursor(ECursorType cursor)
02796 {
02797         OSStatus result = noErr;
02798 
02799         if (cursor == UI_CURSOR_ARROW
02800                 && mBusyCount > 0)
02801         {
02802                 cursor = UI_CURSOR_WORKING;
02803         }
02804         
02805         if(mCurrentCursor == cursor)
02806                 return;
02807 
02808         // RN: replace multi-drag cursors with single versions
02809         if (cursor == UI_CURSOR_ARROWDRAGMULTI)
02810         {
02811                 cursor = UI_CURSOR_ARROWDRAG;
02812         }
02813         else if (cursor == UI_CURSOR_ARROWCOPYMULTI)
02814         {
02815                 cursor = UI_CURSOR_ARROWCOPY;
02816         }
02817 
02818         switch(cursor)
02819         {
02820         default:
02821         case UI_CURSOR_ARROW:
02822                 InitCursor();
02823                 if(mCursorHidden)
02824                 {
02825                         // Since InitCursor resets the hide level, correct for it here.
02826                         ::HideCursor();
02827                 }
02828                 break;
02829 
02830                 // MBW -- XXX -- Some of the standard Windows cursors have no standard Mac equivalents.
02831                 //    Find out what they look like and replicate them.
02832 
02833                 // These are essentially correct
02834         case UI_CURSOR_WAIT:            SetThemeCursor(kThemeWatchCursor);      break;
02835         case UI_CURSOR_IBEAM:           SetThemeCursor(kThemeIBeamCursor);      break;
02836         case UI_CURSOR_CROSS:           SetThemeCursor(kThemeCrossCursor);      break;
02837         case UI_CURSOR_HAND:            SetThemeCursor(kThemePointingHandCursor);       break;
02838                 //              case UI_CURSOR_NO:                      SetThemeCursor(kThemeNotAllowedCursor); break;
02839         case UI_CURSOR_ARROWCOPY:   SetThemeCursor(kThemeCopyArrowCursor);      break;
02840 
02841                 // Double-check these
02842         case UI_CURSOR_NO:
02843         case UI_CURSOR_SIZEWE:
02844         case UI_CURSOR_SIZENS:
02845         case UI_CURSOR_SIZENWSE:
02846         case UI_CURSOR_SIZENESW:
02847         case UI_CURSOR_WORKING:
02848         case UI_CURSOR_TOOLGRAB:
02849         case UI_CURSOR_TOOLLAND:
02850         case UI_CURSOR_TOOLFOCUS:
02851         case UI_CURSOR_TOOLCREATE:
02852         case UI_CURSOR_ARROWDRAG:
02853         case UI_CURSOR_NOLOCKED:
02854         case UI_CURSOR_ARROWLOCKED:
02855         case UI_CURSOR_GRABLOCKED:
02856         case UI_CURSOR_TOOLTRANSLATE:
02857         case UI_CURSOR_TOOLROTATE:
02858         case UI_CURSOR_TOOLSCALE:
02859         case UI_CURSOR_TOOLCAMERA:
02860         case UI_CURSOR_TOOLPAN:
02861         case UI_CURSOR_TOOLZOOMIN:
02862         case UI_CURSOR_TOOLPICKOBJECT3:
02863         case UI_CURSOR_TOOLSIT:
02864         case UI_CURSOR_TOOLBUY:
02865         case UI_CURSOR_TOOLPAY:
02866         case UI_CURSOR_TOOLOPEN:
02867         case UI_CURSOR_TOOLPLAY:
02868         case UI_CURSOR_TOOLPAUSE:
02869         case UI_CURSOR_TOOLMEDIAOPEN:
02870                 result = setImageCursor(gCursors[cursor]);
02871                 break;
02872 
02873         }
02874 
02875         if(result != noErr)
02876         {
02877                 InitCursor();
02878         }
02879 
02880         mCurrentCursor = cursor;
02881 }
02882 
02883 ECursorType LLWindowMacOSX::getCursor() const
02884 {
02885         return mCurrentCursor;
02886 }
02887 
02888 void LLWindowMacOSX::initCursors()
02889 {
02890         initPixmapCursor(UI_CURSOR_NO, 8, 8);
02891         initPixmapCursor(UI_CURSOR_WORKING, 1, 1);
02892         initPixmapCursor(UI_CURSOR_TOOLGRAB, 2, 14);
02893         initPixmapCursor(UI_CURSOR_TOOLLAND, 13, 8);
02894         initPixmapCursor(UI_CURSOR_TOOLFOCUS, 7, 6);
02895         initPixmapCursor(UI_CURSOR_TOOLCREATE, 7, 7);
02896         initPixmapCursor(UI_CURSOR_ARROWDRAG, 1, 1);
02897         initPixmapCursor(UI_CURSOR_ARROWCOPY, 1, 1);
02898         initPixmapCursor(UI_CURSOR_NOLOCKED, 8, 8);
02899         initPixmapCursor(UI_CURSOR_ARROWLOCKED, 1, 1);
02900         initPixmapCursor(UI_CURSOR_GRABLOCKED, 2, 14);
02901         initPixmapCursor(UI_CURSOR_TOOLTRANSLATE, 1, 1);
02902         initPixmapCursor(UI_CURSOR_TOOLROTATE, 1, 1);
02903         initPixmapCursor(UI_CURSOR_TOOLSCALE, 1, 1);
02904         initPixmapCursor(UI_CURSOR_TOOLCAMERA, 7, 6);
02905         initPixmapCursor(UI_CURSOR_TOOLPAN, 7, 6);
02906         initPixmapCursor(UI_CURSOR_TOOLZOOMIN, 7, 6);
02907         initPixmapCursor(UI_CURSOR_TOOLPICKOBJECT3, 1, 1);
02908         initPixmapCursor(UI_CURSOR_TOOLSIT, 1, 1);
02909         initPixmapCursor(UI_CURSOR_TOOLBUY, 1, 1);
02910         initPixmapCursor(UI_CURSOR_TOOLPAY, 1, 1);
02911         initPixmapCursor(UI_CURSOR_TOOLOPEN, 1, 1);
02912         initPixmapCursor(UI_CURSOR_TOOLPLAY, 1, 1);
02913         initPixmapCursor(UI_CURSOR_TOOLPAUSE, 1, 1);
02914         initPixmapCursor(UI_CURSOR_TOOLMEDIAOPEN, 1, 1);
02915 
02916         initPixmapCursor(UI_CURSOR_SIZENWSE, 10, 10);
02917         initPixmapCursor(UI_CURSOR_SIZENESW, 10, 10);
02918         initPixmapCursor(UI_CURSOR_SIZEWE, 10, 10);
02919         initPixmapCursor(UI_CURSOR_SIZENS, 10, 10);
02920 
02921 }
02922 
02923 void LLWindowMacOSX::captureMouse()
02924 {
02925         // By registering a global CarbonEvent handler for mouse move events, we ensure that
02926         // mouse events are always processed.  Thus, capture and release are unnecessary.
02927 }
02928 
02929 void LLWindowMacOSX::releaseMouse()
02930 {
02931         // By registering a global CarbonEvent handler for mouse move events, we ensure that
02932         // mouse events are always processed.  Thus, capture and release are unnecessary.
02933 }
02934 
02935 void LLWindowMacOSX::hideCursor()
02936 {
02937         if(!mCursorHidden)
02938         {
02939                 //              llinfos << "hideCursor: hiding" << llendl;
02940                 mCursorHidden = TRUE;
02941                 mHideCursorPermanent = TRUE;
02942                 ::HideCursor();
02943         }
02944         else
02945         {
02946                 //              llinfos << "hideCursor: already hidden" << llendl;
02947         }
02948 
02949         adjustCursorDecouple();
02950 }
02951 
02952 void LLWindowMacOSX::showCursor()
02953 {
02954         if(mCursorHidden)
02955         {
02956                 //              llinfos << "showCursor: showing" << llendl;
02957                 mCursorHidden = FALSE;
02958                 mHideCursorPermanent = FALSE;
02959                 ::ShowCursor();
02960         }
02961         else
02962         {
02963                 //              llinfos << "showCursor: already visible" << llendl;
02964         }
02965 
02966         adjustCursorDecouple();
02967 }
02968 
02969 void LLWindowMacOSX::showCursorFromMouseMove()
02970 {
02971         if (!mHideCursorPermanent)
02972         {
02973                 showCursor();
02974         }
02975 }
02976 
02977 void LLWindowMacOSX::hideCursorUntilMouseMove()
02978 {
02979         if (!mHideCursorPermanent)
02980         {
02981                 hideCursor();
02982                 mHideCursorPermanent = FALSE;
02983         }
02984 }
02985 
02986 
02987 
02988 //
02989 // LLSplashScreenMacOSX
02990 //
02991 LLSplashScreenMacOSX::LLSplashScreenMacOSX()
02992 {
02993         mWindow = NULL;
02994 }
02995 
02996 LLSplashScreenMacOSX::~LLSplashScreenMacOSX()
02997 {
02998 }
02999 
03000 void LLSplashScreenMacOSX::showImpl()
03001 {
03002         // This code _could_ be used to display a spash screen...
03003 #if 0
03004         IBNibRef nib = NULL;
03005         OSStatus err;
03006 
03007         err = CreateNibReference(CFSTR("SecondLife"), &nib);
03008 
03009         if(err == noErr)
03010         {
03011                 CreateWindowFromNib(nib, CFSTR("Splash Screen"), &mWindow);
03012 
03013                 DisposeNibReference(nib);
03014         }
03015 
03016         if(mWindow != NULL)
03017         {
03018                 ShowWindow(mWindow);
03019         }
03020 #endif
03021 }
03022 
03023 void LLSplashScreenMacOSX::updateImpl(const char* mesg)
03024 {
03025         if(mWindow != NULL)
03026         {
03027                 CFStringRef string = NULL;
03028 
03029                 if(mesg != NULL)
03030                 {
03031                         string = CFStringCreateWithCString(NULL, mesg, kCFStringEncodingUTF8);
03032                 }
03033                 else
03034                 {
03035                         string = CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8);
03036                 }
03037 
03038                 if(string != NULL)
03039                 {
03040                         ControlRef progressText = NULL;
03041                         ControlID id;
03042                         OSStatus err;
03043 
03044                         id.signature = 'what';
03045                         id.id = 0;
03046 
03047                         err = GetControlByID(mWindow, &id, &progressText);
03048                         if(err == noErr)
03049                         {
03050                                 err = SetControlData(progressText, kControlEntireControl, kControlStaticTextCFStringTag, sizeof(CFStringRef), (Ptr)&string);
03051                                 Draw1Control(progressText);
03052                         }
03053 
03054                         CFRelease(string);
03055                 }
03056         }
03057 }
03058 
03059 
03060 void LLSplashScreenMacOSX::hideImpl()
03061 {
03062         if(mWindow != NULL)
03063         {
03064                 DisposeWindow(mWindow);
03065                 mWindow = NULL;
03066         }
03067 }
03068 
03069 
03070 
03071 S32 OSMessageBoxMacOSX(const char* text, const char* caption, U32 type)
03072 {
03073         S32 result = OSBTN_CANCEL;
03074         SInt16 retval_mac = 1;
03075         AlertStdCFStringAlertParamRec params;
03076         CFStringRef errorString = NULL;
03077         CFStringRef explanationString = NULL;
03078         DialogRef alert = NULL;
03079         AlertType alertType = kAlertCautionAlert;
03080         OSStatus err;
03081 
03082         if(text != NULL)
03083         {
03084                 explanationString = CFStringCreateWithCString(NULL, text, kCFStringEncodingUTF8);
03085         }
03086         else
03087         {
03088                 explanationString = CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8);
03089         }
03090 
03091         if(caption != NULL)
03092         {
03093                 errorString = CFStringCreateWithCString(NULL, caption, kCFStringEncodingUTF8);
03094         }
03095         else
03096         {
03097                 errorString = CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8);
03098         }
03099 
03100         params.version = kStdCFStringAlertVersionOne;
03101         params.movable = false;
03102         params.helpButton = false;
03103         params.defaultText = (CFStringRef)kAlertDefaultOKText;
03104         params.cancelText = 0;
03105         params.otherText = 0;
03106         params.defaultButton = 1;
03107         params.cancelButton = 0;
03108         params.position = kWindowDefaultPosition;
03109         params.flags = 0;
03110 
03111         switch(type)
03112         {
03113         case OSMB_OK:
03114         default:
03115                 break;
03116         case OSMB_OKCANCEL:
03117                 params.cancelText = (CFStringRef)kAlertDefaultCancelText;
03118                 params.cancelButton = 2;
03119                 break;
03120         case OSMB_YESNO:
03121                 alertType = kAlertNoteAlert;
03122                 params.defaultText = CFSTR("Yes");
03123                 params.cancelText = CFSTR("No");
03124                 params.cancelButton = 2;
03125                 break;
03126         }
03127 
03128         if(gWindowImplementation != NULL)
03129                 gWindowImplementation->beforeDialog();
03130 
03131         err = CreateStandardAlert(
03132                 alertType,
03133                 errorString,
03134                 explanationString,
03135                 &params,
03136                 &alert);
03137 
03138         if(err == noErr)
03139         {
03140                 err = RunStandardAlert(
03141                         alert,
03142                         NULL,
03143                         &retval_mac);
03144         }
03145 
03146         if(gWindowImplementation != NULL)
03147                 gWindowImplementation->afterDialog();
03148 
03149         switch(type)
03150         {
03151         case OSMB_OK:
03152         case OSMB_OKCANCEL:
03153         default:
03154                 if(retval_mac == 1)
03155                         result = OSBTN_OK;
03156                 else
03157                         result = OSBTN_CANCEL;
03158                 break;
03159         case OSMB_YESNO:
03160                 if(retval_mac == 1)
03161                         result = OSBTN_YES;
03162                 else
03163                         result = OSBTN_NO;
03164                 break;
03165         }
03166 
03167         if(errorString != NULL)
03168         {
03169                 CFRelease(errorString);
03170         }
03171 
03172         if(explanationString != NULL)
03173         {
03174                 CFRelease(explanationString);
03175         }
03176 
03177         return result;
03178 }
03179 
03180 // Open a URL with the user's default web browser.
03181 // Must begin with protocol identifier.
03182 void spawn_web_browser(const char* escaped_url)
03183 {
03184         bool found = false;
03185         S32 i;
03186         for (i = 0; i < gURLProtocolWhitelistCount; i++)
03187         {
03188                 S32 len = strlen(gURLProtocolWhitelist[i]);     /* Flawfinder: ignore */
03189                 if (!strncmp(escaped_url, gURLProtocolWhitelist[i], len)
03190                         && escaped_url[len] == ':')
03191                 {
03192                         found = true;
03193                         break;
03194                 }
03195         }
03196 
03197         if (!found)
03198         {
03199                 llwarns << "spawn_web_browser() called for url with protocol not on whitelist: " << escaped_url << llendl;
03200                 return;
03201         }
03202 
03203         OSStatus result = noErr;
03204         CFURLRef urlRef = NULL;
03205 
03206         llinfos << "Opening URL " << escaped_url << llendl;
03207 
03208         CFStringRef     stringRef = CFStringCreateWithCString(NULL, escaped_url, kCFStringEncodingUTF8);
03209         if (stringRef)
03210         {
03211                 // This will succeed if the string is a full URL, including the http://
03212                 // Note that URLs specified this way need to be properly percent-escaped.
03213                 urlRef = CFURLCreateWithString(NULL, stringRef, NULL);
03214 
03215                 // Don't use CRURLCreateWithFileSystemPath -- only want valid URLs
03216 
03217                 CFRelease(stringRef);
03218         }
03219 
03220         if (urlRef)
03221         {
03222                 result = LSOpenCFURLRef(urlRef, NULL);
03223 
03224                 if (result != noErr)
03225                 {
03226                         llinfos << "Error " << result << " on open." << llendl;
03227                 }
03228 
03229                 CFRelease(urlRef);
03230         }
03231         else
03232         {
03233                 llinfos << "Error: couldn't create URL." << llendl;
03234         }
03235 }
03236 
03237 
03238 BOOL LLWindowMacOSX::dialog_color_picker ( F32 *r, F32 *g, F32 *b)
03239 {
03240         BOOL    retval = FALSE;
03241         OSErr   error = noErr;
03242         NColorPickerInfo        info;
03243 
03244         memset(&info, 0, sizeof(info));
03245         info.theColor.color.rgb.red = (UInt16)(*r * 65535.f);
03246         info.theColor.color.rgb.green = (UInt16)(*g * 65535.f);
03247         info.theColor.color.rgb.blue = (UInt16)(*b * 65535.f);
03248         info.placeWhere = kCenterOnMainScreen;
03249 
03250         if(gWindowImplementation != NULL)
03251                 gWindowImplementation->beforeDialog();
03252 
03253         error = NPickColor(&info);
03254 
03255         if(gWindowImplementation != NULL)
03256                 gWindowImplementation->afterDialog();
03257 
03258         if (error == noErr)
03259         {
03260                 retval = info.newColorChosen;
03261                 if (info.newColorChosen)
03262                 {
03263                         *r = ((float) info.theColor.color.rgb.red) / 65535.0;
03264                         *g = ((float) info.theColor.color.rgb.green) / 65535.0;
03265                         *b = ((float) info.theColor.color.rgb.blue) / 65535.0;
03266                 }
03267         }
03268         return (retval);
03269 }
03270 
03271 static WindowRef dummywindowref = NULL;
03272 
03273 void *LLWindowMacOSX::getPlatformWindow()
03274 {
03275         if(mWindow != NULL)
03276                 return (void*)mWindow;
03277 
03278         // If we're in fullscreen mode, there's no window pointer available.
03279         // Since Mozilla needs one to function, create a dummy window here.
03280         // Note that we will never destroy it, but since only one will be created per run of the application, that's okay.
03281         
03282         if(dummywindowref == NULL)
03283         {
03284                 Rect window_rect = {100, 100, 200, 200};
03285 
03286                 dummywindowref = NewCWindow(
03287                         NULL,
03288                         &window_rect,
03289                         "\p",
03290                         false,                          // Create the window invisible.  
03291                         zoomDocProc,            // Window with a grow box and a zoom box
03292                         kLastWindowOfClass,             // create it behind other windows
03293                         false,                                  // no close box
03294                         0);
03295         }
03296         
03297         return (void*)dummywindowref;
03298 }
03299 
03300 void LLWindowMacOSX::stopDockTileBounce()
03301 {
03302         NMRemove(&mBounceRec);
03303         mBounceTimer.stop();
03304 }
03305 
03306 // get a double value from a dictionary
03307 static double getDictDouble (CFDictionaryRef refDict, CFStringRef key)
03308 {
03309         double double_value;
03310         CFNumberRef number_value = (CFNumberRef) CFDictionaryGetValue(refDict, key);
03311         if (!number_value) // if can't get a number for the dictionary
03312                 return -1;  // fail
03313         if (!CFNumberGetValue(number_value, kCFNumberDoubleType, &double_value)) // or if cant convert it
03314                 return -1; // fail
03315         return double_value; // otherwise return the long value
03316 }
03317 
03318 // get a long value from a dictionary
03319 static long getDictLong (CFDictionaryRef refDict, CFStringRef key)
03320 {
03321         long int_value;
03322         CFNumberRef number_value = (CFNumberRef) CFDictionaryGetValue(refDict, key);
03323         if (!number_value) // if can't get a number for the dictionary
03324                 return -1;  // fail
03325         if (!CFNumberGetValue(number_value, kCFNumberLongType, &int_value)) // or if cant convert it
03326                 return -1; // fail
03327         return int_value; // otherwise return the long value
03328 }
03329 
03330 void LLWindowMacOSX::allowLanguageTextInput(LLPreeditor *preeditor, BOOL b)
03331 {
03332         ScriptLanguageRecord script_language;
03333 
03334         if (preeditor != mPreeditor && !b)
03335         {
03336                 // This condition may occur by a call to
03337                 // setEnabled(BOOL) against LLTextEditor or LLLineEditor
03338                 // when the control is not focused.
03339                 // We need to silently ignore the case so that
03340                 // the language input status of the focused control
03341                 // is not disturbed.
03342                 return;
03343         }
03344 
03345         // Take care of old and new preeditors.
03346         if (preeditor != mPreeditor || !b)
03347         {
03348                 // We need to interrupt before updating mPreeditor,
03349                 // so that the fix string from input method goes to
03350                 // the old preeditor.
03351                 if (mLanguageTextInputAllowed)
03352                 {
03353                         interruptLanguageTextInput();
03354                 }
03355                 mPreeditor = (b ? preeditor : NULL);
03356         }
03357 
03358         if (b == mLanguageTextInputAllowed)
03359         {
03360                 return;
03361         }
03362         mLanguageTextInputAllowed = b;
03363         
03364         if (b)
03365         {
03366                 if (mTSMScriptCode != smRoman)
03367                 {
03368                         script_language.fScript = mTSMScriptCode;
03369                         script_language.fLanguage = mTSMLangCode;
03370                         SetTextServiceLanguage(&script_language);
03371                 }
03372         }
03373         else
03374         {
03375                 GetTextServiceLanguage(&script_language);
03376                 mTSMScriptCode = script_language.fScript;
03377                 mTSMLangCode = script_language.fLanguage;
03378                 if (mTSMScriptCode != smRoman)
03379                 {
03380                         script_language.fScript = smRoman;
03381                         script_language.fLanguage = langEnglish;
03382                         SetTextServiceLanguage(&script_language);
03383                 }
03384         }
03385 }
03386 
03387 void LLWindowMacOSX::interruptLanguageTextInput()
03388 {
03389         if (mTSMDocument)
03390         {
03391                 FixTSMDocument(mTSMDocument);
03392         }
03393         // Don't we need to call resetPreedit here?
03394         // Well, if Apple's TSM document is correct, we don't.
03395 }
03396 
03397 //static
03398 std::string LLWindowMacOSX::getFontListSans()
03399 {
03400         // This is a fairly complete Japanese font that ships with Mac OS X.
03401         // The first filename is in UTF8, but it shows up in the font menu as "Hiragino Kaku Gothic Pro W3".
03402         // The third filename is in UTF8, but it shows up in the font menu as "STHeiti Light"
03403         return "\xE3\x83\x92\xE3\x83\xA9\xE3\x82\xAD\xE3\x82\x99\xE3\x83\x8E\xE8\xA7\x92\xE3\x82\xB3\xE3\x82\x99 Pro W3.otf;\xE3\x83\x92\xE3\x83\xA9\xE3\x82\xAD\xE3\x82\x99\xE3\x83\x8E\xE8\xA7\x92\xE3\x82\xB3\xE3\x82\x99 ProN W3.otf;AppleGothic.dfont;AppleGothic.ttf;\xe5\x8d\x8e\xe6\x96\x87\xe7\xbb\x86\xe9\xbb\x91.ttf";
03404 }
03405 
03406 #endif // LL_DARWIN

Generated on Fri May 16 08:33:03 2008 for SecondLife by  doxygen 1.5.5