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

Generated on Thu Jul 1 06:09:46 2010 for Second Life Viewer by  doxygen 1.4.7