From 2355f545ec652974ac4696728e3cc9e021532130 Mon Sep 17 00:00:00 2001 From: Rhody Lugo Date: Fri, 3 Nov 2017 06:00:29 -0400 Subject: [PATCH] fix window position computation on macOS --- platform/osx/os_osx.h | 6 +++ platform/osx/os_osx.mm | 92 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 85 insertions(+), 13 deletions(-) diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index 53f45511f94..1df847eb792 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -226,6 +226,12 @@ public: virtual Error move_to_trash(const String &p_path); OS_OSX(); + +private: + Point2 get_native_screen_position(int p_screen) const; + Point2 get_native_window_position() const; + void set_native_window_position(const Point2 &p_position); + Point2 get_screens_origin() const; }; #endif diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 6faa8e12edf..2e0e2620bee 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -913,10 +913,15 @@ void OS_OSX::initialize_core() { } static bool keyboard_layout_dirty = true; -static void keyboardLayoutChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) { +static void keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info) { keyboard_layout_dirty = true; } +static bool displays_arrangement_dirty = true; +static void displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info) { + displays_arrangement_dirty = true; +} + void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { /*** OSX INITIALIZATION ***/ @@ -924,13 +929,17 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au /*** OSX INITIALIZATION ***/ keyboard_layout_dirty = true; + displays_arrangement_dirty = true; // Register to be notified on keyboard layout changes CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), - NULL, keyboardLayoutChanged, + NULL, keyboard_layout_changed, kTISNotifySelectedKeyboardInputSourceChanged, NULL, CFNotificationSuspensionBehaviorDeliverImmediately); + // Register to be notified on displays arrangement changes + CGDisplayRegisterReconfigurationCallback(displays_arrangement_changed, NULL); + window_delegate = [[GodotWindowDelegate alloc] init]; // Don't use accumulation buffer support; it's not accelerated @@ -1094,6 +1103,8 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au void OS_OSX::finalize() { CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), NULL, kTISNotifySelectedKeyboardInputSourceChanged, NULL); + CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, NULL); + delete_main_loop(); memdelete(joypad_osx); @@ -1456,6 +1467,32 @@ int OS_OSX::get_screen_count() const { return [screenArray count]; }; +// Returns the native top-left screen coordinate of the smallest rectangle +// that encompasses all screens. Needed in get_screen_position(), +// get_window_position, and set_window_position() +// to convert between OS X native screen coordinates and the ones expected by Godot +Point2 OS_OSX::get_screens_origin() const { + static Point2 origin; + + if (displays_arrangement_dirty) { + origin = Point2(); + + for (int i = 0; i < get_screen_count(); i++) { + Point2 position = get_native_screen_position(i); + if (position.x < origin.x) { + origin.x = position.x; + } + if (position.y > origin.y) { + origin.y = position.y; + } + } + + displays_arrangement_dirty = false; + } + + return origin; +} + static int get_screen_index(NSScreen *screen) { const NSUInteger index = [[NSScreen screens] indexOfObject:screen]; return index == NSNotFound ? 0 : index; @@ -1474,21 +1511,30 @@ void OS_OSX::set_current_screen(int p_screen) { set_window_position(wpos + get_screen_position(p_screen)); }; -Point2 OS_OSX::get_screen_position(int p_screen) const { +Point2 OS_OSX::get_native_screen_position(int p_screen) const { if (p_screen == -1) { p_screen = get_current_screen(); } NSArray *screenArray = [NSScreen screens]; if (p_screen < [screenArray count]) { - float displayScale = _display_scale([screenArray objectAtIndex:p_screen]); + float display_scale = _display_scale([screenArray objectAtIndex:p_screen]); NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame]; - return Point2(nsrect.origin.x, nsrect.origin.y) * displayScale; + // Return the top-left corner of the screen, for OS X the y starts at the bottom + return Point2(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * display_scale; } return Point2(); } +Point2 OS_OSX::get_screen_position(int p_screen) const { + Point2 position = get_native_screen_position(p_screen) - get_screens_origin(); + // OS X native y-coordinate relative to get_screens_origin() is negative, + // Godot expects a positive value + position.y *= -1; + return position; +} + int OS_OSX::get_screen_dpi(int p_screen) const { if (p_screen == -1) { p_screen = get_current_screen(); @@ -1567,28 +1613,48 @@ float OS_OSX::_display_scale(id screen) const { } } -Point2 OS_OSX::get_window_position() const { +Point2 OS_OSX::get_native_window_position() const { - Size2 wp([window_object frame].origin.x, [window_object frame].origin.y); - wp *= _display_scale(); - return wp; + NSRect nsrect = [window_object frame]; + Point2 pos; + float display_scale = _display_scale(); + + // Return the position of the top-left corner, for OS X the y starts at the bottom + pos.x = nsrect.origin.x * display_scale; + pos.y = (nsrect.origin.y + nsrect.size.height) * display_scale; + + return pos; }; -void OS_OSX::set_window_position(const Point2 &p_position) { +Point2 OS_OSX::get_window_position() const { + Point2 position = get_native_window_position() - get_screens_origin(); + // OS X native y-coordinate relative to get_screens_origin() is negative, + // Godot expects a positive value + position.y *= -1; + return position; +} + +void OS_OSX::set_native_window_position(const Point2 &p_position) { - Size2 scr = get_screen_size(); NSPoint pos; float displayScale = _display_scale(); pos.x = p_position.x / displayScale; - // For OS X the y starts at the bottom - pos.y = (scr.height - p_position.y) / displayScale; + pos.y = p_position.y / displayScale; [window_object setFrameTopLeftPoint:pos]; _update_window(); }; +void OS_OSX::set_window_position(const Point2 &p_position) { + Point2 position = p_position; + // OS X native y-coordinate relative to get_screens_origin() is negative, + // Godot passes a positive value + position.y *= -1; + set_native_window_position(get_screens_origin() + position); +}; + Size2 OS_OSX::get_window_size() const { return window_size;