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