root/OpenSceneGraph/trunk/src/osgViewer/GraphicsWindowCocoa.mm @ 13376

Revision 13376, 57.5 kB (checked in by robert, 15 hours ago)

Centralized the calling of #include <stdint.h> and VS fallback into include/osg/Types header

  • Property svn:eol-style set to native
Line 
1/*
2 *  GraphicsWindowCocoa.cpp
3 *  OpenSceneGraph
4 *
5 *  Created by Stephan Huber on 27.06.08.
6 *  Copyright 2008 Stephan Maximilian Huber, digital mind. All rights reserved.
7 *
8 *  Some code borrowed from the implementation of CocoaViewer,
9 *  Created by Eric Wing on 11/12/06. and ported by Martin Lavery 7/06/07
10 *
11 *  Other snippets are borrowed from the Cocoa-implementation of the SDL-lib
12 */
13
14#include <iostream>
15#include <osgViewer/api/Cocoa/PixelBufferCocoa>
16#include <osgViewer/api/Cocoa/GraphicsWindowCocoa>
17
18#include <Cocoa/Cocoa.h>
19
20#include "DarwinUtils.h"
21
22//#define DEBUG_OUT(s) std::cout << "GraphicsWindowCocoa :: " << s << std::endl;
23#define DEBUG_OUT(s) ;
24
25static bool s_quit_requested = false;
26
27#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
28@interface NSApplication(NSAppleMenu)
29- (void)setAppleMenu:(NSMenu *)menu;
30@end
31#endif
32
33
34// ----------------------------------------------------------------------------------------------------------
35// small helper class remapping key-codes
36// ----------------------------------------------------------------------------------------------------------
37// small helper class which maps the raw key codes to osgGA::GUIEventAdapter::Keys
38
39class CocoaKeyboardMap {
40
41    public:
42        CocoaKeyboardMap()
43        {
44            _keymap[27]     = osgGA::GUIEventAdapter::KEY_Escape;
45            _keymap[13]     = osgGA::GUIEventAdapter::KEY_Return;
46            _keymap[3]      = osgGA::GUIEventAdapter::KEY_KP_Enter;
47            _keymap[9]      = osgGA::GUIEventAdapter::KEY_Tab;
48            _keymap[32]     = osgGA::GUIEventAdapter::KEY_Space;
49            _keymap[127]    = osgGA::GUIEventAdapter::KEY_BackSpace;
50
51
52            _keymap[NSHomeFunctionKey]          = osgGA::GUIEventAdapter::KEY_Home;
53            _keymap[NSEndFunctionKey]           = osgGA::GUIEventAdapter::KEY_End;
54            _keymap[NSPageUpFunctionKey]        = osgGA::GUIEventAdapter::KEY_Page_Up;
55            _keymap[NSPageDownFunctionKey]      = osgGA::GUIEventAdapter::KEY_Page_Down;
56            _keymap[NSLeftArrowFunctionKey]     = osgGA::GUIEventAdapter::KEY_Left;
57            _keymap[NSRightArrowFunctionKey]    = osgGA::GUIEventAdapter::KEY_Right;
58            _keymap[NSUpArrowFunctionKey]       = osgGA::GUIEventAdapter::KEY_Up;
59            _keymap[NSDownArrowFunctionKey]     = osgGA::GUIEventAdapter::KEY_Down;
60
61            _keymap[NSDeleteFunctionKey]        = osgGA::GUIEventAdapter::KEY_Delete;
62
63            _keymap[NSF1FunctionKey]  = osgGA::GUIEventAdapter::KEY_F1;
64            _keymap[NSF2FunctionKey]  = osgGA::GUIEventAdapter::KEY_F2;
65            _keymap[NSF3FunctionKey]  = osgGA::GUIEventAdapter::KEY_F3;
66            _keymap[NSF4FunctionKey]  = osgGA::GUIEventAdapter::KEY_F4;
67            _keymap[NSF5FunctionKey]  = osgGA::GUIEventAdapter::KEY_F5;
68            _keymap[NSF6FunctionKey]  = osgGA::GUIEventAdapter::KEY_F6;
69            _keymap[NSF7FunctionKey]  = osgGA::GUIEventAdapter::KEY_F7;
70            _keymap[NSF8FunctionKey]  = osgGA::GUIEventAdapter::KEY_F8;
71            _keymap[NSF9FunctionKey]  = osgGA::GUIEventAdapter::KEY_F9;
72
73            _keymap[NSF10FunctionKey]  = osgGA::GUIEventAdapter::KEY_F10;
74            _keymap[NSF11FunctionKey]  = osgGA::GUIEventAdapter::KEY_F11;
75            _keymap[NSF12FunctionKey]  = osgGA::GUIEventAdapter::KEY_F12;
76            _keymap[NSF13FunctionKey]  = osgGA::GUIEventAdapter::KEY_F13;
77            _keymap[NSF14FunctionKey]  = osgGA::GUIEventAdapter::KEY_F14;
78            _keymap[NSF15FunctionKey]  = osgGA::GUIEventAdapter::KEY_F15;
79            _keymap[NSF16FunctionKey]  = osgGA::GUIEventAdapter::KEY_F16;
80            _keymap[NSF17FunctionKey]  = osgGA::GUIEventAdapter::KEY_F17;
81            _keymap[NSF18FunctionKey]  = osgGA::GUIEventAdapter::KEY_F18;
82            _keymap[NSF19FunctionKey]  = osgGA::GUIEventAdapter::KEY_F19;
83
84            _keymap[NSF20FunctionKey]  = osgGA::GUIEventAdapter::KEY_F20;
85            _keymap[NSF21FunctionKey]  = osgGA::GUIEventAdapter::KEY_F21;
86            _keymap[NSF22FunctionKey]  = osgGA::GUIEventAdapter::KEY_F22;
87            _keymap[NSF23FunctionKey]  = osgGA::GUIEventAdapter::KEY_F23;
88            _keymap[NSF24FunctionKey]  = osgGA::GUIEventAdapter::KEY_F24;
89            _keymap[NSF25FunctionKey]  = osgGA::GUIEventAdapter::KEY_F25;
90            _keymap[NSF26FunctionKey]  = osgGA::GUIEventAdapter::KEY_F26;
91            _keymap[NSF27FunctionKey]  = osgGA::GUIEventAdapter::KEY_F27;
92            _keymap[NSF28FunctionKey]  = osgGA::GUIEventAdapter::KEY_F28;
93            _keymap[NSF29FunctionKey]  = osgGA::GUIEventAdapter::KEY_F29;
94
95            _keymap[NSF30FunctionKey]  = osgGA::GUIEventAdapter::KEY_F30;
96            _keymap[NSF31FunctionKey]  = osgGA::GUIEventAdapter::KEY_F31;
97            _keymap[NSF32FunctionKey]  = osgGA::GUIEventAdapter::KEY_F32;
98            _keymap[NSF33FunctionKey]  = osgGA::GUIEventAdapter::KEY_F33;
99            _keymap[NSF34FunctionKey]  = osgGA::GUIEventAdapter::KEY_F34;
100            _keymap[NSF35FunctionKey]  = osgGA::GUIEventAdapter::KEY_F35;
101
102
103            _keypadmap['='] = osgGA::GUIEventAdapter::KEY_KP_Equal;
104            _keypadmap['*'] = osgGA::GUIEventAdapter::KEY_KP_Multiply;
105            _keypadmap['+'] = osgGA::GUIEventAdapter::KEY_KP_Add;
106            _keypadmap['-'] = osgGA::GUIEventAdapter::KEY_KP_Subtract;
107            _keypadmap['.'] = osgGA::GUIEventAdapter::KEY_KP_Decimal;
108            _keypadmap['/'] = osgGA::GUIEventAdapter::KEY_KP_Divide;
109
110            _keypadmap['0'] = osgGA::GUIEventAdapter::KEY_KP_0;
111            _keypadmap['1'] = osgGA::GUIEventAdapter::KEY_KP_1;
112            _keypadmap['2'] = osgGA::GUIEventAdapter::KEY_KP_2;
113            _keypadmap['3'] = osgGA::GUIEventAdapter::KEY_KP_3;
114            _keypadmap['4'] = osgGA::GUIEventAdapter::KEY_KP_4;
115            _keypadmap['5'] = osgGA::GUIEventAdapter::KEY_KP_5;
116            _keypadmap['6'] = osgGA::GUIEventAdapter::KEY_KP_6;
117            _keypadmap['7'] = osgGA::GUIEventAdapter::KEY_KP_7;
118            _keypadmap['8'] = osgGA::GUIEventAdapter::KEY_KP_8;
119            _keypadmap['9'] = osgGA::GUIEventAdapter::KEY_KP_9;
120        }
121
122        ~CocoaKeyboardMap() {
123        }
124
125        unsigned int remapKey(unsigned int key, bool pressedOnKeypad = false)
126        {
127            if (pressedOnKeypad) {
128                KeyMap::iterator itr = _keypadmap.find(key);
129                if (itr == _keypadmap.end()) return key;
130                else return itr->second;
131            }
132
133            KeyMap::iterator itr = _keymap.find(key);
134            if (itr == _keymap.end()) return key;
135            else return itr->second;
136        }
137    private:
138        typedef std::map<unsigned int, osgGA::GUIEventAdapter::KeySymbol> KeyMap;
139        KeyMap _keymap, _keypadmap;
140};
141
142
143// ----------------------------------------------------------------------------------------------------------
144// remapCocoaKey
145// ----------------------------------------------------------------------------------------------------------
146static unsigned int remapCocoaKey(unsigned int key, unsigned int modifiers)
147{
148    static CocoaKeyboardMap s_CocoaKeyboardMap;
149
150    bool pressedOnKeypad = modifiers & NSNumericPadKeyMask;
151    if (modifiers & NSFunctionKeyMask)
152        pressedOnKeypad = false;
153
154    //std::cout << std::hex << "remap " << key << " keypad: " << pressedOnKeypad << " modifiers: " << modifiers << std::endl;
155
156    return s_CocoaKeyboardMap.remapKey(key, pressedOnKeypad);
157}
158
159
160std::ostream& operator<<(std::ostream& os, const NSRect& rect)
161{
162    os << rect.origin.x << "/" << rect.origin.y << " " << rect.size.width << "x" << rect.size.height;
163    return os;
164}
165
166// ----------------------------------------------------------------------------------------------------------
167// Cocoa uses a coordinate system where its origin is in the bottom left corner,
168// osg and quartz uses top left for the origin
169//
170// these 2 methods convets rects between the different coordinate systems
171// ----------------------------------------------------------------------------------------------------------
172
173static NSRect convertFromQuartzCoordinates(const NSRect& rect)
174{
175    NSRect frame = [[[NSScreen screens] objectAtIndex: 0] frame];
176    float y = frame.size.height - rect.origin.y - rect.size.height;
177    NSRect converted = NSMakeRect(rect.origin.x, y, rect.size.width, rect.size.height);
178
179    // std::cout << "converting from Quartz " << rect << " to " << converted << " using screen rect " << frame << std::endl;
180
181    return converted;
182}
183
184static NSRect convertToQuartzCoordinates(const NSRect& rect)
185{
186    NSRect frame = [[[NSScreen screens] objectAtIndex: 0] frame];
187
188    float y = frame.size.height - (rect.origin.y + rect.size.height);
189    NSRect converted = NSMakeRect(rect.origin.x, y, rect.size.width, rect.size.height);
190
191    // std::cout << "converting To Quartz   " << rect << " to " << converted << " using screen rect " << frame << std::endl;
192
193    return converted;
194}
195
196#pragma mark CocoaAppDelegate
197
198// ----------------------------------------------------------------------------------------------------------
199// the app-delegate, handling quit-requests
200// ----------------------------------------------------------------------------------------------------------
201
202@interface CocoaAppDelegate : NSObject
203{
204}
205
206- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
207- (void)applicationDidFinishLaunching:(NSNotification *)aNotification;
208@end
209
210@implementation CocoaAppDelegate
211- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
212{
213    s_quit_requested = true;
214    DEBUG_OUT("quit requested ");
215    return NSTerminateNow;
216}
217
218- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
219{
220    DEBUG_OUT("applicationDidFinishLaunching");
221}
222
223@end
224
225#pragma mark GraphicsWindowCocoaWindow
226
227// ----------------------------------------------------------------------------------------------------------
228// GraphicsWindowCocoaWindow, implements canBecomeKeyWindow + canBecomeMainWindow
229// ----------------------------------------------------------------------------------------------------------
230
231@interface GraphicsWindowCocoaWindow : NSWindow
232{
233}
234
235- (BOOL) canBecomeKeyWindow;
236- (BOOL) canBecomeMainWindow;
237
238@end
239
240@implementation GraphicsWindowCocoaWindow
241
242
243- (BOOL) canBecomeKeyWindow
244{
245    return YES;
246}
247
248- (BOOL) canBecomeMainWindow
249{
250    return YES;
251}
252
253@end
254
255#pragma mark GraphicsWindowCocoaGLView
256
257
258// ----------------------------------------------------------------------------------------------------------
259// GraphicsWindowCocoaGLView
260// custom NSOpenGLView-class handling mouse- and keyboard-events, forwarding them to the EventQueue
261// some code borrowed from the example osgCocoaViewer from E.Wing
262// ----------------------------------------------------------------------------------------------------------
263
264@interface GraphicsWindowCocoaGLView : NSOpenGLView
265{
266    @private
267        osgViewer::GraphicsWindowCocoa* _win;
268        BOOL _isUsingCtrlClick, _isUsingOptionClick;
269        unsigned int _cachedModifierFlags;
270        BOOL _handleTabletEvents;
271
272        NSMutableDictionary* _touchPoints;
273        unsigned int _lastTouchPointId;
274       
275}
276- (void)setGraphicsWindowCocoa: (osgViewer::GraphicsWindowCocoa*) win;
277
278- (void)keyDown:(NSEvent *)theEvent;
279- (void)keyUp:(NSEvent *)theEvent;
280- (void)flagsChanged:(NSEvent *)theEvent;
281- (void) mouseMoved:(NSEvent*)theEvent;
282- (void) mouseDown:(NSEvent*)theEvent;
283- (void) mouseDragged:(NSEvent*)theEvent;
284- (void) mouseUp:(NSEvent*)theEvent;
285- (void) rightMouseDown:(NSEvent*)theEvent;
286- (void) rightMouseDragged:(NSEvent*)theEvent;
287- (void) rightMouseUp:(NSEvent*)theEvent;
288- (void) otherMouseDown:(NSEvent*)theEvent;
289- (void) otherMouseDragged:(NSEvent*)theEvent;
290- (void) otherMouseUp:(NSEvent*)theEvent;
291
292- (NSPoint) getLocalPoint: (NSEvent*)theEvent;
293- (void) handleModifiers: (NSEvent*)theEvent;
294- (void) setIsUsingCtrlClick:(BOOL)is_using_ctrl_click;
295- (BOOL) isUsingCtrlClick;
296- (void) setIsUsingOptionClick:(BOOL)is_using_option_click;
297- (BOOL) isUsingOptionClick;
298
299- (void) doLeftMouseButtonDown:(NSEvent*)theEvent;
300- (void) doLeftMouseButtonUp:(NSEvent*)theEvent;
301- (void) doRightMouseButtonDown:(NSEvent*)theEvent;
302- (void) doRightMouseButtonUp:(NSEvent*)theEvent;
303- (void) doMiddleMouseButtonDown:(NSEvent*)theEvent;
304- (void) doExtraMouseButtonDown:(NSEvent*)theEvent buttonNumber:(int)button_number;
305- (void) doMiddleMouseButtonUp:(NSEvent*)theEvent;
306- (void) doExtraMouseButtonUp:(NSEvent*)theEvent buttonNumber:(int)button_number;
307- (void) scrollWheel:(NSEvent*)theEvent;
308
309- (void)tabletPoint:(NSEvent *)theEvent;
310- (void)tabletProximity:(NSEvent *)theEvent;
311- (void)handleTabletEvents:(NSEvent*)theEvent;
312
313#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6)
314- (osgGA::GUIEventAdapter::TouchPhase) convertTouchPhase: (NSTouchPhase) phase;
315- (unsigned int)computeTouchId: (NSTouch*) touch;
316- (void)touchesBeganWithEvent:(NSEvent *)event;
317- (void)touchesMovedWithEvent:(NSEvent *)event;
318- (void)touchesEndedWithEvent:(NSEvent *)event;
319- (void)touchesCancelledWithEvent:(NSEvent *)event;
320#endif
321
322- (BOOL)useMultiTouchOnly: (NSEvent*) event;
323
324- (BOOL)acceptsFirstResponder;
325- (BOOL)becomeFirstResponder;
326- (BOOL)resignFirstResponder;
327
328- (void)dealloc;
329
330@end
331
332@implementation GraphicsWindowCocoaGLView
333
334
335-(void) setGraphicsWindowCocoa: (osgViewer::GraphicsWindowCocoa*) win
336{
337    _win = win;
338    _touchPoints = NULL;
339}
340
341-(void) dealloc
342{
343    if (_touchPoints) [_touchPoints release];
344    [super dealloc];
345}
346
347- (BOOL)acceptsFirstResponder
348{
349    return YES;
350}
351
352- (BOOL)becomeFirstResponder
353{
354    return YES;
355}
356
357- (BOOL)resignFirstResponder
358{
359    return YES;
360}
361
362- (BOOL) useMultiTouchOnly: (NSEvent*) event
363{
364#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6)
365    return ([self acceptsTouchEvents] && ([event subtype] == NSTouchEventSubtype));
366#else
367    return false;
368#endif
369}
370
371
372- (NSPoint) getLocalPoint: (NSEvent*)theEvent
373{
374    return  [self convertPoint:[theEvent locationInWindow] fromView:nil];
375}
376
377
378- (void) handleModifiers: (NSEvent*)theEvent
379{
380    DEBUG_OUT("handling modifiers");
381
382    if ((!_win) || (!_win->getEventQueue()))
383        return; // no event    queue in place
384
385    unsigned int flags = [theEvent modifierFlags];
386
387    if (flags == _cachedModifierFlags)
388        return;
389
390    const unsigned int masks[] = {
391        NSShiftKeyMask,
392        NSControlKeyMask,
393        NSAlternateKeyMask,
394        NSCommandKeyMask,
395        NSAlphaShiftKeyMask
396    };
397
398    const unsigned int keys[] = {
399        osgGA::GUIEventAdapter::KEY_Shift_L,
400        osgGA::GUIEventAdapter::KEY_Control_L,
401        osgGA::GUIEventAdapter::KEY_Alt_L,
402        osgGA::GUIEventAdapter::KEY_Super_L,
403        osgGA::GUIEventAdapter::KEY_Caps_Lock
404    };
405
406    // std::cout << "flags: " << flags << " cached: " << _cachedModifierFlags << std::endl;
407
408    for(unsigned int i = 0; i < 5; ++i) {
409
410        if ((flags & masks[i]) && !(_cachedModifierFlags & masks[i]))
411        {
412            _win->getEventQueue()->keyPress(keys[i], _win->getEventQueue()->getTime(), keys[i]);
413
414            // we don't get a key up for the caps lock so emulate it.
415            if (i == 4)
416                _win->getEventQueue()->keyRelease(keys[i], _win->getEventQueue()->getTime(), keys[i]);
417        }
418
419        if (!(flags & masks[i]) && (_cachedModifierFlags & masks[i]))
420        {
421            if (i == 4) {
422                // emulate a key down for caps-lock.
423                _win->getEventQueue()->keyPress(keys[i], _win->getEventQueue()->getTime(), keys[i]);
424            }
425
426            _win->getEventQueue()->keyRelease(keys[i], _win->getEventQueue()->getTime(), keys[i]);
427        }
428    }
429
430    _cachedModifierFlags = flags;
431
432}
433
434- (void)flagsChanged:(NSEvent *)theEvent {
435    [self handleModifiers: theEvent];
436}
437
438- (void) mouseMoved:(NSEvent*)theEvent
439{
440    // if multitouch is enabled, disable standard event handling
441    if ([self useMultiTouchOnly: theEvent])
442        return;
443   
444    NSPoint converted_point = [self getLocalPoint: theEvent];
445    DEBUG_OUT("Mouse moved" << converted_point.x << "/" << converted_point.y);
446    _win->getEventQueue()->mouseMotion(converted_point.x, converted_point.y);
447}
448
449
450
451- (void) mouseDown:(NSEvent*)theEvent
452{
453    // if multitouch is enabled, disable standard event handling
454    if ([self useMultiTouchOnly: theEvent])
455        return;
456       
457    DEBUG_OUT("Mouse down");
458    // Because many Mac users have only a 1-button mouse, we should provide ways
459    // to access the button 2 and 3 actions of osgViewer.
460    // I will use the Ctrl modifer to represent right-clicking
461    // and Option modifier to represent middle clicking.
462    if([theEvent modifierFlags] & NSControlKeyMask)
463    {
464        [self setIsUsingCtrlClick:YES];
465        [self doRightMouseButtonDown:theEvent];
466    }
467    else if([theEvent modifierFlags] & NSAlternateKeyMask)
468    {
469        [self setIsUsingOptionClick:YES];
470        [self doMiddleMouseButtonDown:theEvent];
471    }
472    else
473    {
474        [self doLeftMouseButtonDown:theEvent];
475    }
476
477    if ([theEvent subtype] == NSTabletPointEventSubtype) {
478        _handleTabletEvents = true;
479        [self handleTabletEvents:theEvent];
480    }
481}
482
483
484- (void) mouseDragged:(NSEvent*)theEvent
485{
486    // if multitouch is enabled, disable standard event handling
487    if ([self useMultiTouchOnly: theEvent])
488        return;
489   
490    if (!_win) return;
491
492    NSPoint converted_point = [self getLocalPoint: theEvent];
493    _win->getEventQueue()->mouseMotion(converted_point.x, converted_point.y);
494
495    if (_handleTabletEvents)
496        [self handleTabletEvents:theEvent];
497}
498
499
500- (void) mouseUp:(NSEvent*)theEvent
501{
502    // if multitouch is enabled, disable standard event handling
503    if ([self useMultiTouchOnly: theEvent])
504        return;
505       
506    // Because many Mac users have only a 1-button mouse, we should provide ways
507    // to access the button 2 and 3 actions of osgViewer.
508    // I will use the Ctrl modifer to represent right-clicking
509    // and Option modifier to represent middle clicking.
510    if([self isUsingCtrlClick] == YES)
511    {
512        [self setIsUsingCtrlClick:NO];
513        [self doRightMouseButtonUp:theEvent];
514    }
515    else if([self isUsingOptionClick] == YES)
516    {
517        [self setIsUsingOptionClick:NO];
518        [self doMiddleMouseButtonUp:theEvent];
519    }
520    else
521    {
522        [self doLeftMouseButtonUp:theEvent];
523    }
524    _handleTabletEvents = false;
525}
526
527- (void) rightMouseDown:(NSEvent*)theEvent
528{
529    [self doRightMouseButtonDown:theEvent];
530}
531
532- (void) rightMouseDragged:(NSEvent*)theEvent
533{
534    if (!_win) return;
535
536    NSPoint converted_point = [self getLocalPoint: theEvent];
537    _win->getEventQueue()->mouseMotion(converted_point.x, converted_point.y);
538}
539
540- (void) rightMouseUp:(NSEvent*)theEvent
541{
542    [self doRightMouseButtonUp:theEvent];
543    _handleTabletEvents = false;
544}
545
546// "otherMouse" seems to capture middle button and any other buttons beyond (4th, etc).
547- (void) otherMouseDown:(NSEvent*)theEvent
548{
549    // Button 0 is left
550    // Button 1 is right
551    // Button 2 is middle
552    // Button 3 keeps going
553    // osgViewer expects 1 for left, 3 for right, 2 for middle
554    // osgViewer has a reversed number mapping for right and middle compared to Cocoa
555    if([theEvent buttonNumber] == 2)
556    {
557        [self doMiddleMouseButtonDown:theEvent];
558    }
559    else // buttonNumber should be 3,4,5,etc; must map to 4,5,6,etc in osgViewer
560    {
561        [self doExtraMouseButtonDown:theEvent buttonNumber:[theEvent buttonNumber]];
562    }
563}
564
565- (void) otherMouseDragged:(NSEvent*)theEvent
566{
567    if (!_win) return;
568
569    NSPoint converted_point = [self getLocalPoint: theEvent];
570    _win->getEventQueue()->mouseMotion(converted_point.x, converted_point.y);
571
572}
573
574// "otherMouse" seems to capture middle button and any other buttons beyond (4th, etc).
575- (void) otherMouseUp:(NSEvent*)theEvent
576{
577
578    // Button 0 is left
579    // Button 1 is right
580    // Button 2 is middle
581    // Button 3 keeps going
582    // osgViewer expects 1 for left, 3 for right, 2 for middle
583    // osgViewer has a reversed number mapping for right and middle compared to Cocoa
584    if([theEvent buttonNumber] == 2)
585    {
586        [self doMiddleMouseButtonUp:theEvent];
587    }
588    else // buttonNumber should be 3,4,5,etc; must map to 4,5,6,etc in osgViewer
589    {
590        // I don't think osgViewer does anything for these additional buttons,
591        // but just in case, pass them along. But as a Cocoa programmer, you might
592        // think about things you can do natively here instead of passing the buck.
593        [self doExtraMouseButtonUp:theEvent buttonNumber:[theEvent buttonNumber]];
594    }
595}
596
597- (void) setIsUsingCtrlClick:(BOOL)is_using_ctrl_click
598{
599    _isUsingCtrlClick = is_using_ctrl_click;
600}
601
602- (BOOL) isUsingCtrlClick
603{
604    return _isUsingCtrlClick;
605}
606
607- (void) setIsUsingOptionClick:(BOOL)is_using_option_click
608{
609    _isUsingOptionClick = is_using_option_click;
610}
611
612- (BOOL) isUsingOptionClick
613{
614    return _isUsingOptionClick;
615}
616
617
618- (void)doSingleOrDoubleButtonPress:(NSEvent*)event forButton:(int)button
619{
620    if (!_win)
621    {
622        return;
623    }
624
625    NSPoint convertedPoint = [self getLocalPoint:event];
626    if ([event clickCount] == 1)
627    {
628        _win->getEventQueue()->mouseButtonPress(convertedPoint.x, convertedPoint.y, button);
629    }
630    else
631    {
632        _win->getEventQueue()->mouseDoubleButtonPress(convertedPoint.x, convertedPoint.y, button);
633    }
634
635    //[self setNeedsDisplay:YES];
636}
637
638- (void)doButtonRelease:(NSEvent*)event forButton:(int)button
639{
640    if (!_win)
641    {
642        return;
643    }
644
645    NSPoint convertedPoint = [self getLocalPoint:event];
646    _win->getEventQueue()->mouseButtonRelease(convertedPoint.x, convertedPoint.y, button);
647    //[self setNeedsDisplay:YES];
648}
649
650
651// Left mouse button
652- (void) doLeftMouseButtonDown:(NSEvent*)theEvent
653{
654    [self doSingleOrDoubleButtonPress:theEvent forButton:1];
655}
656
657- (void) doLeftMouseButtonUp:(NSEvent*)theEvent
658{
659    [self doButtonRelease:theEvent forButton:1];
660}
661
662// Right mouse button
663- (void) doRightMouseButtonDown:(NSEvent*)theEvent
664{
665    [self doSingleOrDoubleButtonPress:theEvent forButton:3];
666}
667
668- (void) doRightMouseButtonUp:(NSEvent*)theEvent
669{
670    [self doButtonRelease:theEvent forButton:3];
671}
672
673// Middle mouse button
674- (void) doMiddleMouseButtonDown:(NSEvent*)theEvent
675{
676    [self doSingleOrDoubleButtonPress:theEvent forButton:2];
677}
678
679- (void) doMiddleMouseButtonUp:(NSEvent*)theEvent
680{
681    [self doButtonRelease:theEvent forButton:2];
682}
683
684// Extra mouse buttons
685- (void) doExtraMouseButtonDown:(NSEvent*)theEvent buttonNumber:(int)button_number
686{
687    [self doButtonRelease:theEvent forButton:(button_number+1)];
688}
689
690- (void) doExtraMouseButtonUp:(NSEvent*)theEvent buttonNumber:(int)button_number
691{
692    [self doButtonRelease:theEvent forButton:(button_number+1)];
693}
694
695
696- (void) scrollWheel:(NSEvent*)theEvent
697{
698    if (!_win) return;
699
700    // Unfortunately, it turns out mouseScroll2D doesn't actually do anything.
701    // The camera manipulators don't seem to implement any code that utilize the scroll values.
702    // This this call does nothing.
703    _win->getEventQueue()->mouseScroll2D([theEvent deltaX], [theEvent deltaY]);
704}
705
706
707
708- (void)keyDown:(NSEvent *)theEvent
709{
710    if (!_win) return;
711
712    NSString* unmodified_chars = [theEvent charactersIgnoringModifiers];
713    if ([theEvent modifierFlags] && NSShiftKeyMask) {
714        unmodified_chars = [unmodified_chars lowercaseString];
715    }
716
717    NSString* chars = [theEvent characters];
718
719    if ((chars) && ([chars length] > 0)) {
720        unsigned int unmodified_keyCode = remapCocoaKey([unmodified_chars characterAtIndex:0], [theEvent modifierFlags] );
721        unsigned int keyCode = remapCocoaKey([chars characterAtIndex:0], [theEvent modifierFlags] );
722        //std::cout << std::hex << "key dn: " <<[chars characterAtIndex:0] << "=" << keyCode << " unmodified: " << unmodified_keyCode <<  std::endl;
723        _win->getEventQueue()->keyPress( keyCode, _win->getEventQueue()->getTime(), unmodified_keyCode);
724    }
725}
726
727
728- (void)keyUp:(NSEvent *)theEvent
729{
730    if (!_win) return;
731
732    NSString* unmodified_chars = [theEvent charactersIgnoringModifiers];
733    if ([theEvent modifierFlags] && NSShiftKeyMask) {
734        unmodified_chars = [unmodified_chars lowercaseString];
735    }
736
737    NSString* chars = [theEvent characters];
738
739    if ((chars) && ([chars length] > 0)) {
740        unsigned int unmodified_keyCode = remapCocoaKey([unmodified_chars characterAtIndex:0], [theEvent modifierFlags] );
741        unsigned int keyCode = remapCocoaKey([chars characterAtIndex:0], [theEvent modifierFlags] );
742        //std::cout << std::hex << "key up: " <<[chars characterAtIndex:0] << "=" << keyCode << " unmodified: " << unmodified_keyCode <<  std::endl;
743
744        _win->getEventQueue()->keyRelease( keyCode, _win->getEventQueue()->getTime(), unmodified_keyCode);
745    }
746}
747
748
749- (void)tabletPoint:(NSEvent *)theEvent
750{
751    //_handleTabletEvents = YES;
752    //[self handleTabletEvents:theEvent];
753}
754
755-(void)handleTabletEvents:(NSEvent *)theEvent
756{
757    if (!_win) return;
758
759    float pressure = [theEvent pressure];
760    _win->getEventQueue()->penPressure(pressure);
761    NSPoint tilt = [theEvent tilt];
762
763    _win->getEventQueue()->penOrientation (tilt.x, tilt.y, [theEvent rotation]);
764}
765
766
767- (void)tabletProximity:(NSEvent *)theEvent
768{
769    if (!_win) return;
770
771    osgGA::GUIEventAdapter::TabletPointerType pt(osgGA::GUIEventAdapter::UNKNOWN);
772    switch ([theEvent pointingDeviceType]) {
773        case NSPenPointingDevice:
774            pt = osgGA::GUIEventAdapter::PEN;
775            break;
776        case NSCursorPointingDevice:
777            pt = osgGA::GUIEventAdapter::PUCK;
778            break;
779        case NSEraserPointingDevice:
780            pt = osgGA::GUIEventAdapter::ERASER;
781            break;
782        default:
783            break;
784    }
785    _win->getEventQueue()->penProximity(pt, [theEvent isEnteringProximity]);
786}
787
788#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6)
789
790- (osgGA::GUIEventAdapter::TouchPhase) convertTouchPhase: (NSTouchPhase) phase 
791{
792    switch(phase) {
793   
794        case NSTouchPhaseBegan:
795            return osgGA::GUIEventAdapter::TOUCH_BEGAN;
796            break;
797        case NSTouchPhaseMoved:
798            return osgGA::GUIEventAdapter::TOUCH_MOVED;
799            break;
800
801        case NSTouchPhaseStationary:
802            return osgGA::GUIEventAdapter::TOUCH_STATIONERY;
803            break;
804
805        case NSTouchPhaseEnded:
806        case NSTouchPhaseCancelled:
807            return osgGA::GUIEventAdapter::TOUCH_ENDED;
808            break;
809    }
810   
811    return osgGA::GUIEventAdapter::TOUCH_ENDED;
812
813}
814
815
816- (unsigned int)computeTouchId: (NSTouch*) touch 
817{
818    unsigned int result(0);
819   
820    if(!_touchPoints) {
821        _touchPoints = [[NSMutableDictionary alloc] init];
822        _lastTouchPointId = 0;
823    }
824   
825    switch([touch phase])
826    {
827   
828        case NSTouchPhaseBegan:
829            if ([_touchPoints objectForKey: [touch identity]] == nil)
830            {
831                [_touchPoints setObject: [NSNumber numberWithInt: _lastTouchPointId] forKey: [touch identity]];
832                result = _lastTouchPointId++;
833                break;
834            }
835            // missing "break" by intention!
836       
837        case NSTouchPhaseMoved:
838        case NSTouchPhaseStationary:
839            {
840                NSNumber* n = [_touchPoints objectForKey: [touch identity]];
841                result = [n intValue];
842            }
843            break;
844       
845        case NSTouchPhaseEnded:
846        case NSTouchPhaseCancelled:
847            {
848                NSNumber* n = [_touchPoints objectForKey: [touch identity]];
849                result = [n intValue];
850                [_touchPoints removeObjectForKey: [touch identity]];
851                if([_touchPoints count] == 0) {
852                    _lastTouchPointId = 0;
853                }
854            }
855            break;
856           
857        default:
858            break;
859    }
860       
861    return result;
862}
863
864
865
866- (void)touchesBeganWithEvent:(NSEvent *)event
867{
868    NSSet *allTouches = [event touchesMatchingPhase: NSTouchPhaseAny inView: self];
869   
870    osg::ref_ptr<osgGA::GUIEventAdapter> osg_event(NULL);
871   
872    NSRect bounds = [self bounds];
873    for(unsigned int i=0; i<[allTouches count]; i++)
874    {
875       
876        NSTouch *touch = [[allTouches allObjects] objectAtIndex:i];
877        NSPoint pos = [touch normalizedPosition];
878        osg::Vec2 pixelPos(pos.x * bounds.size.width, (1-pos.y) * bounds.size.height);
879        unsigned int touch_id = [self computeTouchId: touch];
880        if (!osg_event) {
881            osg_event = _win->getEventQueue()->touchBegan(touch_id, [self convertTouchPhase: [touch phase]], pixelPos.x(), pixelPos.y());
882        } else {
883            osg_event->addTouchPoint(touch_id, [self convertTouchPhase: [touch phase]], pixelPos.x(), pixelPos.y());
884        }
885    }
886}
887
888- (void)touchesMovedWithEvent:(NSEvent *)event
889{
890    NSSet *allTouches = [event touchesMatchingPhase: NSTouchPhaseAny inView: self];
891   
892    osg::ref_ptr<osgGA::GUIEventAdapter> osg_event(NULL);
893    NSRect bounds = [self bounds];
894   
895    for(unsigned int i=0; i<[allTouches count]; i++)
896    {
897        NSTouch *touch = [[allTouches allObjects] objectAtIndex:i];
898        NSPoint pos = [touch normalizedPosition];
899        osg::Vec2 pixelPos(pos.x * bounds.size.width, (1 - pos.y) * bounds.size.height);
900        unsigned int touch_id = [self computeTouchId: touch];
901        if (!osg_event) {
902            osg_event = _win->getEventQueue()->touchMoved(touch_id, [self convertTouchPhase: [touch phase]], pixelPos.x(), pixelPos.y());
903        } else {
904            osg_event->addTouchPoint(touch_id, [self convertTouchPhase: [touch phase]], pixelPos.x(), pixelPos.y());
905        }
906    }
907}
908
909
910- (void)touchesEndedWithEvent:(NSEvent *)event
911{
912    NSSet *allTouches = [event touchesMatchingPhase: NSTouchPhaseAny inView: self];
913   
914    osg::ref_ptr<osgGA::GUIEventAdapter> osg_event(NULL);
915    NSRect bounds = [self bounds];
916   
917    for(unsigned int i=0; i<[allTouches count]; i++)
918    {
919        NSTouch *touch = [[allTouches allObjects] objectAtIndex:i];
920        NSPoint pos = [touch normalizedPosition];
921        osg::Vec2 pixelPos(pos.x * bounds.size.width, (1 - pos.y) * bounds.size.height);
922        unsigned int touch_id = [self computeTouchId: touch];
923        if (!osg_event) {
924            osg_event = _win->getEventQueue()->touchEnded(touch_id, [self convertTouchPhase: [touch phase]], pixelPos.x(), pixelPos.y(), 1);
925        } else {
926            osg_event->addTouchPoint(touch_id, [self convertTouchPhase: [touch phase]], pixelPos.x(), pixelPos.y(), 1);
927        }
928
929    }
930}
931
932
933
934- (void)touchesCancelledWithEvent:(NSEvent *)event
935{
936    [self touchesEndedWithEvent: event];
937}
938
939
940#endif
941
942@end
943
944
945#pragma mark GraphicsWindowCocoaDelegate
946
947#ifndef MAC_OS_X_VERSION_10_6
948#define MAC_OS_X_VERSION_10_6 1060
949#endif
950
951
952
953// ----------------------------------------------------------------------------------------------------------
954// the window-delegate, handles moving/resizing of the window etc.
955// ----------------------------------------------------------------------------------------------------------
956
957@interface GraphicsWindowCocoaDelegate : NSObject
958#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
959<NSWindowDelegate>
960#endif
961{
962    @private
963        osgViewer::GraphicsWindowCocoa* _win;
964        BOOL                            _inDidMove;
965}
966
967- (id)initWith: (osgViewer::GraphicsWindowCocoa*) win;
968- (void)windowDidMove:(NSNotification *)notification;
969- (void)windowDidResize:(NSNotification *)notification;
970- (BOOL)windowShouldClose:(id)window;
971- (void)updateWindowBounds;
972
973@end
974
975
976@implementation GraphicsWindowCocoaDelegate
977
978- (id)initWith: (osgViewer::GraphicsWindowCocoa*) win
979{
980    _inDidMove = false;
981    _win = win;
982    return [super init];
983}
984
985
986- (void)windowDidMove:(NSNotification *)notification
987{
988    [self updateWindowBounds];
989}
990
991- (void)windowDidResize:(NSNotification *)notification
992{
993    [self updateWindowBounds];
994}
995
996-(void)updateWindowBounds
997{
998    if (_inDidMove) return;
999    _inDidMove = true;
1000
1001    GraphicsWindowCocoaWindow* nswin = _win->getWindow();
1002    NSRect bounds = [nswin contentRectForFrameRect: [nswin frame] ];
1003
1004    // convert to quartz-coordinate-system
1005    bounds = convertToQuartzCoordinates(bounds);
1006
1007    // std::cout << "windowdidmove: " << bounds.origin.x << " " << bounds.origin.y << " " << bounds.size.width << " " << bounds.size.height << std::endl;
1008
1009    _win->adaptResize(bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height);
1010    //_win->getEventQueue()->windowResize(bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height, _win->getEventQueue()->getTime());
1011    _win->requestRedraw();
1012    _inDidMove = false;
1013}
1014
1015- (BOOL)windowShouldClose:(id)window
1016{
1017    return _win->requestClose();
1018}
1019
1020@end
1021
1022
1023#pragma mark CocoaWindowAdapter
1024
1025
1026
1027using namespace osgDarwin;
1028namespace osgViewer {
1029
1030
1031// ----------------------------------------------------------------------------------------------------------
1032// small adapter class to handle the dock/menubar
1033// ----------------------------------------------------------------------------------------------------------
1034
1035class CocoaWindowAdapter : public MenubarController::WindowAdapter {
1036public:
1037    CocoaWindowAdapter(GraphicsWindowCocoa* win) : MenubarController::WindowAdapter(), _win(win) {}
1038
1039    virtual bool valid() { return (_win.valid() && _win->valid()); }
1040
1041    virtual void getWindowBounds(CGRect& rect)
1042    {
1043        NSRect nsrect = [_win->getWindow() frame];
1044        nsrect = convertToQuartzCoordinates(nsrect);
1045
1046        rect.origin.x = nsrect.origin.x;
1047        rect.origin.y = nsrect.origin.y;
1048        rect.size.width = nsrect.size.width;
1049        rect.size.height = nsrect.size.height;
1050    }
1051
1052    virtual osgViewer::GraphicsWindow* getWindow() {return _win.get(); }
1053private:
1054    osg::observer_ptr<GraphicsWindowCocoa> _win;
1055};
1056
1057#pragma mark GraphicsWindowCocoa
1058
1059
1060
1061// ----------------------------------------------------------------------------------------------------------
1062// init
1063// ----------------------------------------------------------------------------------------------------------
1064
1065void GraphicsWindowCocoa::init()
1066{
1067    if (_initialized) return;
1068
1069    _closeRequested = false;
1070    _ownsWindow = false;
1071    _context = NULL;
1072    _window = NULL;
1073    _pixelformat = NULL;
1074
1075    _updateContext = false;
1076    _valid = _initialized = true;
1077   
1078    // make sure the event queue has the correct window rectangle size and input range
1079    getEventQueue()->syncWindowRectangleWithGraphcisContext();
1080}
1081
1082
1083// ----------------------------------------------------------------------------------------------------------
1084// setupNSWindow
1085// sets up the NSWindow, adds delegates, etc
1086// ----------------------------------------------------------------------------------------------------------
1087
1088void GraphicsWindowCocoa::setupNSWindow(NSWindow* win)
1089{
1090
1091    [win setReleasedWhenClosed:NO];
1092    [win setDisplaysWhenScreenProfileChanges:YES];
1093    GraphicsWindowCocoaDelegate* delegate = [[GraphicsWindowCocoaDelegate alloc] initWith: this];
1094    [win setDelegate: delegate ];
1095    //[delegate autorelease];
1096
1097    [win makeKeyAndOrderFront:nil];
1098    [win setAcceptsMouseMovedEvents: YES];
1099
1100}
1101
1102
1103// ----------------------------------------------------------------------------------------------------------
1104// realizeImplementation, creates the window + context
1105// ----------------------------------------------------------------------------------------------------------
1106
1107bool GraphicsWindowCocoa::realizeImplementation()
1108{
1109    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1110
1111    unsigned int style(NSBorderlessWindowMask);
1112
1113    if (_traits->windowDecoration) {
1114        style = NSClosableWindowMask | NSMiniaturizableWindowMask | NSTitledWindowMask;
1115
1116        // supportsResize works only with windows with titlebar
1117        if (_traits->supportsResize)
1118            style |= NSResizableWindowMask;
1119    }
1120
1121    DarwinWindowingSystemInterface* wsi = dynamic_cast<DarwinWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
1122    int screenLeft(0), screenTop(0);
1123    if (wsi) {
1124        wsi->getScreenTopLeft((*_traits), screenLeft, screenTop);
1125    }
1126
1127    NSRect rect = NSMakeRect(_traits->x + screenLeft, _traits->y + screenTop, _traits->width, _traits->height);
1128
1129    _ownsWindow = true;
1130
1131    // should we create a NSView only??
1132    WindowData* windowData = _traits->inheritedWindowData ? dynamic_cast<WindowData*>(_traits->inheritedWindowData.get()) : NULL;
1133    if (windowData)
1134    {
1135        if (windowData->createOnlyView())
1136            _ownsWindow = false;
1137        _checkForEvents = windowData->checkForEvents();
1138
1139    }
1140
1141
1142    OSG_DEBUG << "GraphicsWindowCocoa::realizeImplementation / ownsWindow: " << _ownsWindow << " checkForEvents: " << _checkForEvents << std::endl;
1143
1144    if (_ownsWindow)
1145    {
1146        _window = [[GraphicsWindowCocoaWindow alloc] initWithContentRect: rect styleMask: style backing: NSBackingStoreBuffered defer: NO];
1147
1148        if (!_window) {
1149            OSG_WARN << "GraphicsWindowCocoa::realizeImplementation :: could not create window" << std::endl;
1150            return false;
1151        }
1152
1153        rect = convertFromQuartzCoordinates(rect);
1154        [_window setFrameOrigin: rect.origin];
1155    }
1156
1157    NSOpenGLPixelFormatAttribute attr[32];
1158    int i = 0;
1159
1160    attr[i++] = NSOpenGLPFADepthSize;
1161    attr[i++] = static_cast<NSOpenGLPixelFormatAttribute>(_traits->depth);
1162
1163    if (_traits->doubleBuffer) {
1164        attr[i++] = NSOpenGLPFADoubleBuffer;
1165    }
1166
1167    if (_traits->alpha) {
1168        attr[i++] = NSOpenGLPFAAlphaSize;
1169        attr[i++] = static_cast<NSOpenGLPixelFormatAttribute>(_traits->alpha);
1170    }
1171
1172    if (_traits->stencil) {
1173        attr[i++] = NSOpenGLPFAStencilSize;
1174        attr[i++] = static_cast<NSOpenGLPixelFormatAttribute>(_traits->stencil);
1175    }
1176
1177
1178    if (_traits->sampleBuffers) {
1179        attr[i++] = NSOpenGLPFASampleBuffers;
1180        attr[i++] = static_cast<NSOpenGLPixelFormatAttribute>(_traits->sampleBuffers);
1181        attr[i++] = NSOpenGLPFASamples;
1182        attr[i++] = static_cast<NSOpenGLPixelFormatAttribute>(_traits->samples);
1183    }
1184
1185
1186    attr[i++] = NSOpenGLPFAAccelerated;
1187    attr[i] = static_cast<NSOpenGLPixelFormatAttribute>(0);
1188
1189    // create the context
1190    NSOpenGLContext* sharedContext = NULL;
1191
1192    GraphicsHandleCocoa* graphicsHandleCocoa = dynamic_cast<GraphicsHandleCocoa*>(_traits->sharedContext.get());
1193    if (graphicsHandleCocoa)
1194    {
1195        sharedContext = graphicsHandleCocoa->getNSOpenGLContext();
1196    }
1197
1198    _pixelformat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr ];
1199    _context = [[NSOpenGLContext alloc] initWithFormat: _pixelformat shareContext: sharedContext];
1200
1201    if (!_context) {
1202        OSG_WARN << "GraphicsWindowCocoa::realizeImplementation :: could not create context" << std::endl;
1203        return false;
1204    }
1205
1206    // set graphics handle for shared usage
1207    setNSOpenGLContext(_context);
1208
1209   
1210    _view = [[ GraphicsWindowCocoaGLView alloc ] initWithFrame:[ _window frame ] ];
1211    [_view setAutoresizingMask:  (NSViewWidthSizable | NSViewHeightSizable) ];
1212    [_view setGraphicsWindowCocoa: this];
1213    [_view setOpenGLContext:_context];
1214   
1215    // enable multitouch
1216    if (_multiTouchEnabled || (windowData && windowData->isMultiTouchEnabled()))
1217    {
1218        setMultiTouchEnabled(true);
1219    }
1220   
1221    OSG_DEBUG << "GraphicsWindowCocoa::realizeImplementation / view: " << _view << std::endl;
1222
1223    if (_ownsWindow) {
1224        [_window setContentView: _view];
1225        setupNSWindow(_window);
1226        [_view release];
1227       
1228        MenubarController::instance()->attachWindow( new CocoaWindowAdapter(this) );
1229    }
1230    else
1231    {
1232        windowData->setCreatedNSView(_view);
1233    }
1234
1235    [pool release];
1236
1237
1238    useCursor(_traits->useCursor);
1239    setWindowName(_traits->windowName);
1240    setSyncToVBlank(_traits->vsync);
1241
1242    MenubarController::instance()->update();
1243
1244    // Cocoa's origin is bottom/left:
1245    getEventQueue()->getCurrentEventState()->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS);
1246
1247    // make sure the event queue has the correct window rectangle size and input range
1248    getEventQueue()->syncWindowRectangleWithGraphcisContext();
1249
1250    _valid = _initialized = _realized = true;
1251    return _valid;
1252}
1253
1254
1255
1256
1257// ----------------------------------------------------------------------------------------------------------
1258// closeImplementation
1259// ----------------------------------------------------------------------------------------------------------
1260void GraphicsWindowCocoa::closeImplementation()
1261{
1262    _valid = false;
1263    _realized = false;
1264
1265    // there's a possibility that the MenubarController is destructed already, so prevent a crash:
1266    MenubarController* mbc = MenubarController::instance();
1267    if (mbc) mbc->detachWindow(this);
1268
1269    if (_view) {
1270        [_view setGraphicsWindowCocoa: NULL];
1271    }
1272
1273    if (_window) {
1274        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1275
1276        // we have to close + release the window in the main-thread
1277
1278        [_window performSelectorOnMainThread: @selector(close) withObject:NULL waitUntilDone: YES];
1279        [_window performSelectorOnMainThread: @selector(release) withObject:NULL waitUntilDone: YES];
1280        [pool release];
1281    }
1282
1283    _window = NULL;
1284    _view = NULL;
1285}
1286
1287
1288// ----------------------------------------------------------------------------------------------------------
1289// makeCurrentImplementation
1290// ----------------------------------------------------------------------------------------------------------
1291
1292bool GraphicsWindowCocoa:: makeCurrentImplementation()
1293{
1294    if (_updateContext)
1295    {
1296        [_context update];
1297        _updateContext = false;
1298    }
1299
1300    [_context makeCurrentContext];
1301    return true;
1302}
1303
1304
1305// ----------------------------------------------------------------------------------------------------------
1306// releaseContextImplementation
1307// ----------------------------------------------------------------------------------------------------------
1308
1309bool GraphicsWindowCocoa::releaseContextImplementation()
1310{
1311    [NSOpenGLContext clearCurrentContext];
1312    return true;
1313}
1314
1315
1316// ----------------------------------------------------------------------------------------------------------
1317// swapBuffersImplementation
1318// ----------------------------------------------------------------------------------------------------------
1319
1320void GraphicsWindowCocoa::swapBuffersImplementation()
1321{
1322    [_context flushBuffer];
1323}
1324
1325
1326// ----------------------------------------------------------------------------------------------------------
1327// checkEvents
1328// process all pending events
1329// ----------------------------------------------------------------------------------------------------------
1330void GraphicsWindowCocoa::checkEvents()
1331{
1332    if (!_checkForEvents)
1333        return;
1334
1335    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1336
1337    while(1)
1338    {
1339        /*  NOTE: It may be better to use something like
1340            NSEventTrackingRunLoopMode since we don't necessarily want all
1341            timers/sources/observers to run, only those which would
1342            run while tracking events.  However, it should be noted that
1343            NSEventTrackingRunLoopMode is in the common set of modes
1344            so it may not effectively make much of a difference.
1345        */
1346        NSEvent *event = [ NSApp
1347                nextEventMatchingMask:NSAnyEventMask
1348                untilDate:[NSDate distantPast]
1349                inMode:NSDefaultRunLoopMode
1350                dequeue: YES];
1351        if(!event)
1352            break;
1353        [NSApp sendEvent: event];
1354    }
1355
1356    if (_closeRequested)
1357        getEventQueue()->closeWindow();
1358
1359    if (s_quit_requested) {
1360        getEventQueue()->quitApplication();
1361        s_quit_requested = false;
1362    }
1363
1364    [pool release];
1365}
1366
1367
1368
1369// ----------------------------------------------------------------------------------------------------------
1370// setWindowDecorationImplementation
1371//
1372// unfortunately there's no way to change the decoration of a window, so we create an new one
1373// and swap the content
1374// ----------------------------------------------------------------------------------------------------------
1375
1376bool GraphicsWindowCocoa::setWindowDecorationImplementation(bool flag)
1377{
1378    if (!_realized || !_ownsWindow) return false;
1379
1380    NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
1381
1382    unsigned int style(NSBorderlessWindowMask);
1383
1384    if (flag) {
1385        style = NSClosableWindowMask | NSMiniaturizableWindowMask | NSTitledWindowMask;
1386
1387        // supportsResize works only with windows with titlebar
1388        if (_traits->supportsResize)
1389            style |= NSResizableWindowMask;
1390    }
1391    NSRect rect = [_window contentRectForFrameRect: [_window frame] ];
1392    GraphicsWindowCocoaWindow* new_win = [[GraphicsWindowCocoaWindow alloc] initWithContentRect: rect styleMask: style backing: NSBackingStoreBuffered defer: NO];
1393
1394    if (new_win) {
1395        [new_win setContentView: [_window contentView]];
1396        setupNSWindow(new_win);
1397        NSString* title = (_traits.valid()) ? [NSString stringWithUTF8String: _traits->windowName.c_str()] : @"";
1398        [new_win setTitle: title ];
1399        [_window close];
1400        [_window release];
1401
1402        _window = new_win;
1403        [_window makeKeyAndOrderFront: nil];
1404    }
1405
1406    [localPool release];
1407
1408    return true;
1409}
1410
1411
1412// ----------------------------------------------------------------------------------------------------------
1413// grabFocus
1414// ----------------------------------------------------------------------------------------------------------
1415void GraphicsWindowCocoa::grabFocus()
1416{
1417    if (_ownsWindow)
1418        [_window makeKeyAndOrderFront: nil];
1419}
1420
1421
1422// ----------------------------------------------------------------------------------------------------------
1423// grabFocusIfPointerInWindow
1424// ----------------------------------------------------------------------------------------------------------
1425void GraphicsWindowCocoa::grabFocusIfPointerInWindow()
1426{
1427    OSG_INFO << "GraphicsWindowCocoa :: grabFocusIfPointerInWindow not implemented yet " << std::endl;
1428}
1429
1430
1431// ----------------------------------------------------------------------------------------------------------
1432// resizedImplementation
1433// ----------------------------------------------------------------------------------------------------------
1434
1435void GraphicsWindowCocoa::resizedImplementation(int x, int y, int width, int height)
1436{
1437    DEBUG_OUT("resized implementation" << x << " " << y << " " << width << " " << height);
1438    GraphicsContext::resizedImplementation(x, y, width, height);
1439
1440    _updateContext = true;
1441
1442    MenubarController::instance()->update();
1443    getEventQueue()->windowResize(x,y,width, height, getEventQueue()->getTime());
1444}
1445
1446
1447
1448
1449// ----------------------------------------------------------------------------------------------------------
1450// setWindowRectangleImplementation
1451// ----------------------------------------------------------------------------------------------------------
1452bool GraphicsWindowCocoa::setWindowRectangleImplementation(int x, int y, int width, int height)
1453{
1454    if (!_ownsWindow)
1455        return false;
1456
1457    NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
1458
1459    DarwinWindowingSystemInterface* wsi = dynamic_cast<DarwinWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
1460    int screenLeft(0), screenTop(0);
1461    if (wsi) {
1462        wsi->getScreenTopLeft((*_traits), screenLeft, screenTop);
1463    }
1464
1465
1466    NSRect rect = NSMakeRect(x+screenLeft,y+screenTop,width, height);
1467    rect = convertFromQuartzCoordinates(rect);
1468
1469    [_window setFrame: [NSWindow frameRectForContentRect: rect styleMask: [_window styleMask]] display: YES];
1470    [_context update];
1471    MenubarController::instance()->update();
1472
1473    [localPool release];
1474
1475    return true;
1476}
1477
1478
1479// ----------------------------------------------------------------------------------------------------------
1480//
1481// ----------------------------------------------------------------------------------------------------------
1482
1483void GraphicsWindowCocoa::adaptResize(int x, int y, int w, int h)
1484{
1485
1486    DarwinWindowingSystemInterface* wsi = dynamic_cast<DarwinWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
1487    int screenLeft(0), screenTop(0);
1488    if (wsi) {
1489
1490        // get the screen containing the window
1491        unsigned int screenNdx = wsi->getScreenContaining(x,y,w,h);
1492
1493        // update traits
1494        _traits->screenNum = screenNdx;
1495
1496        // get top left of screen
1497        wsi->getScreenTopLeft((*_traits), screenLeft, screenTop);
1498    }
1499
1500    resized(x-screenLeft,y-screenTop,w,h);
1501    getEventQueue()->windowResize(x-screenLeft, y-screenTop, w, h, getEventQueue()->getTime());
1502}
1503
1504
1505// ----------------------------------------------------------------------------------------------------------
1506// setWindowName
1507// ----------------------------------------------------------------------------------------------------------
1508
1509void GraphicsWindowCocoa::setWindowName (const std::string & name)
1510{
1511    if (_traits.valid()) _traits->windowName = name;
1512
1513    if (!_ownsWindow)
1514        return;
1515
1516    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1517
1518    NSString* title = [NSString stringWithUTF8String: name.c_str()];
1519    [_window setTitle: title];
1520    [pool release];
1521}
1522
1523// ----------------------------------------------------------------------------------------------------------
1524// requestWarpPointer
1525// ----------------------------------------------------------------------------------------------------------
1526void GraphicsWindowCocoa::requestWarpPointer(float x,float y)
1527{   
1528    CGPoint point;
1529    point.x = x + _traits->x;
1530    point.y = y + _traits->y;
1531   
1532#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
1533    CGEventRef warpEvent = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, point, kCGMouseButtonLeft);
1534    CGEventPost(kCGHIDEventTap, warpEvent);
1535    CFRelease(warpEvent);
1536   
1537#else
1538    DarwinWindowingSystemInterface* wsi = dynamic_cast<DarwinWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
1539   
1540    if (wsi == NULL) {
1541        osg::notify(osg::WARN) << "GraphicsWindowCocoa::useCursor :: could not get OSXCocoaWindowingSystemInterface" << std::endl;
1542        return;
1543    }
1544   
1545    CGDirectDisplayID displayId = wsi->getDisplayID((*_traits));
1546    CGSetLocalEventsSuppressionInterval(0);
1547    CGDisplayMoveCursorToPoint(displayId, point);
1548#endif
1549   
1550    getEventQueue()->mouseWarped(x,y);
1551}
1552
1553
1554
1555// ----------------------------------------------------------------------------------------------------------
1556// useCursor
1557// ----------------------------------------------------------------------------------------------------------
1558
1559void GraphicsWindowCocoa::useCursor(bool cursorOn)
1560{
1561    if (_traits.valid())
1562        _traits->useCursor = cursorOn;
1563    DarwinWindowingSystemInterface* wsi = dynamic_cast<DarwinWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
1564    if (wsi == NULL) {
1565        OSG_WARN << "GraphicsWindowCarbon::useCursor :: could not get OSXCarbonWindowingSystemInterface" << std::endl;
1566        return;
1567    }
1568
1569    CGDirectDisplayID displayId = wsi->getDisplayID((*_traits));
1570    CGDisplayErr err = kCGErrorSuccess;
1571
1572    if (cursorOn) {
1573        err = CGDisplayShowCursor(displayId);
1574    } else {
1575        err = CGDisplayHideCursor(displayId);
1576    }
1577
1578    if (err != kCGErrorSuccess) {
1579        OSG_WARN << "GraphicsWindowCocoa::useCursor failed with " << err << std::endl;
1580    }
1581}
1582
1583
1584// ----------------------------------------------------------------------------------------------------------
1585// setCursor
1586// ----------------------------------------------------------------------------------------------------------
1587
1588void GraphicsWindowCocoa::setCursor(MouseCursor mouseCursor)
1589{
1590    if (_currentCursor == mouseCursor) {
1591        return;
1592    }
1593
1594    NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
1595    switch (mouseCursor)
1596    {
1597
1598        case NoCursor:
1599            [NSCursor hide];
1600            break;
1601
1602        case LeftArrowCursor:
1603        case RightArrowCursor:
1604            [[NSCursor arrowCursor] set];
1605            break;
1606
1607        case TextCursor:
1608            [[NSCursor IBeamCursor] set];
1609            break;
1610
1611        case CrosshairCursor:
1612            [[NSCursor crosshairCursor] set];
1613            break;
1614
1615        default:
1616            OSG_INFO << "GraphicsWindowCocoa::setCursor :: unsupported MouseCursor: " << mouseCursor << std::endl;
1617    }
1618
1619    if (_currentCursor == NoCursor) {
1620        [NSCursor unhide];
1621    }
1622
1623    _currentCursor = mouseCursor;
1624    [localPool release];
1625}
1626
1627
1628// ----------------------------------------------------------------------------------------------------------
1629// setSyncToVBlank
1630// ----------------------------------------------------------------------------------------------------------
1631
1632void GraphicsWindowCocoa::setSyncToVBlank(bool f)
1633{
1634    if (_traits.valid()) _traits->vsync = f;
1635    GLint VBL(f?1:0);
1636    [_context setValues:&VBL forParameter:NSOpenGLCPSwapInterval];
1637}
1638
1639
1640bool GraphicsWindowCocoa::isMultiTouchEnabled()
1641{
1642    return _multiTouchEnabled;
1643}
1644void GraphicsWindowCocoa::setMultiTouchEnabled(bool b)
1645{
1646    _multiTouchEnabled = b;
1647   
1648#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6)
1649    if (_view) [_view setAcceptsTouchEvents: b];
1650#else
1651    if (b) {
1652        OSG_WARN << "GraphicsWindowCocoa :: multi-touch only available for OS X >= 10.6, please check your compile settings" << std::endl;
1653    }
1654#endif
1655}
1656
1657
1658// ----------------------------------------------------------------------------------------------------------
1659// d'tor
1660// ----------------------------------------------------------------------------------------------------------
1661
1662GraphicsWindowCocoa::~GraphicsWindowCocoa()
1663{
1664    close();
1665}
1666
1667
1668
1669#pragma mark CocoaWindowingSystemInterface
1670
1671// ----------------------------------------------------------------------------------------------------------
1672// CocoaWindowingSystemInterface
1673// ----------------------------------------------------------------------------------------------------------
1674
1675struct CocoaWindowingSystemInterface : public DarwinWindowingSystemInterface
1676{
1677
1678    CocoaWindowingSystemInterface() : DarwinWindowingSystemInterface()
1679    {
1680    }
1681
1682    void initAsStandaloneApplication()
1683    {
1684        _init();
1685
1686        static bool s_inited = false;
1687        if (s_inited) return;
1688        s_inited = true;
1689
1690        OSG_INFO << "CocoaWindowingSystemInterface::initAsStandaloneApplication " << std::endl;
1691
1692        ProcessSerialNumber psn;
1693        if (!GetCurrentProcess(&psn)) {
1694            TransformProcessType(&psn, kProcessTransformToForegroundApplication);
1695            SetFrontProcess(&psn);
1696        }
1697
1698        NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
1699
1700        if (NSApp == nil) {
1701            [NSApplication sharedApplication];
1702        }
1703
1704        [NSApp setDelegate: [[CocoaAppDelegate alloc] init] ];
1705
1706        createApplicationMenus();
1707
1708        [NSApp finishLaunching];
1709
1710        [localPool release];
1711    }
1712
1713    virtual osg::GraphicsContext* createGraphicsContext(osg::GraphicsContext::Traits* traits)
1714    {
1715        _init();
1716
1717        if (!traits->pbuffer)
1718        {
1719            GraphicsWindowCocoa::WindowData* windowData = traits->inheritedWindowData ? dynamic_cast<GraphicsWindowCocoa::WindowData*>(traits->inheritedWindowData.get()) : NULL;
1720
1721            if (!windowData || (windowData && windowData->poseAsStandaloneApp()))
1722            {
1723                initAsStandaloneApplication();
1724            }
1725        }
1726
1727        return createGraphicsContextImplementation<PixelBufferCocoa, GraphicsWindowCocoa>(traits);
1728    }
1729
1730    virtual ~CocoaWindowingSystemInterface()
1731    {
1732    }
1733
1734private:
1735    NSString *getApplicationName(void)
1736    {
1737        NSDictionary *dict;
1738        NSString *appName = 0;
1739
1740        /* Determine the application name */
1741        dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
1742        if (dict)
1743            appName = [dict objectForKey: @"CFBundleName"];
1744
1745        if (![appName length])
1746            appName = [[NSProcessInfo processInfo] processName];
1747
1748        return appName;
1749    }
1750
1751    void createApplicationMenus(void)
1752    {
1753        NSString *appName;
1754        NSString *title;
1755        NSMenu *appleMenu;
1756        NSMenuItem *menuItem;
1757
1758        /* Create the main menu bar */
1759        [NSApp setMainMenu:[[NSMenu alloc] init]];
1760
1761        /* Create the application menu */
1762        appName = getApplicationName();
1763        appleMenu = [[NSMenu alloc] initWithTitle:@""];
1764
1765        /* Add menu items */
1766        title = [@"About " stringByAppendingString:appName];
1767        [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
1768
1769        [appleMenu addItem:[NSMenuItem separatorItem]];
1770
1771        NSMenu* service_menu = [[NSMenu alloc] init];
1772        NSMenuItem* service_menu_item = [[NSMenuItem alloc] initWithTitle:@"Services" action:nil keyEquivalent:@""];
1773        [service_menu_item setSubmenu: service_menu];
1774        [appleMenu addItem: service_menu_item];
1775        [NSApp setServicesMenu: service_menu];
1776
1777        [appleMenu addItem:[NSMenuItem separatorItem]];
1778
1779        title = [@"Hide " stringByAppendingString:appName];
1780        [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@/*"h"*/"h"];
1781
1782        menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@/*"h"*/""];
1783        [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
1784
1785        [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
1786
1787        [appleMenu addItem:[NSMenuItem separatorItem]];
1788
1789        title = [@"Quit " stringByAppendingString:appName];
1790        [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@/*"q"*/"q"];
1791
1792        /* Put menu into the menubar */
1793        menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
1794        [menuItem setSubmenu:appleMenu];
1795        [[NSApp mainMenu] addItem:menuItem];
1796        [menuItem release];
1797
1798        /* Tell the application object that this is now the application menu */
1799        [NSApp setAppleMenu:appleMenu];
1800        [appleMenu release];
1801
1802
1803    }
1804
1805};
1806
1807}
1808
1809#ifdef USE_DARWIN_COCOA_IMPLEMENTATION
1810RegisterWindowingSystemInterfaceProxy<osgViewer::CocoaWindowingSystemInterface> createWindowingSystemInterfaceProxy;
1811#endif
1812
1813// declare C entry point for static compilation.
1814extern "C" void graphicswindow_Cocoa(void)
1815{
1816    osg::GraphicsContext::setWindowingSystemInterface(new osgViewer::CocoaWindowingSystemInterface());
1817}
Note: See TracBrowser for help on using the browser.