1//
2// Copyright 2015 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7// OSXWindow.mm: Implementation of OSWindow for OSX
8
9#include "util/osx/OSXWindow.h"
10
11#include <set>
12// Include Carbon to use the keycode names in Carbon's Event.h
13#include <Carbon/Carbon.h>
14
15#include "anglebase/no_destructor.h"
16#include "common/debug.h"
17
18// On OSX 10.12 a number of AppKit interfaces have been renamed for consistency, and the previous
19// symbols tagged as deprecated. However we can't simply use the new symbols as it would break
20// compilation on our automated testing that doesn't use OSX 10.12 yet. So we just ignore the
21// warnings.
22#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
23
24// Some events such as "ShouldTerminate" are sent to the whole application so we keep a list of
25// all the windows in order to forward the event to each of them. However this and calling pushEvent
26// in ApplicationDelegate is inherently unsafe in a multithreaded environment.
27static std::set<OSXWindow *> &AllWindows()
28{
29    static angle::base::NoDestructor<std::set<OSXWindow *>> allWindows;
30    return *allWindows;
31}
32
33@interface Application : NSApplication
34@end
35
36@implementation Application
37- (void)sendEvent:(NSEvent *)nsEvent
38{
39    if ([nsEvent type] == NSApplicationDefined)
40    {
41        for (auto window : AllWindows())
42        {
43            if ([window->getNSWindow() windowNumber] == [nsEvent windowNumber])
44            {
45                Event event;
46                event.Type = Event::EVENT_TEST;
47                window->pushEvent(event);
48            }
49        }
50    }
51    [super sendEvent:nsEvent];
52}
53@end
54
55// The Delegate receiving application-wide events.
56@interface ApplicationDelegate : NSObject
57@end
58
59@implementation ApplicationDelegate
60- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
61{
62    Event event;
63    event.Type = Event::EVENT_CLOSED;
64    for (auto window : AllWindows())
65    {
66        window->pushEvent(event);
67    }
68    return NSTerminateCancel;
69}
70@end
71static ApplicationDelegate *gApplicationDelegate = nil;
72
73static bool InitializeAppKit()
74{
75    if (NSApp != nil)
76    {
77        return true;
78    }
79
80    // Initialize the global variable "NSApp"
81    [Application sharedApplication];
82
83    // Make us appear in the dock
84    [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
85
86    // Register our global event handler
87    gApplicationDelegate = [[ApplicationDelegate alloc] init];
88    if (gApplicationDelegate == nil)
89    {
90        return false;
91    }
92    [NSApp setDelegate:static_cast<id>(gApplicationDelegate)];
93
94    // Set our status to "started" so we are not bouncing in the doc and can activate
95    [NSApp finishLaunching];
96    return true;
97}
98
99// NS's and CG's coordinate systems start at the bottom left, while OSWindow's coordinate
100// system starts at the top left. This function converts the Y coordinate accordingly.
101static float YCoordToFromCG(float y)
102{
103    float screenHeight = CGDisplayBounds(CGMainDisplayID()).size.height;
104    return screenHeight - y;
105}
106
107// Delegate for window-wide events, note that the protocol doesn't contain anything input related.
108@implementation WindowDelegate
109- (id)initWithWindow:(OSXWindow *)window
110{
111    self = [super init];
112    if (self != nil)
113    {
114        mWindow = window;
115    }
116    return self;
117}
118
119- (void)onOSXWindowDeleted
120{
121    mWindow = nil;
122}
123
124- (BOOL)windowShouldClose:(id)sender
125{
126    Event event;
127    event.Type = Event::EVENT_CLOSED;
128    mWindow->pushEvent(event);
129    return NO;
130}
131
132- (void)windowDidResize:(NSNotification *)notification
133{
134    NSSize windowSize = [[mWindow->getNSWindow() contentView] frame].size;
135    Event event;
136    event.Type        = Event::EVENT_RESIZED;
137    event.Size.Width  = (int)windowSize.width;
138    event.Size.Height = (int)windowSize.height;
139    mWindow->pushEvent(event);
140}
141
142- (void)windowDidMove:(NSNotification *)notification
143{
144    NSRect screenspace = [mWindow->getNSWindow() frame];
145    Event event;
146    event.Type   = Event::EVENT_MOVED;
147    event.Move.X = (int)screenspace.origin.x;
148    event.Move.Y = (int)YCoordToFromCG(screenspace.origin.y + screenspace.size.height);
149    mWindow->pushEvent(event);
150}
151
152- (void)windowDidBecomeKey:(NSNotification *)notification
153{
154    Event event;
155    event.Type = Event::EVENT_GAINED_FOCUS;
156    mWindow->pushEvent(event);
157    [self retain];
158}
159
160- (void)windowDidResignKey:(NSNotification *)notification
161{
162    if (mWindow != nil)
163    {
164        Event event;
165        event.Type = Event::EVENT_LOST_FOCUS;
166        mWindow->pushEvent(event);
167    }
168    [self release];
169}
170@end
171
172static Key NSCodeToKey(int keyCode)
173{
174    // Missing KEY_PAUSE
175    switch (keyCode)
176    {
177        case kVK_Shift:
178            return KEY_LSHIFT;
179        case kVK_RightShift:
180            return KEY_RSHIFT;
181        case kVK_Option:
182            return KEY_LALT;
183        case kVK_RightOption:
184            return KEY_RALT;
185        case kVK_Control:
186            return KEY_LCONTROL;
187        case kVK_RightControl:
188            return KEY_RCONTROL;
189        case kVK_Command:
190            return KEY_LSYSTEM;
191        // Right System doesn't have a name, but shows up as 0x36.
192        case 0x36:
193            return KEY_RSYSTEM;
194        case kVK_Function:
195            return KEY_MENU;
196
197        case kVK_ANSI_Semicolon:
198            return KEY_SEMICOLON;
199        case kVK_ANSI_Slash:
200            return KEY_SLASH;
201        case kVK_ANSI_Equal:
202            return KEY_EQUAL;
203        case kVK_ANSI_Minus:
204            return KEY_DASH;
205        case kVK_ANSI_LeftBracket:
206            return KEY_LBRACKET;
207        case kVK_ANSI_RightBracket:
208            return KEY_RBRACKET;
209        case kVK_ANSI_Comma:
210            return KEY_COMMA;
211        case kVK_ANSI_Period:
212            return KEY_PERIOD;
213        case kVK_ANSI_Backslash:
214            return KEY_BACKSLASH;
215        case kVK_ANSI_Grave:
216            return KEY_TILDE;
217        case kVK_Escape:
218            return KEY_ESCAPE;
219        case kVK_Space:
220            return KEY_SPACE;
221        case kVK_Return:
222            return KEY_RETURN;
223        case kVK_Delete:
224            return KEY_BACK;
225        case kVK_Tab:
226            return KEY_TAB;
227        case kVK_PageUp:
228            return KEY_PAGEUP;
229        case kVK_PageDown:
230            return KEY_PAGEDOWN;
231        case kVK_End:
232            return KEY_END;
233        case kVK_Home:
234            return KEY_HOME;
235        case kVK_Help:
236            return KEY_INSERT;
237        case kVK_ForwardDelete:
238            return KEY_DELETE;
239        case kVK_ANSI_KeypadPlus:
240            return KEY_ADD;
241        case kVK_ANSI_KeypadMinus:
242            return KEY_SUBTRACT;
243        case kVK_ANSI_KeypadMultiply:
244            return KEY_MULTIPLY;
245        case kVK_ANSI_KeypadDivide:
246            return KEY_DIVIDE;
247
248        case kVK_F1:
249            return KEY_F1;
250        case kVK_F2:
251            return KEY_F2;
252        case kVK_F3:
253            return KEY_F3;
254        case kVK_F4:
255            return KEY_F4;
256        case kVK_F5:
257            return KEY_F5;
258        case kVK_F6:
259            return KEY_F6;
260        case kVK_F7:
261            return KEY_F7;
262        case kVK_F8:
263            return KEY_F8;
264        case kVK_F9:
265            return KEY_F9;
266        case kVK_F10:
267            return KEY_F10;
268        case kVK_F11:
269            return KEY_F11;
270        case kVK_F12:
271            return KEY_F12;
272        case kVK_F13:
273            return KEY_F13;
274        case kVK_F14:
275            return KEY_F14;
276        case kVK_F15:
277            return KEY_F15;
278
279        case kVK_LeftArrow:
280            return KEY_LEFT;
281        case kVK_RightArrow:
282            return KEY_RIGHT;
283        case kVK_DownArrow:
284            return KEY_DOWN;
285        case kVK_UpArrow:
286            return KEY_UP;
287
288        case kVK_ANSI_Keypad0:
289            return KEY_NUMPAD0;
290        case kVK_ANSI_Keypad1:
291            return KEY_NUMPAD1;
292        case kVK_ANSI_Keypad2:
293            return KEY_NUMPAD2;
294        case kVK_ANSI_Keypad3:
295            return KEY_NUMPAD3;
296        case kVK_ANSI_Keypad4:
297            return KEY_NUMPAD4;
298        case kVK_ANSI_Keypad5:
299            return KEY_NUMPAD5;
300        case kVK_ANSI_Keypad6:
301            return KEY_NUMPAD6;
302        case kVK_ANSI_Keypad7:
303            return KEY_NUMPAD7;
304        case kVK_ANSI_Keypad8:
305            return KEY_NUMPAD8;
306        case kVK_ANSI_Keypad9:
307            return KEY_NUMPAD9;
308
309        case kVK_ANSI_A:
310            return KEY_A;
311        case kVK_ANSI_B:
312            return KEY_B;
313        case kVK_ANSI_C:
314            return KEY_C;
315        case kVK_ANSI_D:
316            return KEY_D;
317        case kVK_ANSI_E:
318            return KEY_E;
319        case kVK_ANSI_F:
320            return KEY_F;
321        case kVK_ANSI_G:
322            return KEY_G;
323        case kVK_ANSI_H:
324            return KEY_H;
325        case kVK_ANSI_I:
326            return KEY_I;
327        case kVK_ANSI_J:
328            return KEY_J;
329        case kVK_ANSI_K:
330            return KEY_K;
331        case kVK_ANSI_L:
332            return KEY_L;
333        case kVK_ANSI_M:
334            return KEY_M;
335        case kVK_ANSI_N:
336            return KEY_N;
337        case kVK_ANSI_O:
338            return KEY_O;
339        case kVK_ANSI_P:
340            return KEY_P;
341        case kVK_ANSI_Q:
342            return KEY_Q;
343        case kVK_ANSI_R:
344            return KEY_R;
345        case kVK_ANSI_S:
346            return KEY_S;
347        case kVK_ANSI_T:
348            return KEY_T;
349        case kVK_ANSI_U:
350            return KEY_U;
351        case kVK_ANSI_V:
352            return KEY_V;
353        case kVK_ANSI_W:
354            return KEY_W;
355        case kVK_ANSI_X:
356            return KEY_X;
357        case kVK_ANSI_Y:
358            return KEY_Y;
359        case kVK_ANSI_Z:
360            return KEY_Z;
361
362        case kVK_ANSI_1:
363            return KEY_NUM1;
364        case kVK_ANSI_2:
365            return KEY_NUM2;
366        case kVK_ANSI_3:
367            return KEY_NUM3;
368        case kVK_ANSI_4:
369            return KEY_NUM4;
370        case kVK_ANSI_5:
371            return KEY_NUM5;
372        case kVK_ANSI_6:
373            return KEY_NUM6;
374        case kVK_ANSI_7:
375            return KEY_NUM7;
376        case kVK_ANSI_8:
377            return KEY_NUM8;
378        case kVK_ANSI_9:
379            return KEY_NUM9;
380        case kVK_ANSI_0:
381            return KEY_NUM0;
382    }
383
384    return Key(0);
385}
386
387static void AddNSKeyStateToEvent(Event *event, NSEventModifierFlags state)
388{
389    event->Key.Shift   = state & NSShiftKeyMask;
390    event->Key.Control = state & NSControlKeyMask;
391    event->Key.Alt     = state & NSAlternateKeyMask;
392    event->Key.System  = state & NSCommandKeyMask;
393}
394
395static MouseButton TranslateMouseButton(NSInteger button)
396{
397    switch (button)
398    {
399        case 2:
400            return MOUSEBUTTON_MIDDLE;
401        case 3:
402            return MOUSEBUTTON_BUTTON4;
403        case 4:
404            return MOUSEBUTTON_BUTTON5;
405        default:
406            return MOUSEBUTTON_UNKNOWN;
407    }
408}
409
410// Delegate for NSView events, mostly the input events
411@implementation ContentView
412- (id)initWithWindow:(OSXWindow *)window
413{
414    self = [super init];
415    if (self != nil)
416    {
417        mWindow          = window;
418        mTrackingArea    = nil;
419        mCurrentModifier = 0;
420        [self updateTrackingAreas];
421    }
422    return self;
423}
424
425- (void)dealloc
426{
427    [mTrackingArea release];
428    [super dealloc];
429}
430
431- (void)updateTrackingAreas
432{
433    if (mTrackingArea != nil)
434    {
435        [self removeTrackingArea:mTrackingArea];
436        [mTrackingArea release];
437        mTrackingArea = nil;
438    }
439
440    NSRect bounds               = [self bounds];
441    NSTrackingAreaOptions flags = NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow |
442                                  NSTrackingCursorUpdate | NSTrackingInVisibleRect |
443                                  NSTrackingAssumeInside;
444    mTrackingArea = [[NSTrackingArea alloc] initWithRect:bounds
445                                                 options:flags
446                                                   owner:self
447                                                userInfo:nil];
448
449    [self addTrackingArea:mTrackingArea];
450    [super updateTrackingAreas];
451}
452
453// Helps with performance
454- (BOOL)isOpaque
455{
456    return YES;
457}
458
459- (BOOL)canBecomeKeyView
460{
461    return YES;
462}
463
464- (BOOL)acceptsFirstResponder
465{
466    return YES;
467}
468
469// Handle mouse events from the NSResponder protocol
470- (float)translateMouseY:(float)y
471{
472    return [self frame].size.height - y;
473}
474
475- (void)addButtonEvent:(NSEvent *)nsEvent
476                  type:(Event::EventType)eventType
477                button:(MouseButton)button
478{
479    Event event;
480    event.Type               = eventType;
481    event.MouseButton.Button = button;
482    event.MouseButton.X      = (int)[nsEvent locationInWindow].x;
483    event.MouseButton.Y      = (int)[self translateMouseY:[nsEvent locationInWindow].y];
484    mWindow->pushEvent(event);
485}
486
487- (void)mouseDown:(NSEvent *)event
488{
489    [self addButtonEvent:event type:Event::EVENT_MOUSE_BUTTON_PRESSED button:MOUSEBUTTON_LEFT];
490}
491
492- (void)mouseDragged:(NSEvent *)event
493{
494    [self mouseMoved:event];
495}
496
497- (void)mouseUp:(NSEvent *)event
498{
499    [self addButtonEvent:event type:Event::EVENT_MOUSE_BUTTON_RELEASED button:MOUSEBUTTON_LEFT];
500}
501
502- (void)mouseMoved:(NSEvent *)nsEvent
503{
504    Event event;
505    event.Type        = Event::EVENT_MOUSE_MOVED;
506    event.MouseMove.X = (int)[nsEvent locationInWindow].x;
507    event.MouseMove.Y = (int)[self translateMouseY:[nsEvent locationInWindow].y];
508    mWindow->pushEvent(event);
509}
510
511- (void)mouseEntered:(NSEvent *)nsEvent
512{
513    Event event;
514    event.Type = Event::EVENT_MOUSE_ENTERED;
515    mWindow->pushEvent(event);
516}
517
518- (void)mouseExited:(NSEvent *)nsEvent
519{
520    Event event;
521    event.Type = Event::EVENT_MOUSE_LEFT;
522    mWindow->pushEvent(event);
523}
524
525- (void)rightMouseDown:(NSEvent *)event
526{
527    [self addButtonEvent:event type:Event::EVENT_MOUSE_BUTTON_PRESSED button:MOUSEBUTTON_RIGHT];
528}
529
530- (void)rightMouseDragged:(NSEvent *)event
531{
532    [self mouseMoved:event];
533}
534
535- (void)rightMouseUp:(NSEvent *)event
536{
537    [self addButtonEvent:event type:Event::EVENT_MOUSE_BUTTON_RELEASED button:MOUSEBUTTON_RIGHT];
538}
539
540- (void)otherMouseDown:(NSEvent *)event
541{
542    [self addButtonEvent:event
543                    type:Event::EVENT_MOUSE_BUTTON_PRESSED
544                  button:TranslateMouseButton([event buttonNumber])];
545}
546
547- (void)otherMouseDragged:(NSEvent *)event
548{
549    [self mouseMoved:event];
550}
551
552- (void)otherMouseUp:(NSEvent *)event
553{
554    [self addButtonEvent:event
555                    type:Event::EVENT_MOUSE_BUTTON_RELEASED
556                  button:TranslateMouseButton([event buttonNumber])];
557}
558
559- (void)scrollWheel:(NSEvent *)nsEvent
560{
561    if (static_cast<int>([nsEvent deltaY]) == 0)
562    {
563        return;
564    }
565
566    Event event;
567    event.Type             = Event::EVENT_MOUSE_WHEEL_MOVED;
568    event.MouseWheel.Delta = (int)[nsEvent deltaY];
569    mWindow->pushEvent(event);
570}
571
572// Handle key events from the NSResponder protocol
573- (void)keyDown:(NSEvent *)nsEvent
574{
575    // TODO(cwallez) also send text events
576    Event event;
577    event.Type     = Event::EVENT_KEY_PRESSED;
578    event.Key.Code = NSCodeToKey([nsEvent keyCode]);
579    AddNSKeyStateToEvent(&event, [nsEvent modifierFlags]);
580    mWindow->pushEvent(event);
581}
582
583- (void)keyUp:(NSEvent *)nsEvent
584{
585    Event event;
586    event.Type     = Event::EVENT_KEY_RELEASED;
587    event.Key.Code = NSCodeToKey([nsEvent keyCode]);
588    AddNSKeyStateToEvent(&event, [nsEvent modifierFlags]);
589    mWindow->pushEvent(event);
590}
591
592// Modifier keys do not trigger keyUp/Down events but only flagsChanged events.
593- (void)flagsChanged:(NSEvent *)nsEvent
594{
595    Event event;
596
597    // Guess if the key has been pressed or released with the change of modifiers
598    // It currently doesn't work when modifiers are unchanged, such as when pressing
599    // both shift keys. GLFW has a solution for this but it requires tracking the
600    // state of the keys. Implementing this is still TODO(cwallez)
601    int modifier = [nsEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
602    if (modifier < mCurrentModifier)
603    {
604        event.Type = Event::EVENT_KEY_RELEASED;
605    }
606    else
607    {
608        event.Type = Event::EVENT_KEY_PRESSED;
609    }
610    mCurrentModifier = modifier;
611
612    event.Key.Code = NSCodeToKey([nsEvent keyCode]);
613    AddNSKeyStateToEvent(&event, [nsEvent modifierFlags]);
614    mWindow->pushEvent(event);
615}
616@end
617
618OSXWindow::OSXWindow() : mWindow(nil), mDelegate(nil), mView(nil) {}
619
620OSXWindow::~OSXWindow()
621{
622    destroy();
623}
624
625bool OSXWindow::initializeImpl(const std::string &name, int width, int height)
626{
627    if (!InitializeAppKit())
628    {
629        return false;
630    }
631
632    unsigned int styleMask = NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask |
633                             NSMiniaturizableWindowMask;
634    mWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, width, height)
635                                          styleMask:styleMask
636                                            backing:NSBackingStoreBuffered
637                                              defer:NO];
638
639    if (mWindow == nil)
640    {
641        return false;
642    }
643
644    mDelegate = [[WindowDelegate alloc] initWithWindow:this];
645    if (mDelegate == nil)
646    {
647        return false;
648    }
649    [mWindow setDelegate:static_cast<id>(mDelegate)];
650
651    mView = [[ContentView alloc] initWithWindow:this];
652    if (mView == nil)
653    {
654        return false;
655    }
656    [mView setWantsLayer:YES];
657
658    // Disable scaling for this view. If scaling is enabled, the metal backend's
659    // frame buffer's size will be this window's size multiplied by contentScale.
660    // It will cause inconsistent testing & example apps' results.
661    mView.layer.contentsScale = 1;
662
663    [mWindow setContentView:mView];
664    [mWindow setTitle:[NSString stringWithUTF8String:name.c_str()]];
665    [mWindow setAcceptsMouseMovedEvents:YES];
666    [mWindow center];
667
668    [NSApp activateIgnoringOtherApps:YES];
669
670    mX      = 0;
671    mY      = 0;
672    mWidth  = width;
673    mHeight = height;
674
675    AllWindows().insert(this);
676    return true;
677}
678
679void OSXWindow::disableErrorMessageDialog() {}
680
681void OSXWindow::destroy()
682{
683    AllWindows().erase(this);
684
685    [mView release];
686    mView = nil;
687    [mDelegate onOSXWindowDeleted];
688    [mDelegate release];
689    mDelegate = nil;
690    // NSWindow won't be completely released unless its content view is set to nil:
691    [mWindow setContentView:nil];
692    [mWindow release];
693    mWindow = nil;
694}
695
696void OSXWindow::resetNativeWindow() {}
697
698EGLNativeWindowType OSXWindow::getNativeWindow() const
699{
700    return [mView layer];
701}
702
703EGLNativeDisplayType OSXWindow::getNativeDisplay() const
704{
705    // TODO(cwallez): implement it once we have defined what EGLNativeDisplayType is
706    return static_cast<EGLNativeDisplayType>(0);
707}
708
709void OSXWindow::messageLoop()
710{
711    @autoreleasepool
712    {
713        while (true)
714        {
715            NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask
716                                                untilDate:[NSDate distantPast]
717                                                   inMode:NSDefaultRunLoopMode
718                                                  dequeue:YES];
719            if (event == nil)
720            {
721                break;
722            }
723
724            if ([event type] == NSAppKitDefined)
725            {
726                continue;
727            }
728            [NSApp sendEvent:event];
729        }
730    }
731}
732
733void OSXWindow::setMousePosition(int x, int y)
734{
735    y = (int)([mWindow frame].size.height) - y - 1;
736    NSPoint screenspace;
737
738#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
739    screenspace = [mWindow convertBaseToScreen:NSMakePoint(x, y)];
740#else
741    screenspace = [mWindow convertRectToScreen:NSMakeRect(x, y, 0, 0)].origin;
742#endif
743    CGWarpMouseCursorPosition(CGPointMake(screenspace.x, YCoordToFromCG(screenspace.y)));
744}
745
746bool OSXWindow::setOrientation(int width, int height)
747{
748    UNIMPLEMENTED();
749    return false;
750}
751
752bool OSXWindow::setPosition(int x, int y)
753{
754    // Given CG and NS's coordinate system, the "Y" position of a window is the Y coordinate
755    // of the bottom of the window.
756    int newBottom    = (int)([mWindow frame].size.height) + y;
757    NSRect emptyRect = NSMakeRect(x, YCoordToFromCG(newBottom), 0, 0);
758    [mWindow setFrameOrigin:[mWindow frameRectForContentRect:emptyRect].origin];
759    return true;
760}
761
762bool OSXWindow::resize(int width, int height)
763{
764    [mWindow setContentSize:NSMakeSize(width, height)];
765    return true;
766}
767
768void OSXWindow::setVisible(bool isVisible)
769{
770    if (isVisible)
771    {
772        [mWindow makeKeyAndOrderFront:nil];
773    }
774    else
775    {
776        [mWindow orderOut:nil];
777    }
778}
779
780void OSXWindow::signalTestEvent()
781{
782    @autoreleasepool
783    {
784        NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined
785                                            location:NSMakePoint(0, 0)
786                                       modifierFlags:0
787                                           timestamp:0.0
788                                        windowNumber:[mWindow windowNumber]
789                                             context:nil
790                                             subtype:0
791                                               data1:0
792                                               data2:0];
793        [NSApp postEvent:event atStart:YES];
794    }
795}
796
797NSWindow *OSXWindow::getNSWindow() const
798{
799    return mWindow;
800}
801
802// static
803OSWindow *OSWindow::New()
804{
805    return new OSXWindow;
806}
807