diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index fccaeec7b65..4818a340885 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -686,10 +686,8 @@ uint64_t GHOST_SystemCocoa::getMilliSeconds() const uint8_t GHOST_SystemCocoa::getNumDisplays() const { - /* Note that OS X supports monitor hot plug. - * We do not support multiple monitors at the moment. */ @autoreleasepool { - return NSScreen.screens.count; + return [[NSScreen screens] count]; } } @@ -697,7 +695,7 @@ void GHOST_SystemCocoa::getMainDisplayDimensions(uint32_t &width, uint32_t &heig { @autoreleasepool { /* Get visible frame, that is frame excluding dock and top menu bar. */ - const NSRect frame = [[NSScreen mainScreen] visibleFrame]; + const NSRect frame = [GHOST_WindowCocoa::getPrimaryScreen() visibleFrame]; /* Returns max window contents (excluding title bar...). */ const NSRect contentRect = [NSWindow @@ -730,18 +728,13 @@ GHOST_IWindow *GHOST_SystemCocoa::createWindow(const char *title, GHOST_IWindow *window = nullptr; @autoreleasepool { /* Get the available rect for including window contents. */ - const NSRect frame = [[NSScreen mainScreen] visibleFrame]; - const NSRect contentRect = [NSWindow - contentRectForFrameRect:frame + const NSRect primaryScreenFrame = [GHOST_WindowCocoa::getPrimaryScreen() visibleFrame]; + const NSRect primaryScreenContentRect = [NSWindow + contentRectForFrameRect:primaryScreenFrame styleMask:(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable)]; - int32_t bottom = (contentRect.size.height - 1) - height - top; - - /* Ensures window top left is inside this available rect. */ - left = left > contentRect.origin.x ? left : contentRect.origin.x; - /* Add `contentRect.origin.y` to respect dock-size. */ - bottom = bottom > contentRect.origin.y ? bottom + contentRect.origin.y : contentRect.origin.y; + const int32_t bottom = primaryScreenContentRect.size.height - top - height; window = new GHOST_WindowCocoa(this, title, diff --git a/intern/ghost/intern/GHOST_WindowCocoa.hh b/intern/ghost/intern/GHOST_WindowCocoa.hh index 12e6151ef43..ac22abb48b6 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.hh +++ b/intern/ghost/intern/GHOST_WindowCocoa.hh @@ -184,10 +184,18 @@ class GHOST_WindowCocoa : public GHOST_Window { void screenToClientIntern(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const; /** - * Gets the screen the window is displayed in - * \return The NSScreen object + * Return the screen the window is displayed in. + * \return The current screen NSScreen object */ - NSScreen *getScreen(); + NSScreen *getScreen() const; + + /** + * Return the primary screen, the screen defined as "Main Display" in macOS Settings, source of + * all screen coordinates. + * \note This function is placed in WindowCocoa since SystemCocoa cannot include Obj-C types. + * \return The primary screen NSScreen object + */ + static NSScreen *getPrimaryScreen(); /** * Sets the state of the window (normal, minimized, maximized). diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm index fed0ecc0090..1534b8fbb51 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.mm +++ b/intern/ghost/intern/GHOST_WindowCocoa.mm @@ -365,6 +365,11 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa, styleMask:styleMask backing:NSBackingStoreBuffered defer:NO]; + /* By default, AppKit repositions the window in the context of the current "mainMonitor" + * (the monitor which has focus), bypass this by forcing the window back into its correct + * position. Since we use global screen coordinate indexed on the first, primary screen. + */ + [m_window setFrameOrigin:NSMakePoint(left, bottom)]; /* Forbid to resize the window below the blender defined minimum one. */ const NSSize minSize = {320, 240}; @@ -600,13 +605,24 @@ void GHOST_WindowCocoa::getWindowBounds(GHOST_Rect &bounds) const GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getWindowBounds(): window invalid"); @autoreleasepool { - const NSRect screenSize = m_window.screen.visibleFrame; - const NSRect rect = m_window.frame; + /* All coordinates are based off the primary screen. */ + const NSRect screenFrame = [getPrimaryScreen() visibleFrame]; + const NSRect windowFrame = m_window.frame; - bounds.m_b = screenSize.size.height - (rect.origin.y - screenSize.origin.y); - bounds.m_l = rect.origin.x - screenSize.origin.x; - bounds.m_r = rect.origin.x - screenSize.origin.x + rect.size.width; - bounds.m_t = screenSize.size.height - (rect.origin.y + rect.size.height - screenSize.origin.y); + /* Flip the Y axis, from bottom left coordinate to top left, which is the expected coordinate + * return format for GHOST, even though the Window Manager later reflips it to bottom-left + * this is the expected coordinate system for all GHOST backends + */ + + const int32_t screenMaxY = screenFrame.origin.y + screenFrame.size.height; + + /* Flip the coordinates vertically from a bottom-left origin to a top-left origin, + * as expected by GHOST. */ + bounds.m_b = screenMaxY - windowFrame.origin.y; + bounds.m_t = screenMaxY - windowFrame.origin.y - windowFrame.size.height; + + bounds.m_l = windowFrame.origin.x; + bounds.m_r = windowFrame.origin.x + windowFrame.size.width; } } @@ -615,19 +631,25 @@ void GHOST_WindowCocoa::getClientBounds(GHOST_Rect &bounds) const GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getClientBounds(): window invalid"); @autoreleasepool { - const NSRect screenSize = m_window.screen.visibleFrame; + /* All coordinates are based off the primary screen. */ + const NSRect screenFrame = [getPrimaryScreen() visibleFrame]; + /* Screen Content Rectangle (excluding Menu Bar and Dock). */ + const NSRect screenContentRect = [NSWindow contentRectForFrameRect:screenFrame + styleMask:[m_window styleMask]]; - /* Max window contents as screen size (excluding title bar...). */ - const NSRect contentRect = [BlenderWindow contentRectForFrameRect:screenSize - styleMask:[m_window styleMask]]; + const NSRect windowFrame = m_window.frame; + /* Window Content Rectangle (excluding Titlebar and borders) */ + const NSRect windowContentRect = [m_window contentRectForFrameRect:windowFrame]; - const NSRect rect = [m_window contentRectForFrameRect:[m_window frame]]; + const int32_t screenMaxY = screenContentRect.origin.y + screenContentRect.size.height; - bounds.m_b = contentRect.size.height - (rect.origin.y - contentRect.origin.y); - bounds.m_l = rect.origin.x - contentRect.origin.x; - bounds.m_r = rect.origin.x - contentRect.origin.x + rect.size.width; - bounds.m_t = contentRect.size.height - - (rect.origin.y + rect.size.height - contentRect.origin.y); + /* Flip the coordinates vertically from a bottom-left origin to a top-left origin, + * as expected by GHOST. */ + bounds.m_b = screenMaxY - windowContentRect.origin.y; + bounds.m_t = screenMaxY - windowContentRect.origin.y - windowContentRect.size.height; + + bounds.m_l = windowContentRect.origin.x; + bounds.m_r = windowContentRect.origin.x + windowContentRect.size.width; } } @@ -763,11 +785,17 @@ void GHOST_WindowCocoa::clientToScreenIntern(int32_t inX, outY = screenCoord.origin.y; } -NSScreen *GHOST_WindowCocoa::getScreen() +NSScreen *GHOST_WindowCocoa::getScreen() const { return m_window.screen; } +NSScreen *GHOST_WindowCocoa::getPrimaryScreen() +{ + /* The first element of the screens array is guaranted to be the primary screen by AppKit. */ + return [[NSScreen screens] firstObject]; +} + /* called for event, when window leaves monitor to another */ void GHOST_WindowCocoa::setNativePixelSize() {