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

Revision 13130, 57.2 kB (checked in by robert, 9 hours ago)

Release OpenSceneGraph-3.3.3

  • 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
1079
1080// ----------------------------------------------------------------------------------------------------------
1081// setupNSWindow
1082// sets up the NSWindow, adds delegates, etc
1083// ----------------------------------------------------------------------------------------------------------
1084
1085void GraphicsWindowCocoa::setupNSWindow(NSWindow* win)
1086{
1087
1088    [win setReleasedWhenClosed:NO];
1089    [win setDisplaysWhenScreenProfileChanges:YES];
1090    GraphicsWindowCocoaDelegate* delegate = [[GraphicsWindowCocoaDelegate alloc] initWith: this];
1091    [win setDelegate: delegate ];
1092    //[delegate autorelease];
1093
1094    [win makeKeyAndOrderFront:nil];
1095    [win setAcceptsMouseMovedEvents: YES];
1096
1097}
1098
1099
1100// ----------------------------------------------------------------------------------------------------------
1101// realizeImplementation, creates the window + context
1102// ----------------------------------------------------------------------------------------------------------
1103
1104bool GraphicsWindowCocoa::realizeImplementation()
1105{
1106    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1107
1108    unsigned int style(NSBorderlessWindowMask);
1109
1110    if (_traits->windowDecoration) {
1111        style = NSClosableWindowMask | NSMiniaturizableWindowMask | NSTitledWindowMask;
1112
1113        // supportsResize works only with windows with titlebar
1114        if (_traits->supportsResize)
1115            style |= NSResizableWindowMask;
1116    }
1117
1118    DarwinWindowingSystemInterface* wsi = dynamic_cast<DarwinWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
1119    int screenLeft(0), screenTop(0);
1120    if (wsi) {
1121        wsi->getScreenTopLeft((*_traits), screenLeft, screenTop);
1122    }
1123
1124    NSRect rect = NSMakeRect(_traits->x + screenLeft, _traits->y + screenTop, _traits->width, _traits->height);
1125
1126    _ownsWindow = true;
1127
1128    // should we create a NSView only??
1129    WindowData* windowData = _traits->inheritedWindowData ? dynamic_cast<WindowData*>(_traits->inheritedWindowData.get()) : NULL;
1130    if (windowData)
1131    {
1132        if (windowData->createOnlyView())
1133            _ownsWindow = false;
1134        _checkForEvents = windowData->checkForEvents();
1135
1136    }
1137
1138
1139    OSG_DEBUG << "GraphicsWindowCocoa::realizeImplementation / ownsWindow: " << _ownsWindow << " checkForEvents: " << _checkForEvents << std::endl;
1140
1141    if (_ownsWindow)
1142    {
1143        _window = [[GraphicsWindowCocoaWindow alloc] initWithContentRect: rect styleMask: style backing: NSBackingStoreBuffered defer: NO];
1144
1145        if (!_window) {
1146            OSG_WARN << "GraphicsWindowCocoa::realizeImplementation :: could not create window" << std::endl;
1147            return false;
1148        }
1149
1150        rect = convertFromQuartzCoordinates(rect);
1151        [_window setFrameOrigin: rect.origin];
1152    }
1153
1154    NSOpenGLPixelFormatAttribute attr[32];
1155    int i = 0;
1156
1157    attr[i++] = NSOpenGLPFADepthSize;
1158    attr[i++] = static_cast<NSOpenGLPixelFormatAttribute>(_traits->depth);
1159
1160    if (_traits->doubleBuffer) {
1161        attr[i++] = NSOpenGLPFADoubleBuffer;
1162    }
1163
1164    if (_traits->alpha) {
1165        attr[i++] = NSOpenGLPFAAlphaSize;
1166        attr[i++] = static_cast<NSOpenGLPixelFormatAttribute>(_traits->alpha);
1167    }
1168
1169    if (_traits->stencil) {
1170        attr[i++] = NSOpenGLPFAStencilSize;
1171        attr[i++] = static_cast<NSOpenGLPixelFormatAttribute>(_traits->stencil);
1172    }
1173
1174
1175    if (_traits->sampleBuffers) {
1176        attr[i++] = NSOpenGLPFASampleBuffers;
1177        attr[i++] = static_cast<NSOpenGLPixelFormatAttribute>(_traits->sampleBuffers);
1178        attr[i++] = NSOpenGLPFASamples;
1179        attr[i++] = static_cast<NSOpenGLPixelFormatAttribute>(_traits->samples);
1180    }
1181
1182
1183    attr[i++] = NSOpenGLPFAAccelerated;
1184    attr[i] = static_cast<NSOpenGLPixelFormatAttribute>(0);
1185
1186    // create the context
1187    NSOpenGLContext* sharedContext = NULL;
1188
1189    GraphicsHandleCocoa* graphicsHandleCocoa = dynamic_cast<GraphicsHandleCocoa*>(_traits->sharedContext.get());
1190    if (graphicsHandleCocoa)
1191    {
1192        sharedContext = graphicsHandleCocoa->getNSOpenGLContext();
1193    }
1194
1195    _pixelformat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr ];
1196    _context = [[NSOpenGLContext alloc] initWithFormat: _pixelformat shareContext: sharedContext];
1197
1198    if (!_context) {
1199        OSG_WARN << "GraphicsWindowCocoa::realizeImplementation :: could not create context" << std::endl;
1200        return false;
1201    }
1202
1203    // set graphics handle for shared usage
1204    setNSOpenGLContext(_context);
1205
1206   
1207    _view = [[ GraphicsWindowCocoaGLView alloc ] initWithFrame:[ _window frame ] ];
1208    [_view setAutoresizingMask:  (NSViewWidthSizable | NSViewHeightSizable) ];
1209    [_view setGraphicsWindowCocoa: this];
1210    [_view setOpenGLContext:_context];
1211   
1212    // enable multitouch
1213    if (_multiTouchEnabled || (windowData && windowData->isMultiTouchEnabled()))
1214    {
1215        setMultiTouchEnabled(true);
1216    }
1217   
1218    OSG_DEBUG << "GraphicsWindowCocoa::realizeImplementation / view: " << _view << std::endl;
1219
1220    if (_ownsWindow) {
1221        [_window setContentView: _view];
1222        setupNSWindow(_window);
1223        [_view release];
1224       
1225        MenubarController::instance()->attachWindow( new CocoaWindowAdapter(this) );
1226    }
1227    else
1228    {
1229        windowData->setCreatedNSView(_view);
1230    }
1231
1232    [pool release];
1233
1234
1235    useCursor(_traits->useCursor);
1236    setWindowName(_traits->windowName);
1237    setSyncToVBlank(_traits->vsync);
1238
1239    MenubarController::instance()->update();
1240
1241    // Cocoa's origin is bottom/left:
1242    getEventQueue()->getCurrentEventState()->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS);
1243
1244    _valid = _initialized = _realized = true;
1245    return _valid;
1246}
1247
1248
1249
1250
1251// ----------------------------------------------------------------------------------------------------------
1252// closeImplementation
1253// ----------------------------------------------------------------------------------------------------------
1254void GraphicsWindowCocoa::closeImplementation()
1255{
1256    _valid = false;
1257    _realized = false;
1258
1259    // there's a possibility that the MenubarController is destructed already, so prevent a crash:
1260    MenubarController* mbc = MenubarController::instance();
1261    if (mbc) mbc->detachWindow(this);
1262
1263    if (_view) {
1264        [_view setGraphicsWindowCocoa: NULL];
1265    }
1266
1267    if (_window) {
1268        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1269
1270        // we have to close + release the window in the main-thread
1271
1272        [_window performSelectorOnMainThread: @selector(close) withObject:NULL waitUntilDone: YES];
1273        [_window performSelectorOnMainThread: @selector(release) withObject:NULL waitUntilDone: YES];
1274        [pool release];
1275    }
1276
1277    _window = NULL;
1278    _view = NULL;
1279}
1280
1281
1282// ----------------------------------------------------------------------------------------------------------
1283// makeCurrentImplementation
1284// ----------------------------------------------------------------------------------------------------------
1285
1286bool GraphicsWindowCocoa:: makeCurrentImplementation()
1287{
1288    if (_updateContext)
1289    {
1290        [_context update];
1291        _updateContext = false;
1292    }
1293
1294    [_context makeCurrentContext];
1295    return true;
1296}
1297
1298
1299// ----------------------------------------------------------------------------------------------------------
1300// releaseContextImplementation
1301// ----------------------------------------------------------------------------------------------------------
1302
1303bool GraphicsWindowCocoa::releaseContextImplementation()
1304{
1305    [NSOpenGLContext clearCurrentContext];
1306    return true;
1307}
1308
1309
1310// ----------------------------------------------------------------------------------------------------------
1311// swapBuffersImplementation
1312// ----------------------------------------------------------------------------------------------------------
1313
1314void GraphicsWindowCocoa::swapBuffersImplementation()
1315{
1316    [_context flushBuffer];
1317}
1318
1319
1320// ----------------------------------------------------------------------------------------------------------
1321// checkEvents
1322// process all pending events
1323// ----------------------------------------------------------------------------------------------------------
1324void GraphicsWindowCocoa::checkEvents()
1325{
1326    if (!_checkForEvents)
1327        return;
1328
1329    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1330
1331    while(1)
1332    {
1333        /*  NOTE: It may be better to use something like
1334            NSEventTrackingRunLoopMode since we don't necessarily want all
1335            timers/sources/observers to run, only those which would
1336            run while tracking events.  However, it should be noted that
1337            NSEventTrackingRunLoopMode is in the common set of modes
1338            so it may not effectively make much of a difference.
1339        */
1340        NSEvent *event = [ NSApp
1341                nextEventMatchingMask:NSAnyEventMask
1342                untilDate:[NSDate distantPast]
1343                inMode:NSDefaultRunLoopMode
1344                dequeue: YES];
1345        if(!event)
1346            break;
1347        [NSApp sendEvent: event];
1348    }
1349
1350    if (_closeRequested)
1351        getEventQueue()->closeWindow();
1352
1353    if (s_quit_requested) {
1354        getEventQueue()->quitApplication();
1355        s_quit_requested = false;
1356    }
1357
1358    [pool release];
1359}
1360
1361
1362
1363// ----------------------------------------------------------------------------------------------------------
1364// setWindowDecorationImplementation
1365//
1366// unfortunately there's no way to change the decoration of a window, so we create an new one
1367// and swap the content
1368// ----------------------------------------------------------------------------------------------------------
1369
1370bool GraphicsWindowCocoa::setWindowDecorationImplementation(bool flag)
1371{
1372    if (!_realized || !_ownsWindow) return false;
1373
1374    NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
1375
1376    unsigned int style(NSBorderlessWindowMask);
1377
1378    if (flag) {
1379        style = NSClosableWindowMask | NSMiniaturizableWindowMask | NSTitledWindowMask;
1380
1381        // supportsResize works only with windows with titlebar
1382        if (_traits->supportsResize)
1383            style |= NSResizableWindowMask;
1384    }
1385    NSRect rect = [_window contentRectForFrameRect: [_window frame] ];
1386    GraphicsWindowCocoaWindow* new_win = [[GraphicsWindowCocoaWindow alloc] initWithContentRect: rect styleMask: style backing: NSBackingStoreBuffered defer: NO];
1387
1388    if (new_win) {
1389        [new_win setContentView: [_window contentView]];
1390        setupNSWindow(new_win);
1391        NSString* title = (_traits.valid()) ? [NSString stringWithUTF8String: _traits->windowName.c_str()] : @"";
1392        [new_win setTitle: title ];
1393        [_window close];
1394        [_window release];
1395
1396        _window = new_win;
1397        [_window makeKeyAndOrderFront: nil];
1398    }
1399
1400    [localPool release];
1401
1402    return true;
1403}
1404
1405
1406// ----------------------------------------------------------------------------------------------------------
1407// grabFocus
1408// ----------------------------------------------------------------------------------------------------------
1409void GraphicsWindowCocoa::grabFocus()
1410{
1411    if (_ownsWindow)
1412        [_window makeKeyAndOrderFront: nil];
1413}
1414
1415
1416// ----------------------------------------------------------------------------------------------------------
1417// grabFocusIfPointerInWindow
1418// ----------------------------------------------------------------------------------------------------------
1419void GraphicsWindowCocoa::grabFocusIfPointerInWindow()
1420{
1421    OSG_INFO << "GraphicsWindowCocoa :: grabFocusIfPointerInWindow not implemented yet " << std::endl;
1422}
1423
1424
1425// ----------------------------------------------------------------------------------------------------------
1426// resizedImplementation
1427// ----------------------------------------------------------------------------------------------------------
1428
1429void GraphicsWindowCocoa::resizedImplementation(int x, int y, int width, int height)
1430{
1431    DEBUG_OUT("resized implementation" << x << " " << y << " " << width << " " << height);
1432    GraphicsContext::resizedImplementation(x, y, width, height);
1433
1434    _updateContext = true;
1435
1436    MenubarController::instance()->update();
1437    getEventQueue()->windowResize(x,y,width, height, getEventQueue()->getTime());
1438}
1439
1440
1441
1442
1443// ----------------------------------------------------------------------------------------------------------
1444// setWindowRectangleImplementation
1445// ----------------------------------------------------------------------------------------------------------
1446bool GraphicsWindowCocoa::setWindowRectangleImplementation(int x, int y, int width, int height)
1447{
1448    if (!_ownsWindow)
1449        return false;
1450
1451    NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
1452
1453    DarwinWindowingSystemInterface* wsi = dynamic_cast<DarwinWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
1454    int screenLeft(0), screenTop(0);
1455    if (wsi) {
1456        wsi->getScreenTopLeft((*_traits), screenLeft, screenTop);
1457    }
1458
1459
1460    NSRect rect = NSMakeRect(x+screenLeft,y+screenTop,width, height);
1461    rect = convertFromQuartzCoordinates(rect);
1462
1463    [_window setFrame: [NSWindow frameRectForContentRect: rect styleMask: [_window styleMask]] display: YES];
1464    [_context update];
1465    MenubarController::instance()->update();
1466
1467    [localPool release];
1468
1469    return true;
1470}
1471
1472
1473// ----------------------------------------------------------------------------------------------------------
1474//
1475// ----------------------------------------------------------------------------------------------------------
1476
1477void GraphicsWindowCocoa::adaptResize(int x, int y, int w, int h)
1478{
1479
1480    DarwinWindowingSystemInterface* wsi = dynamic_cast<DarwinWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
1481    int screenLeft(0), screenTop(0);
1482    if (wsi) {
1483
1484        // get the screen containing the window
1485        unsigned int screenNdx = wsi->getScreenContaining(x,y,w,h);
1486
1487        // update traits
1488        _traits->screenNum = screenNdx;
1489
1490        // get top left of screen
1491        wsi->getScreenTopLeft((*_traits), screenLeft, screenTop);
1492    }
1493
1494    resized(x-screenLeft,y-screenTop,w,h);
1495    getEventQueue()->windowResize(x-screenLeft, y-screenTop, w, h, getEventQueue()->getTime());
1496}
1497
1498
1499// ----------------------------------------------------------------------------------------------------------
1500// setWindowName
1501// ----------------------------------------------------------------------------------------------------------
1502
1503void GraphicsWindowCocoa::setWindowName (const std::string & name)
1504{
1505    if (_traits.valid()) _traits->windowName = name;
1506
1507    if (!_ownsWindow)
1508        return;
1509
1510    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1511
1512    NSString* title = [NSString stringWithUTF8String: name.c_str()];
1513    [_window setTitle: title];
1514    [pool release];
1515}
1516
1517// ----------------------------------------------------------------------------------------------------------
1518// requestWarpPointer
1519// ----------------------------------------------------------------------------------------------------------
1520void GraphicsWindowCocoa::requestWarpPointer(float x,float y)
1521{   
1522    CGPoint point;
1523    point.x = x + _traits->x;
1524    point.y = y + _traits->y;
1525   
1526#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
1527    CGEventRef warpEvent = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, point, kCGMouseButtonLeft);
1528    CGEventPost(kCGHIDEventTap, warpEvent);
1529    CFRelease(warpEvent);
1530   
1531#else
1532    DarwinWindowingSystemInterface* wsi = dynamic_cast<DarwinWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
1533   
1534    if (wsi == NULL) {
1535        osg::notify(osg::WARN) << "GraphicsWindowCocoa::useCursor :: could not get OSXCocoaWindowingSystemInterface" << std::endl;
1536        return;
1537    }
1538   
1539    CGDirectDisplayID displayId = wsi->getDisplayID((*_traits));
1540    CGSetLocalEventsSuppressionInterval(0);
1541    CGDisplayMoveCursorToPoint(displayId, point);
1542#endif
1543   
1544    getEventQueue()->mouseWarped(x,y);
1545}
1546
1547
1548
1549// ----------------------------------------------------------------------------------------------------------
1550// useCursor
1551// ----------------------------------------------------------------------------------------------------------
1552
1553void GraphicsWindowCocoa::useCursor(bool cursorOn)
1554{
1555    if (_traits.valid())
1556        _traits->useCursor = cursorOn;
1557    DarwinWindowingSystemInterface* wsi = dynamic_cast<DarwinWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
1558    if (wsi == NULL) {
1559        OSG_WARN << "GraphicsWindowCarbon::useCursor :: could not get OSXCarbonWindowingSystemInterface" << std::endl;
1560        return;
1561    }
1562
1563    CGDirectDisplayID displayId = wsi->getDisplayID((*_traits));
1564    CGDisplayErr err = kCGErrorSuccess;
1565
1566    if (cursorOn) {
1567        err = CGDisplayShowCursor(displayId);
1568    } else {
1569        err = CGDisplayHideCursor(displayId);
1570    }
1571
1572    if (err != kCGErrorSuccess) {
1573        OSG_WARN << "GraphicsWindowCocoa::useCursor failed with " << err << std::endl;
1574    }
1575}
1576
1577
1578// ----------------------------------------------------------------------------------------------------------
1579// setCursor
1580// ----------------------------------------------------------------------------------------------------------
1581
1582void GraphicsWindowCocoa::setCursor(MouseCursor mouseCursor)
1583{
1584    if (_currentCursor == mouseCursor) {
1585        return;
1586    }
1587
1588    NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
1589    switch (mouseCursor)
1590    {
1591
1592        case NoCursor:
1593            [NSCursor hide];
1594            break;
1595
1596        case LeftArrowCursor:
1597        case RightArrowCursor:
1598            [[NSCursor arrowCursor] set];
1599            break;
1600
1601        case TextCursor:
1602            [[NSCursor IBeamCursor] set];
1603            break;
1604
1605        case CrosshairCursor:
1606            [[NSCursor crosshairCursor] set];
1607            break;
1608
1609        default:
1610            OSG_INFO << "GraphicsWindowCocoa::setCursor :: unsupported MouseCursor: " << mouseCursor << std::endl;
1611    }
1612
1613    if (_currentCursor == NoCursor) {
1614        [NSCursor unhide];
1615    }
1616
1617    _currentCursor = mouseCursor;
1618    [localPool release];
1619}
1620
1621
1622// ----------------------------------------------------------------------------------------------------------
1623// setSyncToVBlank
1624// ----------------------------------------------------------------------------------------------------------
1625
1626void GraphicsWindowCocoa::setSyncToVBlank(bool f)
1627{
1628    if (_traits.valid()) _traits->vsync = f;
1629    GLint VBL(f?1:0);
1630    [_context setValues:&VBL forParameter:NSOpenGLCPSwapInterval];
1631}
1632
1633
1634bool GraphicsWindowCocoa::isMultiTouchEnabled()
1635{
1636    return _multiTouchEnabled;
1637}
1638void GraphicsWindowCocoa::setMultiTouchEnabled(bool b)
1639{
1640    _multiTouchEnabled = b;
1641   
1642#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6)
1643    if (_view) [_view setAcceptsTouchEvents: b];
1644#else
1645    if (b) {
1646        OSG_WARN << "GraphicsWindowCocoa :: multi-touch only available for OS X >= 10.6, please check your compile settings" << std::endl;
1647    }
1648#endif
1649}
1650
1651
1652// ----------------------------------------------------------------------------------------------------------
1653// d'tor
1654// ----------------------------------------------------------------------------------------------------------
1655
1656GraphicsWindowCocoa::~GraphicsWindowCocoa()
1657{
1658    close();
1659}
1660
1661
1662
1663#pragma mark CocoaWindowingSystemInterface
1664
1665// ----------------------------------------------------------------------------------------------------------
1666// CocoaWindowingSystemInterface
1667// ----------------------------------------------------------------------------------------------------------
1668
1669struct CocoaWindowingSystemInterface : public DarwinWindowingSystemInterface
1670{
1671
1672    CocoaWindowingSystemInterface() : DarwinWindowingSystemInterface()
1673    {
1674    }
1675
1676    void initAsStandaloneApplication()
1677    {
1678        _init();
1679
1680        static bool s_inited = false;
1681        if (s_inited) return;
1682        s_inited = true;
1683
1684        OSG_INFO << "CocoaWindowingSystemInterface::initAsStandaloneApplication " << std::endl;
1685
1686        ProcessSerialNumber psn;
1687        if (!GetCurrentProcess(&psn)) {
1688            TransformProcessType(&psn, kProcessTransformToForegroundApplication);
1689            SetFrontProcess(&psn);
1690        }
1691
1692        NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
1693
1694        if (NSApp == nil) {
1695            [NSApplication sharedApplication];
1696        }
1697
1698        [NSApp setDelegate: [[CocoaAppDelegate alloc] init] ];
1699
1700        createApplicationMenus();
1701
1702        [NSApp finishLaunching];
1703
1704        [localPool release];
1705    }
1706
1707    virtual osg::GraphicsContext* createGraphicsContext(osg::GraphicsContext::Traits* traits)
1708    {
1709        _init();
1710
1711        if (!traits->pbuffer)
1712        {
1713            GraphicsWindowCocoa::WindowData* windowData = traits->inheritedWindowData ? dynamic_cast<GraphicsWindowCocoa::WindowData*>(traits->inheritedWindowData.get()) : NULL;
1714
1715            if (!windowData || (windowData && windowData->poseAsStandaloneApp()))
1716            {
1717                initAsStandaloneApplication();
1718            }
1719        }
1720
1721        return createGraphicsContextImplementation<PixelBufferCocoa, GraphicsWindowCocoa>(traits);
1722    }
1723
1724    virtual ~CocoaWindowingSystemInterface()
1725    {
1726    }
1727
1728private:
1729    NSString *getApplicationName(void)
1730    {
1731        NSDictionary *dict;
1732        NSString *appName = 0;
1733
1734        /* Determine the application name */
1735        dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
1736        if (dict)
1737            appName = [dict objectForKey: @"CFBundleName"];
1738
1739        if (![appName length])
1740            appName = [[NSProcessInfo processInfo] processName];
1741
1742        return appName;
1743    }
1744
1745    void createApplicationMenus(void)
1746    {
1747        NSString *appName;
1748        NSString *title;
1749        NSMenu *appleMenu;
1750        NSMenuItem *menuItem;
1751
1752        /* Create the main menu bar */
1753        [NSApp setMainMenu:[[NSMenu alloc] init]];
1754
1755        /* Create the application menu */
1756        appName = getApplicationName();
1757        appleMenu = [[NSMenu alloc] initWithTitle:@""];
1758
1759        /* Add menu items */
1760        title = [@"About " stringByAppendingString:appName];
1761        [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
1762
1763        [appleMenu addItem:[NSMenuItem separatorItem]];
1764
1765        NSMenu* service_menu = [[NSMenu alloc] init];
1766        NSMenuItem* service_menu_item = [[NSMenuItem alloc] initWithTitle:@"Services" action:nil keyEquivalent:@""];
1767        [service_menu_item setSubmenu: service_menu];
1768        [appleMenu addItem: service_menu_item];
1769        [NSApp setServicesMenu: service_menu];
1770
1771        [appleMenu addItem:[NSMenuItem separatorItem]];
1772
1773        title = [@"Hide " stringByAppendingString:appName];
1774        [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@/*"h"*/"h"];
1775
1776        menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@/*"h"*/""];
1777        [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
1778
1779        [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
1780
1781        [appleMenu addItem:[NSMenuItem separatorItem]];
1782
1783        title = [@"Quit " stringByAppendingString:appName];
1784        [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@/*"q"*/"q"];
1785
1786        /* Put menu into the menubar */
1787        menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
1788        [menuItem setSubmenu:appleMenu];
1789        [[NSApp mainMenu] addItem:menuItem];
1790        [menuItem release];
1791
1792        /* Tell the application object that this is now the application menu */
1793        [NSApp setAppleMenu:appleMenu];
1794        [appleMenu release];
1795
1796
1797    }
1798
1799};
1800
1801}
1802
1803#ifdef USE_DARWIN_COCOA_IMPLEMENTATION
1804RegisterWindowingSystemInterfaceProxy<osgViewer::CocoaWindowingSystemInterface> createWindowingSystemInterfaceProxy;
1805#endif
1806
1807// declare C entry point for static compilation.
1808extern "C" void graphicswindow_Cocoa(void)
1809{
1810    osg::GraphicsContext::setWindowingSystemInterface(new osgViewer::CocoaWindowingSystemInterface());
1811}
Note: See TracBrowser for help on using the browser.