1 /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version. The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * OpenSceneGraph Public License for more details.
12 */
13
14 #if defined (__APPLE__) && (!__LP64__)
15
16 #include <osg/observer_ptr>
17
18 #include <osgViewer/api/Carbon/PixelBufferCarbon>
19 #include <osgViewer/api/Carbon/GraphicsWindowCarbon>
20
21 #include <osg/DeleteHandler>
22
23 #include <Carbon/Carbon.h>
24 #include <OpenGL/OpenGL.h>
25
26 #include <iostream>
27
28 #include "DarwinUtils.h"
29
30 using namespace osgViewer;
31 using namespace osgDarwin;
32
33
34 // Carbon-Eventhandler to handle the click in the close-widget and the resize of windows
35
GraphicsWindowEventHandler(EventHandlerCallRef nextHandler,EventRef event,void * userData)36 static pascal OSStatus GraphicsWindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void* userData)
37 {
38 WindowRef window;
39 Rect bounds;
40 OSStatus result = eventNotHandledErr; /* report failure by default */
41
42 OSG_INFO << "GraphicsWindowEventHandler" << std::endl;
43
44 GraphicsWindowCarbon* w = (GraphicsWindowCarbon*)userData;
45 if (!w)
46 return result;
47
48 GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL,
49 sizeof(window), NULL, &window);
50
51 switch(GetEventClass(event))
52 {
53 case kEventClassTablet:
54 case kEventClassMouse:
55 if (w->handleMouseEvent(event))
56 result = noErr;
57 break;
58
59 case kEventClassKeyboard:
60 if (w->handleKeyboardEvent(event))
61 result = noErr;
62 break;
63
64 case kEventClassWindow: {
65
66 switch (GetEventKind(event))
67 {
68 case kEventWindowBoundsChanging:
69 // left the code for live-resizing, but it is not used, because of window-refreshing issues...
70 GetEventParameter( event, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &bounds );
71
72 w->adaptResize(bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top);
73 w->requestRedraw();
74 result = noErr;
75 break;
76
77 case kEventWindowBoundsChanged:
78 InvalWindowRect(window, GetWindowPortBounds(window, &bounds));
79 GetWindowBounds(window, kWindowContentRgn, &bounds);
80 w->adaptResize(bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top);
81 result = noErr;
82 break;
83
84 case kEventWindowClose:
85 w->requestClose();
86 result = noErr;
87 break;
88
89 default:
90 break;
91 }
92 }
93 default:
94 //std::cout << "unknown: " << GetEventClass(event) << std::endl;
95 break;
96 }
97
98 //if (result == eventNotHandledErr)
99 // result = CallNextEventHandler (nextHandler, event);
100
101 return result;
102 }
103
104
105 static bool s_quit_requested = false;
106
107 // Application eventhandler -- listens for a quit-event
ApplicationEventHandler(EventHandlerCallRef inHandlerCallRef,EventRef inEvent,void * inUserData)108 static pascal OSStatus ApplicationEventHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
109 {
110
111 HICommand commandStruct;
112
113 OSErr err = eventNotHandledErr;
114
115 GetEventParameter (inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &commandStruct);
116
117 switch(commandStruct.commandID) {
118 case kHICommandQuit:
119 s_quit_requested = true;
120 err = noErr;
121 break;
122
123 }
124
125 return err;
126 }
127
128 // AppleEventHandler, listens to the Quit-AppleEvent
QuitAppleEventHandler(const AppleEvent * theAppleEvent,AppleEvent * reply,long handlerRefcon)129 static pascal OSErr QuitAppleEventHandler(const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) {
130 s_quit_requested = true;
131 return (noErr);
132 }
133
134
135 namespace osgViewer
136 {
137
138 // small helper class which maps the raw key codes to osgGA::GUIEventAdapter::Keys
139
140 class CarbonKeyboardMap {
141
142 public:
CarbonKeyboardMap()143 CarbonKeyboardMap()
144 {
145 _keymap[53 ] = osgGA::GUIEventAdapter::KEY_Escape;
146 _keymap[115 ] = osgGA::GUIEventAdapter::KEY_Home;
147 _keymap[76 ] = osgGA::GUIEventAdapter::KEY_KP_Enter;
148 _keymap[119 ] = osgGA::GUIEventAdapter::KEY_End;
149 _keymap[36 ] = osgGA::GUIEventAdapter::KEY_Return;
150 _keymap[116 ] = osgGA::GUIEventAdapter::KEY_Page_Up;
151 _keymap[121 ] = osgGA::GUIEventAdapter::KEY_Page_Down;
152 _keymap[123 ] = osgGA::GUIEventAdapter::KEY_Left;
153 _keymap[124 ] = osgGA::GUIEventAdapter::KEY_Right;
154 _keymap[126 ] = osgGA::GUIEventAdapter::KEY_Up;
155 _keymap[125 ] = osgGA::GUIEventAdapter::KEY_Down;
156 _keymap[51 ] = osgGA::GUIEventAdapter::KEY_BackSpace;
157 _keymap[48 ] = osgGA::GUIEventAdapter::KEY_Tab;
158 _keymap[49 ] = osgGA::GUIEventAdapter::KEY_Space;
159 _keymap[117 ] = osgGA::GUIEventAdapter::KEY_Delete;
160
161 _keymap[122 ] = osgGA::GUIEventAdapter::KEY_F1;
162 _keymap[120 ] = osgGA::GUIEventAdapter::KEY_F2;
163 _keymap[99 ] = osgGA::GUIEventAdapter::KEY_F3;
164 _keymap[118 ] = osgGA::GUIEventAdapter::KEY_F4;
165 _keymap[96 ] = osgGA::GUIEventAdapter::KEY_F5;
166 _keymap[97 ] = osgGA::GUIEventAdapter::KEY_F6;
167 _keymap[98 ] = osgGA::GUIEventAdapter::KEY_F7;
168 _keymap[100 ] = osgGA::GUIEventAdapter::KEY_F8;
169 _keymap[101 ] = osgGA::GUIEventAdapter::KEY_F9;
170 _keymap[109 ] = osgGA::GUIEventAdapter::KEY_F10;
171 _keymap[103 ] = osgGA::GUIEventAdapter::KEY_F11;
172 _keymap[111 ] = osgGA::GUIEventAdapter::KEY_F12;
173
174 _keymap[75 ] = osgGA::GUIEventAdapter::KEY_KP_Divide;
175 _keymap[67 ] = osgGA::GUIEventAdapter::KEY_KP_Multiply;
176 _keymap[78 ] = osgGA::GUIEventAdapter::KEY_KP_Subtract;
177 _keymap[69 ] = osgGA::GUIEventAdapter::KEY_KP_Add;
178 _keymap[89 ] = osgGA::GUIEventAdapter::KEY_KP_Home;
179 _keymap[91 ] = osgGA::GUIEventAdapter::KEY_KP_Up;
180 _keymap[92 ] = osgGA::GUIEventAdapter::KEY_KP_Page_Up;
181 _keymap[86 ] = osgGA::GUIEventAdapter::KEY_KP_Left;
182 _keymap[87 ] = osgGA::GUIEventAdapter::KEY_KP_Begin;
183 _keymap[88 ] = osgGA::GUIEventAdapter::KEY_KP_Right;
184 _keymap[83 ] = osgGA::GUIEventAdapter::KEY_KP_End;
185 _keymap[84 ] = osgGA::GUIEventAdapter::KEY_KP_Down;
186 _keymap[85 ] = osgGA::GUIEventAdapter::KEY_KP_Page_Down;
187 _keymap[82 ] = osgGA::GUIEventAdapter::KEY_KP_Insert;
188 _keymap[65 ] = osgGA::GUIEventAdapter::KEY_KP_Delete;
189
190 }
191
~CarbonKeyboardMap()192 ~CarbonKeyboardMap() {
193 }
194
remapKey(unsigned int key,unsigned int rawkey)195 unsigned int remapKey(unsigned int key, unsigned int rawkey)
196 {
197 KeyMap::iterator itr = _keymap.find(rawkey);
198 if (itr == _keymap.end()) return key;
199 else return itr->second;
200 }
201 private:
202 typedef std::map<unsigned int, osgGA::GUIEventAdapter::KeySymbol> KeyMap;
203 KeyMap _keymap;
204 };
205
206 /** remaps a native os x keycode to a GUIEventAdapter-keycode */
remapCarbonKey(unsigned int key,unsigned int rawkey)207 static unsigned int remapCarbonKey(unsigned int key, unsigned int rawkey)
208 {
209 static CarbonKeyboardMap s_CarbonKeyboardMap;
210 return s_CarbonKeyboardMap.remapKey(key,rawkey);
211 }
212
213
214 class CarbonWindowAdapter : public MenubarController::WindowAdapter {
215 public:
CarbonWindowAdapter(GraphicsWindowCarbon * win)216 CarbonWindowAdapter(GraphicsWindowCarbon* win) : MenubarController::WindowAdapter(), _win(win) {}
valid()217 virtual bool valid() {return (_win.valid() && _win->valid()); }
getWindowBounds(CGRect & rect)218 virtual void getWindowBounds(CGRect& rect)
219 {
220 Rect windowBounds;
221 OSErr error = GetWindowBounds(_win->getNativeWindowRef(), kWindowStructureRgn, &windowBounds);
222 rect.origin.x = windowBounds.left;
223 rect.origin.y = windowBounds.top;
224 rect.size.width = windowBounds.right - windowBounds.left;
225 rect.size.height = windowBounds.bottom - windowBounds.top;
226 }
227
getWindow()228 osgViewer::GraphicsWindow* getWindow() { return _win.get(); }
229 private:
230 osg::observer_ptr<GraphicsWindowCarbon> _win;
231 };
232
233
234
init()235 void GraphicsWindowCarbon::init()
236 {
237 if (_initialized) return;
238
239 // getEventQueue()->setCurrentEventState(osgGA::GUIEventAdapter::getAccumulatedEventState().get());
240
241 _lastModifierKeys = 0;
242 _windowTitleHeight = 0;
243 _closeRequested = false;
244 _ownsWindow = false;
245 _context = NULL;
246 _window = NULL;
247 _pixelFormat = PixelBufferCarbon::createPixelFormat(_traits.get());
248 if (!_pixelFormat)
249 {
250 OSG_WARN << "GraphicsWindowCarbon::init could not create a valid pixelformat" << std::endl;
251 }
252 _valid = (_pixelFormat != NULL);
253 _initialized = true;
254
255 // make sure the event queue has the correct window rectangle size and input range
256 getEventQueue()->syncWindowRectangleWithGraphicsContext();
257 }
258
setWindowDecorationImplementation(bool flag)259 bool GraphicsWindowCarbon::setWindowDecorationImplementation(bool flag)
260 {
261 _useWindowDecoration = flag;
262
263 if (_realized)
264 {
265 OSErr err = noErr;
266 Rect bounds;
267 GetWindowBounds(getNativeWindowRef(), kWindowContentRgn, &bounds);
268
269 if (_useWindowDecoration)
270 {
271 err = ChangeWindowAttributes(getNativeWindowRef(), kWindowStandardDocumentAttributes, kWindowNoTitleBarAttribute | kWindowNoShadowAttribute);
272 SetWindowBounds(getNativeWindowRef(), kWindowContentRgn, &bounds);
273 }
274 else
275 {
276 err = ChangeWindowAttributes(getNativeWindowRef(), kWindowNoTitleBarAttribute | kWindowNoShadowAttribute, kWindowStandardDocumentAttributes);
277 SetWindowBounds(getNativeWindowRef(), kWindowContentRgn, &bounds);
278 }
279
280 if (err != noErr)
281 {
282 OSG_WARN << "GraphicsWindowCarbon::setWindowDecoration failed with " << err << std::endl;
283 return false;
284 }
285
286 // update titlebar-height
287 Rect titleRect;
288 GetWindowBounds(_window, kWindowTitleBarRgn, &titleRect);
289 _windowTitleHeight = abs(titleRect.bottom - titleRect.top);
290
291 // sth: I don't know why I have to reattach the context to the window here, If I don't do this I get blank areas, where the titlebar was.
292 // InvalWindowRect doesn't help here :-/
293
294 aglSetDrawable(_context, 0);
295 aglSetDrawable(_context, GetWindowPort(_window));
296
297 MenubarController::instance()->update();
298 }
299
300 return true;
301 }
302
303
computeWindowAttributes(bool useWindowDecoration,bool supportsResize)304 WindowAttributes GraphicsWindowCarbon::computeWindowAttributes(bool useWindowDecoration, bool supportsResize) {
305 WindowAttributes attr;
306
307 if (useWindowDecoration)
308 {
309 if (supportsResize)
310 attr = (kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute);
311 else
312 attr = (kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute) & ~kWindowResizableAttribute;
313 }
314 else
315 {
316 attr = kWindowNoTitleBarAttribute | kWindowNoShadowAttribute | kWindowStandardHandlerAttribute;
317 if (supportsResize)
318 attr |= kWindowResizableAttribute;
319 }
320 return attr;
321 }
322
installEventHandler()323 void GraphicsWindowCarbon::installEventHandler() {
324
325 // register window event handler to receive resize-events
326 EventTypeSpec windEventList[] = {
327 { kEventClassWindow, kEventWindowBoundsChanged},
328 { kEventClassWindow, kEventWindowClose},
329
330 {kEventClassMouse, kEventMouseDown},
331 {kEventClassMouse, kEventMouseUp},
332 {kEventClassMouse, kEventMouseMoved},
333 {kEventClassMouse, kEventMouseDragged},
334 {kEventClassMouse, kEventMouseWheelMoved},
335 {kEventClassMouse, 11 /* kEventMouseScroll */},
336
337 {kEventClassKeyboard, kEventRawKeyDown},
338 {kEventClassKeyboard, kEventRawKeyRepeat},
339 {kEventClassKeyboard, kEventRawKeyUp},
340 {kEventClassKeyboard, kEventRawKeyModifiersChanged},
341 {kEventClassKeyboard, kEventHotKeyPressed},
342 {kEventClassKeyboard, kEventHotKeyReleased},
343 };
344
345 InstallWindowEventHandler(_window, NewEventHandlerUPP(GraphicsWindowEventHandler), GetEventTypeCount(windEventList), windEventList, this, NULL);
346 }
347
348
realizeImplementation()349 bool GraphicsWindowCarbon::realizeImplementation()
350 {
351 if (!_initialized) init();
352 if (!_initialized) return false;
353 if (!_traits) return false;
354
355 OSG_INFO << "GraphicsWindowCarbon::realizeImplementation" << std::endl;
356
357 setWindowDecoration(_traits->windowDecoration);
358 useCursor(_traits->useCursor);
359
360 // move the window to the right screen
361 DarwinWindowingSystemInterface* wsi = dynamic_cast<DarwinWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
362 int screenLeft = 0, screenTop = 0;
363 if (wsi)
364 {
365 wsi->getScreenTopLeft((*_traits), screenLeft, screenTop);
366 }
367
368 WindowData *windowData = ( _traits.get() && _traits->inheritedWindowData.get() ) ? static_cast<osgViewer::GraphicsWindowCarbon::WindowData*>(_traits->inheritedWindowData.get()) : 0;
369
370 _ownsWindow = (windowData) ? (windowData->getNativeWindowRef() == NULL) : true;
371
372 if (_ownsWindow) {
373
374 // create the window
375 Rect bounds = {_traits->y + screenTop, _traits->x + screenLeft, _traits->y + _traits->height + screenTop, _traits->x + _traits->width + screenLeft};
376 OSStatus err = 0;
377 WindowAttributes attr = computeWindowAttributes(_useWindowDecoration, _traits->supportsResize);
378
379 err = CreateNewWindow(kDocumentWindowClass, attr, &bounds, &_window);
380
381 if (err) {
382 OSG_WARN << "GraphicsWindowCarbon::realizeImplementation: failed to create window: " << err << std::endl;
383 return false;
384 } else {
385 OSG_INFO << "GraphicsWindowCarbon::realizeImplementation: window created with bounds(" << bounds.top << ", " << bounds.left << ", " << bounds.bottom << ", " << bounds.right << ")" << std::endl;
386 }
387 }
388 else {
389 _window = windowData->getNativeWindowRef();
390 }
391
392 Rect titleRect;
393 GetWindowBounds(_window, kWindowTitleBarRgn, &titleRect);
394 _windowTitleHeight = abs(titleRect.bottom - titleRect.top);
395
396 if ((_ownsWindow) || (windowData && windowData->installEventHandler()))
397 installEventHandler();
398
399 // set the window title
400 setWindowName(_traits->windowName);
401
402 // create the context
403 AGLContext sharedContextCarbon = NULL;
404
405 GraphicsHandleCarbon* graphicsHandleCarbon = dynamic_cast<GraphicsHandleCarbon*>(_traits->sharedContext.get());
406 if (graphicsHandleCarbon)
407 {
408 sharedContextCarbon = graphicsHandleCarbon->getAGLContext();
409 }
410
411 _context = aglCreateContext (_pixelFormat, sharedContextCarbon);
412 if (!_context) {
413 OSG_WARN << "GraphicsWindowCarbon::realizeImplementation: failed to create context: " << aglGetError() << std::endl;
414 return false;
415 }
416
417
418 if ( windowData && windowData->getAGLDrawable() ) {
419 aglSetDrawable(_context, (AGLDrawable)*(windowData->getAGLDrawable()) );
420
421 } else {
422 aglSetDrawable(_context, GetWindowPort(_window));
423 ShowWindow(_window);
424 MenubarController::instance()->attachWindow( new CarbonWindowAdapter(this) );
425 }
426
427 makeCurrent();
428
429 if ((_traits->useMultiThreadedOpenGLEngine) && (OpenThreads::GetNumberOfProcessors() > 1)) {
430 // enable Multi-threaded OpenGL Execution:
431 CGLError cgerr = kCGLNoError;
432 CGLContextObj ctx = CGLGetCurrentContext();
433
434 #if 0
435 cgerr = CGLEnable( ctx, kCGLCEMPEngine);
436 #else
437 // the above use of kCGLCEMPEngine is not backwards compatible
438 // so we'll use the raw value of it to keep things compiling on older
439 // versions of OSX.
440 cgerr = CGLEnable( ctx, static_cast <CGLContextEnable>(313) );
441 #endif
442 if (cgerr != kCGLNoError )
443 {
444 OSG_INFO << "GraphicsWindowCarbon::realizeImplementation: multi-threaded OpenGL Execution not available" << std::endl;
445 }
446 }
447
448 InitCursor();
449
450 // enable vsync
451 if (_traits->vsync) {
452 GLint swap = 1;
453 aglSetInteger (_context, AGL_SWAP_INTERVAL, &swap);
454 }
455 _currentVSync = _traits->vsync;
456
457 _realized = true;
458
459 // make sure the event queue has the correct window rectangle size and input range
460 getEventQueue()->syncWindowRectangleWithGraphicsContext();
461
462 return _realized;
463 }
464
465
466
makeCurrentImplementation()467 bool GraphicsWindowCarbon::makeCurrentImplementation()
468 {
469
470 return (aglSetCurrentContext(_context) == GL_TRUE);
471 }
472
releaseContextImplementation()473 bool GraphicsWindowCarbon::releaseContextImplementation()
474 {
475 if (!_realized)
476 {
477 OSG_NOTICE<<"Warning: GraphicsWindow not realized, cannot do makeCurrent."<<std::endl;
478 return false;
479 }
480
481 // OSG_NOTICE<<"makeCurrentImplementation "<<this<<" "<<OpenThreads::Thread::CurrentThread()<<std::endl;
482 // OSG_NOTICE<<" glXMakeCurrent ("<<_display<<","<<_window<<","<<_glxContext<<std::endl;
483 return (aglSetCurrentContext(NULL) == GL_TRUE);
484 }
485
486
487
closeImplementation()488 void GraphicsWindowCarbon::closeImplementation()
489 {
490 // OSG_INFO << "GraphicsWindowCarbon::closeImplementation" << std::endl;
491 _valid = false;
492 _realized = false;
493
494 // there's a possibility that the MenubarController is destructed already, so prevent a crash:
495 MenubarController* mbc = MenubarController::instance();
496 if (mbc) mbc->detachWindow(this);
497
498 if (_pixelFormat)
499 {
500 aglDestroyPixelFormat(_pixelFormat);
501 _pixelFormat = NULL;
502 }
503
504 if (_context)
505 {
506 aglSetDrawable(_context, NULL);
507 aglSetCurrentContext(NULL);
508 aglDestroyContext(_context);
509 _context = NULL;
510 }
511
512 if (_ownsWindow && _window) DisposeWindow(_window);
513 _window = NULL;
514 }
515
516
517
swapBuffersImplementation()518 void GraphicsWindowCarbon::swapBuffersImplementation()
519 {
520 // check for vsync change
521 if (_traits.valid() && _traits->vsync != _currentVSync)
522 {
523 const bool on = _traits->vsync;
524 GLint swap = (on ? 1 : 0);
525 aglSetInteger (_context, AGL_SWAP_INTERVAL, &swap);
526 OSG_NOTICE << "GraphicsWindowCarbon: VSync=" << (on ? "on" : "off") << std::endl;
527 _currentVSync = on;
528 }
529
530 aglSwapBuffers(_context);
531 }
532
533
534
resizedImplementation(int x,int y,int width,int height)535 void GraphicsWindowCarbon::resizedImplementation(int x, int y, int width, int height)
536 {
537 GraphicsContext::resizedImplementation(x, y, width, height);
538
539 aglUpdateContext(_context);
540 MenubarController::instance()->update();
541
542 getEventQueue()->windowResize(x,y,width, height, getEventQueue()->getTime());
543 }
544
545
546
handleMouseEvent(EventRef theEvent)547 bool GraphicsWindowCarbon::handleMouseEvent(EventRef theEvent)
548 {
549
550 static unsigned int lastEmulatedMouseButton = 0;
551 // mouse down event
552 Point wheresMyMouse;
553 GetEventParameter (theEvent, kEventParamWindowMouseLocation, typeQDPoint, NULL, sizeof(wheresMyMouse), NULL, &wheresMyMouse);
554
555 wheresMyMouse.v -= _windowTitleHeight;
556 if (_useWindowDecoration && (wheresMyMouse.v < 0))
557 return false;
558
559 Point wheresMyMouseGlobal;
560 GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(wheresMyMouse), NULL, &wheresMyMouseGlobal);
561
562 EventMouseButton mouseButton = 0;
563 GetEventParameter (theEvent, kEventParamMouseButton, typeMouseButton, NULL, sizeof(mouseButton), NULL, &mouseButton);
564
565 UInt32 modifierKeys;
566 GetEventParameter (theEvent,kEventParamKeyModifiers,typeUInt32, NULL,sizeof(modifierKeys), NULL,&modifierKeys);
567
568
569 WindowRef win;
570 int fwres = FindWindow(wheresMyMouseGlobal, &win);
571 // return false when Window is inactive; For enabling click-to-active on window by delegating event to default handler
572 if (((fwres != inContent) && (fwres > 0) && (mouseButton >= 1)) || !IsWindowActive(win))
573 {
574 return false;
575 }
576 else
577 {
578 UInt32 clickCount;
579 GetEventParameter(theEvent, kEventParamClickCount, typeUInt32, NULL, sizeof(clickCount), NULL, &clickCount);
580 // swap right and middle buttons so that middle button is 2, right button is 3.
581 if (mouseButton==3) mouseButton = 2;
582 else if (mouseButton==2) mouseButton = 3;
583
584 // check tablet pointer device and map it to a mouse button
585 TabletProximityRec theTabletRecord; // The Tablet Proximity Record
586 // Extract the Tablet Proximity reccord from the event.
587 if(noErr == GetEventParameter(theEvent, kEventParamTabletProximityRec,
588 typeTabletProximityRec, NULL,
589 sizeof(TabletProximityRec),
590 NULL, (void *)&theTabletRecord))
591 {
592 osgGA::GUIEventAdapter::TabletPointerType pointerType;
593 switch(theTabletRecord.pointerType)
594 {
595 case 1: // pen
596 pointerType = osgGA::GUIEventAdapter::PEN;
597 break;
598
599 case 2: // puck
600 pointerType = osgGA::GUIEventAdapter::PUCK;
601 break;
602
603 case 3: // eraser
604 pointerType = osgGA::GUIEventAdapter::ERASER;
605 break;
606
607 default:
608 pointerType = osgGA::GUIEventAdapter::UNKNOWN;
609 break;
610 }
611
612 getEventQueue()->penProximity(pointerType, (theTabletRecord.enterProximity != 0));
613 }
614
615 // get tilt and rotation from the pen
616 TabletPointRec theTabletPointRecord;
617 if(noErr == GetEventParameter(theEvent, kEventParamTabletPointRec, typeTabletPointRec, NULL,
618 sizeof(TabletPointRec), NULL, (void *)&theTabletPointRecord))
619 {
620 int penRotation = (int)theTabletPointRecord.rotation * 9 / 575; //to get angle between 0 to 360 grad
621 penRotation = -(((penRotation + 180) % 360) - 180) ; //for same range on all platforms we need -180 to 180
622 getEventQueue()->penOrientation (
623 theTabletPointRecord.tiltX * 60 / 32767.0f, //multiply with 60 to get angle between -60 to 60 grad
624 -theTabletPointRecord.tiltY * 60 / 32767.0f, //multiply with 60 to get angle between -60 to 60 grad
625 penRotation
626 );
627 }
628
629 switch(GetEventKind(theEvent))
630 {
631 case kEventMouseDown:
632 {
633 float mx = wheresMyMouse.h;
634 float my = wheresMyMouse.v;
635 transformMouseXY(mx, my);
636
637 lastEmulatedMouseButton = 0;
638
639 if (mouseButton == 1)
640 {
641 if( modifierKeys & cmdKey )
642 {
643 mouseButton = lastEmulatedMouseButton = 3;
644 }
645 else if( modifierKeys & optionKey )
646 {
647 mouseButton = lastEmulatedMouseButton = 2;
648 }
649 }
650
651 if (clickCount > 1)
652 getEventQueue()->mouseDoubleButtonPress(mx,my, mouseButton);
653 else
654 getEventQueue()->mouseButtonPress(mx, my, mouseButton);
655 }
656 break;
657 case kEventMouseUp:
658 {
659 float mx = wheresMyMouse.h;
660 float my = wheresMyMouse.v;
661 transformMouseXY(mx, my);
662 if (lastEmulatedMouseButton > 0) {
663 getEventQueue()->mouseButtonRelease(mx, my, lastEmulatedMouseButton);
664 lastEmulatedMouseButton = 0;
665 }
666 else {
667 getEventQueue()->mouseButtonRelease(mx, my, mouseButton);
668 }
669 }
670 break;
671
672 case kEventMouseDragged:
673 {
674 // get pressure from the pen, only when mouse/pen is dragged
675 TabletPointRec theTabletRecord;
676 if(noErr == GetEventParameter(theEvent, kEventParamTabletPointRec, typeTabletPointRec, NULL,
677 sizeof(TabletPointRec), NULL, (void *)&theTabletRecord)) {
678
679 getEventQueue()->penPressure(theTabletRecord.pressure / 65535.0f);
680 }
681
682 float mx = wheresMyMouse.h;
683 float my = wheresMyMouse.v;
684 transformMouseXY(mx, my);
685 getEventQueue()->mouseMotion(mx, my);
686 }
687 break;
688
689 case kEventMouseMoved:
690 {
691 float mx = wheresMyMouse.h;
692 float my = wheresMyMouse.v;
693 transformMouseXY(mx, my);
694 getEventQueue()->mouseMotion(mx, my);
695 }
696 break;
697
698 // mouse with scroll-wheels
699 case kEventMouseWheelMoved:
700 {
701 EventMouseWheelAxis axis;
702 SInt32 delta;
703 if (noErr == GetEventParameter( theEvent, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis )) {
704 if (noErr == GetEventParameter( theEvent, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(delta), NULL, &delta )) {
705 switch (axis) {
706 case kEventMouseWheelAxisX:
707 getEventQueue()->mouseScroll( (delta > 0) ? osgGA::GUIEventAdapter::SCROLL_RIGHT : osgGA::GUIEventAdapter::SCROLL_LEFT);
708 break;
709 case kEventMouseWheelAxisY:
710 getEventQueue()->mouseScroll( (delta < 0) ? osgGA::GUIEventAdapter::SCROLL_DOWN : osgGA::GUIEventAdapter::SCROLL_UP);
711 break;
712 }
713 }
714 }
715 }
716 break;
717
718 // new trackpads and mighty mouse, (not officially documented, see http://developer.apple.com/qa/qa2005/qa1453.html )
719 case 11:
720 {
721 enum
722 {
723 kEventParamMouseWheelSmoothVerticalDelta = 'saxy', // typeSInt32
724 kEventParamMouseWheelSmoothHorizontalDelta = 'saxx' // typeSInt32
725 };
726
727 SInt32 scroll_delta_x = 0;
728 SInt32 scroll_delta_y = 0;
729 OSErr err = noErr;
730 err = GetEventParameter( theEvent, kEventParamMouseWheelSmoothVerticalDelta, typeLongInteger, NULL, sizeof(scroll_delta_y), NULL, &scroll_delta_y );
731 err = GetEventParameter( theEvent, kEventParamMouseWheelSmoothHorizontalDelta, typeLongInteger, NULL, sizeof(scroll_delta_x), NULL, &scroll_delta_x );
732
733 if ((scroll_delta_x != 0) || (scroll_delta_y != 0)) {
734 getEventQueue()->mouseScroll2D( scroll_delta_x, scroll_delta_y);
735 }
736 }
737 break;
738
739 default:
740 return false;
741 }
742 }
743
744 return true;
745 }
746
747
748
handleKeyboardEvent(EventRef theEvent)749 bool GraphicsWindowCarbon::handleKeyboardEvent(EventRef theEvent)
750 {
751 handleModifierKeys(theEvent);
752
753 OSStatus status;
754
755 UInt32 rawkey;
756 GetEventParameter (theEvent,kEventParamKeyCode,typeUInt32, NULL,sizeof(rawkey), NULL,&rawkey);
757
758 // OSG_INFO << "key code: " << rawkey << " modifiers: " << modifierKeys << std::endl;
759
760 UInt32 dataSize;
761 /* jbw check return status so that we don't allocate a huge array */
762 status = GetEventParameter( theEvent, kEventParamKeyUnicodes, typeUnicodeText, NULL, 0, &dataSize, NULL );
763 if (status != noErr) return false;
764 if (dataSize<=1) return false;
765
766 UniChar* uniChars = new UniChar[dataSize+1];
767 GetEventParameter( theEvent, kEventParamKeyUnicodes, typeUnicodeText, NULL, dataSize, NULL, (void*)uniChars );
768
769 unsigned int keychar = remapCarbonKey(static_cast<unsigned long>(uniChars[0]), rawkey);
770
771 switch(GetEventKind(theEvent))
772 {
773 case kEventRawKeyDown:
774 case kEventRawKeyRepeat:
775 {
776 //OSG_INFO << "GraphicsWindowCarbon::keyPress Up" << std::endl;
777 //getEventQueue()->getCurrentEventState()->setModKeyMask(modifierMask);
778 //OSG_INFO << "GraphicsWindowCarbon::keyPress" << std::endl;
779 getEventQueue()->keyPress(keychar);
780 break;
781 }
782
783 case kEventRawKeyUp:
784 {
785 //OSG_INFO << "GraphicsWindowCarbon::keyPress" << std::endl;
786 //getEventQueue()->getCurrentEventState()->setModKeyMask(modifierMask);
787 getEventQueue()->keyRelease(keychar);
788 break;
789 }
790
791 default:
792 break;
793
794 }
795
796 delete[] uniChars;
797
798 return true;
799 }
800
handleModifierKey(UInt32 modifierKey,UInt32 modifierMask,osgGA::GUIEventAdapter::KeySymbol keySymbol)801 void GraphicsWindowCarbon::handleModifierKey(UInt32 modifierKey, UInt32 modifierMask, osgGA::GUIEventAdapter::KeySymbol keySymbol) {
802
803 if ((modifierKey & modifierMask) && !(_lastModifierKeys & modifierMask))
804 {
805 getEventQueue()->keyPress(keySymbol);
806 }
807
808 if (!(modifierKey & modifierMask) && (_lastModifierKeys & modifierMask))
809 {
810 getEventQueue()->keyRelease(keySymbol);
811 }
812 }
813
handleModifierKeys(EventRef theEvent)814 bool GraphicsWindowCarbon::handleModifierKeys(EventRef theEvent)
815 {
816 UInt32 modifierKeys;
817 GetEventParameter (theEvent,kEventParamKeyModifiers,typeUInt32, NULL,sizeof(modifierKeys), NULL,&modifierKeys);
818
819 //std::cout << modifierKeys << std::endl;
820 if (_lastModifierKeys == modifierKeys)
821 return false;
822
823 handleModifierKey(modifierKeys, shiftKey, osgGA::GUIEventAdapter::KEY_Shift_L);
824 handleModifierKey(modifierKeys, controlKey, osgGA::GUIEventAdapter::KEY_Control_L);
825 handleModifierKey(modifierKeys, optionKey, osgGA::GUIEventAdapter::KEY_Alt_L);
826 handleModifierKey(modifierKeys, cmdKey, osgGA::GUIEventAdapter::KEY_Super_L);
827
828 // Caps lock needs some special handling, i did not find a way to get informed when the caps-lock-key gets released
829 if ((modifierKeys & alphaLock) && !(_lastModifierKeys & alphaLock))
830 {
831 getEventQueue()->keyPress(osgGA::GUIEventAdapter::KEY_Caps_Lock);
832 getEventQueue()->keyRelease(osgGA::GUIEventAdapter::KEY_Caps_Lock);
833 }
834
835 if (!(modifierKeys & alphaLock) && (_lastModifierKeys & alphaLock))
836 {
837 getEventQueue()->keyPress(osgGA::GUIEventAdapter::KEY_Caps_Lock);
838 getEventQueue()->keyRelease(osgGA::GUIEventAdapter::KEY_Caps_Lock);
839 }
840
841 _lastModifierKeys = modifierKeys;
842 return true;
843 }
844
845
846
checkEvents()847 bool GraphicsWindowCarbon::checkEvents()
848 {
849 if (!_realized) return false;
850
851 EventRef theEvent;
852 EventTargetRef theTarget = GetEventDispatcherTarget();
853 while (ReceiveNextEvent(0, NULL, 0,true, &theEvent)== noErr)
854 {
855 switch(GetEventClass(theEvent))
856 {
857 case kEventClassMouse:
858 {
859 // handle the menubar
860 Point wheresMyMouse;
861 GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(wheresMyMouse), NULL, &wheresMyMouse);
862
863 EventMouseButton mouseButton = 0;
864 GetEventParameter (theEvent, kEventParamMouseButton, typeMouseButton, NULL, sizeof(mouseButton), NULL, &mouseButton);
865
866 WindowRef win;
867 int fwres = FindWindow(wheresMyMouse, &win);
868
869 if ((fwres == inMenuBar) && (mouseButton >= 1)) {
870 MenuSelect(wheresMyMouse);
871 HiliteMenu(0);
872 return !(getEventQueue()->empty());
873 }
874 break;
875 }
876
877 case kEventClassApplication:
878 switch (GetEventKind(theEvent)) {
879 case kEventAppQuit:
880 getEventQueue()->quitApplication();
881 break;
882 }
883 break;
884
885 case kEventClassAppleEvent:
886 {
887 EventRecord eventRecord;
888 ConvertEventRefToEventRecord(theEvent, &eventRecord);
889 AEProcessAppleEvent(&eventRecord);
890 return;
891 }
892 break;
893
894 }
895 SendEventToEventTarget (theEvent, theTarget);
896 ReleaseEvent(theEvent);
897 }
898 if (_closeRequested)
899 getEventQueue()->closeWindow();
900
901 if (s_quit_requested) {
902 getEventQueue()->quitApplication();
903 s_quit_requested = false;
904 }
905
906 return !(getEventQueue()->empty());
907 }
908
909
setWindowRectangleImplementation(int x,int y,int width,int height)910 bool GraphicsWindowCarbon::setWindowRectangleImplementation(int x, int y, int width, int height)
911 {
912 int screenLeft(0), screenTop(0);
913 DarwinWindowingSystemInterface* wsi = dynamic_cast<DarwinWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
914 if (wsi)
915 {
916 wsi->getScreenTopLeft((*_traits), screenLeft, screenTop);
917 }
918
919 Rect bounds = {y + screenTop, x + screenLeft, y + height + screenTop, x + width + screenLeft};
920 SetWindowBounds(getNativeWindowRef(), kWindowContentRgn, &bounds);
921 aglUpdateContext(_context);
922 MenubarController::instance()->update();
923 return true;
924 }
925
926
927
adaptResize(int x,int y,int w,int h)928 void GraphicsWindowCarbon::adaptResize(int x, int y, int w, int h)
929 {
930 DarwinWindowingSystemInterface* wsi = dynamic_cast<DarwinWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
931 int screenLeft(0), screenTop(0);
932 if (wsi) {
933
934 // get the screen containing the window
935 unsigned int screenNdx = wsi->getScreenContaining(x,y,w,h);
936
937 // update traits
938 _traits->screenNum = screenNdx;
939
940 // get top left of screen
941 wsi->getScreenTopLeft((*_traits), screenLeft, screenTop);
942 }
943
944 resized(x-screenLeft,y-screenTop,w,h);
945 }
946
947
948
grabFocus()949 void GraphicsWindowCarbon::grabFocus()
950 {
951 SelectWindow(_window);
952 }
953
954
955
grabFocusIfPointerInWindow()956 void GraphicsWindowCarbon::grabFocusIfPointerInWindow()
957 {
958 // TODO: implement
959 OSG_NOTIFY(osg::ALWAYS) << "GraphicsWindowCarbon::grabFocusIfPointerInWindow: not implemented" << std::endl;
960 }
961
962
useCursor(bool cursorOn)963 void GraphicsWindowCarbon::useCursor(bool cursorOn)
964 {
965 if (_traits.valid())
966 _traits->useCursor = cursorOn;
967 DarwinWindowingSystemInterface* wsi = dynamic_cast<DarwinWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
968 if (wsi == NULL) {
969 OSG_WARN << "GraphicsWindowCarbon::useCursor: could not get OSXCarbonWindowingSystemInterface" << std::endl;
970 return;
971 }
972
973 CGDirectDisplayID displayId = wsi->getDisplayID((*_traits));
974 CGDisplayErr err = (cursorOn ? CGDisplayShowCursor(displayId) : CGDisplayHideCursor(displayId));
975 if (err != kCGErrorSuccess) {
976 OSG_WARN << "GraphicsWindowCarbon::useCursor: failed with " << err << std::endl;
977 }
978 }
979
980 // FIXME: need to implement all cursor types
981 // FIXME: I used deprecated functions, but don't know if there are any substitutable newer functions...
setCursor(MouseCursor mouseCursor)982 void GraphicsWindowCarbon::setCursor(MouseCursor mouseCursor)
983 {
984 if (_currentCursor == mouseCursor)
985 return;
986
987 UInt32 cursor;
988 switch (mouseCursor)
989 {
990 case NoCursor:
991 HideCursor();
992 _currentCursor = mouseCursor;
993 return;
994 case RightArrowCursor:
995 cursor = kThemeArrowCursor;
996 break;
997 case CrosshairCursor:
998 cursor = kThemeCrossCursor;
999 break;
1000 case TextCursor:
1001 cursor = kThemeIBeamCursor;
1002 break;
1003 case UpDownCursor:
1004 cursor = kThemeResizeUpDownCursor;
1005 break;
1006 case LeftRightCursor:
1007 cursor = kThemeResizeLeftRightCursor;
1008 break;
1009 default:
1010 cursor = kThemeArrowCursor;
1011 OSG_WARN << "GraphicsWindowCarbon::setCursor doesn't implement cursor: type = " << mouseCursor << std::endl;
1012 }
1013
1014 _currentCursor = mouseCursor;
1015 SetThemeCursor(cursor);
1016 ShowCursor();
1017 }
1018
setSyncToVBlank(bool on)1019 void GraphicsWindowCarbon::setSyncToVBlank(bool on)
1020 {
1021 if (_traits.valid()) {
1022 _traits->vsync = on;
1023 }
1024 }
1025
setWindowName(const std::string & name)1026 void GraphicsWindowCarbon::setWindowName (const std::string& name)
1027 {
1028 _traits->windowName = name;
1029 if (!_traits->windowName.empty())
1030 {
1031 CFStringRef windowtitle = CFStringCreateWithBytes( kCFAllocatorDefault, (const UInt8*)(_traits->windowName.c_str()), _traits->windowName.length(),kCFStringEncodingUTF8, false );
1032 SetWindowTitleWithCFString( _window, windowtitle );
1033 CFRelease(windowtitle);
1034 }
1035 }
1036
requestWarpPointer(float x,float y)1037 void GraphicsWindowCarbon::requestWarpPointer(float x,float y)
1038 {
1039 if (!_realized)
1040 {
1041 OSG_INFO<<"GraphicsWindowCarbon::requestWarpPointer() - Window not realized; cannot warp pointer, screenNum="<< _traits->screenNum<<std::endl;
1042 return;
1043 }
1044
1045 DarwinWindowingSystemInterface* wsi = dynamic_cast<DarwinWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
1046 if (wsi == NULL)
1047 {
1048 OSG_WARN << "GraphicsWindowCarbon::useCursor: could not get OSXCarbonWindowingSystemInterface" << std::endl;
1049 return;
1050 }
1051
1052 CGDirectDisplayID displayId = wsi->getDisplayID((*_traits));
1053
1054 CGPoint point;
1055 point.x = x + _traits->x;
1056 point.y = y + _traits->y;
1057 CGDisplayMoveCursorToPoint(displayId, point);
1058
1059 getEventQueue()->mouseWarped(x,y);
1060 }
1061
1062
transformMouseXY(float & x,float & y)1063 void GraphicsWindowCarbon::transformMouseXY(float& x, float& y)
1064 {
1065 if (getEventQueue()->getUseFixedMouseInputRange())
1066 {
1067 osgGA::GUIEventAdapter* eventState = getEventQueue()->getCurrentEventState();
1068 x = eventState->getXmin() + (eventState->getXmax()-eventState->getXmin())*x/float(_traits->width);
1069 y = eventState->getYmin() + (eventState->getYmax()-eventState->getYmin())*y/float(_traits->height);
1070 }
1071 }
1072
1073 class CarbonWindowingSystemInterface : public DarwinWindowingSystemInterface {
1074 public:
CarbonWindowingSystemInterface()1075 CarbonWindowingSystemInterface() : DarwinWindowingSystemInterface()
1076 {
1077 }
1078
createGraphicsContext(osg::GraphicsContext::Traits * traits)1079 virtual osg::GraphicsContext* createGraphicsContext(osg::GraphicsContext::Traits* traits)
1080 {
1081 _init();
1082
1083 return createGraphicsContextImplementation<PixelBufferCarbon, GraphicsWindowCarbon>(traits);
1084 }
1085
_init()1086 virtual void _init()
1087 {
1088 if (_initialized) return;
1089
1090 DarwinWindowingSystemInterface::_init();
1091
1092 // register application event handler and AppleEventHandler to get quit-events:
1093 static const EventTypeSpec menueventSpec = {kEventClassCommand, kEventCommandProcess};
1094 OSErr status = InstallEventHandler(GetApplicationEventTarget(), NewEventHandlerUPP(ApplicationEventHandler), 1, &menueventSpec, 0, NULL);
1095 status = AEInstallEventHandler( kCoreEventClass, kAEQuitApplication, NewAEEventHandlerUPP(QuitAppleEventHandler), 0, false);
1096 }
1097
1098 };
1099
1100 REGISTER_WINDOWINGSYSTEMINTERFACE(Carbon, CarbonWindowingSystemInterface)
1101
1102 }
1103
1104 #endif
1105
1106