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