diff --git a/glfw/cocoa_monitor.m b/glfw/cocoa_monitor.m index 9742faccd..217d53e29 100644 --- a/glfw/cocoa_monitor.m +++ b/glfw/cocoa_monitor.m @@ -213,6 +213,30 @@ static void endFadeReservation(CGDisplayFadeReservationToken token) } } +// Finds and caches the NSScreen corresponding to the specified monitor +// +GLFWbool refreshMonitorScreen(_GLFWmonitor* monitor) +{ + if (monitor->ns.screen) + return GLFW_TRUE; + + for (NSScreen* screen in [NSScreen screens]) + { + NSNumber* displayID = [screen deviceDescription][@"NSScreenNumber"]; + + // HACK: Compare unit numbers instead of display IDs to work around + // display replacement on machines with automatic graphics + // switching + if (monitor->ns.unitNumber == CGDisplayUnitNumber([displayID unsignedIntValue])) + { + monitor->ns.screen = screen; + return GLFW_TRUE; + } + } + + _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to find a screen for monitor"); + return GLFW_FALSE; +} ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// @@ -429,35 +453,8 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, float* xscale, float* yscale) { - if (!monitor->ns.screen) - { - NSUInteger i; - NSArray* screens = [NSScreen screens]; - - for (i = 0; i < [screens count]; i++) - { - NSScreen* screen = [screens objectAtIndex:i]; - NSNumber* displayID = - [[screen deviceDescription] objectForKey:@"NSScreenNumber"]; - - // HACK: Compare unit numbers instead of display IDs to work around - // display replacement on machines with automatic graphics - // switching - if (monitor->ns.unitNumber == - CGDisplayUnitNumber([displayID unsignedIntValue])) - { - monitor->ns.screen = screen; - break; - } - } - - if (i == [screens count]) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to find a screen for monitor"); - return; - } - } + if (!refreshMonitorScreen(monitor)) + return; const NSRect points = [monitor->ns.screen frame]; const NSRect pixels = [monitor->ns.screen convertRectToBacking:points]; @@ -470,20 +467,20 @@ void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int *width, int *height) { - NSScreen *resultScreen; - for (NSScreen *screen in [NSScreen screens]) { - if ([[[screen deviceDescription] valueForKey:@"NSScreenNumber"] intValue] == monitor->ns.displayID) { - resultScreen = screen; - break; - } - } + if (!refreshMonitorScreen(monitor)) + return; - NSRect frameRect = [[NSScreen resultScreen] visibleFrame]; + const NSRect frameRect = [monitor->ns.screen visibleFrame]; + + if (xpos) + *xpos = frameRect.origin.x; + if (ypos) + *ypos = _glfwTransformYNS(frameRect.origin.y + frameRect.size.height); + if (width) + *width = frameRect.size.width; + if (height) + *height = frameRect.size.height; - *xpos = NSMinX(frameRect); - *ypos = NSMinY(frameRect); - *width = NSMaxX(frameRect); - *height = NSMaxY(frameRect); } GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) diff --git a/glfw/cocoa_platform.h b/glfw/cocoa_platform.h index 49c2e1712..d5c9594e5 100644 --- a/glfw/cocoa_platform.h +++ b/glfw/cocoa_platform.h @@ -202,6 +202,7 @@ void _glfwInitTimerNS(void); void _glfwPollMonitorsNS(void); void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired); void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor); +float _glfwTransformYNS(float y); void _glfwClearDisplayLinks(); void _glfwCocoaPostEmptyEvent(short subtype, long data1, bool at_start); void _glfwDispatchTickCallback(); diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 0605494f2..e0b6ae97d 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -149,14 +149,6 @@ static void updateCursorMode(_GLFWwindow* window) updateCursorImage(window); } -// Transforms the specified y-coordinate between the CG display and NS screen -// coordinate systems -// -static float transformY(float y) -{ - return CGDisplayBounds(CGMainDisplayID()).size.height - y; -} - // Make the specified window and its video mode active on its monitor // static void acquireMonitor(_GLFWwindow* window) @@ -164,7 +156,7 @@ static void acquireMonitor(_GLFWwindow* window) _glfwSetVideoModeNS(window->monitor, &window->videoMode); const CGRect bounds = CGDisplayBounds(window->monitor->ns.displayID); const NSRect frame = NSMakeRect(bounds.origin.x, - transformY(bounds.origin.y + bounds.size.height), + _glfwTransformYNS(bounds.origin.y + bounds.size.height), bounds.size.width, bounds.size.height); @@ -1039,7 +1031,7 @@ is_ascii_control_char(char x) { int xpos, ypos; _glfwPlatformGetWindowPos(window, &xpos, &ypos); const NSRect contentRect = [window->ns.view frame]; - return NSMakeRect(xpos, transformY(ypos + contentRect.size.height), 0.0, 0.0); + return NSMakeRect(xpos, _glfwTransformYNS(ypos + contentRect.size.height), 0.0, 0.0); } - (void)insertText:(id)string replacementRange:(NSRange)replacementRange @@ -1437,13 +1429,13 @@ void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) if (xpos) *xpos = contentRect.origin.x; if (ypos) - *ypos = transformY(contentRect.origin.y + contentRect.size.height); + *ypos = _glfwTransformYNS(contentRect.origin.y + contentRect.size.height); } void _glfwPlatformSetWindowPos(_GLFWwindow* window, int x, int y) { const NSRect contentRect = [window->ns.view frame]; - const NSRect dummyRect = NSMakeRect(x, transformY(y + contentRect.size.height), 0, 0); + const NSRect dummyRect = NSMakeRect(x, _glfwTransformYNS(y + contentRect.size.height), 0, 0); const NSRect frameRect = [window->ns.object frameRectForContentRect:dummyRect]; [window->ns.object setFrameOrigin:frameRect.origin]; } @@ -1606,7 +1598,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, else { const NSRect contentRect = - NSMakeRect(xpos, transformY(ypos + height), width, height); + NSMakeRect(xpos, _glfwTransformYNS(ypos + height), width, height); const NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect styleMask:getStyleMask(window)]; @@ -1636,7 +1628,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, } else { - NSRect contentRect = NSMakeRect(xpos, transformY(ypos + height), + NSRect contentRect = NSMakeRect(xpos, _glfwTransformYNS(ypos + height), width, height); NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect styleMask:styleMask]; @@ -1864,7 +1856,7 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) const NSPoint globalPoint = globalRect.origin; CGWarpMouseCursorPosition(CGPointMake(globalPoint.x, - transformY(globalPoint.y))); + _glfwTransformYNS(globalPoint.y))); } } @@ -2225,3 +2217,14 @@ GLFWAPI void glfwGetCocoaKeyEquivalent(int glfw_key, int glfw_mods, unsigned sho #undef K } } + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Transforms a y-coordinate between the CG display and NS screen spaces +// +float _glfwTransformYNS(float y) +{ + return CGDisplayBounds(CGMainDisplayID()).size.height - y; +} diff --git a/glfw/glfw3.h b/glfw/glfw3.h index 59835edeb..b0a89f1ca 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -1940,23 +1940,27 @@ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); /*! @brief Returns the work area of the monitor. * * This function returns the position, in screen coordinates, of the upper-left - * corner of the specified monitor. + * corner of the specified monitor alongwith the work area size in screen co-ordinates. + * The work area is defined as the area of the monitor not occluded by the operating + * system chrome (task bar, global menubar, etc.). * - * Any or all of the position arguments may be `NULL`. If an error occurs, all - * non-`NULL` position arguments will be set to zero. + * Any or all of the position and size arguments may be `NULL`. If an error occurs, all + * non-`NULL` position and size arguments will be set to zero. * * @param[in] monitor The monitor to query. * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. + * @param[out] width Where to store the monitor width, or `NULL`. +* @param[out] height Where to store the monitor height, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * - * @sa @ref monitor_properties + * @sa @ref monitor_workarea * - * @since Added in version 3.0. + * @since Added in version 3.3. * * @ingroup monitor */ diff --git a/glfw/monitor.c b/glfw/monitor.c index 32a955126..ae0ce3c16 100644 --- a/glfw/monitor.c +++ b/glfw/monitor.c @@ -341,8 +341,8 @@ GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* handle, int* xpos, int* ypos, i *ypos = 0; if (width) *width = 0; - if (width) - *width = 0; + if (height) + *height = 0; _GLFW_REQUIRE_INIT(); diff --git a/glfw/wl_monitor.c b/glfw/wl_monitor.c index 8d50637da..f258c5893 100644 --- a/glfw/wl_monitor.c +++ b/glfw/wl_monitor.c @@ -170,6 +170,18 @@ void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, *yscale = (float) monitor->wl.scale; } +void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int *width, int *height) +{ + if (xpos) + *xpos = monitor->wl.x; + if (ypos) + *ypos = monitor->wl.y; + if (width) + *width = monitor->modes[monitor->wl.currentMode].width; + if (height) + *height = monitor->modes[monitor->wl.currentMode].height; +} + GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) { *found = monitor->modeCount; diff --git a/glfw/x11_init.c b/glfw/x11_init.c index 3f6cd9c4f..a879dae07 100644 --- a/glfw/x11_init.c +++ b/glfw/x11_init.c @@ -139,6 +139,8 @@ static void detectEWMH(void) getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_NORMAL"); _glfw.x11.NET_WORKAREA = getSupportedAtom(supportedAtoms, atomCount, "_NET_WORKAREA"); + _glfw.x11.NET_CURRENT_DESKTOP = + getSupportedAtom(supportedAtoms, atomCount, "_NET_CURRENT_DESKTOP"); _glfw.x11.NET_ACTIVE_WINDOW = getSupportedAtom(supportedAtoms, atomCount, "_NET_ACTIVE_WINDOW"); _glfw.x11.NET_FRAME_EXTENTS = diff --git a/glfw/x11_monitor.c b/glfw/x11_monitor.c index 61100d58a..784b64749 100644 --- a/glfw/x11_monitor.c +++ b/glfw/x11_monitor.c @@ -351,18 +351,96 @@ void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int *width, int *height) { + int areaX = 0, areaY = 0, areaWidth = 0, areaHeight = 0; + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) { - Atom* extents = NULL; + XRRScreenResources* sr; + XRRCrtcInfo* ci; - _glfwGetWindowPropertyX11(_glfw.x11.root, _glfw.x11.NET_WORKAREA, XA_CARDINAL, (unsigned char**) &extents); + sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); + ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); - *xpos = extents[0]; - *ypos = extents[1]; - *width = extents[2]; - *height = extents[3]; - XFree(extents); + areaX = ci->x; + areaY = ci->y; + + const XRRModeInfo* mi = getModeInfo(sr, ci->mode); + + if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) + { + areaWidth = mi->height; + areaHeight = mi->width; + } + else + { + areaWidth = mi->width; + areaHeight = mi->height; + } + + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); } + else + { + areaWidth = DisplayWidth(_glfw.x11.display, _glfw.x11.screen); + areaHeight = DisplayHeight(_glfw.x11.display, _glfw.x11.screen); + } + + if (_glfw.x11.NET_WORKAREA && _glfw.x11.NET_CURRENT_DESKTOP) + { + Atom* extents = NULL; + Atom* desktop = NULL; + const unsigned long extentCount = + _glfwGetWindowPropertyX11(_glfw.x11.root, + _glfw.x11.NET_WORKAREA, + XA_CARDINAL, + (unsigned char**) &extents); + + if (_glfwGetWindowPropertyX11(_glfw.x11.root, + _glfw.x11.NET_CURRENT_DESKTOP, + XA_CARDINAL, + (unsigned char**) &desktop) > 0) + { + if (extentCount >= 4 && *desktop < extentCount / 4) + { + const int globalX = extents[*desktop * 4 + 0]; + const int globalY = extents[*desktop * 4 + 1]; + const int globalWidth = extents[*desktop * 4 + 2]; + const int globalHeight = extents[*desktop * 4 + 3]; + + if (areaX < globalX) + { + areaWidth -= globalX - areaX; + areaX = globalX; + } + + if (areaY < globalY) + { + areaHeight -= globalY - areaY; + areaY = globalY; + } + + if (areaX + areaWidth > globalX + globalWidth) + areaWidth = globalX - areaX + globalWidth; + if (areaY + areaHeight > globalY + globalHeight) + areaHeight = globalY - areaY + globalHeight; + } + } + + if (extents) + XFree(extents); + if (desktop) + XFree(desktop); + } + + if (xpos) + *xpos = areaX; + if (ypos) + *ypos = areaY; + if (width) + *width = areaWidth; + if (height) + *height = areaHeight; } GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) diff --git a/glfw/x11_platform.h b/glfw/x11_platform.h index 070ce84f1..cbf3a371c 100644 --- a/glfw/x11_platform.h +++ b/glfw/x11_platform.h @@ -249,6 +249,7 @@ typedef struct _GLFWlibraryX11 Atom NET_WM_WINDOW_OPACITY; Atom NET_WM_CM_Sx; Atom NET_WORKAREA; + Atom NET_CURRENT_DESKTOP; Atom NET_ACTIVE_WINDOW; Atom NET_FRAME_EXTENTS; Atom NET_REQUEST_FRAME_EXTENTS;