root/OpenSceneGraph/branches/osg-cocoa-dev/src/osgViewer/GraphicsWindowCocoa.mm @ 9852

Revision 9852, 40.7 kB (checked in by shuber, 6 years ago)

improved multi-monitor-support for GraphicsWindowCocoa?

  • Property svn:executable set to *
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
12#include <iostream>
13#include <osgViewer/api/Cocoa/PixelBufferCocoa>
14#include <osgViewer/api/Cocoa/GraphicsWindowCocoa>
15
16#include <Cocoa/Cocoa.h>
17
18#include "DarwinUtils.h"
19
20
21static bool s_quit_requested = false;
22
23
24// ----------------------------------------------------------------------------------------------------------
25// small helper class remapping key-codes
26// ----------------------------------------------------------------------------------------------------------
27// small helper class which maps the raw key codes to osgGA::GUIEventAdapter::Keys
28
29class CocoaKeyboardMap {
30
31    public:
32        CocoaKeyboardMap()
33        {
34            _keymap[27]     = osgGA::GUIEventAdapter::KEY_Escape;
35            _keymap[13]     = osgGA::GUIEventAdapter::KEY_KP_Enter;
36            _keymap[3]      = osgGA::GUIEventAdapter::KEY_Return;
37            _keymap[9]      = osgGA::GUIEventAdapter::KEY_Tab;
38            _keymap[32]     = osgGA::GUIEventAdapter::KEY_Space;
39            _keymap[127]    = osgGA::GUIEventAdapter::KEY_BackSpace;
40           
41           
42            _keymap[NSHomeFunctionKey]          = osgGA::GUIEventAdapter::KEY_Home;
43            _keymap[NSEndFunctionKey]           = osgGA::GUIEventAdapter::KEY_End;
44            _keymap[NSPageUpFunctionKey]        = osgGA::GUIEventAdapter::KEY_Page_Up;
45            _keymap[NSPageDownFunctionKey]      = osgGA::GUIEventAdapter::KEY_Page_Down;
46            _keymap[NSLeftArrowFunctionKey]     = osgGA::GUIEventAdapter::KEY_Left;
47            _keymap[NSRightArrowFunctionKey]    = osgGA::GUIEventAdapter::KEY_Right;
48            _keymap[NSUpArrowFunctionKey]       = osgGA::GUIEventAdapter::KEY_Up;
49            _keymap[NSDownArrowFunctionKey]     = osgGA::GUIEventAdapter::KEY_Down;
50           
51            _keymap[NSDeleteFunctionKey]        = osgGA::GUIEventAdapter::KEY_Delete;
52           
53            _keymap[NSF1FunctionKey]  = osgGA::GUIEventAdapter::KEY_F1;
54            _keymap[NSF2FunctionKey]  = osgGA::GUIEventAdapter::KEY_F2;
55            _keymap[NSF3FunctionKey]  = osgGA::GUIEventAdapter::KEY_F3;
56            _keymap[NSF4FunctionKey]  = osgGA::GUIEventAdapter::KEY_F4;
57            _keymap[NSF5FunctionKey]  = osgGA::GUIEventAdapter::KEY_F5;
58            _keymap[NSF6FunctionKey]  = osgGA::GUIEventAdapter::KEY_F6;
59            _keymap[NSF7FunctionKey]  = osgGA::GUIEventAdapter::KEY_F7;
60            _keymap[NSF8FunctionKey]  = osgGA::GUIEventAdapter::KEY_F8;
61            _keymap[NSF9FunctionKey]  = osgGA::GUIEventAdapter::KEY_F9;
62           
63            _keymap[NSF10FunctionKey]  = osgGA::GUIEventAdapter::KEY_F10;
64            _keymap[NSF11FunctionKey]  = osgGA::GUIEventAdapter::KEY_F11;
65            _keymap[NSF12FunctionKey]  = osgGA::GUIEventAdapter::KEY_F12;
66            _keymap[NSF13FunctionKey]  = osgGA::GUIEventAdapter::KEY_F13;
67            _keymap[NSF14FunctionKey]  = osgGA::GUIEventAdapter::KEY_F14;
68            _keymap[NSF15FunctionKey]  = osgGA::GUIEventAdapter::KEY_F15;
69            _keymap[NSF16FunctionKey]  = osgGA::GUIEventAdapter::KEY_F16;
70            _keymap[NSF17FunctionKey]  = osgGA::GUIEventAdapter::KEY_F17;
71            _keymap[NSF18FunctionKey]  = osgGA::GUIEventAdapter::KEY_F18;
72            _keymap[NSF19FunctionKey]  = osgGA::GUIEventAdapter::KEY_F19;
73           
74            _keymap[NSF20FunctionKey]  = osgGA::GUIEventAdapter::KEY_F20;
75            _keymap[NSF21FunctionKey]  = osgGA::GUIEventAdapter::KEY_F21;
76            _keymap[NSF22FunctionKey]  = osgGA::GUIEventAdapter::KEY_F22;
77            _keymap[NSF23FunctionKey]  = osgGA::GUIEventAdapter::KEY_F23;
78            _keymap[NSF24FunctionKey]  = osgGA::GUIEventAdapter::KEY_F24;
79            _keymap[NSF25FunctionKey]  = osgGA::GUIEventAdapter::KEY_F25;
80            _keymap[NSF26FunctionKey]  = osgGA::GUIEventAdapter::KEY_F26;
81            _keymap[NSF27FunctionKey]  = osgGA::GUIEventAdapter::KEY_F27;
82            _keymap[NSF28FunctionKey]  = osgGA::GUIEventAdapter::KEY_F28;
83            _keymap[NSF29FunctionKey]  = osgGA::GUIEventAdapter::KEY_F29;
84           
85            _keymap[NSF30FunctionKey]  = osgGA::GUIEventAdapter::KEY_F30;
86            _keymap[NSF31FunctionKey]  = osgGA::GUIEventAdapter::KEY_F31;
87            _keymap[NSF32FunctionKey]  = osgGA::GUIEventAdapter::KEY_F32;
88            _keymap[NSF33FunctionKey]  = osgGA::GUIEventAdapter::KEY_F33;
89            _keymap[NSF34FunctionKey]  = osgGA::GUIEventAdapter::KEY_F34;
90            _keymap[NSF35FunctionKey]  = osgGA::GUIEventAdapter::KEY_F35;
91                       
92           
93            _keypadmap['='] = osgGA::GUIEventAdapter::KEY_KP_Equal;
94            _keypadmap['*'] = osgGA::GUIEventAdapter::KEY_KP_Multiply;
95            _keypadmap['+'] = osgGA::GUIEventAdapter::KEY_KP_Add;
96            _keypadmap['-'] = osgGA::GUIEventAdapter::KEY_KP_Subtract;
97            _keypadmap['.'] = osgGA::GUIEventAdapter::KEY_KP_Decimal;
98            _keypadmap['/'] = osgGA::GUIEventAdapter::KEY_KP_Divide;
99           
100            _keypadmap['0'] = osgGA::GUIEventAdapter::KEY_KP_0;
101            _keypadmap['1'] = osgGA::GUIEventAdapter::KEY_KP_1;
102            _keypadmap['2'] = osgGA::GUIEventAdapter::KEY_KP_2;
103            _keypadmap['3'] = osgGA::GUIEventAdapter::KEY_KP_3;
104            _keypadmap['4'] = osgGA::GUIEventAdapter::KEY_KP_4;
105            _keypadmap['5'] = osgGA::GUIEventAdapter::KEY_KP_5;
106            _keypadmap['6'] = osgGA::GUIEventAdapter::KEY_KP_6;
107            _keypadmap['7'] = osgGA::GUIEventAdapter::KEY_KP_7;
108            _keypadmap['8'] = osgGA::GUIEventAdapter::KEY_KP_8;
109            _keypadmap['9'] = osgGA::GUIEventAdapter::KEY_KP_9;
110        }
111       
112        ~CocoaKeyboardMap() {
113        }
114       
115        unsigned int remapKey(unsigned int key, bool pressedOnKeypad = false)
116        {
117            if (pressedOnKeypad) {
118                 KeyMap::iterator itr = _keypadmap.find(key);
119                if (itr == _keypadmap.end()) return key;
120                else return itr->second;
121            }
122           
123            KeyMap::iterator itr = _keymap.find(key);
124            if (itr == _keymap.end()) return key;
125            else return itr->second;
126        }
127    private:
128        typedef std::map<unsigned int, osgGA::GUIEventAdapter::KeySymbol> KeyMap;
129        KeyMap _keymap, _keypadmap;
130};
131
132
133// ----------------------------------------------------------------------------------------------------------
134// remapCocoaKey
135// ----------------------------------------------------------------------------------------------------------
136static unsigned int remapCocoaKey(unsigned int key, bool pressedOnKeypad = false)
137{
138    static CocoaKeyboardMap s_CocoaKeyboardMap;
139    return s_CocoaKeyboardMap.remapKey(key, pressedOnKeypad);
140}
141
142
143std::ostream& operator<<(std::ostream& os, const NSRect& rect)
144{
145        os << rect.origin.x << "/" << rect.origin.y << " " << rect.size.width << "x" << rect.size.height;
146        return os;
147}
148
149// ----------------------------------------------------------------------------------------------------------
150// Cocoa uses a coordinate system where its origin is in the bottom left corner,
151// osg and quartz uses top left for the origin
152//
153// these 2 methods convets rects between the different coordinate systems
154// ----------------------------------------------------------------------------------------------------------
155
156static NSRect convertFromQuartzCoordinates(const NSRect& rect) 
157{
158    NSRect frame = [[[NSScreen screens] objectAtIndex: 0] frame];
159    float y = frame.size.height - rect.origin.y - rect.size.height;
160        NSRect converted = NSMakeRect(rect.origin.x, y, rect.size.width, rect.size.height);
161       
162        // std::cout << "converting from Quartz " << rect << " to " << converted << " using screen rect " << frame << std::endl;
163       
164    return converted;
165}
166
167static NSRect convertToQuartzCoordinates(const NSRect& rect)
168{
169    NSRect frame = [[[NSScreen screens] objectAtIndex: 0] frame];
170   
171    float y = frame.size.height - (rect.origin.y + rect.size.height);
172        NSRect converted = NSMakeRect(rect.origin.x, y, rect.size.width, rect.size.height);
173       
174        // std::cout << "converting To Quartz   " << rect << " to " << converted << " using screen rect " << frame << std::endl;
175       
176    return converted;
177}
178
179#pragma mark CocoaAppDelegate
180
181// ----------------------------------------------------------------------------------------------------------
182// the app-delegate, handling quit-requests
183// ----------------------------------------------------------------------------------------------------------
184
185@interface CocoaAppDelegate : NSObject 
186{
187}
188
189- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
190@end
191
192@implementation CocoaAppDelegate
193- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
194{
195    s_quit_requested = true;
196    // std::cout << "quit requested " << std::endl;
197    return NSTerminateNow;
198}
199
200@end
201
202#pragma mark GraphicsWindowCocoaWindow
203
204// ----------------------------------------------------------------------------------------------------------
205// GraphicsWindowCocoaWindow, implements canBecomeKeyWindow + canBecomeMainWindow
206// ----------------------------------------------------------------------------------------------------------
207
208@interface GraphicsWindowCocoaWindow : NSWindow
209{
210}
211
212- (BOOL) canBecomeKeyWindow;
213- (BOOL) canBecomeMainWindow;
214
215@end
216
217@implementation GraphicsWindowCocoaWindow
218
219
220- (BOOL) canBecomeKeyWindow
221{
222    return YES;
223}
224
225- (BOOL) canBecomeMainWindow
226{
227    return YES;
228}
229
230@end
231
232#pragma mark GraphicsWindowCocoaGLView
233
234
235// ----------------------------------------------------------------------------------------------------------
236// GraphicsWindowCocoaGLView
237// custom NSOpenGLView-class handling mouse- and keyboard-events, forwarding them to the EventQueue
238// some code borrowed from the example osgCocoaViewer from E.Wing
239// ----------------------------------------------------------------------------------------------------------
240
241@interface GraphicsWindowCocoaGLView : NSOpenGLView
242{
243    @private
244        osgViewer::GraphicsWindowCocoa* _win;
245        BOOL _isUsingCtrlClick, _isUsingOptionClick;
246        unsigned int _cachedModifierFlags;
247                BOOL _handleTabletEvents;
248       
249}
250- (void)setGraphicsWindowCocoa: (osgViewer::GraphicsWindowCocoa*) win;
251
252- (void)keyDown:(NSEvent *)theEvent;
253- (void)keyUp:(NSEvent *)theEvent;
254- (void)flagsChanged:(NSEvent *)theEvent;
255- (void) mouseMoved:(NSEvent*)theEvent;
256- (void) mouseDown:(NSEvent*)theEvent;
257- (void) mouseDragged:(NSEvent*)theEvent;
258- (void) mouseUp:(NSEvent*)theEvent;
259- (void) rightMouseDown:(NSEvent*)theEvent;
260- (void) rightMouseDragged:(NSEvent*)theEvent;
261- (void) rightMouseUp:(NSEvent*)theEvent;
262- (void) otherMouseDown:(NSEvent*)theEvent;
263- (void) otherMouseDragged:(NSEvent*)theEvent;
264- (void) otherMouseUp:(NSEvent*)theEvent;
265
266- (NSPoint) getLocalPoint: (NSEvent*)theEvent;
267- (void) handleModifiers: (NSEvent*)theEvent;
268- (void) setIsUsingCtrlClick:(BOOL)is_using_ctrl_click;
269- (BOOL) isUsingCtrlClick;
270- (void) setIsUsingOptionClick:(BOOL)is_using_option_click;
271- (BOOL) isUsingOptionClick;
272
273- (void) doLeftMouseButtonDown:(NSEvent*)theEvent;
274- (void) doLeftMouseButtonUp:(NSEvent*)theEvent;
275- (void) doRightMouseButtonDown:(NSEvent*)theEvent;
276- (void) doRightMouseButtonUp:(NSEvent*)theEvent;
277- (void) doMiddleMouseButtonDown:(NSEvent*)theEvent;
278- (void) doExtraMouseButtonDown:(NSEvent*)theEvent buttonNumber:(int)button_number;
279- (void) doMiddleMouseButtonUp:(NSEvent*)theEvent;
280- (void) doExtraMouseButtonUp:(NSEvent*)theEvent buttonNumber:(int)button_number;
281- (void) scrollWheel:(NSEvent*)theEvent;
282
283- (void)tabletPoint:(NSEvent *)theEvent;
284- (void)tabletProximity:(NSEvent *)theEvent;
285- (void)handleTabletEvents:(NSEvent*)theEvent;
286
287- (BOOL)acceptsFirstResponder;
288- (BOOL)becomeFirstResponder;
289- (BOOL)resignFirstResponder;
290
291@end
292
293@implementation GraphicsWindowCocoaGLView 
294
295
296-(void) setGraphicsWindowCocoa: (osgViewer::GraphicsWindowCocoa*) win
297{
298    _win = win;
299}
300
301- (BOOL)acceptsFirstResponder
302{
303  return YES;
304}
305
306- (BOOL)becomeFirstResponder
307{
308  return YES;
309}
310
311- (BOOL)resignFirstResponder
312{
313  return YES;
314}
315
316
317- (NSPoint) getLocalPoint: (NSEvent*)theEvent
318{
319    return  [self convertPoint:[theEvent locationInWindow] fromView:nil];
320}
321
322
323- (void) handleModifiers: (NSEvent*)theEvent
324{
325    unsigned int flags = [theEvent modifierFlags];
326   
327    if (flags == _cachedModifierFlags)
328        return;
329   
330    const unsigned int masks[] = {
331        NSShiftKeyMask,
332        NSControlKeyMask,
333        NSAlternateKeyMask,
334        NSCommandKeyMask,
335        NSAlphaShiftKeyMask
336    };
337   
338    const unsigned int keys[] = {
339        osgGA::GUIEventAdapter::KEY_Shift_L,
340        osgGA::GUIEventAdapter::KEY_Control_L,
341        osgGA::GUIEventAdapter::KEY_Alt_L,
342        osgGA::GUIEventAdapter::KEY_Super_L,
343        osgGA::GUIEventAdapter::KEY_Caps_Lock
344    };
345   
346    for(unsigned int i = 0; i < 5; ++i) {
347       
348        if ((flags & masks[i]) && !(_cachedModifierFlags & masks[i]))
349        {
350            _win->getEventQueue()->keyPress(keys[i]);
351        }
352       
353        if (!(flags & masks[i]) && (_cachedModifierFlags & masks[i]))
354        {
355            _win->getEventQueue()->keyRelease(keys[i]);
356        }
357    }
358   
359    _cachedModifierFlags = flags;
360   
361}
362
363- (void)flagsChanged:(NSEvent *)theEvent {
364        [self handleModifiers: theEvent];
365}
366
367- (void) mouseMoved:(NSEvent*)theEvent 
368{
369    NSPoint converted_point = [self getLocalPoint: theEvent];
370    _win->getEventQueue()->mouseMotion(converted_point.x, converted_point.y);
371}
372
373
374
375- (void) mouseDown:(NSEvent*)theEvent
376{
377    // Because many Mac users have only a 1-button mouse, we should provide ways
378    // to access the button 2 and 3 actions of osgViewer.
379    // I will use the Ctrl modifer to represent right-clicking
380    // and Option modifier to represent middle clicking.
381    if([theEvent modifierFlags] & NSControlKeyMask)
382    {
383        [self setIsUsingCtrlClick:YES];
384        [self doRightMouseButtonDown:theEvent];
385    }
386    else if([theEvent modifierFlags] & NSAlternateKeyMask)
387    {
388        [self setIsUsingOptionClick:YES];
389        [self doMiddleMouseButtonDown:theEvent];
390    }
391    else
392    {
393        [self doLeftMouseButtonDown:theEvent];
394    }
395       
396        if ([theEvent subtype] == NSTabletPointEventSubtype) {
397                _handleTabletEvents = true;
398                [self handleTabletEvents:theEvent];
399        }
400}
401
402
403- (void) mouseDragged:(NSEvent*)theEvent
404{
405    NSPoint converted_point = [self getLocalPoint: theEvent];   
406    _win->getEventQueue()->mouseMotion(converted_point.x, converted_point.y);
407       
408        if (_handleTabletEvents)
409                [self handleTabletEvents:theEvent];
410}
411
412
413- (void) mouseUp:(NSEvent*)theEvent
414{
415    // Because many Mac users have only a 1-button mouse, we should provide ways
416    // to access the button 2 and 3 actions of osgViewer.
417    // I will use the Ctrl modifer to represent right-clicking
418    // and Option modifier to represent middle clicking.
419    if([self isUsingCtrlClick] == YES)
420    {
421        [self setIsUsingCtrlClick:NO];
422        [self doRightMouseButtonUp:theEvent];
423    }
424    else if([self isUsingOptionClick] == YES)
425    {
426        [self setIsUsingOptionClick:NO];
427        [self doMiddleMouseButtonUp:theEvent];
428    }
429    else
430    {
431        [self doLeftMouseButtonUp:theEvent];
432    }
433        _handleTabletEvents = false;
434}
435
436- (void) rightMouseDown:(NSEvent*)theEvent
437{
438    [self doRightMouseButtonDown:theEvent];
439}
440
441- (void) rightMouseDragged:(NSEvent*)theEvent
442{
443   
444    NSPoint converted_point = [self getLocalPoint: theEvent];
445    _win->getEventQueue()->mouseMotion(converted_point.x, converted_point.y);
446}
447
448- (void) rightMouseUp:(NSEvent*)theEvent
449{
450    [self doRightMouseButtonUp:theEvent];
451        _handleTabletEvents = false;
452}
453
454// "otherMouse" seems to capture middle button and any other buttons beyond (4th, etc).
455- (void) otherMouseDown:(NSEvent*)theEvent
456{
457    // Button 0 is left
458    // Button 1 is right
459    // Button 2 is middle
460    // Button 3 keeps going
461    // osgViewer expects 1 for left, 3 for right, 2 for middle
462    // osgViewer has a reversed number mapping for right and middle compared to Cocoa
463    if([theEvent buttonNumber] == 2)
464    {
465        [self doMiddleMouseButtonDown:theEvent];
466    }
467    else // buttonNumber should be 3,4,5,etc; must map to 4,5,6,etc in osgViewer
468    {
469        [self doExtraMouseButtonDown:theEvent buttonNumber:[theEvent buttonNumber]];
470    }
471}
472
473- (void) otherMouseDragged:(NSEvent*)theEvent
474{
475    NSPoint converted_point = [self getLocalPoint: theEvent];   
476    _win->getEventQueue()->mouseMotion(converted_point.x, converted_point.y);
477   
478}
479
480// "otherMouse" seems to capture middle button and any other buttons beyond (4th, etc).
481- (void) otherMouseUp:(NSEvent*)theEvent
482{
483   
484    // Button 0 is left
485    // Button 1 is right
486    // Button 2 is middle
487    // Button 3 keeps going
488    // osgViewer expects 1 for left, 3 for right, 2 for middle
489    // osgViewer has a reversed number mapping for right and middle compared to Cocoa
490    if([theEvent buttonNumber] == 2)
491    {
492        [self doMiddleMouseButtonUp:theEvent];
493    }
494    else // buttonNumber should be 3,4,5,etc; must map to 4,5,6,etc in osgViewer
495    {
496        // I don't think osgViewer does anything for these additional buttons,
497        // but just in case, pass them along. But as a Cocoa programmer, you might
498        // think about things you can do natively here instead of passing the buck.
499        [self doExtraMouseButtonUp:theEvent buttonNumber:[theEvent buttonNumber]];
500    }
501}
502
503- (void) setIsUsingCtrlClick:(BOOL)is_using_ctrl_click
504{
505    _isUsingCtrlClick = is_using_ctrl_click;
506}
507
508- (BOOL) isUsingCtrlClick
509{
510    return _isUsingCtrlClick;
511}
512
513- (void) setIsUsingOptionClick:(BOOL)is_using_option_click
514{
515    _isUsingOptionClick = is_using_option_click;
516}
517
518- (BOOL) isUsingOptionClick
519{
520    return _isUsingOptionClick;
521}
522
523
524- (void) doLeftMouseButtonDown:(NSEvent*)theEvent
525{
526    NSPoint converted_point = [self getLocalPoint: theEvent];
527   
528    if([theEvent clickCount] == 1)
529    {
530        _win->getEventQueue()->mouseButtonPress(converted_point.x, converted_point.y, 1);
531    }
532    else
533    {
534        _win->getEventQueue()->mouseDoubleButtonPress(converted_point.x, converted_point.y, 1);
535    }
536}
537
538- (void) doLeftMouseButtonUp:(NSEvent*)theEvent
539{
540    NSPoint converted_point = [self getLocalPoint: theEvent];
541   
542    _win->getEventQueue()->mouseButtonRelease(converted_point.x, converted_point.y, 1);
543
544}
545
546- (void) doRightMouseButtonDown:(NSEvent*)theEvent
547{
548    NSPoint converted_point = [self getLocalPoint: theEvent];
549    if([theEvent clickCount] == 1)
550    {
551        _win->getEventQueue()->mouseButtonPress(converted_point.x, converted_point.y, 3);
552    }
553    else
554    {
555        _win->getEventQueue()->mouseDoubleButtonPress(converted_point.x, converted_point.y, 3);
556    }
557
558}
559
560
561- (void) doRightMouseButtonUp:(NSEvent*)theEvent
562{
563    NSPoint converted_point = [self getLocalPoint: theEvent];   
564    _win->getEventQueue()->mouseButtonRelease(converted_point.x, converted_point.y, 3);
565}
566
567- (void) doMiddleMouseButtonDown:(NSEvent*)theEvent
568{
569    NSPoint converted_point = [self getLocalPoint: theEvent];
570   
571    if([theEvent clickCount] == 1)
572    {
573        _win->getEventQueue()->mouseButtonPress(converted_point.x, converted_point.y, 2);
574    }
575    else
576    {
577        _win->getEventQueue()->mouseDoubleButtonPress(converted_point.x, converted_point.y, 2);
578    }
579}
580
581- (void) doExtraMouseButtonDown:(NSEvent*)theEvent buttonNumber:(int)button_number
582{
583    NSPoint converted_point = [self getLocalPoint: theEvent];
584    if([theEvent clickCount] == 1)
585    {
586        _win->getEventQueue()->mouseButtonPress(converted_point.x, converted_point.y, button_number+1);
587    }
588    else
589    {
590        _win->getEventQueue()->mouseDoubleButtonPress(converted_point.x, converted_point.y, button_number+1);
591    }
592}
593
594
595- (void) doMiddleMouseButtonUp:(NSEvent*)theEvent
596{
597    NSPoint converted_point = [self getLocalPoint: theEvent];
598    _win->getEventQueue()->mouseButtonRelease(converted_point.x, converted_point.y, 2);
599
600}
601
602- (void) doExtraMouseButtonUp:(NSEvent*)theEvent buttonNumber:(int)button_number
603{
604    NSPoint converted_point = [self getLocalPoint: theEvent];
605    _win->getEventQueue()->mouseButtonRelease(converted_point.x, converted_point.y, button_number+1);
606}
607
608
609
610- (void) scrollWheel:(NSEvent*)theEvent
611{
612    // Unfortunately, it turns out mouseScroll2D doesn't actually do anything.
613    // The camera manipulators don't seem to implement any code that utilize the scroll values.
614    // This this call does nothing.
615    _win->getEventQueue()->mouseScroll2D([theEvent deltaX], [theEvent deltaY]);
616}
617
618
619
620- (void)keyDown:(NSEvent *)theEvent 
621{
622    NSString* chars = [theEvent charactersIgnoringModifiers];
623    unsigned int keyCode = remapCocoaKey([chars characterAtIndex:0], ([theEvent modifierFlags] & NSFunctionKeyMask) );
624    // std::cout << "key dn: " <<[chars characterAtIndex:0] << "=" << keyCode << std::endl;   
625    _win->getEventQueue()->keyPress( remapCocoaKey(keyCode), [theEvent timestamp]);
626}
627
628
629- (void)keyUp:(NSEvent *)theEvent 
630{   
631    NSString* chars = [theEvent charactersIgnoringModifiers];
632    unsigned int keyCode = remapCocoaKey([chars characterAtIndex:0], ([theEvent modifierFlags] & NSFunctionKeyMask));
633    // std::cout << "key up: " <<[chars characterAtIndex:0] << "=" << keyCode << std::endl;   
634    _win->getEventQueue()->keyRelease( remapCocoaKey(keyCode), [theEvent timestamp]);
635}
636
637
638- (void)tabletPoint:(NSEvent *)theEvent
639{
640        //_handleTabletEvents = YES;
641        //[self handleTabletEvents:theEvent];
642}
643
644-(void)handleTabletEvents:(NSEvent *)theEvent
645{
646        float pressure = [theEvent pressure];
647        _win->getEventQueue()->penPressure(pressure);
648        NSPoint tilt = [theEvent tilt];
649       
650        _win->getEventQueue()->penOrientation (tilt.x, tilt.y, [theEvent rotation]);
651}
652
653
654- (void)tabletProximity:(NSEvent *)theEvent
655{
656        osgGA::GUIEventAdapter::TabletPointerType pt(osgGA::GUIEventAdapter::UNKNOWN);
657        switch ([theEvent pointingDeviceType]) {
658                case NSPenPointingDevice:
659                        pt = osgGA::GUIEventAdapter::PEN;
660                        break;
661                case NSCursorPointingDevice:
662                        pt = osgGA::GUIEventAdapter::PUCK;
663                        break;
664                case NSEraserPointingDevice:
665                        pt = osgGA::GUIEventAdapter::ERASER;
666                        break;
667                default:
668                        break;
669        }
670        _win->getEventQueue()->penProximity(pt, [theEvent isEnteringProximity]);
671}
672
673
674@end
675
676
677#pragma mark GraphicsWindowCocoaDelegate
678
679
680// ----------------------------------------------------------------------------------------------------------
681// the window-delegate, handles moving/resizing of the window etc.
682// ----------------------------------------------------------------------------------------------------------
683
684@interface GraphicsWindowCocoaDelegate : NSObject
685{
686    @private
687        osgViewer::GraphicsWindowCocoa* _win;
688        BOOL                            _inDidMove;
689}
690
691- (id)initWith: (osgViewer::GraphicsWindowCocoa*) win;
692- (void)windowDidMove:(NSNotification *)notification;
693- (void)windowDidResize:(NSNotification *)notification;
694- (BOOL)windowShouldClose:(id)window;
695- (void)updateWindowBounds;
696
697@end
698
699
700@implementation GraphicsWindowCocoaDelegate
701
702- (id)initWith: (osgViewer::GraphicsWindowCocoa*) win
703{
704    _inDidMove = false;
705    _win = win;
706    return [super init];
707}
708
709
710- (void)windowDidMove:(NSNotification *)notification
711{
712    [self updateWindowBounds];
713}
714
715- (void)windowDidResize:(NSNotification *)notification
716{
717    [self updateWindowBounds];
718}
719
720-(void)updateWindowBounds
721{
722    if (_inDidMove) return;
723    _inDidMove = true;
724   
725    GraphicsWindowCocoaWindow* nswin = _win->getWindow();
726    NSRect bounds = [nswin contentRectForFrameRect: [nswin frame] ];
727   
728    // convert to quartz-coordinate-system
729    bounds = convertToQuartzCoordinates(bounds);
730   
731    // std::cout << "windowdidmove: " << bounds.origin.x << " " << bounds.origin.y << " " << bounds.size.width << " " << bounds.size.height << std::endl;
732   
733    _win->resized(bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height);
734    _win->getEventQueue()->windowResize(bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height, _win->getEventQueue()->getTime());
735    _win->requestRedraw();
736    _inDidMove = false;
737}
738
739- (BOOL)windowShouldClose:(id)window 
740{
741    return _win->requestClose();
742}
743
744@end
745
746
747#pragma mark CocoaWindowAdapter
748
749
750
751using namespace osgDarwin;
752namespace osgViewer {
753
754
755// ----------------------------------------------------------------------------------------------------------
756// small adapter class to handle the dock/menubar
757// ----------------------------------------------------------------------------------------------------------
758
759class CocoaWindowAdapter : public MenubarController::WindowAdapter {
760public:
761    CocoaWindowAdapter(GraphicsWindowCocoa* win) : MenubarController::WindowAdapter(), _win(win) {}
762   
763    virtual bool valid() { return (_win.valid() && _win->valid()); }
764       
765    virtual void getWindowBounds(CGRect& rect)
766    {
767        NSRect nsrect = [_win->getWindow() frame];
768        nsrect = convertToQuartzCoordinates(nsrect);
769       
770        rect.origin.x = nsrect.origin.x;
771        rect.origin.y = nsrect.origin.y;
772        rect.size.width = nsrect.size.width;
773        rect.size.height = nsrect.size.height;
774    }
775       
776    virtual osgViewer::GraphicsWindow* getWindow() {return _win.get(); }
777private:
778    osg::observer_ptr<GraphicsWindowCocoa> _win;
779};
780
781#pragma mark GraphicsWindowCocoa
782
783
784
785// ----------------------------------------------------------------------------------------------------------
786// init
787// ----------------------------------------------------------------------------------------------------------
788
789void GraphicsWindowCocoa::init()
790{
791    if (_initialized) return;
792
793    _closeRequested = false;
794    _ownsWindow = false;
795    _context = NULL;
796    _window = NULL;
797    _valid = _initialized = true;
798}
799
800
801// ----------------------------------------------------------------------------------------------------------
802// setupNSWindow
803// sets up the NSWindow, adds delegates, etc
804// ----------------------------------------------------------------------------------------------------------
805
806void GraphicsWindowCocoa::setupNSWindow(NSWindow* win)
807{
808
809    [win setReleasedWhenClosed:NO];
810        [win setDisplaysWhenScreenProfileChanges:YES]; 
811    GraphicsWindowCocoaDelegate* delegate = [[GraphicsWindowCocoaDelegate alloc] initWith: this];
812    [win setDelegate: delegate ];
813    //[delegate autorelease];
814           
815    [win makeKeyAndOrderFront:nil];
816    [win setAcceptsMouseMovedEvents: YES];
817
818}
819
820
821// ----------------------------------------------------------------------------------------------------------
822// realizeImplementation, creates the window + context
823// ----------------------------------------------------------------------------------------------------------
824
825bool GraphicsWindowCocoa::realizeImplementation()
826{
827    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
828   
829    unsigned int style(NSBorderlessWindowMask);
830   
831    if (_traits->windowDecoration) {
832        style = NSClosableWindowMask | NSMiniaturizableWindowMask | NSTitledWindowMask;
833       
834        // supportsResize works only with windows with titlebar
835        if (_traits->supportsResize)
836            style |= NSResizableWindowMask;
837    }
838       
839    DarwinWindowingSystemInterface* wsi = dynamic_cast<DarwinWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
840    int screenLeft(0), screenTop(0);
841    if (wsi) {
842       
843        wsi->getScreenTopLeft((*_traits), screenLeft, screenTop);
844        _traits->y += screenTop;
845        _traits->x += screenLeft;
846    }
847   
848        NSRect rect = NSMakeRect(_traits->x, _traits->y, _traits->width, _traits->height);
849   
850        _window = [[GraphicsWindowCocoaWindow alloc] initWithContentRect: rect styleMask: style backing: NSBackingStoreBuffered defer: NO];
851       
852    if (!_window) {
853        osg::notify(osg::WARN) << "GraphicsWindowCocoa::realizeImplementation :: could not create window" << std::endl;
854        return false;
855    }
856   
857    rect = convertFromQuartzCoordinates(rect);
858    [_window setFrameOrigin: rect.origin];
859         
860        NSOpenGLPixelFormatAttribute attr[32];
861    int i = 0;
862   
863    attr[i++] = NSOpenGLPFADepthSize;
864    attr[i++] = static_cast<NSOpenGLPixelFormatAttribute>(_traits->depth);
865
866    if (_traits->doubleBuffer) {
867        attr[i++] = NSOpenGLPFADoubleBuffer;
868    }
869   
870    if (_traits->alpha) {
871        attr[i++] = NSOpenGLPFAAlphaSize;
872        attr[i++] = static_cast<NSOpenGLPixelFormatAttribute>(_traits->alpha);
873    }
874
875    if (_traits->stencil) {
876        attr[i++] = NSOpenGLPFAStencilSize;
877        attr[i++] = static_cast<NSOpenGLPixelFormatAttribute>(_traits->stencil);
878    }
879 
880
881    if (_traits->sampleBuffers) {
882        attr[i++] = NSOpenGLPFASampleBuffers;
883        attr[i++] = static_cast<NSOpenGLPixelFormatAttribute>(_traits->sampleBuffers);
884        attr[i++] = NSOpenGLPFASamples;
885        attr[i++] = static_cast<NSOpenGLPixelFormatAttribute>(_traits->samples);
886    }
887
888   
889    attr[i++] = NSOpenGLPFAAccelerated;
890    attr[i] = static_cast<NSOpenGLPixelFormatAttribute>(0);
891   
892    // create the context
893    NSOpenGLContext* sharedContext = NULL;
894   
895    GraphicsWindowCocoa* graphicsWindowCocoa = dynamic_cast<GraphicsWindowCocoa*>(_traits->sharedContext);
896    if (graphicsWindowCocoa)
897    {
898        sharedContext = graphicsWindowCocoa->getContext();
899    }
900    else
901    {
902        PixelBufferCocoa* pixelbuffer = dynamic_cast<PixelBufferCocoa*>(_traits->sharedContext);
903        if (pixelbuffer) {
904            sharedContext = pixelbuffer->getContext();
905        }
906    }
907       
908        NSOpenGLPixelFormat* pixelformat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr ];
909    _context = [[NSOpenGLContext alloc] initWithFormat: pixelformat shareContext: sharedContext];
910   
911    if (!_context) {
912        osg::notify(osg::WARN) << "GraphicsWindowCocoa::realizeImplementation :: could not create context" << std::endl;
913        return false;
914    }
915        GraphicsWindowCocoaGLView* theView = [[ GraphicsWindowCocoaGLView alloc ] initWithFrame:[ _window frame ] ];
916    [theView setAutoresizingMask:  (NSViewWidthSizable | NSViewHeightSizable) ];
917    [theView setGraphicsWindowCocoa: this];
918    [theView setOpenGLContext:_context];
919        [_window setContentView: theView];
920       
921    setupNSWindow(_window);
922   
923    [theView release];
924    [pool release];
925       
926    MenubarController::instance()->attachWindow( new CocoaWindowAdapter(this) );
927   
928    useCursor(_traits->useCursor);
929    setWindowName(_traits->windowName);
930    setVSync(_traits->vsync);
931   
932    MenubarController::instance()->update();
933   
934    // Cocoa's origin is bottom/left:
935    getEventQueue()->getCurrentEventState()->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS);
936   
937    _valid = _initialized = _realized = true;
938    return _valid;
939}
940
941
942
943
944// ----------------------------------------------------------------------------------------------------------
945// closeImplementation
946// ----------------------------------------------------------------------------------------------------------
947void GraphicsWindowCocoa::closeImplementation()
948{
949    _valid = false;
950    _realized = false;
951   
952    // there's a possibility that the MenubarController is destructed already, so prevent a crash:
953    MenubarController* mbc = MenubarController::instance();
954    if (mbc) mbc->detachWindow(this);
955   
956        [_window close];
957    [_window release];
958}
959
960
961// ----------------------------------------------------------------------------------------------------------
962// makeCurrentImplementation
963// ----------------------------------------------------------------------------------------------------------
964
965bool GraphicsWindowCocoa:: makeCurrentImplementation()
966{
967        [_context makeCurrentContext];
968        return true;
969}
970
971
972// ----------------------------------------------------------------------------------------------------------
973// releaseContextImplementation
974// ----------------------------------------------------------------------------------------------------------
975
976bool GraphicsWindowCocoa::releaseContextImplementation()
977{
978        [NSOpenGLContext clearCurrentContext];
979        return true;
980}
981
982
983// ----------------------------------------------------------------------------------------------------------
984// swapBuffersImplementation
985// ----------------------------------------------------------------------------------------------------------
986
987void GraphicsWindowCocoa::swapBuffersImplementation()
988{
989        [_context flushBuffer];
990}
991
992
993// ----------------------------------------------------------------------------------------------------------
994// checkEvents
995// process all pending events
996// ----------------------------------------------------------------------------------------------------------
997void GraphicsWindowCocoa::checkEvents()
998{
999        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1000   
1001    while(1)
1002    {
1003        /*  NOTE: It may be better to use something like
1004            NSEventTrackingRunLoopMode since we don't necessarily want all
1005            timers/sources/observers to run, only those which would
1006            run while tracking events.  However, it should be noted that
1007            NSEventTrackingRunLoopMode is in the common set of modes
1008            so it may not effectively make much of a difference.
1009         */
1010        NSEvent *event = [ [NSApplication sharedApplication]
1011                nextEventMatchingMask:NSAnyEventMask
1012                untilDate:[NSDate distantPast]
1013                inMode:NSDefaultRunLoopMode
1014                dequeue: YES];
1015        if(!event)
1016            break;
1017        [[NSApplication sharedApplication] sendEvent: event];
1018    }   
1019   
1020    if (_closeRequested)
1021        getEventQueue()->closeWindow();
1022       
1023    if (s_quit_requested) {
1024        getEventQueue()->quitApplication();
1025        s_quit_requested = false;
1026    }
1027       
1028        [pool release];
1029}
1030
1031
1032
1033// ----------------------------------------------------------------------------------------------------------
1034// setWindowDecorationImplementation
1035//
1036// unfortunately there's no way to change the decoration of a window, so we create an new one
1037// and swap the content
1038// ----------------------------------------------------------------------------------------------------------
1039
1040bool GraphicsWindowCocoa::setWindowDecorationImplementation(bool flag)
1041{
1042    if (!_realized) return false;
1043       
1044    unsigned int style(NSBorderlessWindowMask);
1045   
1046    if (flag) {
1047        style = NSClosableWindowMask | NSMiniaturizableWindowMask | NSTitledWindowMask;
1048       
1049        // supportsResize works only with windows with titlebar
1050        if (_traits->supportsResize)
1051            style |= NSResizableWindowMask;
1052    }
1053        NSRect rect = [_window contentRectForFrameRect: [_window frame] ];
1054        GraphicsWindowCocoaWindow* new_win = [[GraphicsWindowCocoaWindow alloc] initWithContentRect: rect styleMask: style backing: NSBackingStoreBuffered defer: NO];
1055   
1056    if (new_win) {
1057        [new_win setContentView: [_window contentView]];
1058        setupNSWindow(new_win);
1059        [new_win setTitle: [_window title]];
1060        [_window close];
1061        [_window release];
1062
1063        _window = new_win;
1064        [_window makeKeyAndOrderFront: nil];
1065    }
1066   
1067        return true;
1068}
1069
1070
1071// ----------------------------------------------------------------------------------------------------------
1072// grabFocus
1073// ----------------------------------------------------------------------------------------------------------
1074void GraphicsWindowCocoa::grabFocus()
1075{
1076        [_window makeKeyAndOrderFront: nil];
1077}
1078
1079
1080// ----------------------------------------------------------------------------------------------------------
1081// grabFocusIfPointerInWindow
1082// ----------------------------------------------------------------------------------------------------------
1083void GraphicsWindowCocoa::grabFocusIfPointerInWindow()
1084{
1085        osg::notify(osg::INFO) << "GraphicsWindowCocoa :: grabFocusIfPointerInWindow not implemented yet " << std::endl;
1086}
1087
1088
1089// ----------------------------------------------------------------------------------------------------------
1090// resizedImplementation
1091// ----------------------------------------------------------------------------------------------------------
1092
1093void GraphicsWindowCocoa::resizedImplementation(int x, int y, int width, int height)
1094{
1095        GraphicsContext::resizedImplementation(x, y, width, height);
1096   
1097    [_context update];
1098    MenubarController::instance()->update();
1099}
1100
1101
1102// ----------------------------------------------------------------------------------------------------------
1103// setWindowRectangleImplementation
1104// ----------------------------------------------------------------------------------------------------------
1105bool GraphicsWindowCocoa::setWindowRectangleImplementation(int x, int y, int width, int height)
1106{
1107   
1108    NSRect rect = NSMakeRect(x,y,width, height);
1109    rect = convertFromQuartzCoordinates(rect);
1110   
1111    [_window setFrame: [NSWindow frameRectForContentRect: rect styleMask: [_window styleMask]] display: YES];
1112    [_context update];
1113    MenubarController::instance()->update();
1114   
1115    return true;
1116}
1117
1118
1119
1120// ----------------------------------------------------------------------------------------------------------
1121// setWindowName
1122// ----------------------------------------------------------------------------------------------------------
1123
1124void GraphicsWindowCocoa::setWindowName (const std::string & name)
1125{
1126        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1127   
1128    NSString* title = [NSString stringWithCString: name.c_str() encoding: NSUTF8StringEncoding];
1129        [_window setTitle: title];
1130        [title release];
1131    [pool release];
1132}
1133
1134
1135// ----------------------------------------------------------------------------------------------------------
1136// useCursor
1137// ----------------------------------------------------------------------------------------------------------
1138
1139void GraphicsWindowCocoa::useCursor(bool cursorOn)
1140{
1141        if (_traits.valid())
1142        _traits->useCursor = cursorOn;
1143    DarwinWindowingSystemInterface* wsi = dynamic_cast<DarwinWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
1144    if (wsi == NULL) {
1145        osg::notify(osg::WARN) << "GraphicsWindowCarbon::useCursor :: could not get OSXCarbonWindowingSystemInterface" << std::endl;
1146        return;
1147    }
1148   
1149    CGDirectDisplayID displayId = wsi->getDisplayID((*_traits));
1150    CGDisplayErr err = kCGErrorSuccess;
1151    switch (cursorOn)
1152    {
1153        case true:
1154            err = CGDisplayShowCursor(displayId);
1155            break;
1156        case false:
1157            err = CGDisplayHideCursor(displayId);
1158            break;
1159    }
1160    if (err != kCGErrorSuccess) {
1161        osg::notify(osg::WARN) << "GraphicsWindowCocoa::useCursor failed with " << err << std::endl;
1162    }
1163}
1164
1165
1166// ----------------------------------------------------------------------------------------------------------
1167// setCursor
1168// ----------------------------------------------------------------------------------------------------------
1169
1170void GraphicsWindowCocoa::setCursor(MouseCursor mouseCursor)
1171{
1172        switch (mouseCursor)
1173    {
1174
1175        case NoCursor:
1176            [NSCursor hide];
1177            break;
1178   
1179        case LeftArrowCursor:
1180            [[NSCursor arrowCursor] set];
1181            break;
1182       
1183        case TextCursor:
1184            [[NSCursor IBeamCursor] set];
1185            break;
1186           
1187        case CrosshairCursor:
1188            [[NSCursor crosshairCursor] set];
1189            break;
1190       
1191        default:
1192            osg::notify(osg::INFO) << "GraphicsWindowCocoa::setCursor :: unsupported MouseCursor: " << mouseCursor << std::endl;   
1193    }
1194}
1195
1196
1197// ----------------------------------------------------------------------------------------------------------
1198// setVSync
1199// ----------------------------------------------------------------------------------------------------------
1200
1201void GraphicsWindowCocoa::setVSync(bool f)
1202{
1203    GLint VBL(f?1:0);
1204        [_context setValues:&VBL forParameter:NSOpenGLCPSwapInterval];
1205}
1206
1207
1208// ----------------------------------------------------------------------------------------------------------
1209// d'tor
1210// ----------------------------------------------------------------------------------------------------------
1211
1212GraphicsWindowCocoa::~GraphicsWindowCocoa()
1213{
1214}
1215
1216
1217
1218#pragma mark CocoaWindowingSystemInterface
1219
1220// ----------------------------------------------------------------------------------------------------------
1221// CocoaWindowingSystemInterface
1222// ----------------------------------------------------------------------------------------------------------
1223
1224struct CocoaWindowingSystemInterface : public DarwinWindowingSystemInterface {
1225       
1226        CocoaWindowingSystemInterface()
1227        :       DarwinWindowingSystemInterface()
1228        {
1229                localPool = [[NSAutoreleasePool alloc] init];
1230        [[NSApplication sharedApplication] setDelegate: [[CocoaAppDelegate alloc] init] ];
1231        }
1232       
1233        virtual osg::GraphicsContext* createGraphicsContext(osg::GraphicsContext::Traits* traits)
1234        {
1235                return createGraphicsContextImplementation<PixelBufferCocoa, GraphicsWindowCocoa>(traits);
1236        }
1237       
1238        virtual ~CocoaWindowingSystemInterface()
1239        {
1240                [localPool release];
1241        }
1242       
1243        NSAutoreleasePool *localPool;
1244
1245};
1246
1247
1248}
1249
1250#ifdef USE_DARWIN_COCOA_IMPLEMENTATION
1251RegisterWindowingSystemInterfaceProxy<osgViewer::CocoaWindowingSystemInterface> createWindowingSystemInterfaceProxy;
1252#endif
1253
1254// declare C entry point for static compilation.
1255extern "C" void graphicswindow_Cocoa(void)
1256{
1257    osg::GraphicsContext::setWindowingSystemInterface(new osgViewer::CocoaWindowingSystemInterface());
1258}
Note: See TracBrowser for help on using the browser.