llwindowmacosx.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:98k
- /**
- * @file llwindowmacosx.cpp
- * @brief Platform-dependent implementation of llwindow
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2010, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "linden_common.h"
- #include "llwindowmacosx.h"
- #include "llkeyboardmacosx.h"
- #include "llwindowcallbacks.h"
- #include "llwindowmacosx-objc.h"
- #include "llpreeditor.h"
- #include "llerror.h"
- #include "llgl.h"
- #include "llstring.h"
- #include "lldir.h"
- #include "indra_constants.h"
- #include <Carbon/Carbon.h>
- #include <OpenGL/OpenGL.h>
- extern BOOL gDebugWindowProc;
- // culled from winuser.h
- //const S32 WHEEL_DELTA = 120; /* Value for rolling one detent */
- // On the Mac, the scroll wheel reports a delta of 1 for each detent.
- // There's also acceleration for faster scrolling, based on a slider in the system preferences.
- const S32 WHEEL_DELTA = 1; /* Value for rolling one detent */
- const S32 BITS_PER_PIXEL = 32;
- const S32 MAX_NUM_RESOLUTIONS = 32;
- //
- // LLWindowMacOSX
- //
- BOOL LLWindowMacOSX::sUseMultGL = FALSE;
- WindowRef LLWindowMacOSX::sMediaWindow = NULL;
- // Cross-platform bits:
- BOOL check_for_card(const char* RENDERER, const char* bad_card)
- {
- if (!strnicmp(RENDERER, bad_card, strlen(bad_card)))
- {
- std::string buffer = llformat(
- "Your video card appears to be a %s, which Second Life does not support.n"
- "n"
- "Second Life requires a video card with 32 Mb of memory or more, as well asn"
- "multitexture support. We explicitly support nVidia GeForce 2 or better, n"
- "and ATI Radeon 8500 or better.n"
- "n"
- "If you own a supported card and continue to receive this message, try n"
- "updating to the latest video card drivers. Otherwise look in then"
- "secondlife.com support section or e-mail technical supportn"
- "n"
- "You can try to run Second Life, but it will probably crash or runn"
- "very slowly. Try anyway?",
- bad_card);
- S32 button = OSMessageBox(buffer.c_str(), "Unsupported video card", OSMB_YESNO);
- if (OSBTN_YES == button)
- {
- return FALSE;
- }
- else
- {
- return TRUE;
- }
- }
- return FALSE;
- }
- // Switch to determine whether we capture all displays, or just the main one.
- // We may want to base this on the setting of _DEBUG...
- #define CAPTURE_ALL_DISPLAYS 0
- static double getDictDouble (CFDictionaryRef refDict, CFStringRef key);
- static long getDictLong (CFDictionaryRef refDict, CFStringRef key);
- // CarbonEvents we're interested in.
- static EventTypeSpec WindowHandlerEventList[] =
- {
- // Window-related events
- // { kEventClassWindow, kEventWindowCollapsing },
- // { kEventClassWindow, kEventWindowCollapsed },
- // { kEventClassWindow, kEventWindowShown },
- { kEventClassWindow, kEventWindowActivated },
- { kEventClassWindow, kEventWindowDeactivated },
- { kEventClassWindow, kEventWindowShown },
- { kEventClassWindow, kEventWindowHidden },
- { kEventClassWindow, kEventWindowCollapsed },
- { kEventClassWindow, kEventWindowExpanded },
- { kEventClassWindow, kEventWindowGetClickActivation },
- { kEventClassWindow, kEventWindowClose },
- { kEventClassWindow, kEventWindowBoundsChanging },
- { kEventClassWindow, kEventWindowBoundsChanged },
- // { kEventClassWindow, kEventWindowZoomed },
- // { kEventClassWindow, kEventWindowDrawContent },
- // Mouse events
- { kEventClassMouse, kEventMouseDown },
- { kEventClassMouse, kEventMouseUp },
- { kEventClassMouse, kEventMouseDragged },
- { kEventClassMouse, kEventMouseWheelMoved },
- { kEventClassMouse, kEventMouseMoved },
- // Keyboard events
- // No longer handle raw key down events directly.
- // When text input events come in, extract the raw key events from them and process at that point.
- // This allows input methods to eat keystrokes the way they're supposed to.
- // { kEventClassKeyboard, kEventRawKeyDown },
- // { kEventClassKeyboard, kEventRawKeyRepeat },
- { kEventClassKeyboard, kEventRawKeyUp },
- { kEventClassKeyboard, kEventRawKeyModifiersChanged },
- // Text input events
- { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
- { kEventClassTextInput, kEventTextInputUpdateActiveInputArea },
- { kEventClassTextInput, kEventTextInputOffsetToPos },
- { kEventClassTextInput, kEventTextInputPosToOffset },
- { kEventClassTextInput, kEventTextInputShowHideBottomWindow },
- { kEventClassTextInput, kEventTextInputGetSelectedText },
- { kEventClassTextInput, kEventTextInputFilterText },
- // TSM Document Access events (advanced input method support)
- { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetLength },
- { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetSelectedRange },
- { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetCharacters },
- { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetFont },
- { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetGlyphInfo },
- { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessLockDocument },
- { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessUnlockDocument }
- };
- static EventTypeSpec GlobalHandlerEventList[] =
- {
- // Mouse events
- { kEventClassMouse, kEventMouseDown },
- { kEventClassMouse, kEventMouseUp },
- { kEventClassMouse, kEventMouseDragged },
- { kEventClassMouse, kEventMouseWheelMoved },
- { kEventClassMouse, kEventMouseMoved },
- // Keyboard events
- // No longer handle raw key down events directly.
- // When text input events come in, extract the raw key events from them and process at that point.
- // This allows input methods to eat keystrokes the way they're supposed to.
- // { kEventClassKeyboard, kEventRawKeyDown },
- // { kEventClassKeyboard, kEventRawKeyRepeat },
- { kEventClassKeyboard, kEventRawKeyUp },
- { kEventClassKeyboard, kEventRawKeyModifiersChanged },
- // Text input events
- { kEventClassTextInput, kEventTextInputUpdateActiveInputArea },
- { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
- { kEventClassTextInput, kEventTextInputOffsetToPos },
- { kEventClassTextInput, kEventTextInputPosToOffset },
- { kEventClassTextInput, kEventTextInputShowHideBottomWindow },
- { kEventClassTextInput, kEventTextInputGetSelectedText },
- { kEventClassTextInput, kEventTextInputFilterText },
- // TSM Document Access events (advanced input method support)
- { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetLength },
- { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetSelectedRange },
- { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetCharacters },
- { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetFont },
- { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetGlyphInfo },
- { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessLockDocument },
- { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessUnlockDocument }
- };
- static EventTypeSpec CommandHandlerEventList[] =
- {
- { kEventClassCommand, kEventCommandProcess }
- };
- // MBW -- HACK ALERT
- // On the Mac, to put up an OS dialog in full screen mode, we must first switch OUT of full screen mode.
- // The proper way to do this is to bracket the dialog with calls to beforeDialog() and afterDialog(), but these
- // require a pointer to the LLWindowMacOSX object. Stash it here and maintain in the constructor and destructor.
- // This assumes that there will be only one object of this class at any time. Hopefully this is true.
- static LLWindowMacOSX *gWindowImplementation = NULL;
- LLWindowMacOSX::LLWindowMacOSX(LLWindowCallbacks* callbacks,
- const std::string& title, const std::string& name, S32 x, S32 y, S32 width,
- S32 height, U32 flags,
- BOOL fullscreen, BOOL clearBg,
- BOOL disable_vsync, BOOL use_gl,
- BOOL ignore_pixel_depth,
- U32 fsaa_samples)
- : LLWindow(NULL, fullscreen, flags)
- {
- // *HACK: During window construction we get lots of OS events for window
- // reshape, activate, etc. that the viewer isn't ready to handle.
- // Route them to a dummy callback structure until the end of constructor.
- LLWindowCallbacks null_callbacks;
- mCallbacks = &null_callbacks;
-
- // Voodoo for calling cocoa from carbon (see llwindowmacosx-objc.mm).
- setupCocoa();
-
- // Initialize the keyboard
- gKeyboard = new LLKeyboardMacOSX();
- gKeyboard->setCallbacks(callbacks);
- // Ignore use_gl for now, only used for drones on PC
- mWindow = NULL;
- mContext = NULL;
- mPixelFormat = NULL;
- mDisplay = CGMainDisplayID();
- mOldDisplayMode = NULL;
- mTimer = NULL;
- mSimulatedRightClick = FALSE;
- mLastModifiers = 0;
- mHandsOffEvents = FALSE;
- mCursorDecoupled = FALSE;
- mCursorLastEventDeltaX = 0;
- mCursorLastEventDeltaY = 0;
- mCursorIgnoreNextDelta = FALSE;
- mNeedsResize = FALSE;
- mOverrideAspectRatio = 0.f;
- mMinimized = FALSE;
- mTSMDocument = NULL; // Just in case.
- mLanguageTextInputAllowed = FALSE;
- mTSMScriptCode = 0;
- mTSMLangCode = 0;
- mPreeditor = NULL;
- mRawKeyEvent = NULL;
- mFSAASamples = fsaa_samples;
- mForceRebuild = FALSE;
-
- // For reasons that aren't clear to me, LLTimers seem to be created in the "started" state.
- // 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.
- mBounceTimer.stop();
- // Get the original aspect ratio of the main device.
- mOriginalAspectRatio = (double)CGDisplayPixelsWide(mDisplay) / (double)CGDisplayPixelsHigh(mDisplay);
- // Stash the window title
- strcpy((char*)mWindowTitle + 1, title.c_str()); /* Flawfinder: ignore */
- mWindowTitle[0] = title.length();
- mEventHandlerUPP = NewEventHandlerUPP(staticEventHandler);
- mMoveEventCampartorUPP = NewEventComparatorUPP(staticMoveEventComparator);
- mGlobalHandlerRef = NULL;
- mWindowHandlerRef = NULL;
-
- mDragOverrideCursor = -1;
- // We're not clipping yet
- SetRect( &mOldMouseClip, 0, 0, 0, 0 );
- // Set up global event handlers (the fullscreen case needs this)
- InstallStandardEventHandler(GetApplicationEventTarget());
- // Stash an object pointer for OSMessageBox()
- gWindowImplementation = this;
- // Create the GL context and set it up for windowed or fullscreen, as appropriate.
- if(createContext(x, y, width, height, 32, fullscreen, disable_vsync))
- {
- if(mWindow != NULL)
- {
- // MBW -- XXX -- I think we can now do this here?
- // Constrain the window to the screen it's mostly on, resizing if necessary.
- ConstrainWindowToScreen(
- mWindow,
- kWindowStructureRgn,
- kWindowConstrainMayResize |
- // kWindowConstrainStandardOptions |
- 0,
- NULL,
- NULL);
- MacShowWindow(mWindow);
- BringToFront(mWindow);
- }
- if (!gGLManager.initGL())
- {
- setupFailure(
- "Second Life is unable to run because your video card driversn"
- "are out of date or unsupported. Please make sure you haven"
- "the latest video card drivers installed.n"
- "If you continue to receive this message, contact customer service.",
- "Error",
- OSMB_OK);
- return;
- }
- //start with arrow cursor
- initCursors();
- setCursor( UI_CURSOR_ARROW );
- }
- mCallbacks = callbacks;
- stop_glerror();
- }
- BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL disable_vsync)
- {
- OSStatus err;
- BOOL glNeedsInit = FALSE;
- if(mGlobalHandlerRef == NULL)
- {
- InstallApplicationEventHandler(mEventHandlerUPP, GetEventTypeCount (CommandHandlerEventList), CommandHandlerEventList, (void*)this, &mGlobalHandlerRef);
- }
- mFullscreen = fullscreen;
- if (mFullscreen && (mOldDisplayMode == NULL))
- {
- LL_INFOS("Window") << "createContext: setting up fullscreen " << width << "x" << height << LL_ENDL;
- // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays. Plan accordingly.
- double refresh = getDictDouble (CGDisplayCurrentMode (mDisplay), kCGDisplayRefreshRate);
- // If the requested width or height is 0, find the best default for the monitor.
- if((width == 0) || (height == 0))
- {
- // Scan through the list of modes, looking for one which has:
- // height between 700 and 800
- // aspect ratio closest to the user's original mode
- S32 resolutionCount = 0;
- LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount);
- if(resolutionList != NULL)
- {
- F32 closestAspect = 0;
- U32 closestHeight = 0;
- U32 closestWidth = 0;
- int i;
- LL_DEBUGS("Window") << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << LL_ENDL;
- for(i=0; i < resolutionCount; i++)
- {
- F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight;
- LL_DEBUGS("Window") << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << LL_ENDL;
- if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) &&
- (fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio)))
- {
- LL_DEBUGS("Window") << " (new closest mode) " << LL_ENDL;
- // This is the closest mode we've seen yet.
- closestWidth = resolutionList[i].mWidth;
- closestHeight = resolutionList[i].mHeight;
- closestAspect = aspect;
- }
- }
- width = closestWidth;
- height = closestHeight;
- }
- }
- if((width == 0) || (height == 0))
- {
- // Mode search failed for some reason. Use the old-school default.
- width = 1024;
- height = 768;
- }
- if (true)
- {
- // Fullscreen support
- CFDictionaryRef refDisplayMode = 0;
- boolean_t exactMatch = false;
- #if CAPTURE_ALL_DISPLAYS
- // Capture all displays (may want to do this for final build)
- CGCaptureAllDisplays ();
- #else
- // Capture only the main display (useful for debugging)
- CGDisplayCapture (mDisplay);
- #endif
- // Switch the display to the desired resolution and refresh
- refDisplayMode = CGDisplayBestModeForParametersAndRefreshRate(
- mDisplay,
- BITS_PER_PIXEL,
- width,
- height,
- refresh,
- &exactMatch);
- if (refDisplayMode)
- {
- LL_DEBUGS("Window") << "createContext: switching display resolution" << LL_ENDL;
- mOldDisplayMode = CGDisplayCurrentMode (mDisplay);
- CGDisplaySwitchToMode (mDisplay, refDisplayMode);
- // CFRelease(refDisplayMode);
- AddEventTypesToHandler(mGlobalHandlerRef, GetEventTypeCount (GlobalHandlerEventList), GlobalHandlerEventList);
- }
- mFullscreen = TRUE;
- mFullscreenWidth = CGDisplayPixelsWide(mDisplay);
- mFullscreenHeight = CGDisplayPixelsHigh(mDisplay);
- mFullscreenBits = CGDisplayBitsPerPixel(mDisplay);
- mFullscreenRefresh = llround(getDictDouble (CGDisplayCurrentMode (mDisplay), kCGDisplayRefreshRate));
- LL_INFOS("Window") << "Running at " << mFullscreenWidth
- << "x" << mFullscreenHeight
- << "x" << mFullscreenBits
- << " @ " << mFullscreenRefresh
- << LL_ENDL;
- }
- else
- {
- // No fullscreen support
- mFullscreen = FALSE;
- mFullscreenWidth = -1;
- mFullscreenHeight = -1;
- mFullscreenBits = -1;
- mFullscreenRefresh = -1;
- std::string error= llformat("Unable to run fullscreen at %d x %d.nRunning in window.", width, height);
- OSMessageBox(error, "Error", OSMB_OK);
- }
- }
- if(!mFullscreen && (mWindow == NULL))
- {
- Rect window_rect;
- //int displayWidth = CGDisplayPixelsWide(mDisplay);
- //int displayHeight = CGDisplayPixelsHigh(mDisplay);
- //const int menuBarPlusTitleBar = 44; // Ugly magic number.
- LL_DEBUGS("Window") << "createContext: creating window" << LL_ENDL;
- window_rect.left = (long) x;
- window_rect.right = (long) x + width;
- window_rect.top = (long) y;
- window_rect.bottom = (long) y + height;
- //-----------------------------------------------------------------------
- // Create the window
- //-----------------------------------------------------------------------
- mWindow = NewCWindow(
- NULL,
- &window_rect,
- mWindowTitle,
- false, // Create the window invisible. Whoever calls createContext() should show it after any moving/resizing.
- // noGrowDocProc, // Window with no grow box and no zoom box
- zoomDocProc, // Window with a grow box and a zoom box
- // zoomNoGrow, // Window with a zoom box but no grow box
- kFirstWindowOfClass,
- true,
- (long)this);
- if (!mWindow)
- {
- setupFailure("Window creation error", "Error", OSMB_OK);
- return FALSE;
- }
- // Turn on live resize.
- // For this to work correctly, we need to be able to call LLViewerWindow::draw from
- // the event handler for kEventWindowBoundsChanged. It's not clear that we have access from here.
- // err = ChangeWindowAttributes(mWindow, kWindowLiveResizeAttribute, 0);
- // Set up window event handlers (some window-related events ONLY go to window handlers.)
- InstallStandardEventHandler(GetWindowEventTarget(mWindow));
- InstallWindowEventHandler(mWindow, mEventHandlerUPP, GetEventTypeCount (WindowHandlerEventList), WindowHandlerEventList, (void*)this, &mWindowHandlerRef); // add event handler
- #if LL_OS_DRAGDROP_ENABLED
- InstallTrackingHandler( dragTrackingHandler, mWindow, (void*)this );
- InstallReceiveHandler( dragReceiveHandler, mWindow, (void*)this );
- #endif // LL_OS_DRAGDROP_ENABLED
- }
- {
- // Create and initialize our TSM document for language text input.
- // If an error occured, we can do nothing better than simply ignore it.
- // mTSMDocument will be kept NULL in case.
- if (mTSMDocument)
- {
- DeactivateTSMDocument(mTSMDocument);
- DeleteTSMDocument(mTSMDocument);
- mTSMDocument = NULL;
- }
- static InterfaceTypeList types = { kUnicodeDocument };
- err = NewTSMDocument(1, types, &mTSMDocument, 0);
- if (err != noErr)
- {
- LL_WARNS("Window") << "createContext: couldn't create a TSMDocument (" << err << ")" << LL_ENDL;
- }
- if (mTSMDocument)
- {
- ActivateTSMDocument(mTSMDocument);
- allowLanguageTextInput(NULL, FALSE);
- }
- }
- if(mContext == NULL)
- {
- AGLRendererInfo rendererInfo = NULL;
- //-----------------------------------------------------------------------
- // Create GL drawing context
- //-----------------------------------------------------------------------
- if(mPixelFormat == NULL)
- {
- if(mFullscreen)
- {
- GLint fullscreenAttrib[] =
- {
- AGL_RGBA,
- AGL_FULLSCREEN,
- // AGL_NO_RECOVERY, // MBW -- XXX -- Not sure if we want this attribute
- AGL_SAMPLE_BUFFERS_ARB, mFSAASamples > 0 ? 1 : 0,
- AGL_SAMPLES_ARB, mFSAASamples,
- AGL_DOUBLEBUFFER,
- AGL_CLOSEST_POLICY,
- AGL_ACCELERATED,
- AGL_RED_SIZE, 8,
- AGL_GREEN_SIZE, 8,
- AGL_BLUE_SIZE, 8,
- AGL_ALPHA_SIZE, 8,
- AGL_DEPTH_SIZE, 24,
- AGL_STENCIL_SIZE, 8,
- AGL_NONE
- };
- LL_DEBUGS("Window") << "createContext: creating fullscreen pixelformat" << LL_ENDL;
- GDHandle gdhDisplay = NULL;
- err = DMGetGDeviceByDisplayID ((DisplayIDType)mDisplay, &gdhDisplay, false);
- mPixelFormat = aglChoosePixelFormat(&gdhDisplay, 1, fullscreenAttrib);
- rendererInfo = aglQueryRendererInfo(&gdhDisplay, 1);
- }
- else
- {
- GLint windowedAttrib[] =
- {
- AGL_RGBA,
- AGL_DOUBLEBUFFER,
- AGL_CLOSEST_POLICY,
- AGL_ACCELERATED,
- AGL_SAMPLE_BUFFERS_ARB, mFSAASamples > 0 ? 1 : 0,
- AGL_SAMPLES_ARB, mFSAASamples,
- AGL_RED_SIZE, 8,
- AGL_GREEN_SIZE, 8,
- AGL_BLUE_SIZE, 8,
- AGL_ALPHA_SIZE, 8,
- AGL_DEPTH_SIZE, 24,
- AGL_STENCIL_SIZE, 8,
- AGL_NONE
- };
- LL_DEBUGS("Window") << "createContext: creating windowed pixelformat" << LL_ENDL;
- mPixelFormat = aglChoosePixelFormat(NULL, 0, windowedAttrib);
- GDHandle gdhDisplay = GetMainDevice();
- rendererInfo = aglQueryRendererInfo(&gdhDisplay, 1);
- }
- // May want to get the real error text like this:
- // (char *) aglErrorString(aglGetError());
- if(aglGetError() != AGL_NO_ERROR)
- {
- setupFailure("Can't find suitable pixel format", "Error", OSMB_OK);
- return FALSE;
- }
- }
- if(mPixelFormat)
- {
- LL_DEBUGS("Window") << "createContext: creating GL context" << LL_ENDL;
- mContext = aglCreateContext(mPixelFormat, NULL);
- }
- if(mContext == NULL)
- {
- setupFailure("Can't make GL context", "Error", OSMB_OK);
- return FALSE;
- }
- gGLManager.mVRAM = 0;
- if(rendererInfo != NULL)
- {
- GLint result;
- if(aglDescribeRenderer(rendererInfo, AGL_VIDEO_MEMORY, &result))
- {
- // llinfos << "createContext: aglDescribeRenderer(AGL_VIDEO_MEMORY) returned " << result << llendl;
- gGLManager.mVRAM = result / (1024 * 1024);
- }
- else
- {
- // llinfos << "createContext: aglDescribeRenderer(AGL_VIDEO_MEMORY) failed." << llendl;
- }
- // This could be useful at some point, if it takes into account the memory already used by screen buffers, etc...
- if(aglDescribeRenderer(rendererInfo, AGL_TEXTURE_MEMORY, &result))
- {
- // llinfos << "createContext: aglDescribeRenderer(AGL_TEXTURE_MEMORY) returned " << result << llendl;
- }
- else
- {
- // llinfos << "createContext: aglDescribeRenderer(AGL_TEXTURE_MEMORY) failed." << llendl;
- }
- aglDestroyRendererInfo(rendererInfo);
- }
- // Since we just created the context, it needs to be set up.
- glNeedsInit = TRUE;
- }
- // Hook up the context to a drawable
- if (mFullscreen && (mOldDisplayMode != NULL))
- {
- // We successfully captured the display. Use a fullscreen drawable
- LL_DEBUGS("Window") << "createContext: attaching fullscreen drawable" << LL_ENDL;
- #if CAPTURE_ALL_DISPLAYS
- // Capture all displays (may want to do this for final build)
- aglDisable (mContext, AGL_FS_CAPTURE_SINGLE);
- #else
- // Capture only the main display (useful for debugging)
- aglEnable (mContext, AGL_FS_CAPTURE_SINGLE);
- #endif
- if (!aglSetFullScreen (mContext, 0, 0, 0, 0))
- {
- setupFailure("Can't set GL fullscreen", "Error", OSMB_OK);
- return FALSE;
- }
- }
- else if(!mFullscreen && (mWindow != NULL))
- {
- LL_DEBUGS("Window") << "createContext: attaching windowed drawable" << LL_ENDL;
- // We created a window. Use it as the drawable.
- if(!aglSetDrawable(mContext, GetWindowPort (mWindow)))
- {
- setupFailure("Can't set GL drawable", "Error", OSMB_OK);
- return FALSE;
- }
- }
- else
- {
- setupFailure("Can't get fullscreen or windowed drawable.", "Error", OSMB_OK);
- return FALSE;
- }
- if(mContext != NULL)
- {
- LL_DEBUGS("Window") << "createContext: setting current context" << LL_ENDL;
- if (!aglSetCurrentContext(mContext))
- {
- setupFailure("Can't activate GL rendering context", "Error", OSMB_OK);
- return FALSE;
- }
- }
- if(glNeedsInit)
- {
- // Check for some explicitly unsupported cards.
- const char* RENDERER = (const char*) glGetString(GL_RENDERER);
- const char* CARD_LIST[] =
- { "RAGE 128",
- "RIVA TNT2",
- "Intel 810",
- "3Dfx/Voodoo3",
- "Radeon 7000",
- "Radeon 7200",
- "Radeon 7500",
- "Radeon DDR",
- "Radeon VE",
- "GDI Generic" };
- const S32 CARD_COUNT = LL_ARRAY_SIZE(CARD_LIST);
- // Future candidates:
- // ProSavage/Twister
- // SuperSavage
- S32 i;
- for (i = 0; i < CARD_COUNT; i++)
- {
- if (check_for_card(RENDERER, CARD_LIST[i]))
- {
- close();
- return FALSE;
- }
- }
- }
- GLint colorBits, alphaBits, depthBits, stencilBits;
- if( !aglDescribePixelFormat(mPixelFormat, AGL_BUFFER_SIZE, &colorBits) ||
- !aglDescribePixelFormat(mPixelFormat, AGL_ALPHA_SIZE, &alphaBits) ||
- !aglDescribePixelFormat(mPixelFormat, AGL_DEPTH_SIZE, &depthBits) ||
- !aglDescribePixelFormat(mPixelFormat, AGL_STENCIL_SIZE, &stencilBits))
- {
- close();
- setupFailure("Can't get pixel format description", "Error", OSMB_OK);
- return FALSE;
- }
- LL_INFOS("GLInit") << "GL buffer: Color Bits " << S32(colorBits)
- << " Alpha Bits " << S32(alphaBits)
- << " Depth Bits " << S32(depthBits)
- << " Stencil Bits" << S32(stencilBits)
- << LL_ENDL;
- if (colorBits < 32)
- {
- close();
- setupFailure(
- "Second Life requires True Color (32-bit) to run in a window.n"
- "Please go to Control Panels -> Display -> Settings andn"
- "set the screen to 32-bit color.n"
- "Alternately, if you choose to run fullscreen, Second Lifen"
- "will automatically adjust the screen each time it runs.",
- "Error",
- OSMB_OK);
- return FALSE;
- }
- if (alphaBits < 8)
- {
- close();
- setupFailure(
- "Second Life is unable to run because it can't get an 8 bit alphan"
- "channel. Usually this is due to video card driver issues.n"
- "Please make sure you have the latest video card drivers installed.n"
- "Also be sure your monitor is set to True Color (32-bit) inn"
- "Control Panels -> Display -> Settings.n"
- "If you continue to receive this message, contact customer service.",
- "Error",
- OSMB_OK);
- return FALSE;
- }
- // Disable vertical sync for swap
- GLint frames_per_swap = 0;
- if (disable_vsync)
- {
- LL_DEBUGS("GLInit") << "Disabling vertical sync" << LL_ENDL;
- frames_per_swap = 0;
- }
- else
- {
- LL_DEBUGS("GLinit") << "Keeping vertical sync" << LL_ENDL;
- frames_per_swap = 1;
- }
- aglSetInteger(mContext, AGL_SWAP_INTERVAL, &frames_per_swap);
- //enable multi-threaded OpenGL
- if (sUseMultGL)
- {
- CGLError cgl_err;
- CGLContextObj ctx = CGLGetCurrentContext();
- cgl_err = CGLEnable( ctx, kCGLCEMPEngine);
- if (cgl_err != kCGLNoError )
- {
- LL_DEBUGS("GLInit") << "Multi-threaded OpenGL not available." << LL_ENDL;
- }
- else
- {
- LL_DEBUGS("GLInit") << "Multi-threaded OpenGL enabled." << LL_ENDL;
- }
- }
- // Don't need to get the current gamma, since there's a call that restores it to the system defaults.
- return TRUE;
- }
- // changing fullscreen resolution, or switching between windowed and fullscreen mode.
- BOOL LLWindowMacOSX::switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp)
- {
- BOOL needsRebuild = FALSE;
- BOOL result = true;
- if(fullscreen)
- {
- if(mFullscreen)
- {
- // Switching resolutions in fullscreen mode. Don't need to rebuild for this.
- // Fullscreen support
- CFDictionaryRef refDisplayMode = 0;
- boolean_t exactMatch = false;
- // Switch the display to the desired resolution and refresh
- refDisplayMode = CGDisplayBestModeForParametersAndRefreshRate(
- mDisplay,
- BITS_PER_PIXEL,
- size.mX,
- size.mY,
- getDictDouble (CGDisplayCurrentMode (mDisplay), kCGDisplayRefreshRate),
- &exactMatch);
- if (refDisplayMode)
- {
- CGDisplaySwitchToMode (mDisplay, refDisplayMode);
- // CFRelease(refDisplayMode);
- }
- mFullscreenWidth = CGDisplayPixelsWide(mDisplay);
- mFullscreenHeight = CGDisplayPixelsHigh(mDisplay);
- mFullscreenBits = CGDisplayBitsPerPixel(mDisplay);
- mFullscreenRefresh = llround(getDictDouble (CGDisplayCurrentMode (mDisplay), kCGDisplayRefreshRate));
- LL_INFOS("Window") << "Switched resolution to " << mFullscreenWidth
- << "x" << mFullscreenHeight
- << "x" << mFullscreenBits
- << " @ " << mFullscreenRefresh
- << LL_ENDL;
- // Update the GL context to the new screen size
- if (!aglUpdateContext(mContext))
- {
- setupFailure("Can't set GL fullscreen", "Error", OSMB_OK);
- result = FALSE;
- }
- }
- else
- {
- // Switching from windowed to fullscreen
- needsRebuild = TRUE;
- }
- }
- else
- {
- if(mFullscreen)
- {
- // Switching from fullscreen to windowed
- needsRebuild = TRUE;
- }
- else
- {
- // Windowed to windowed -- not sure why we would be called like this. Just change the window size.
- // The bounds changed event handler will do the rest.
- if(mWindow != NULL)
- {
- ::SizeWindow(mWindow, size.mX, size.mY, true);
- }
- }
- }
- stop_glerror();
- if(needsRebuild || mForceRebuild)
- {
- mForceRebuild = FALSE;
- destroyContext();
- result = createContext(0, 0, size.mX, size.mY, 0, fullscreen, disable_vsync);
- if (result)
- {
- if(mWindow != NULL)
- {
- MacShowWindow(mWindow);
- BringToFront(mWindow);
- }
- llverify(gGLManager.initGL());
- //start with arrow cursor
- initCursors();
- setCursor( UI_CURSOR_ARROW );
- }
- }
- stop_glerror();
- return result;
- }
- void LLWindowMacOSX::destroyContext()
- {
- if (!mContext)
- {
- // We don't have a context
- return;
- }
- // Unhook the GL context from any drawable it may have
- if(mContext != NULL)
- {
- LL_DEBUGS("Window") << "destroyContext: unhooking drawable " << LL_ENDL;
- aglSetCurrentContext (NULL);
- aglSetDrawable(mContext, NULL);
- }
- // Make sure the display resolution gets restored
- if(mOldDisplayMode != NULL)
- {
- LL_DEBUGS("Window") << "destroyContext: restoring display resolution " << LL_ENDL;
- CGDisplaySwitchToMode (mDisplay, mOldDisplayMode);
- #if CAPTURE_ALL_DISPLAYS
- // Uncapture all displays (may want to do this for final build)
- CGReleaseAllDisplays ();
- #else
- // Uncapture only the main display (useful for debugging)
- CGDisplayRelease (mDisplay);
- #endif
- // CFRelease(mOldDisplayMode);
- mOldDisplayMode = NULL;
- // Remove the global event handlers the fullscreen case needed
- RemoveEventTypesFromHandler(mGlobalHandlerRef, GetEventTypeCount (GlobalHandlerEventList), GlobalHandlerEventList);
- }
- // Clean up remaining GL state before blowing away window
- gGLManager.shutdownGL();
- // Clean up the pixel format
- if(mPixelFormat != NULL)
- {
- LL_DEBUGS("Window") << "destroyContext: destroying pixel format " << LL_ENDL;
- aglDestroyPixelFormat(mPixelFormat);
- mPixelFormat = NULL;
- }
- // Remove any Carbon Event handlers we installed
- if(mGlobalHandlerRef != NULL)
- {
- LL_DEBUGS("Window") << "destroyContext: removing global event handler" << LL_ENDL;
- RemoveEventHandler(mGlobalHandlerRef);
- mGlobalHandlerRef = NULL;
- }
- if(mWindowHandlerRef != NULL)
- {
- LL_DEBUGS("Window") << "destroyContext: removing window event handler" << LL_ENDL;
- RemoveEventHandler(mWindowHandlerRef);
- mWindowHandlerRef = NULL;
- }
- // Cleanup any TSM document we created.
- if(mTSMDocument != NULL)
- {
- LL_DEBUGS("Window") << "destroyContext: deleting TSM document" << LL_ENDL;
- DeactivateTSMDocument(mTSMDocument);
- DeleteTSMDocument(mTSMDocument);
- mTSMDocument = NULL;
- }
- // Close the window
- if(mWindow != NULL)
- {
- LL_DEBUGS("Window") << "destroyContext: disposing window" << LL_ENDL;
- DisposeWindow(mWindow);
- mWindow = NULL;
- }
- // Clean up the GL context
- if(mContext != NULL)
- {
- LL_DEBUGS("Window") << "destroyContext: destroying GL context" << LL_ENDL;
- aglDestroyContext(mContext);
- mContext = NULL;
- }
- }
- LLWindowMacOSX::~LLWindowMacOSX()
- {
- destroyContext();
- if(mSupportedResolutions != NULL)
- {
- delete []mSupportedResolutions;
- }
- gWindowImplementation = NULL;
- }
- void LLWindowMacOSX::show()
- {
- if(IsWindowCollapsed(mWindow))
- CollapseWindow(mWindow, false);
- MacShowWindow(mWindow);
- BringToFront(mWindow);
- }
- void LLWindowMacOSX::hide()
- {
- setMouseClipping(FALSE);
- HideWindow(mWindow);
- }
- //virtual
- void LLWindowMacOSX::minimize()
- {
- setMouseClipping(FALSE);
- showCursor();
- CollapseWindow(mWindow, true);
- }
- //virtual
- void LLWindowMacOSX::restore()
- {
- show();
- }
- // close() destroys all OS-specific code associated with a window.
- // Usually called from LLWindowManager::destroyWindow()
- void LLWindowMacOSX::close()
- {
- // Is window is already closed?
- // if (!mWindow)
- // {
- // return;
- // }
- // Make sure cursor is visible and we haven't mangled the clipping state.
- setMouseClipping(FALSE);
- showCursor();
- destroyContext();
- }
- BOOL LLWindowMacOSX::isValid()
- {
- if(mFullscreen)
- {
- return(TRUE);
- }
- return (mWindow != NULL);
- }
- BOOL LLWindowMacOSX::getVisible()
- {
- BOOL result = FALSE;
- if(mFullscreen)
- {
- result = TRUE;
- }if (mWindow)
- {
- if(MacIsWindowVisible(mWindow))
- result = TRUE;
- }
- return(result);
- }
- BOOL LLWindowMacOSX::getMinimized()
- {
- BOOL result = FALSE;
-
- // Since the set of states where we want to act "minimized" is non-trivial, it's easier to
- // track things locally than to try and retrieve the state from the window manager.
- result = mMinimized;
- return(result);
- }
- BOOL LLWindowMacOSX::getMaximized()
- {
- BOOL result = FALSE;
- if (mWindow)
- {
- // TODO
- }
- return(result);
- }
- BOOL LLWindowMacOSX::maximize()
- {
- // TODO
- return FALSE;
- }
- BOOL LLWindowMacOSX::getFullscreen()
- {
- return mFullscreen;
- }
- void LLWindowMacOSX::gatherInput()
- {
- // stop bouncing icon after fixed period of time
- if (mBounceTimer.getStarted() && mBounceTimer.getElapsedTimeF32() > mBounceTime)
- {
- stopDockTileBounce();
- }
- // Use the old-school version so we get AppleEvent handler dispatch and menuselect handling.
- // Anything that has an event handler will get processed inside WaitNextEvent, so we only need to handle
- // the odd stuff here.
- EventRecord evt;
- while(WaitNextEvent(everyEvent, &evt, 0, NULL))
- {
- // printf("WaitNextEvent returned true, event is %d.n", evt.what);
- switch(evt.what)
- {
- case mouseDown:
- {
- short part;
- WindowRef window;
- long selectResult;
- part = FindWindow(evt.where, &window);
- switch ( part )
- {
- case inMenuBar:
- selectResult = MenuSelect(evt.where);
- HiliteMenu(0);
- break;
- }
- }
- break;
- case kHighLevelEvent:
- AEProcessAppleEvent (&evt);
- break;
- case updateEvt:
- // We shouldn't be getting these regularly (since our window will be buffered), but we need to handle them correctly...
- BeginUpdate((WindowRef)evt.message);
- EndUpdate((WindowRef)evt.message);
- break;
- }
- }
- }
- BOOL LLWindowMacOSX::getPosition(LLCoordScreen *position)
- {
- Rect window_rect;
- OSStatus err = -1;
- if(mFullscreen)
- {
- position->mX = 0;
- position->mY = 0;
- err = noErr;
- }
- else if(mWindow)
- {
- err = GetWindowBounds(mWindow, kWindowContentRgn, &window_rect);
- position->mX = window_rect.left;
- position->mY = window_rect.top;
- }
- else
- {
- llerrs << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << llendl;
- }
- return (err == noErr);
- }
- BOOL LLWindowMacOSX::getSize(LLCoordScreen *size)
- {
- Rect window_rect;
- OSStatus err = -1;
- if(mFullscreen)
- {
- size->mX = mFullscreenWidth;
- size->mY = mFullscreenHeight;
- err = noErr;
- }
- else if(mWindow)
- {
- err = GetWindowBounds(mWindow, kWindowContentRgn, &window_rect);
- size->mX = window_rect.right - window_rect.left;
- size->mY = window_rect.bottom - window_rect.top;
- }
- else
- {
- llerrs << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << llendl;
- }
- return (err == noErr);
- }
- BOOL LLWindowMacOSX::getSize(LLCoordWindow *size)
- {
- Rect window_rect;
- OSStatus err = -1;
- if(mFullscreen)
- {
- size->mX = mFullscreenWidth;
- size->mY = mFullscreenHeight;
- err = noErr;
- }
- else if(mWindow)
- {
- err = GetWindowBounds(mWindow, kWindowContentRgn, &window_rect);
- size->mX = window_rect.right - window_rect.left;
- size->mY = window_rect.bottom - window_rect.top;
- }
- else
- {
- llerrs << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << llendl;
- }
- return (err == noErr);
- }
- BOOL LLWindowMacOSX::setPosition(const LLCoordScreen position)
- {
- if(mWindow)
- {
- MacMoveWindow(mWindow, position.mX, position.mY, false);
- }
- return TRUE;
- }
- BOOL LLWindowMacOSX::setSize(const LLCoordScreen size)
- {
- if(mWindow)
- {
- SizeWindow(mWindow, size.mX, size.mY, true);
- }
- return TRUE;
- }
- void LLWindowMacOSX::swapBuffers()
- {
- aglSwapBuffers(mContext);
- }
- F32 LLWindowMacOSX::getGamma()
- {
- F32 result = 1.8; // Default to something sane
- CGGammaValue redMin;
- CGGammaValue redMax;
- CGGammaValue redGamma;
- CGGammaValue greenMin;
- CGGammaValue greenMax;
- CGGammaValue greenGamma;
- CGGammaValue blueMin;
- CGGammaValue blueMax;
- CGGammaValue blueGamma;
- if(CGGetDisplayTransferByFormula(
- mDisplay,
- &redMin,
- &redMax,
- &redGamma,
- &greenMin,
- &greenMax,
- &greenGamma,
- &blueMin,
- &blueMax,
- &blueGamma) == noErr)
- {
- // So many choices...
- // Let's just return the green channel gamma for now.
- result = greenGamma;
- }
- return result;
- }
- U32 LLWindowMacOSX::getFSAASamples()
- {
- return mFSAASamples;
- }
- void LLWindowMacOSX::setFSAASamples(const U32 samples)
- {
- mFSAASamples = samples;
- mForceRebuild = TRUE;
- }
- BOOL LLWindowMacOSX::restoreGamma()
- {
- CGDisplayRestoreColorSyncSettings();
- return true;
- }
- BOOL LLWindowMacOSX::setGamma(const F32 gamma)
- {
- CGGammaValue redMin;
- CGGammaValue redMax;
- CGGammaValue redGamma;
- CGGammaValue greenMin;
- CGGammaValue greenMax;
- CGGammaValue greenGamma;
- CGGammaValue blueMin;
- CGGammaValue blueMax;
- CGGammaValue blueGamma;
- // MBW -- XXX -- Should we allow this in windowed mode?
- if(CGGetDisplayTransferByFormula(
- mDisplay,
- &redMin,
- &redMax,
- &redGamma,
- &greenMin,
- &greenMax,
- &greenGamma,
- &blueMin,
- &blueMax,
- &blueGamma) != noErr)
- {
- return false;
- }
- if(CGSetDisplayTransferByFormula(
- mDisplay,
- redMin,
- redMax,
- gamma,
- greenMin,
- greenMax,
- gamma,
- blueMin,
- blueMax,
- gamma) != noErr)
- {
- return false;
- }
- return true;
- }
- BOOL LLWindowMacOSX::isCursorHidden()
- {
- return mCursorHidden;
- }
- // Constrains the mouse to the window.
- void LLWindowMacOSX::setMouseClipping( BOOL b )
- {
- // Just stash the requested state. We'll simulate this when the cursor is hidden by decoupling.
- mIsMouseClipping = b;
- if(b)
- {
- // llinfos << "setMouseClipping(TRUE)" << llendl;
- }
- else
- {
- // llinfos << "setMouseClipping(FALSE)" << llendl;
- }
- adjustCursorDecouple();
- }
- BOOL LLWindowMacOSX::setCursorPosition(const LLCoordWindow position)
- {
- BOOL result = FALSE;
- LLCoordScreen screen_pos;
- if (!convertCoords(position, &screen_pos))
- {
- return FALSE;
- }
- CGPoint newPosition;
- // llinfos << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << llendl;
- newPosition.x = screen_pos.mX;
- newPosition.y = screen_pos.mY;
- CGSetLocalEventsSuppressionInterval(0.0);
- if(CGWarpMouseCursorPosition(newPosition) == noErr)
- {
- result = TRUE;
- }
- // Under certain circumstances, this will trigger us to decouple the cursor.
- adjustCursorDecouple(true);
- // trigger mouse move callback
- LLCoordGL gl_pos;
- convertCoords(position, &gl_pos);
- mCallbacks->handleMouseMove(this, gl_pos, (MASK)0);
- return result;
- }
- static void fixOrigin(void)
- {
- GrafPtr port;
- Rect portrect;
- ::GetPort(&port);
- ::GetPortBounds(port, &portrect);
- if((portrect.left != 0) || (portrect.top != 0))
- {
- // Mozilla sometimes changes our port origin.
- ::SetOrigin(0,0);
- }
- }
- BOOL LLWindowMacOSX::getCursorPosition(LLCoordWindow *position)
- {
- Point cursor_point;
- LLCoordScreen screen_pos;
- GrafPtr save;
-
- if(mWindow == NULL)
- return FALSE;
-
- ::GetPort(&save);
- ::SetPort(GetWindowPort(mWindow));
- fixOrigin();
- // gets the mouse location in local coordinates
- ::GetMouse(&cursor_point);
-
- // lldebugs << "getCursorPosition(): cursor is at " << cursor_point.h << ", " << cursor_point.v << " port origin: " << portrect.left << ", " << portrect.top << llendl;
- ::SetPort(save);
- if(mCursorDecoupled)
- {
- // CGMouseDelta x, y;
- // If the cursor's decoupled, we need to read the latest movement delta as well.
- // CGGetLastMouseDelta( &x, &y );
- // cursor_point.h += x;
- // cursor_point.v += y;
- // CGGetLastMouseDelta may behave strangely when the cursor's first captured.
- // Stash in the event handler instead.
- cursor_point.h += mCursorLastEventDeltaX;
- cursor_point.v += mCursorLastEventDeltaY;
- }
- position->mX = cursor_point.h;
- position->mY = cursor_point.v;
- return TRUE;
- }
- void LLWindowMacOSX::adjustCursorDecouple(bool warpingMouse)
- {
- if(mIsMouseClipping && mCursorHidden)
- {
- if(warpingMouse)
- {
- // The cursor should be decoupled. Make sure it is.
- if(!mCursorDecoupled)
- {
- // llinfos << "adjustCursorDecouple: decoupling cursor" << llendl;
- CGAssociateMouseAndMouseCursorPosition(false);
- mCursorDecoupled = true;
- FlushSpecificEventsFromQueue(GetCurrentEventQueue(), mMoveEventCampartorUPP, NULL);
- mCursorIgnoreNextDelta = TRUE;
- }
- }
- }
- else
- {
- // The cursor should not be decoupled. Make sure it isn't.
- if(mCursorDecoupled)
- {
- // llinfos << "adjustCursorDecouple: recoupling cursor" << llendl;
- CGAssociateMouseAndMouseCursorPosition(true);
- mCursorDecoupled = false;
- }
- }
- }
- F32 LLWindowMacOSX::getNativeAspectRatio()
- {
- if (mFullscreen)
- {
- return (F32)mFullscreenWidth / (F32)mFullscreenHeight;
- }
- else
- {
- // The constructor for this class grabs the aspect ratio of the monitor before doing any resolution
- // switching, and stashes it in mOriginalAspectRatio. Here, we just return it.
-
- if (mOverrideAspectRatio > 0.f)
- {
- return mOverrideAspectRatio;
- }
- return mOriginalAspectRatio;
- }
- }
- F32 LLWindowMacOSX::getPixelAspectRatio()
- {
- //OS X always enforces a 1:1 pixel aspect ratio, regardless of video mode
- return 1.f;
- }
- //static SInt32 oldWindowLevel;
- // MBW -- XXX -- There's got to be a better way than this. Find it, please...
- void LLWindowMacOSX::beforeDialog()
- {
- if(mFullscreen)
- {
- #if CAPTURE_ALL_DISPLAYS
- // Uncapture all displays (may want to do this for final build)
- CGReleaseAllDisplays ();
- #else
- // Uncapture only the main display (useful for debugging)
- CGDisplayRelease (mDisplay);
- #endif
- // kDocumentWindowClass
- // kMovableModalWindowClass
- // kAllWindowClasses
- // GLint order = 0;
- // aglSetInteger(mContext, AGL_ORDER_CONTEXT_TO_FRONT, &order);
- aglSetDrawable(mContext, NULL);
- // GetWindowGroupLevel(GetWindowGroupOfClass(kAllWindowClasses), &oldWindowLevel);
- // SetWindowGroupLevel(GetWindowGroupOfClass(kAllWindowClasses), CGShieldingWindowLevel());
- mHandsOffEvents = TRUE;
- }
- }
- void LLWindowMacOSX::afterDialog()
- {
- if(mFullscreen)
- {
- mHandsOffEvents = FALSE;
- // SetWindowGroupLevel(GetWindowGroupOfClass(kAllWindowClasses), oldWindowLevel);
- aglSetFullScreen(mContext, 0, 0, 0, 0);
- // GLint order = 1;
- // aglSetInteger(mContext, AGL_ORDER_CONTEXT_TO_FRONT, &order);
- #if CAPTURE_ALL_DISPLAYS
- // Capture all displays (may want to do this for final build)
- CGCaptureAllDisplays ();
- #else
- // Capture only the main display (useful for debugging)
- CGDisplayCapture (mDisplay);
- #endif
- }
- }
- void LLWindowMacOSX::flashIcon(F32 seconds)
- {
- // Don't do this if we're already started, since this would try to install the NMRec twice.
- if(!mBounceTimer.getStarted())
- {
- OSErr err;
- mBounceTime = seconds;
- memset(&mBounceRec, 0, sizeof(mBounceRec));
- mBounceRec.qType = nmType;
- mBounceRec.nmMark = 1;
- err = NMInstall(&mBounceRec);
- if(err == noErr)
- {
- mBounceTimer.start();
- }
- else
- {
- // This is very not-fatal (only problem is the icon will not bounce), but we'd like to find out about it somehow...
- llinfos << "NMInstall failed with error code " << err << llendl;
- }
- }
- }
- BOOL LLWindowMacOSX::isClipboardTextAvailable()
- {
- OSStatus err;
- ScrapRef scrap;
- ScrapFlavorFlags flags;
- BOOL result = false;
- err = GetCurrentScrap(&scrap);
- if(err == noErr)
- {
- err = GetScrapFlavorFlags(scrap, kScrapFlavorTypeUnicode, &flags);
- }
- if(err == noErr)
- result = true;
- return result;
- }
- BOOL LLWindowMacOSX::pasteTextFromClipboard(LLWString &dst)
- {
- OSStatus err;
- ScrapRef scrap;
- Size len;
- BOOL result = false;
- err = GetCurrentScrap(&scrap);
- if(err == noErr)
- {
- err = GetScrapFlavorSize(scrap, kScrapFlavorTypeUnicode, &len);
- }
- if((err == noErr) && (len > 0))
- {
- int u16len = len / sizeof(U16);
- U16 *temp = new U16[u16len + 1];
- if (temp)
- {
- memset(temp, 0, (u16len + 1) * sizeof(temp[0]));
- err = GetScrapFlavorData(scrap, kScrapFlavorTypeUnicode, &len, temp);
- if (err == noErr)
- {
- // convert rn to n and r to n in the incoming text.
- U16 *s, *d;
- for(s = d = temp; s[0] != ' '; s++, d++)
- {
- if(s[0] == 'r')
- {
- if(s[1] == 'n')
- {
- // CRLF, a.k.a. DOS newline. Collapse to a single 'n'.
- s++;
- }
- d[0] = 'n';
- }
- else
- {
- d[0] = s[0];
- }
- }
- d[0] = ' ';
- dst = utf16str_to_wstring(temp);
- result = true;
- }
- delete[] temp;
- }
- }
- return result;
- }
- BOOL LLWindowMacOSX::copyTextToClipboard(const LLWString &s)
- {
- OSStatus err;
- ScrapRef scrap;
- //Size len;
- //char *temp;
- BOOL result = false;
- if (!s.empty())
- {
- err = GetCurrentScrap(&scrap);
- if (err == noErr)
- err = ClearScrap(&scrap);
- if (err == noErr)
- {
- llutf16string utf16str = wstring_to_utf16str(s);
- size_t u16len = utf16str.length() * sizeof(U16);
- err = PutScrapFlavor(scrap, kScrapFlavorTypeUnicode, kScrapFlavorMaskNone, u16len, utf16str.data());
- if (err == noErr)
- result = true;
- }
- }
- return result;
- }
- // protected
- BOOL LLWindowMacOSX::resetDisplayResolution()
- {
- // This is only called from elsewhere in this class, and it's not used by the Mac implementation.
- return true;
- }
- LLWindow::LLWindowResolution* LLWindowMacOSX::getSupportedResolutions(S32 &num_resolutions)
- {
- if (!mSupportedResolutions)
- {
- CFArrayRef modes = CGDisplayAvailableModes(mDisplay);
- if(modes != NULL)
- {
- CFIndex index, cnt;
- mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
- mNumSupportedResolutions = 0;
- // Examine each mode
- cnt = CFArrayGetCount( modes );
- for ( index = 0; (index < cnt) && (mNumSupportedResolutions < MAX_NUM_RESOLUTIONS); index++ )
- {
- // Pull the mode dictionary out of the CFArray
- CFDictionaryRef mode = (CFDictionaryRef)CFArrayGetValueAtIndex( modes, index );
- long width = getDictLong(mode, kCGDisplayWidth);
- long height = getDictLong(mode, kCGDisplayHeight);
- long bits = getDictLong(mode, kCGDisplayBitsPerPixel);
- if(bits == BITS_PER_PIXEL && width >= 800 && height >= 600)
- {
- BOOL resolution_exists = FALSE;
- for(S32 i = 0; i < mNumSupportedResolutions; i++)
- {
- if (mSupportedResolutions[i].mWidth == width &&
- mSupportedResolutions[i].mHeight == height)
- {
- resolution_exists = TRUE;
- }
- }
- if (!resolution_exists)
- {
- mSupportedResolutions[mNumSupportedResolutions].mWidth = width;
- mSupportedResolutions[mNumSupportedResolutions].mHeight = height;
- mNumSupportedResolutions++;
- }
- }
- }
- }
- }
- num_resolutions = mNumSupportedResolutions;
- return mSupportedResolutions;
- }
- BOOL LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordWindow *to)
- {
- S32 client_height;
- Rect client_rect;
- if(mFullscreen)
- {
- // In the fullscreen case, the "window" is the entire screen.
- client_rect.left = 0;
- client_rect.top = 0;
- client_rect.right = mFullscreenWidth;
- client_rect.bottom = mFullscreenHeight;
- }
- else if (!mWindow ||
- (GetWindowBounds(mWindow, kWindowContentRgn, &client_rect) != noErr) ||
- NULL == to)
- {
- return FALSE;
- }
- to->mX = from.mX;
- client_height = client_rect.bottom - client_rect.top;
- to->mY = client_height - from.mY - 1;
- return TRUE;
- }
- BOOL LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordGL* to)
- {
- S32 client_height;
- Rect client_rect;
- if(mFullscreen)
- {
- // In the fullscreen case, the "window" is the entire screen.
- client_rect.left = 0;
- client_rect.top = 0;
- client_rect.right = mFullscreenWidth;
- client_rect.bottom = mFullscreenHeight;
- }
- else if (!mWindow ||
- (GetWindowBounds(mWindow, kWindowContentRgn, &client_rect) != noErr) ||
- NULL == to)
- {
- return FALSE;
- }
- to->mX = from.mX;
- client_height = client_rect.bottom - client_rect.top;
- to->mY = client_height - from.mY - 1;
- return TRUE;
- }
- BOOL LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordWindow* to)
- {
- if(mFullscreen)
- {
- // In the fullscreen case, window and screen coordinates are the same.
- to->mX = from.mX;
- to->mY = from.mY;
- return TRUE;
- }
- else if(mWindow)
- {
- GrafPtr save;
- Point mouse_point;
- mouse_point.h = from.mX;
- mouse_point.v = from.mY;
- ::GetPort(&save);
- ::SetPort(GetWindowPort(mWindow));
- fixOrigin();
- ::GlobalToLocal(&mouse_point);
- to->mX = mouse_point.h;
- to->mY = mouse_point.v;
- ::SetPort(save);
- return TRUE;
- }
- return FALSE;
- }
- BOOL LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordScreen *to)
- {
- if(mFullscreen)
- {
- // In the fullscreen case, window and screen coordinates are the same.
- to->mX = from.mX;
- to->mY = from.mY;
- return TRUE;
- }
- else if(mWindow)
- {
- GrafPtr save;
- Point mouse_point;
- mouse_point.h = from.mX;
- mouse_point.v = from.mY;
- ::GetPort(&save);
- ::SetPort(GetWindowPort(mWindow));
- fixOrigin();
- LocalToGlobal(&mouse_point);
- to->mX = mouse_point.h;
- to->mY = mouse_point.v;
- ::SetPort(save);
- return TRUE;
- }
- return FALSE;
- }
- BOOL LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordGL *to)
- {
- LLCoordWindow window_coord;
- return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
- }
- BOOL LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordScreen *to)
- {
- LLCoordWindow window_coord;
- return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
- }
- void LLWindowMacOSX::setupFailure(const std::string& text, const std::string& caption, U32 type)
- {
- destroyContext();
- OSMessageBox(text, caption, type);
- }
- pascal Boolean LLWindowMacOSX::staticMoveEventComparator( EventRef event, void* data)
- {
- UInt32 evtClass = GetEventClass (event);
- UInt32 evtKind = GetEventKind (event);
- if ((evtClass == kEventClassMouse) && ((evtKind == kEventMouseDragged) || (evtKind == kEventMouseMoved)))
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- pascal OSStatus LLWindowMacOSX::staticEventHandler(EventHandlerCallRef myHandler, EventRef event, void* userData)
- {
- LLWindowMacOSX *self = (LLWindowMacOSX*)userData;
- return(self->eventHandler(myHandler, event));
- }
- OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef event)
- {
- OSStatus result = eventNotHandledErr;
- UInt32 evtClass = GetEventClass (event);
- UInt32 evtKind = GetEventKind (event);
- // Always handle command events, even in hands-off mode.
- if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess))
- {
- HICommand command;
- GetEventParameter (event, kEventParamDirectObject, typeHICommand, NULL, sizeof(command), NULL, &command);
- switch(command.commandID)
- {
- case kHICommandQuit:
- if(mCallbacks->handleCloseRequest(this))
- {
- // Get the app to initiate cleanup.
- mCallbacks->handleQuit(this);
- // The app is responsible for calling destroyWindow when done with GL
- }
- result = noErr;
- break;
- default:
- // MBW -- XXX -- Should we handle other events here?
- break;
- }
- }
- if(mHandsOffEvents)
- {
- return(result);
- }
- switch (evtClass)
- {
- case kEventClassTextInput:
- {
- switch (evtKind)
- {
- case kEventTextInputUpdateActiveInputArea:
- {
- EventParamType param_type;
- long fix_len;
- UInt32 text_len;
- if (mPreeditor
- && (result = GetEventParameter(event, kEventParamTextInputSendFixLen,
- typeLongInteger, ¶m_type, sizeof(fix_len), NULL, &fix_len)) == noErr
- && typeLongInteger == param_type
- && (result = GetEventParameter(event, kEventParamTextInputSendText,
- typeUnicodeText, ¶m_type, 0, &text_len, NULL)) == noErr
- && typeUnicodeText == param_type)
- {
- // Handle an optional (but essential to facilitate TSMDA) ReplaceRange param.
- CFRange range;
- if (GetEventParameter(event, kEventParamTextInputSendReplaceRange,
- typeCFRange, ¶m_type, sizeof(range), NULL, &range) == noErr
- && typeCFRange == param_type)
- {
- // Although the spec. is unclear, replace range should
- // not present when there is an active preedit. We just
- // ignore the case. markAsPreedit will detect the case and warn it.
- const LLWString & text = mPreeditor->getPreeditString();
- const S32 location = wstring_wstring_length_from_utf16_length(text, 0, range.location);
- const S32 length = wstring_wstring_length_from_utf16_length(text, location, range.length);
- mPreeditor->markAsPreedit(location, length);
- }
- mPreeditor->resetPreedit();
-
- // Receive the text from input method.
- U16 *const text = new U16[text_len / sizeof(U16)];
- GetEventParameter(event, kEventParamTextInputSendText, typeUnicodeText, NULL, text_len, NULL, text);
- if (fix_len < 0)
- {
- // Do we still need this? Seems obsolete...
- fix_len = text_len;
- }
- const LLWString fix_string
- = utf16str_to_wstring(llutf16string(text, fix_len / sizeof(U16)));
- const LLWString preedit_string
- = utf16str_to_wstring(llutf16string(text + fix_len / sizeof(U16), (text_len - fix_len) / sizeof(U16)));
- delete[] text;
- // Handle fixed (comitted) string.
- if (fix_string.length() > 0)
- {
- for (LLWString::const_iterator i = fix_string.begin(); i != fix_string.end(); i++)
- {
- mPreeditor->handleUnicodeCharHere(*i);
- }
- }
- // Receive the segment info and caret position.
- LLPreeditor::segment_lengths_t preedit_segment_lengths;
- LLPreeditor::standouts_t preedit_standouts;
- S32 caret_position = preedit_string.length();
- UInt32 text_range_array_size;
- if (GetEventParameter(event, kEventParamTextInputSendHiliteRng, typeTextRangeArray,
- ¶m_type, 0, &text_range_array_size, NULL) == noErr
- && typeTextRangeArray == param_type
- && text_range_array_size > sizeof(TextRangeArray))
- {
- // TextRangeArray is a variable-length struct.
- TextRangeArray * const text_range_array = (TextRangeArray *) new char[text_range_array_size];
- GetEventParameter(event, kEventParamTextInputSendHiliteRng, typeTextRangeArray,
- NULL, text_range_array_size, NULL, text_range_array);
- // WARNING: We assume ranges are in ascending order,
- // although the condition is undocumented. It seems
- // OK to assume this. I also assumed
- // the ranges are contiguous in previous versions, but I
- // have heard a rumore that older versions os ATOK may
- // return ranges with some _gap_. I don't know whether
- // it is true, but I'm preparing my code for the case.
- const S32 ranges = text_range_array->fNumOfRanges;
- preedit_segment_lengths.reserve(ranges);
- preedit_standouts.reserve(ranges);
- S32 last_bytes = 0;
- S32 last_utf32 = 0;
- for (S32 i = 0; i < ranges; i++)
- {
- const TextRange &range = text_range_array->fRange[i];
- if (range.fStart > last_bytes)
- {
- const S32 length_utf16 = (range.fStart - last_bytes) / sizeof(U16);
- const S32 length_utf32 = wstring_wstring_length_from_utf16_length(preedit_string, last_utf32, length_utf16);
- preedit_segment_lengths.push_back(length_utf32);
- preedit_standouts.push_back(FALSE);
- last_utf32 += length_utf32;
- }
- if (range.fEnd > range.fStart)
- {
- const S32 length_utf16 = (range.fEnd - range.fStart) / sizeof(U16);
- const S32 length_utf32 = wstring_wstring_length_from_utf16_length(preedit_string, last_utf32, length_utf16);
- preedit_segment_lengths.push_back(length_utf32);
- preedit_standouts.push_back(
- kTSMHiliteSelectedRawText == range.fHiliteStyle
- || kTSMHiliteSelectedConvertedText == range.fHiliteStyle
- || kTSMHiliteSelectedText == range.fHiliteStyle);
- last_utf32 += length_utf32;
- }
- if (kTSMHiliteCaretPosition == range.fHiliteStyle)
- {
- caret_position = last_utf32;
- }
- last_bytes = range.fEnd;
- }
- if (preedit_string.length() > last_utf32)
- {
- preedit_segment_lengths.push_back(preedit_string.length() - last_utf32);
- preedit_standouts.push_back(FALSE);
- }
- delete[] (char *) text_range_array;
- }
- // Handle preedit string.
- if (preedit_string.length() == 0)
- {
- preedit_segment_lengths.clear();
- preedit_standouts.clear();
- }
- else if (preedit_segment_lengths.size() == 0)
- {
- preedit_segment_lengths.push_back(preedit_string.length());
- preedit_standouts.push_back(FALSE);
- }
- mPreeditor->updatePreedit(preedit_string, preedit_segment_lengths, preedit_standouts, caret_position);
- result = noErr;
- }
- }
- break;
-
- case kEventTextInputUnicodeForKeyEvent:
- {
- UInt32 modifiers = 0;
- // First, process the raw event.
- {
- EventRef rawEvent = NULL;
-
- // Get the original event and extract the modifier keys, so we can ignore command-key events.
- if (GetEventParameter(event, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(rawEvent), NULL, &rawEvent) == noErr)
- {
- // Grab the modifiers for later use in this function...
- GetEventParameter (rawEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
- // and call this function recursively to handle the raw key event.
- eventHandler (myHandler, rawEvent);
-
- // save the raw event until we're done processing the unicode input as well.
- mRawKeyEvent = rawEvent;
- }
- }
- OSStatus err = noErr;
- EventParamType actualType = typeUnicodeText;
- UInt32 actualSize = 0;
- size_t actualCount = 0;
- U16 *buffer = NULL;
- // Get the size of the unicode data
- err = GetEventParameter (event, kEventParamTextInputSendText, typeUnicodeText, &actualType, 0, &actualSize, NULL);
- if(err == noErr)
- {
- // allocate a buffer and get the actual data.
- actualCount = actualSize / sizeof(U16);
- buffer = new U16[actualCount];
- err = GetEventParameter (event, kEventParamTextInputSendText, typeUnicodeText, &actualType, actualSize, &actualSize, buffer);
- }
- if(err == noErr)
- {
- if(modifiers & (cmdKey | controlKey))
- {
- // This was a menu key equivalent. Ignore it.
- }
- else
- {
- MASK mask = LLWindowMacOSX::modifiersToMask(modifiers);
-
- llassert( actualType == typeUnicodeText );
- // The result is a UTF16 buffer. Pass the characters in turn to handleUnicodeChar.
- // Convert to UTF32 and go character-by-character.
- llutf16string utf16(buffer, actualCount);
- LLWString utf32 = utf16str_to_wstring(utf16);
- LLWString::iterator iter;
- for(iter = utf32.begin(); iter != utf32.end(); iter++)
- {
- mCallbacks->handleUnicodeChar(*iter, mask);
- }
- }
- }
- if(buffer != NULL)
- {
- delete[] buffer;
- }
- mRawKeyEvent = NULL;
- result = err;
- }
- break;
-
- case kEventTextInputOffsetToPos:
- {
- EventParamType param_type;
- long offset;
- if (mPreeditor
- && GetEventParameter(event, kEventParamTextInputSendTextOffset, typeLongInteger,
- ¶m_type, sizeof(offset), NULL, &offset) == noErr
- && typeLongInteger == param_type)
- {
- S32 preedit, preedit_length;
- mPreeditor->getPreeditRange(&preedit, &preedit_length);
- const LLWString & text = mPreeditor->getPreeditString();
-
- LLCoordGL caret_coord;
- LLRect preedit_bounds;
- if (0 <= offset
- && mPreeditor->getPreeditLocation(wstring_wstring_length_from_utf16_length(text, preedit, offset / sizeof(U16)),
- &caret_coord, &preedit_bounds, NULL))
- {
- LLCoordGL caret_base_coord(caret_coord.mX, preedit_bounds.mBottom);
- LLCoordScreen caret_base_coord_screen;
- convertCoords(caret_base_coord, &caret_base_coord_screen);
- Point qd_point;
- qd_point.h = caret_base_coord_screen.mX;
- qd_point.v = caret_base_coord_screen.mY;
- SetEventParameter(event, kEventParamTextInputReplyPoint, typeQDPoint, sizeof(qd_point), &qd_point);
-
- short line_height = (short) preedit_bounds.getHeight();
- SetEventParameter(event, kEventParamTextInputReplyLineHeight, typeShortInteger, sizeof(line_height), &line_height);
-
- result = noErr;
- }
- else
- {
- result = errOffsetInvalid;
- }
- }
- }
- break;
- case kEventTextInputGetSelectedText:
- {
- if (mPreeditor)
- {
- S32 selection, selection_length;
- mPreeditor->getSelectionRange(&selection, &selection_length);
- if (selection_length)
- {
- const LLWString text = mPreeditor->getPreeditString().substr(selection, selection_length);
- const llutf16string text_utf16 = wstring_to_utf16str(text);
- result = SetEventParameter(event, kEventParamTextInputReplyText, typeUnicodeText,
- text_utf16.length() * sizeof(U16), text_utf16.c_str());
- }
- }
- }
- break;
- }
- }
- break;
- case kEventClassKeyboard:
- {
- UInt32 keyCode = 0;
- char charCode = 0;
- UInt32 modifiers = 0;
- // Some of these may fail for some event types. That's fine.
- GetEventParameter (event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode);
- GetEventParameter (event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
- // save the raw event so getNativeKeyData can use it.
- mRawKeyEvent = event;
- // printf("key event, key code = 0x%08x, char code = 0x%02x (%c), modifiers = 0x%08xn", keyCode, charCode, (char)charCode, modifiers);
- // fflush(stdout);
- switch (evtKind)
- {
- case kEventRawKeyDown:
- case kEventRawKeyRepeat:
- if (gDebugWindowProc)
- {
- printf("key down, key code = 0x%08x, char code = 0x%02x (%c), modifiers = 0x%08xn",
- (unsigned int)keyCode, charCode, (char)charCode, (unsigned int)modifiers);
- fflush(stdout);
- }
- gKeyboard->handleKeyDown(keyCode, modifiers);
- result = eventNotHandledErr;
- break;
- case kEventRawKeyUp:
- if (gDebugWindowProc)
- {
- printf("key up, key code = 0x%08x, char code = 0x%02x (%c), modifiers = 0x%08xn",
- (unsigned int)keyCode, charCode, (char)charCode, (unsigned int)modifiers);
- fflush(stdout);
- }
- gKeyboard->handleKeyUp(keyCode, modifiers);
- result = eventNotHandledErr;
- break;
- case kEventRawKeyModifiersChanged:
- // The keyboard input system wants key up/down events for modifier keys.
- // Mac OS doesn't supply these directly, but can supply events when the collective modifier state changes.
- // Use these events to generate up/down events for the modifiers.
- if((modifiers & shiftKey) && !(mLastModifiers & shiftKey))
- {
- if (gDebugWindowProc) printf("Shift key down eventn");
- gKeyboard->handleKeyDown(0x38, (modifiers & 0x00FFFFFF) | ((0x38 << 24) & 0xFF000000));
- }
- else if(!(modifiers & shiftKey) && (mLastModifiers & shiftKey))
- {
- if (gDebugWindowProc) printf("Shift key up eventn");
- gKeyboard->handleKeyUp(0x38, (modifiers & 0x00FFFFFF) | ((0x38 << 24) & 0xFF000000));
- }
- if((modifiers & alphaLock) && !(mLastModifiers & alphaLock))
- {
- if (gDebugWindowProc) printf("Caps lock down eventn");
- gKeyboard->handleKeyDown(0x39, (modifiers & 0x00FFFFFF) | ((0x39 << 24) & 0xFF000000));
- }
- else if(!(modifiers & alphaLock) && (mLastModifiers & alphaLock))
- {
- if (gDebugWindowProc) printf("Caps lock up eventn");
- gKeyboard->handleKeyUp(0x39, (modifiers & 0x00FFFFFF) | ((0x39 << 24) & 0xFF000000));
- }
- if((modifiers & controlKey) && !(mLastModifiers & controlKey))
- {
- if (gDebugWindowProc) printf("Control key down eventn");
- gKeyboard->handleKeyDown(0x3b, (modifiers & 0x00FFFFFF) | ((0x3b << 24) & 0xFF000000));
- }
- else if(!(modifiers & controlKey) && (mLastModifiers & controlKey))
- {
- if (gDebugWindowProc) printf("Control key up eventn");
- gKeyboard->handleKeyUp(0x3b, (modifiers & 0x00FFFFFF) | ((0x3b << 24) & 0xFF000000));
- }
- if((modifiers & optionKey) && !(mLastModifiers & optionKey))
- {
- if (gDebugWindowProc) printf("Option key down eventn");
- gKeyboard->handleKeyDown(0x3a, (modifiers & 0x00FFFFFF) | ((0x3a << 24) & 0xFF000000));
- }
- else if(!(modifiers & optionKey) && (mLastModifiers & optionKey))
- {
- if (gDebugWindowProc) printf("Option key up eventn");
- gKeyboard->handleKeyUp(0x3a, (modifiers & 0x00FFFFFF) | ((0x3a << 24) & 0xFF000000));
- }
- // When the state of the 'Fn' key (the one that changes some of the mappings on a powerbook/macbook keyboard
- // to an embedded keypad) changes, it may subsequently cause a key up event to be lost, which may lead to
- // a movement key getting "stuck" down. This is bad.
- // This is an OS bug -- even the GetKeys() API doesn't tell you the key has been released.
- // This workaround causes all held-down keys to be reset whenever the state of the Fn key changes. This isn't
- // exactly what we want, but it does avoid the case where you get stuck running forward.
- if((modifiers & kEventKeyModifierFnMask) != (mLastModifiers & kEventKeyModifierFnMask))
- {
- if (gDebugWindowProc) printf("Fn key state change eventn");
- gKeyboard->resetKeys();
- }
-
- if (gDebugWindowProc) fflush(stdout);
- mLastModifiers = modifiers;
- result = eventNotHandledErr;
- break;
- }
-
- mRawKeyEvent = NULL;
- }
- break;
- case kEventClassMouse:
- {
- result = CallNextEventHandler(myHandler, event);
- if (eventNotHandledErr == result)
- { // only handle events not already handled (prevents wierd resize interaction)
- EventMouseButton button = kEventMouseButtonPrimary;
- HIPoint location = {0.0f, 0.0f};
- UInt32 modifiers = 0;
- UInt32 clickCount = 1;
- long wheelDelta = 0;
- LLCoordScreen inCoords;
- LLCoordGL outCoords;
- MASK mask = 0;
- GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
- GetEventParameter(event, kEventParamMouseLocation, typeHIPoint, NULL, sizeof(location), NULL, &location);
- GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
- GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(wheelDelta), NULL, &wheelDelta);
- GetEventParameter(event, kEventParamClickCount, typeUInt32, NULL, sizeof(clickCount), NULL, &clickCount);
- inCoords.mX = llround(location.x);
- inCoords.mY = llround(location.y);
- if(modifiers & shiftKey) { mask |= MASK_SHIFT; }
- if(modifiers & controlKey) { mask |= MASK_CONTROL; }
- if(modifiers & optionKey) { mask |= MASK_ALT; }
- if(mCursorDecoupled)
- {
- CGMouseDelta x, y;
- // If the cursor's decoupled, we need to read the latest movement delta as well.
- CGGetLastMouseDelta( &x, &y );
- mCursorLastEventDeltaX = x;
- mCursorLastEventDeltaY = y;
- if(mCursorIgnoreNextDelta)
- {
- mCursorLastEventDeltaX = 0;
- mCursorLastEventDeltaY = 0;
- mCursorIgnoreNextDelta = FALSE;
- }
- }
- else
- {
- mCursorLastEventDeltaX = 0;
- mCursorLastEventDeltaY = 0;
- }
- inCoords.mX += mCursorLastEventDeltaX;
- inCoords.mY += mCursorLastEventDeltaY;
- convertCoords(inCoords, &outCoords);
- // printf("coords in: %d, %d; coords out: %d, %dn", inCoords.mX, inCoords.mY, outCoords.mX, outCoords.mY);
- // fflush(stdout);
- switch (evtKind)
- {
- case kEventMouseDown:
- if (mLanguageTextInputAllowed)
- {
- // We need to interrupt before handling mouse events,
- // so that the fixed string from IM are delivered to
- // the currently focused UI component.
- interruptLanguageTextInput();
- }
- switch(button)
- {
- case kEventMouseButtonPrimary:
- if(modifiers & cmdKey)
- {
- // Simulate a right click
- mSimulatedRightClick = true;
- mCallbacks->handleRightMouseDown(this, outCoords, mask);
- }
- else if(clickCount == 2)
- {
- // Windows double-click events replace the second mousedown event in a double-click.
- mCallbacks->handleDoubleClick(this, outCoords, mask);
- }
- else
- {
- mCallbacks->handleMouseDown(this, outCoords, mask);
- }
- break;
- case kEventMouseButtonSecondary:
- mCallbacks->handleRightMouseDown(this, outCoords, mask);
- break;
-
- case kEventMouseButtonTertiary:
- mCallbacks->handleMiddleMouseDown(this, outCoords, mask);
- break;
- }
- result = noErr;
- break;
- case kEventMouseUp:
- switch(button)
- {
- case kEventMouseButtonPrimary:
- if(mSimulatedRightClick)
- {
- // End of simulated right click
- mSimulatedRightClick = false;
- mCallbacks->handleRightMouseUp(this, outCoords, mask);
- }
- else
- {
- mCallbacks->handleMouseUp(this, outCoords, mask);
- }
- break;
- case kEventMouseButtonSecondary:
- mCallbacks->handleRightMouseUp(this, outCoords, mask);
- break;
- case kEventMouseButtonTertiary:
- mCallbacks->handleMiddleMouseUp(this, outCoords, mask);
- break;
- }
- result = noErr;
- break;
- case kEventMouseWheelMoved:
- {
- static S32 z_delta = 0;
- z_delta += wheelDelta;
- if (z_delta <= -WHEEL_DELTA || WHEEL_DELTA <= z_delta)
- {
- mCallbacks->handleScrollWheel(this, -z_delta / WHEEL_DELTA);
- z_delta = 0;
- }
- }
- result = noErr;
- break;
- case kEventMouseDragged:
- case kEventMouseMoved:
- mCallbacks->handleMouseMove(this, outCoords, mask);
- result = noErr;
- break;
- }
- }
- }
- break;
- case kEventClassWindow:
- switch(evtKind)
- {
- case kEventWindowActivated:
- if (mTSMDocument)
- {
- ActivateTSMDocument(mTSMDocument);
- }
- mCallbacks->handleFocus(this);
- break;
- case kEventWindowDeactivated:
- if (mTSMDocument)
- {
- DeactivateTSMDocument(mTSMDocument);
- }
- mCallbacks->handleFocusLost(this);
- break;
-
- case kEventWindowBoundsChanging:
- {
- // This is where we would constrain move/resize to a particular screen
- const S32 MIN_WIDTH = 320;
- const S32 MIN_HEIGHT = 240;
-
- Rect currentBounds;
- Rect previousBounds;
- GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, ¤tBounds);
- GetEventParameter(event, kEventParamPreviousBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &previousBounds);
- if ((currentBounds.right - currentBounds.left) < MIN_WIDTH)
- {
- currentBounds.right = currentBounds.left + MIN_WIDTH;
- }
- if ((currentBounds.bottom - currentBounds.top) < MIN_HEIGHT)
- {
- currentBounds.bottom = currentBounds.top + MIN_HEIGHT;
- }
-
- SetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, sizeof(Rect), ¤tBounds);
- result = noErr;
- }
- break;
- case kEventWindowBoundsChanged:
- {
- Rect newBounds;
- GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &newBounds);
- aglUpdateContext(mContext);
- mCallbacks->handleResize(this, newBounds.right - newBounds.left, newBounds.bottom - newBounds.top);
- }
- break;
- case kEventWindowClose:
- if(mCallbacks->handleCloseRequest(this))
- {
- // Get the app to initiate cleanup.
- mCallbacks->handleQuit(this);
- // The app is responsible for calling destroyWindow when done with GL
- }
- result = noErr;
- break;
- case kEventWindowHidden:
- // llinfos << "LLWindowMacOSX: Deactivating on hide" << llendl;
- mMinimized = TRUE;
- mCallbacks->handleActivate(this, false);
- // result = noErr;
- break;
- case kEventWindowShown:
- // llinfos << "LLWindowMacOSX: Activating on show" << llendl;
- mMinimized = FALSE;
- mCallbacks->handleActivate(this, true);
- // result = noErr;
- break;
- case kEventWindowCollapsed:
- // llinfos << "LLWindowMacOSX: Deactivating on collapse" << llendl;
- mMinimized = TRUE;
- mCallbacks->handleActivate(this, false);
- // result = noErr;
- break;
- case kEventWindowExpanded:
- // llinfos << "LLWindowMacOSX: Activating on expand" << llendl;
- mMinimized = FALSE;
- mCallbacks->handleActivate(this, true);
- // result = noErr;
- break;
- case kEventWindowGetClickActivation:
- // BringToFront(mWindow);
- // result = noErr;
- break;
- }
- break;
- case kEventClassTSMDocumentAccess:
- if (mPreeditor)
- {
- switch(evtKind)
- {
- case kEventTSMDocumentAccessGetLength:
- {
- // Return the number of UTF-16 units in the text, excluding those for preedit.
- S32 preedit, preedit_length;
- mPreeditor->getPreeditRange(&preedit, &preedit_length);
- const LLWString & text = mPreeditor->getPreeditString();
- const CFIndex length = wstring_utf16_length(text, 0, preedit)
- + wstring_utf16_length(text, preedit + preedit_length, text.length());
- result = SetEventParameter(event, kEventParamTSMDocAccessCharacterCount, typeCFIndex, sizeof(length), &length);
- }
- break;
- case kEventTSMDocumentAccessGetSelectedRange:
- {
- // Return the selected range, excluding preedit.
- // In our preeditor, preedit and selection are exclusive, so,
- // when it has a preedit, there is no selection and the
- // insertion point is on the preedit that corrupses into the
- // beginning of the preedit when the preedit was removed.
- S32 preedit, preedit_length;
- mPreeditor->getPreeditRange(&preedit, &preedit_length);
- const LLWString & text = mPreeditor->getPreeditString();
-
- CFRange range;
- if (preedit_length)
- {
- range.location = wstring_utf16_length(text, 0, preedit);
- range.length = 0;
- }
- else
- {
- S32 selection, selection_length;
- mPreeditor->getSelectionRange(&selection, &selection_length);
- range.location = wstring_utf16_length(text, 0, selection);
- range.length = wstring_utf16_length(text, selection, selection_length);
- }
- result = SetEventParameter(event, kEventParamTSMDocAccessReplyCharacterRange, typeCFRange, sizeof(range), &range);
- }
- break;
- case kEventTSMDocumentAccessGetCharacters:
- {
- UniChar *target_pointer;
- CFRange range;
- EventParamType param_type;
- if ((result = GetEventParameter(event, kEventParamTSMDocAccessSendCharacterRange,
- typeCFRange, ¶m_type, sizeof(range), NULL, &range)) == noErr
- && typeCFRange == param_type
- && (result = GetEventParameter(event, kEventParamTSMDocAccessSendCharactersPtr,
- typePtr, ¶m_type, sizeof(target_pointer), NULL, &target_pointer)) == noErr
- && typePtr == param_type)
- {
- S32 preedit, preedit_length;
- mPreeditor->getPreeditRange(&preedit, &preedit_length);
- const LLWString & text = mPreeditor->getPreeditString();
- // The GetCharacters event of TSMDA has a fundamental flaw;
- // An input method need to decide the starting offset and length
- // *before* it actually see the contents, so it is impossible
- // to guarantee the character-aligned access. The event reply
- // has no way to indicate a condition something like "Request
- // was not fulfilled due to unaligned access. Please retry."
- // Any error sent back to the input method stops use of TSMDA
- // entirely during the session...
- // We need to simulate very strictly the behaviour as if the
- // underlying *text engine* holds the contents in UTF-16.
- // I guess this is the reason why Apple repeats saying "all
- // text handling application should use UTF-16." They are
- // trying to _fix_ the flaw by changing the appliations...
- // ... or, domination of UTF-16 in the industry may be a part
- // of the company vision, and Apple is trying to force third
- // party developers to obey their vision. Remember that use
- // of 16 bits per _a_character_ was one of the very fundamental
- // Unicode design policy on its early days (during late 80s)
- // and the original Unicode design was by two Apple employees...
- const llutf16string text_utf16
- = wstring_to_utf16str(text, preedit)
- + wstring_to_utf16str(text.substr(preedit + preedit_length));
- llassert_always(sizeof(U16) == sizeof(UniChar));
- llassert(0 <= range.location && 0 <= range.length && range.location + range.length <= text_utf16.length());
- memcpy(target_pointer, text_utf16.c_str() + range.location, range.length * sizeof(UniChar));
- // Note that result has already been set above.
- }
- }
- break;
- }
- }
- break;
- }
- return result;
- }
- const char* cursorIDToName(int id)
- {
- switch (id)
- {
- case UI_CURSOR_ARROW: return "UI_CURSOR_ARROW";
- case UI_CURSOR_WAIT: return "UI_CURSOR_WAIT";
- case UI_CURSOR_HAND: return "UI_CURSOR_HAND";
- case UI_CURSOR_IBEAM: return "UI_CURSOR_IBEAM";
- case UI_CURSOR_CROSS: return "UI_CURSOR_CROSS";
- case UI_CURSOR_SIZENWSE: return "UI_CURSOR_SIZENWSE";
- case UI_CURSOR_SIZENESW: return "UI_CURSOR_SIZENESW";
- case UI_CURSOR_SIZEWE: return "UI_CURSOR_SIZEWE";
- case UI_CURSOR_SIZENS: return "UI_CURSOR_SIZENS";
- case UI_CURSOR_NO: return "UI_CURSOR_NO";
- case UI_CURSOR_WORKING: return "UI_CURSOR_WORKING";
- case UI_CURSOR_TOOLGRAB: return "UI_CURSOR_TOOLGRAB";
- case UI_CURSOR_TOOLLAND: return "UI_CURSOR_TOOLLAND";
- case UI_CURSOR_TOOLFOCUS: return "UI_CURSOR_TOOLFOCUS";
- case UI_CURSOR_TOOLCREATE: return "UI_CURSOR_TOOLCREATE";
- case UI_CURSOR_ARROWDRAG: return "UI_CURSOR_ARROWDRAG";
- case UI_CURSOR_ARROWCOPY: return "UI_CURSOR_ARROWCOPY";
- case UI_CURSOR_ARROWDRAGMULTI: return "UI_CURSOR_ARROWDRAGMULTI";
- case UI_CURSOR_ARROWCOPYMULTI: return "UI_CURSOR_ARROWCOPYMULTI";
- case UI_CURSOR_NOLOCKED: return "UI_CURSOR_NOLOCKED";
- case UI_CURSOR_ARROWLOCKED: return "UI_CURSOR_ARROWLOCKED";
- case UI_CURSOR_GRABLOCKED: return "UI_CURSOR_GRABLOCKED";
- case UI_CURSOR_TOOLTRANSLATE: return "UI_CURSOR_TOOLTRANSLATE";
- case UI_CURSOR_TOOLROTATE: return "UI_CURSOR_TOOLROTATE";
- case UI_CURSOR_TOOLSCALE: return "UI_CURSOR_TOOLSCALE";
- case UI_CURSOR_TOOLCAMERA: return "UI_CURSOR_TOOLCAMERA";
- case UI_CURSOR_TOOLPAN: return "UI_CURSOR_TOOLPAN";
- case UI_CURSOR_TOOLZOOMIN: return "UI_CURSOR_TOOLZOOMIN";
- case UI_CURSOR_TOOLPICKOBJECT3: return "UI_CURSOR_TOOLPICKOBJECT3";
- case UI_CURSOR_TOOLPLAY: return "UI_CURSOR_TOOLPLAY";
- case UI_CURSOR_TOOLPAUSE: return "UI_CURSOR_TOOLPAUSE";
- case UI_CURSOR_TOOLMEDIAOPEN: return "UI_CURSOR_TOOLMEDIAOPEN";
- case UI_CURSOR_PIPETTE: return "UI_CURSOR_PIPETTE";
- }
- llerrs << "cursorIDToName: unknown cursor id" << id << llendl;
-
- return "UI_CURSOR_ARROW";
- }
- static CursorRef gCursors[UI_CURSOR_COUNT];
- static void initPixmapCursor(int cursorid, int hotspotX, int hotspotY)
- {
- // cursors are in <Application Bundle>/Contents/Resources/cursors_mac/UI_CURSOR_FOO.tif
- std::string fullpath = gDirUtilp->getAppRODataDir();
- fullpath += gDirUtilp->getDirDelimiter();
- fullpath += "cursors_mac";
- fullpath += gDirUtilp->getDirDelimiter();
- fullpath += cursorIDToName(cursorid);
- fullpath += ".tif";
-
- gCursors[cursorid] = createImageCursor(fullpath.c_str(), hotspotX, hotspotY);
- }
- void LLWindowMacOSX::setCursor(ECursorType cursor)
- {
- OSStatus result = noErr;
- if (mDragOverrideCursor != -1)
- {
- // A drag is in progress...remember the requested cursor and we'll
- // restore it when it is done
- mCurrentCursor = cursor;
- return;
- }
-
- if (cursor == UI_CURSOR_ARROW
- && mBusyCount > 0)
- {
- cursor = UI_CURSOR_WORKING;
- }
-
- if(mCurrentCursor == cursor)
- return;
- // RN: replace multi-drag cursors with single versions
- if (cursor == UI_CURSOR_ARROWDRAGMULTI)
- {
- cursor = UI_CURSOR_ARROWDRAG;
- }
- else if (cursor == UI_CURSOR_ARROWCOPYMULTI)
- {
- cursor = UI_CURSOR_ARROWCOPY;
- }
- switch(cursor)
- {
- default:
- case UI_CURSOR_ARROW:
- InitCursor();
- if(mCursorHidden)
- {
- // Since InitCursor resets the hide level, correct for it here.
- ::HideCursor();
- }
- break;
- // MBW -- XXX -- Some of the standard Windows cursors have no standard Mac equivalents.
- // Find out what they look like and replicate them.
- // These are essentially correct
- case UI_CURSOR_WAIT: SetThemeCursor(kThemeWatchCursor); break;
- case UI_CURSOR_IBEAM: SetThemeCursor(kThemeIBeamCursor); break;
- case UI_CURSOR_CROSS: SetThemeCursor(kThemeCrossCursor); break;
- case UI_CURSOR_HAND: SetThemeCursor(kThemePointingHandCursor); break;
- // case UI_CURSOR_NO: SetThemeCursor(kThemeNotAllowedCursor); break;
- case UI_CURSOR_ARROWCOPY: SetThemeCursor(kThemeCopyArrowCursor); break;
- // Double-check these
- case UI_CURSOR_NO:
- case UI_CURSOR_SIZEWE:
- case UI_CURSOR_SIZENS:
- case UI_CURSOR_SIZENWSE:
- case UI_CURSOR_SIZENESW:
- case UI_CURSOR_WORKING:
- case UI_CURSOR_TOOLGRAB:
- case UI_CURSOR_TOOLLAND:
- case UI_CURSOR_TOOLFOCUS:
- case UI_CURSOR_TOOLCREATE:
- case UI_CURSOR_ARROWDRAG:
- case UI_CURSOR_NOLOCKED:
- case UI_CURSOR_ARROWLOCKED:
- case UI_CURSOR_GRABLOCKED:
- case UI_CURSOR_TOOLTRANSLATE:
- case UI_CURSOR_TOOLROTATE:
- case UI_CURSOR_TOOLSCALE:
- case UI_CURSOR_TOOLCAMERA:
- case UI_CURSOR_TOOLPAN:
- case UI_CURSOR_TOOLZOOMIN:
- case UI_CURSOR_TOOLPICKOBJECT3:
- case UI_CURSOR_TOOLPLAY:
- case UI_CURSOR_TOOLPAUSE:
- case UI_CURSOR_TOOLMEDIAOPEN:
- result = setImageCursor(gCursors[cursor]);
- break;
- }
- if(result != noErr)
- {
- InitCursor();
- }
- mCurrentCursor = cursor;
- }
- ECursorType LLWindowMacOSX::getCursor() const
- {
- return mCurrentCursor;
- }
- void LLWindowMacOSX::initCursors()
- {
- initPixmapCursor(UI_CURSOR_NO, 8, 8);
- initPixmapCursor(UI_CURSOR_WORKING, 1, 1);
- initPixmapCursor(UI_CURSOR_TOOLGRAB, 2, 14);
- initPixmapCursor(UI_CURSOR_TOOLLAND, 13, 8);
- initPixmapCursor(UI_CURSOR_TOOLFOCUS, 7, 6);
- initPixmapCursor(UI_CURSOR_TOOLCREATE, 7, 7);
- initPixmapCursor(UI_CURSOR_ARROWDRAG, 1, 1);
- initPixmapCursor(UI_CURSOR_ARROWCOPY, 1, 1);
- initPixmapCursor(UI_CURSOR_NOLOCKED, 8, 8);
- initPixmapCursor(UI_CURSOR_ARROWLOCKED, 1, 1);
- initPixmapCursor(UI_CURSOR_GRABLOCKED, 2, 14);
- initPixmapCursor(UI_CURSOR_TOOLTRANSLATE, 1, 1);
- initPixmapCursor(UI_CURSOR_TOOLROTATE, 1, 1);
- initPixmapCursor(UI_CURSOR_TOOLSCALE, 1, 1);
- initPixmapCursor(UI_CURSOR_TOOLCAMERA, 7, 6);
- initPixmapCursor(UI_CURSOR_TOOLPAN, 7, 6);
- initPixmapCursor(UI_CURSOR_TOOLZOOMIN, 7, 6);
- initPixmapCursor(UI_CURSOR_TOOLPICKOBJECT3, 1, 1);
- initPixmapCursor(UI_CURSOR_TOOLPLAY, 1, 1);
- initPixmapCursor(UI_CURSOR_TOOLPAUSE, 1, 1);
- initPixmapCursor(UI_CURSOR_TOOLMEDIAOPEN, 1, 1);
- initPixmapCursor(UI_CURSOR_SIZENWSE, 10, 10);
- initPixmapCursor(UI_CURSOR_SIZENESW, 10, 10);
- initPixmapCursor(UI_CURSOR_SIZEWE, 10, 10);
- initPixmapCursor(UI_CURSOR_SIZENS, 10, 10);
- }
- void LLWindowMacOSX::captureMouse()
- {
- // By registering a global CarbonEvent handler for mouse move events, we ensure that
- // mouse events are always processed. Thus, capture and release are unnecessary.
- }
- void LLWindowMacOSX::releaseMouse()
- {
- // By registering a global CarbonEvent handler for mouse move events, we ensure that
- // mouse events are always processed. Thus, capture and release are unnecessary.
- }
- void LLWindowMacOSX::hideCursor()
- {
- if(!mCursorHidden)
- {
- // llinfos << "hideCursor: hiding" << llendl;
- mCursorHidden = TRUE;
- mHideCursorPermanent = TRUE;
- ::HideCursor();
- }
- else
- {
- // llinfos << "hideCursor: already hidden" << llendl;
- }
- adjustCursorDecouple();
- }
- void LLWindowMacOSX::showCursor()
- {
- if(mCursorHidden)
- {
- // llinfos << "showCursor: showing" << llendl;
- mCursorHidden = FALSE;
- mHideCursorPermanent = FALSE;
- ::ShowCursor();
- }
- else
- {
- // llinfos << "showCursor: already visible" << llendl;
- }
- adjustCursorDecouple();
- }
- void LLWindowMacOSX::showCursorFromMouseMove()
- {
- if (!mHideCursorPermanent)
- {
- showCursor();
- }
- }
- void LLWindowMacOSX::hideCursorUntilMouseMove()
- {
- if (!mHideCursorPermanent)
- {
- hideCursor();
- mHideCursorPermanent = FALSE;
- }
- }
- //
- // LLSplashScreenMacOSX
- //
- LLSplashScreenMacOSX::LLSplashScreenMacOSX()
- {
- mWindow = NULL;
- }
- LLSplashScreenMacOSX::~LLSplashScreenMacOSX()
- {
- }
- void LLSplashScreenMacOSX::showImpl()
- {
- // This code _could_ be used to display a spash screen...
- #if 0
- IBNibRef nib = NULL;
- OSStatus err;
- err = CreateNibReference(CFSTR("SecondLife"), &nib);
- if(err == noErr)
- {
- CreateWindowFromNib(nib, CFSTR("Splash Screen"), &mWindow);
- DisposeNibReference(nib);
- }
- if(mWindow != NULL)
- {
- ShowWindow(mWindow);
- }
- #endif
- }
- void LLSplashScreenMacOSX::updateImpl(const std::string& mesg)
- {
- if(mWindow != NULL)
- {
- CFStringRef string = NULL;
- string = CFStringCreateWithCString(NULL, mesg.c_str(), kCFStringEncodingUTF8);
- if(string != NULL)
- {
- ControlRef progressText = NULL;
- ControlID id;
- OSStatus err;
- id.signature = 'what';
- id.id = 0;
- err = GetControlByID(mWindow, &id, &progressText);
- if(err == noErr)
- {
- err = SetControlData(progressText, kControlEntireControl, kControlStaticTextCFStringTag, sizeof(CFStringRef), (Ptr)&string);
- Draw1Control(progressText);
- }
- CFRelease(string);
- }
- }
- }
- void LLSplashScreenMacOSX::hideImpl()
- {
- if(mWindow != NULL)
- {
- DisposeWindow(mWindow);
- mWindow = NULL;
- }
- }
- S32 OSMessageBoxMacOSX(const std::string& text, const std::string& caption, U32 type)
- {
- S32 result = OSBTN_CANCEL;
- SInt16 retval_mac = 1;
- AlertStdCFStringAlertParamRec params;
- CFStringRef errorString = NULL;
- CFStringRef explanationString = NULL;
- DialogRef alert = NULL;
- AlertType alertType = kAlertCautionAlert;
- OSStatus err;
- explanationString = CFStringCreateWithCString(NULL, text.c_str(), kCFStringEncodingUTF8);
- errorString = CFStringCreateWithCString(NULL, caption.c_str(), kCFStringEncodingUTF8);
- params.version = kStdCFStringAlertVersionOne;
- params.movable = false;
- params.helpButton = false;
- params.defaultText = (CFStringRef)kAlertDefaultOKText;
- params.cancelText = 0;
- params.otherText = 0;
- params.defaultButton = 1;
- params.cancelButton = 0;
- params.position = kWindowDefaultPosition;
- params.flags = 0;
- switch(type)
- {
- case OSMB_OK:
- default:
- break;
- case OSMB_OKCANCEL:
- params.cancelText = (CFStringRef)kAlertDefaultCancelText;
- params.cancelButton = 2;
- break;
- case OSMB_YESNO:
- alertType = kAlertNoteAlert;
- params.defaultText = CFSTR("Yes");
- params.cancelText = CFSTR("No");
- params.cancelButton = 2;
- break;
- }
- if(gWindowImplementation != NULL)
- gWindowImplementation->beforeDialog();
- err = CreateStandardAlert(
- alertType,
- errorString,
- explanationString,
- ¶ms,
- &alert);
- if(err == noErr)
- {
- err = RunStandardAlert(
- alert,
- NULL,
- &retval_mac);
- }
- if(gWindowImplementation != NULL)
- gWindowImplementation->afterDialog();
- switch(type)
- {
- case OSMB_OK:
- case OSMB_OKCANCEL:
- default:
- if(retval_mac == 1)
- result = OSBTN_OK;
- else
- result = OSBTN_CANCEL;
- break;
- case OSMB_YESNO:
- if(retval_mac == 1)
- result = OSBTN_YES;
- else
- result = OSBTN_NO;
- break;
- }
- if(errorString != NULL)
- {
- CFRelease(errorString);
- }
- if(explanationString != NULL)
- {
- CFRelease(explanationString);
- }
- return result;
- }
- // Open a URL with the user's default web browser.
- // Must begin with protocol identifier.
- void LLWindowMacOSX::spawnWebBrowser(const std::string& escaped_url)
- {
- bool found = false;
- S32 i;
- for (i = 0; i < gURLProtocolWhitelistCount; i++)
- {
- if (escaped_url.find(gURLProtocolWhitelist[i]) != std::string::npos)
- {
- found = true;
- break;
- }
- }
- if (!found)
- {
- llwarns << "spawn_web_browser called for url with protocol not on whitelist: " << escaped_url << llendl;
- return;
- }
- OSStatus result = noErr;
- CFURLRef urlRef = NULL;
- llinfos << "Opening URL " << escaped_url << llendl;
- CFStringRef stringRef = CFStringCreateWithCString(NULL, escaped_url.c_str(), kCFStringEncodingUTF8);
- if (stringRef)
- {
- // This will succeed if the string is a full URL, including the http://
- // Note that URLs specified this way need to be properly percent-escaped.
- urlRef = CFURLCreateWithString(NULL, stringRef, NULL);
- // Don't use CRURLCreateWithFileSystemPath -- only want valid URLs
- CFRelease(stringRef);
- }
- if (urlRef)
- {
- result = LSOpenCFURLRef(urlRef, NULL);
- if (result != noErr)
- {
- llinfos << "Error " << result << " on open." << llendl;
- }
- CFRelease(urlRef);
- }
- else
- {
- llinfos << "Error: couldn't create URL." << llendl;
- }
- }
- LLSD LLWindowMacOSX::getNativeKeyData()
- {
- LLSD result = LLSD::emptyMap();
-
- if(mRawKeyEvent)
- {
- char char_code = 0;
- UInt32 key_code = 0;
- UInt32 modifiers = 0;
- UInt32 keyboard_type = 0;
-
- GetEventParameter (mRawKeyEvent, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &char_code);
- GetEventParameter (mRawKeyEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &key_code);
- GetEventParameter (mRawKeyEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
- GetEventParameter (mRawKeyEvent, kEventParamKeyboardType, typeUInt32, NULL, sizeof(UInt32), NULL, &keyboard_type);
- result["char_code"] = (S32)char_code;
- result["key_code"] = (S32)key_code;
- result["modifiers"] = (S32)modifiers;
- result["keyboard_type"] = (S32)keyboard_type;
-
- #if 0
- // This causes trouble for control characters -- apparently character codes less than 32 (escape, control-A, etc)
- // cause llsd serialization to create XML that the llsd deserializer won't parse!
- std::string unicode;
- OSStatus err = noErr;
- EventParamType actualType = typeUTF8Text;
- UInt32 actualSize = 0;
- char *buffer = NULL;
-
- err = GetEventParameter (mRawKeyEvent, kEventParamKeyUnicodes, typeUTF8Text, &actualType, 0, &actualSize, NULL);
- if(err == noErr)
- {
- // allocate a buffer and get the actual data.
- buffer = new char[actualSize];
- err = GetEventParameter (mRawKeyEvent, kEventParamKeyUnicodes, typeUTF8Text, &actualType, actualSize, &actualSize, buffer);
- if(err == noErr)
- {
- unicode.assign(buffer, actualSize);
- }
- delete[] buffer;
- }
-
- result["unicode"] = unicode;
- #endif
- }
- lldebugs << "native key data is: " << result << llendl;
-
- return result;
- }
- BOOL LLWindowMacOSX::dialogColorPicker( F32 *r, F32 *g, F32 *b)
- {
- BOOL retval = FALSE;
- OSErr error = noErr;
- NColorPickerInfo info;
- memset(&info, 0, sizeof(info));
- info.theColor.color.rgb.red = (UInt16)(*r * 65535.f);
- info.theColor.color.rgb.green = (UInt16)(*g * 65535.f);
- info.theColor.color.rgb.blue = (UInt16)(*b * 65535.f);
- info.placeWhere = kCenterOnMainScreen;
- if(gWindowImplementation != NULL)
- gWindowImplementation->beforeDialog();
- error = NPickColor(&info);
- if(gWindowImplementation != NULL)
- gWindowImplementation->afterDialog();
- if (error == noErr)
- {
- retval = info.newColorChosen;
- if (info.newColorChosen)
- {
- *r = ((float) info.theColor.color.rgb.red) / 65535.0;
- *g = ((float) info.theColor.color.rgb.green) / 65535.0;
- *b = ((float) info.theColor.color.rgb.blue) / 65535.0;
- }
- }
- return (retval);
- }
- void *LLWindowMacOSX::getPlatformWindow()
- {
- // NOTE: this will be NULL in fullscreen mode. Plan accordingly.
- return (void*)mWindow;
- }
- void *LLWindowMacOSX::getMediaWindow()
- {
- /*
- Mozilla needs to be initialized with a WindowRef to function properly.
- (There's no good reason for this, since it shouldn't be interacting with our window in any way, but that's another issue.)
- If we're in windowed mode, we _could_ hand it our actual window pointer, but a subsequent switch to fullscreen will destroy that window,
- which trips up Mozilla.
- Instead of using our actual window, we create an invisible window which will persist for the lifetime of the application and pass that to Mozilla.
- This satisfies its deep-seated need to latch onto a WindowRef and solves the issue with switching between fullscreen and windowed modes.
- Note that we will never destroy this window (by design!), but since only one will ever be created per run of the application, that's okay.
- */
-
- if(sMediaWindow == NULL)
- {
- Rect window_rect = {100, 100, 200, 200};
- sMediaWindow = NewCWindow(
- NULL,
- &window_rect,
- (ConstStr255Param) "p",
- false, // Create the window invisible.
- zoomDocProc, // Window with a grow box and a zoom box
- kLastWindowOfClass, // create it behind other windows
- false, // no close box
- 0);
- }
-
- return (void*)sMediaWindow;
- }
- void LLWindowMacOSX::stopDockTileBounce()
- {
- NMRemove(&mBounceRec);
- mBounceTimer.stop();
- }
- // get a double value from a dictionary
- static double getDictDouble (CFDictionaryRef refDict, CFStringRef key)
- {
- double double_value;
- CFNumberRef number_value = (CFNumberRef) CFDictionaryGetValue(refDict, key);
- if (!number_value) // if can't get a number for the dictionary
- return -1; // fail
- if (!CFNumberGetValue(number_value, kCFNumberDoubleType, &double_value)) // or if cant convert it
- return -1; // fail
- return double_value; // otherwise return the long value
- }
- // get a long value from a dictionary
- static long getDictLong (CFDictionaryRef refDict, CFStringRef key)
- {
- long int_value;
- CFNumberRef number_value = (CFNumberRef) CFDictionaryGetValue(refDict, key);
- if (!number_value) // if can't get a number for the dictionary
- return -1; // fail
- if (!CFNumberGetValue(number_value, kCFNumberLongType, &int_value)) // or if cant convert it
- return -1; // fail
- return int_value; // otherwise return the long value
- }
- void LLWindowMacOSX::allowLanguageTextInput(LLPreeditor *preeditor, BOOL b)
- {
- ScriptLanguageRecord script_language;
- if (preeditor != mPreeditor && !b)
- {
- // This condition may occur by a call to
- // setEnabled(BOOL) against LLTextEditor or LLLineEditor
- // when the control is not focused.
- // We need to silently ignore the case so that
- // the language input status of the focused control
- // is not disturbed.
- return;
- }
- UseInputWindow(mTSMDocument, !b);
-
- // Take care of old and new preeditors.
- if (preeditor != mPreeditor || !b)
- {
- // We need to interrupt before updating mPreeditor,
- // so that the fix string from input method goes to
- // the old preeditor.
- if (mLanguageTextInputAllowed)
- {
- interruptLanguageTextInput();
- }
- mPreeditor = (b ? preeditor : NULL);
- }
- if (b == mLanguageTextInputAllowed)
- {
- return;
- }
- mLanguageTextInputAllowed = b;
-
- if (b)
- {
- if (mTSMScriptCode != smRoman)
- {
- script_language.fScript = mTSMScriptCode;
- script_language.fLanguage = mTSMLangCode;
- SetTextServiceLanguage(&script_language);
- }
- }
- else
- {
- GetTextServiceLanguage(&script_language);
- mTSMScriptCode = script_language.fScript;
- mTSMLangCode = script_language.fLanguage;
- if (mTSMScriptCode != smRoman)
- {
- script_language.fScript = smRoman;
- script_language.fLanguage = langEnglish;
- SetTextServiceLanguage(&script_language);
- }
- }
- }
- void LLWindowMacOSX::interruptLanguageTextInput()
- {
- if (mTSMDocument)
- {
- FixTSMDocument(mTSMDocument);
- }
- // Don't we need to call resetPreedit here?
- // Well, if Apple's TSM document is correct, we don't.
- }
- //static
- std::vector<std::string> LLWindowMacOSX::getDynamicFallbackFontList()
- {
- // Fonts previously in getFontListSans() have moved to fonts.xml.
- return std::vector<std::string>();
- }
- // static
- MASK LLWindowMacOSX::modifiersToMask(SInt16 modifiers)
- {
- MASK mask = 0;
- if(modifiers & shiftKey) { mask |= MASK_SHIFT; }
- if(modifiers & (cmdKey | controlKey)) { mask |= MASK_CONTROL; }
- if(modifiers & optionKey) { mask |= MASK_ALT; }
- return mask;
- }
- #if LL_OS_DRAGDROP_ENABLED
- OSErr LLWindowMacOSX::dragTrackingHandler(DragTrackingMessage message, WindowRef theWindow,
- void * handlerRefCon, DragRef drag)
- {
- OSErr result = noErr;
- LLWindowMacOSX *self = (LLWindowMacOSX*)handlerRefCon;
- lldebugs << "drag tracking handler, message = " << message << llendl;
-
- switch(message)
- {
- case kDragTrackingInWindow:
- result = self->handleDragNDrop(drag, LLWindowCallbacks::DNDA_TRACK);
- break;
-
- case kDragTrackingEnterHandler:
- result = self->handleDragNDrop(drag, LLWindowCallbacks::DNDA_START_TRACKING);
- break;
-
- case kDragTrackingLeaveHandler:
- result = self->handleDragNDrop(drag, LLWindowCallbacks::DNDA_STOP_TRACKING);
- break;
-
- default:
- break;
- }
-
- return result;
- }
- OSErr LLWindowMacOSX::dragReceiveHandler(WindowRef theWindow, void * handlerRefCon,
- DragRef drag)
- {
- LLWindowMacOSX *self = (LLWindowMacOSX*)handlerRefCon;
- return self->handleDragNDrop(drag, LLWindowCallbacks::DNDA_DROPPED);
- }
- OSErr LLWindowMacOSX::handleDragNDrop(DragRef drag, LLWindowCallbacks::DragNDropAction action)
- {
- OSErr result = dragNotAcceptedErr; // overall function result
- OSErr err = noErr; // for local error handling
-
- // Get the mouse position and modifiers of this drag.
- SInt16 modifiers, mouseDownModifiers, mouseUpModifiers;
- ::GetDragModifiers(drag, &modifiers, &mouseDownModifiers, &mouseUpModifiers);
- MASK mask = LLWindowMacOSX::modifiersToMask(modifiers);
-
- Point mouse_point;
- // This will return the mouse point in global screen coords
- ::GetDragMouse(drag, &mouse_point, NULL);
- LLCoordScreen screen_coords(mouse_point.h, mouse_point.v);
- LLCoordGL gl_pos;
- convertCoords(screen_coords, &gl_pos);
-
- // Look at the pasteboard and try to extract an URL from it
- PasteboardRef pasteboard;
- if(GetDragPasteboard(drag, &pasteboard) == noErr)
- {
- ItemCount num_items = 0;
- // Treat an error here as an item count of 0
- (void)PasteboardGetItemCount(pasteboard, &num_items);
-
- // Only deal with single-item drags.
- if(num_items == 1)
- {
- PasteboardItemID item_id = NULL;
- CFArrayRef flavors = NULL;
- CFDataRef data = NULL;
-
- err = PasteboardGetItemIdentifier(pasteboard, 1, &item_id); // Yes, this really is 1-based.
-
- // Try to extract an URL from the pasteboard
- if(err == noErr)
- {
- err = PasteboardCopyItemFlavors( pasteboard, item_id, &flavors);
- }
-
- if(err == noErr)
- {
- if(CFArrayContainsValue(flavors, CFRangeMake(0, CFArrayGetCount(flavors)), kUTTypeURL))
- {
- // This is an URL.
- err = PasteboardCopyItemFlavorData(pasteboard, item_id, kUTTypeURL, &data);
- }
- else if(CFArrayContainsValue(flavors, CFRangeMake(0, CFArrayGetCount(flavors)), kUTTypeUTF8PlainText))
- {
- // This is a string that might be an URL.
- err = PasteboardCopyItemFlavorData(pasteboard, item_id, kUTTypeUTF8PlainText, &data);
- }
-
- }
-
- if(flavors != NULL)
- {
- CFRelease(flavors);
- }
- if(data != NULL)
- {
- std::string url;
- url.assign((char*)CFDataGetBytePtr(data), CFDataGetLength(data));
- CFRelease(data);
-
- if(!url.empty())
- {
- LLWindowCallbacks::DragNDropResult res =
- mCallbacks->handleDragNDrop(this, gl_pos, mask, action, url);
-
- switch (res) {
- case LLWindowCallbacks::DND_NONE: // No drop allowed
- if (action == LLWindowCallbacks::DNDA_TRACK)
- {
- mDragOverrideCursor = kThemeNotAllowedCursor;
- }
- else {
- mDragOverrideCursor = -1;
- }
- break;
- case LLWindowCallbacks::DND_MOVE: // Drop accepted would result in a "move" operation
- mDragOverrideCursor = kThemePointingHandCursor;
- result = noErr;
- break;
- case LLWindowCallbacks::DND_COPY: // Drop accepted would result in a "copy" operation
- mDragOverrideCursor = kThemeCopyArrowCursor;
- result = noErr;
- break;
- case LLWindowCallbacks::DND_LINK: // Drop accepted would result in a "link" operation:
- mDragOverrideCursor = kThemeAliasArrowCursor;
- result = noErr;
- break;
- default:
- mDragOverrideCursor = -1;
- break;
- }
- // This overrides the cursor being set by setCursor.
- // This is a bit of a hack workaround because lots of areas
- // within the viewer just blindly set the cursor.
- if (mDragOverrideCursor == -1)
- {
- // Restore the cursor
- ECursorType temp_cursor = mCurrentCursor;
- // get around the "setting the same cursor" code in setCursor()
- mCurrentCursor = UI_CURSOR_COUNT;
- setCursor(temp_cursor);
- }
- else {
- // Override the cursor
- SetThemeCursor(mDragOverrideCursor);
- }
- }
- }
- }
- }
-
- return result;
- }
- #endif // LL_OS_DRAGDROP_ENABLED