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