From 577f9f9e6209e31d566213668363fc2e1fc8c5c2 Mon Sep 17 00:00:00 2001 From: Carsten Tewes Date: Sun, 20 Nov 2022 14:20:30 +0100 Subject: [PATCH 1/6] CHANGE: Added window API function glfwGetMonitorFromWindow. The function returns the monitor which has the largest area of intersection with the area of the specified window. --- include/GLFW/glfw3.h | 22 +++++++++++ src/window.c | 88 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index d4b40dd4..96b623cb 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -3890,6 +3890,28 @@ GLFWAPI void glfwFocusWindow(GLFWwindow* window); */ GLFWAPI void glfwRequestWindowAttention(GLFWwindow* window); +/*! @brief Returns the monitor with the largest intersection area of the window + * + * This function returns the handle of the monitor which has the largest area of + * intersection with the area of the specified window + * + * @param[in] window The window to query. + * @return The monitor, or `NULL` + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_monitor + * @sa @ref glfwGetWindowMonitor + * + * @since Added in version 3.4. + * + * @ingroup window + */ +GLFWAPI GLFWmonitor* glfwGetMonitorFromWindow(GLFWwindow* window); + /*! @brief Returns the monitor that the window uses for full screen mode. * * This function returns the handle of the monitor that the specified window is diff --git a/src/window.c b/src/window.c index 1c8519ff..90f2f30b 100644 --- a/src/window.c +++ b/src/window.c @@ -954,6 +954,94 @@ GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value) _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); } +typedef struct _rect { int x, y; int w, h; } _rect; + +#define RECT_INTERSECTS(ra, rb) \ + ((ra)->x <= ((rb)->x + (rb)->w) && ((ra)->x + (ra)->w) >= (rb)->x && \ + (ra)->y <= ((rb)->y + (rb)->h) && ((ra)->y + (ra)->h) >= (rb)->y) + +static _rect _get_intersection(_rect* ra, _rect* rb) +{ + _rect result = { 0, 0, 0, 0 }; + if (RECT_INTERSECTS(ra, rb)) + { + result.x = _glfw_max(ra->x, rb->x); + result.w = _glfw_min((ra->x + ra->w), (rb->x + rb->w)) - result.x; + result.y = _glfw_max(ra->y, rb->y); + result.h = _glfw_min((ra->y + ra->h), (rb->y + rb->h)) - result.y; + } + return result; +} + +GLFWAPI GLFWmonitor* glfwGetMonitorFromWindow(GLFWwindow* window) +{ + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + GLFWmonitor* result = NULL; + + int monitorCount; + GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + + if (monitorCount == 1) + { + result = monitors[0]; + } + else if (monitorCount > 1) + { + _rect windowRect; + glfwGetWindowPos(window, &windowRect.x, &windowRect.y); + int windowWidth, windowHeight; + glfwGetWindowSize(window, &windowWidth, &windowHeight); + windowRect.w = windowWidth; + windowRect.h = windowHeight; + + int windowFrameLeft, windowFrameTop, windowFrameRight, windowFrameBottom; + glfwGetWindowFrameSize(window, &windowFrameLeft, &windowFrameTop, + &windowFrameRight, &windowFrameBottom); + + windowRect.x -= windowFrameLeft; + windowRect.y -= windowFrameTop; + windowRect.w += windowFrameLeft + windowFrameRight; + windowRect.h += windowFrameTop + windowFrameBottom; + + _rect currentRect = { 0, 0, 0, 0 }; + _rect overlapRect = { 0, 0, 0, 0 }; + unsigned int currentDim, overlapDim; + int overlapMonitor = -1; + + int i; + for (i = 0; i < monitorCount; i++) + { + _rect monitorRect; + glfwGetMonitorPos(monitors[i], &monitorRect.x, &monitorRect.y); + + const GLFWvidmode* vidmode = glfwGetVideoMode(monitors[i]); + monitorRect.w = vidmode->width; + monitorRect.h = vidmode->height; + + currentRect = _get_intersection(&windowRect, &monitorRect); + + currentDim = currentRect.w * currentRect.h; + overlapDim = overlapRect.w * overlapRect.h; + + if (currentDim > 0 && currentDim > overlapDim) + { + overlapRect = currentRect; + overlapMonitor = i; + } + } + + if (overlapMonitor >= 0) + { + result = monitors[overlapMonitor]; + } + } + + return result; +} + GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; From 5cdc469a86dbcd148f7b4ea5d02d1287990229e7 Mon Sep 17 00:00:00 2001 From: Carsten Tewes Date: Sun, 20 Nov 2022 15:14:28 +0100 Subject: [PATCH 2/6] CHANGE: Updated changelog and guide doc --- README.md | 2 ++ docs/window.dox | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/README.md b/README.md index 8b4a1546..70bd9095 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,8 @@ information on what to include when reporting a bug. ## Changelog + - Added `glfwGetMonitorFromWindow` function to query the monitor where the + largest part of a window is currently located (#1699) - Added `GLFW_PLATFORM` init hint for runtime platform selection (#1958) - Added `GLFW_ANY_PLATFORM`, `GLFW_PLATFORM_WIN32`, `GLFW_PLATFORM_COCOA`, `GLFW_PLATFORM_WAYLAND`, `GLFW_PLATFORM_X11` and `GLFW_PLATFORM_NULL` symbols to diff --git a/docs/window.dox b/docs/window.dox index 3cec6358..f58c1df2 100644 --- a/docs/window.dox +++ b/docs/window.dox @@ -907,6 +907,19 @@ glfwSetWindowIcon(window, 0, NULL); @endcode +@subsection window_monitor_from Window monitor + +Windows are always located on one or more monitors. You can get the +handle for the monitor were the biggest part of a specified window is +currently located with @ref glfwGetMonitorFromWindow + +@code +GLFWmonitor* monitor = glfwGetMonitorFromWindow(window); +@endcode + +This monitor handle is one of those returned by @ref glfwGetMonitors. + + @subsection window_monitor Window monitor Full screen windows are associated with a specific monitor. You can get the From e01fb0f6de7879f8f98ae412367742b40569268f Mon Sep 17 00:00:00 2001 From: Carsten Tewes Date: Sun, 20 Nov 2022 15:46:52 +0100 Subject: [PATCH 3/6] CHANGE: Changed code to alllow ISO C90 compilation, why do we compile using a 32 years old standard with has been replaced more than once --- src/window.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/window.c b/src/window.c index 90f2f30b..6d4561b3 100644 --- a/src/window.c +++ b/src/window.c @@ -974,15 +974,30 @@ static _rect _get_intersection(_rect* ra, _rect* rb) } GLFWAPI GLFWmonitor* glfwGetMonitorFromWindow(GLFWwindow* window) -{ +{ + GLFWmonitor* result = NULL; + + int monitorCount; + GLFWmonitor** monitors; + const GLFWvidmode* vidmode; + + int windowWidth, windowHeight; + int windowFrameLeft, windowFrameTop, windowFrameRight, windowFrameBottom; + + unsigned int currentDim, overlapDim; + int overlapMonitor; + int i; + + _rect windowRect; + _rect monitorRect; + _rect currentRect = { 0, 0, 0, 0 }; + _rect overlapRect = { 0, 0, 0, 0 }; + assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - GLFWmonitor* result = NULL; - - int monitorCount; - GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + monitors = glfwGetMonitors(&monitorCount); if (monitorCount == 1) { @@ -990,14 +1005,11 @@ GLFWAPI GLFWmonitor* glfwGetMonitorFromWindow(GLFWwindow* window) } else if (monitorCount > 1) { - _rect windowRect; glfwGetWindowPos(window, &windowRect.x, &windowRect.y); - int windowWidth, windowHeight; glfwGetWindowSize(window, &windowWidth, &windowHeight); windowRect.w = windowWidth; windowRect.h = windowHeight; - int windowFrameLeft, windowFrameTop, windowFrameRight, windowFrameBottom; glfwGetWindowFrameSize(window, &windowFrameLeft, &windowFrameTop, &windowFrameRight, &windowFrameBottom); @@ -1006,18 +1018,13 @@ GLFWAPI GLFWmonitor* glfwGetMonitorFromWindow(GLFWwindow* window) windowRect.w += windowFrameLeft + windowFrameRight; windowRect.h += windowFrameTop + windowFrameBottom; - _rect currentRect = { 0, 0, 0, 0 }; - _rect overlapRect = { 0, 0, 0, 0 }; - unsigned int currentDim, overlapDim; - int overlapMonitor = -1; + overlapMonitor = -1; - int i; for (i = 0; i < monitorCount; i++) { - _rect monitorRect; glfwGetMonitorPos(monitors[i], &monitorRect.x, &monitorRect.y); - const GLFWvidmode* vidmode = glfwGetVideoMode(monitors[i]); + vidmode = glfwGetVideoMode(monitors[i]); monitorRect.w = vidmode->width; monitorRect.h = vidmode->height; From 47d2e58e485b3fa77e6aa1c4e213444e91bc1c20 Mon Sep 17 00:00:00 2001 From: Carsten Tewes Date: Sun, 20 Nov 2022 17:05:36 +0100 Subject: [PATCH 4/6] CHANGE: Elimated variable overhead in glfwGetMonitorFromWindow. --- src/window.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/window.c b/src/window.c index 6d4561b3..96a6ec06 100644 --- a/src/window.c +++ b/src/window.c @@ -981,16 +981,12 @@ GLFWAPI GLFWmonitor* glfwGetMonitorFromWindow(GLFWwindow* window) GLFWmonitor** monitors; const GLFWvidmode* vidmode; - int windowWidth, windowHeight; - int windowFrameLeft, windowFrameTop, windowFrameRight, windowFrameBottom; - unsigned int currentDim, overlapDim; - int overlapMonitor; - int i; + int overlapMonitor, i; _rect windowRect; _rect monitorRect; - _rect currentRect = { 0, 0, 0, 0 }; + _rect scratchRect = { 0, 0, 0, 0 }; _rect overlapRect = { 0, 0, 0, 0 }; assert(window != NULL); @@ -1006,17 +1002,15 @@ GLFWAPI GLFWmonitor* glfwGetMonitorFromWindow(GLFWwindow* window) else if (monitorCount > 1) { glfwGetWindowPos(window, &windowRect.x, &windowRect.y); - glfwGetWindowSize(window, &windowWidth, &windowHeight); - windowRect.w = windowWidth; - windowRect.h = windowHeight; + glfwGetWindowSize(window, &windowRect.w, &windowRect.h); - glfwGetWindowFrameSize(window, &windowFrameLeft, &windowFrameTop, - &windowFrameRight, &windowFrameBottom); + glfwGetWindowFrameSize(window, &scratchRect.x, &scratchRect.y, + &scratchRect.w, &scratchRect.h); - windowRect.x -= windowFrameLeft; - windowRect.y -= windowFrameTop; - windowRect.w += windowFrameLeft + windowFrameRight; - windowRect.h += windowFrameTop + windowFrameBottom; + windowRect.x -= scratchRect.x; + windowRect.y -= scratchRect.y; + windowRect.w += scratchRect.x + scratchRect.w; + windowRect.h += scratchRect.y + scratchRect.h; overlapMonitor = -1; @@ -1028,14 +1022,14 @@ GLFWAPI GLFWmonitor* glfwGetMonitorFromWindow(GLFWwindow* window) monitorRect.w = vidmode->width; monitorRect.h = vidmode->height; - currentRect = _get_intersection(&windowRect, &monitorRect); + scratchRect = _get_intersection(&windowRect, &monitorRect); - currentDim = currentRect.w * currentRect.h; + currentDim = scratchRect.w * scratchRect.h; overlapDim = overlapRect.w * overlapRect.h; if (currentDim > 0 && currentDim > overlapDim) { - overlapRect = currentRect; + overlapRect = scratchRect; overlapMonitor = i; } } From a58d1bf3efb4eb2eaac10c0809fc205befcc458f Mon Sep 17 00:00:00 2001 From: Carsten Tewes Date: Sun, 20 Nov 2022 20:35:14 +0100 Subject: [PATCH 5/6] BUGFIX: Issue #2137, moving (real) fullscreen windows between monitors. Using glfwGetMonitorFromWindow we can set the moving fullscreen window to the right values by setting the window monitor before invoking the callback. This should be platform indepent. --- src/window.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/window.c b/src/window.c index 96a6ec06..098a0afc 100644 --- a/src/window.c +++ b/src/window.c @@ -76,10 +76,35 @@ void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused) // void _glfwInputWindowPos(_GLFWwindow* window, int x, int y) { - assert(window != NULL); + GLFWmonitor* windowMon; + GLFWmonitor* detectedMon; + const GLFWvidmode* vidmode; + + assert(window != NULL); - if (window->callbacks.pos) - window->callbacks.pos((GLFWwindow*) window, x, y); + windowMon = glfwGetWindowMonitor((GLFWwindow *) window); + if (windowMon != NULL) + { + // When windowMon != NULL, this is a fullscreen window. + // We then check if windowMon is really the monitor where + // the window is located and if not we set the monitor for + // that window to the other monitor including width, height + // and refreshrate. At last we simply set the position vars + // to 0, because the relative position of a fullscreen window + // content area moving from one monitor to another is always 0, 0 + detectedMon = glfwGetMonitorFromWindow((GLFWwindow*) window); + if (detectedMon != NULL && windowMon != detectedMon) + { + vidmode = glfwGetVideoMode(detectedMon); + if (vidmode != NULL) + { + glfwSetWindowMonitor((GLFWwindow*) window, detectedMon, x = 0, y = 0, vidmode->width, vidmode->height, vidmode->refreshRate); + } + } + } + + if (window->callbacks.pos) + window->callbacks.pos((GLFWwindow*) window, x, y); } // Notifies shared code that a window has been resized From d838446882a7f3460a23af802c9700d4812d4a4f Mon Sep 17 00:00:00 2001 From: Carsten Tewes Date: Mon, 21 Nov 2022 00:42:23 +0100 Subject: [PATCH 6/6] CHANGE: Do not touch the input params x and y, just pass it through to a possible callback. --- src/window.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/window.c b/src/window.c index 098a0afc..517d1974 100644 --- a/src/window.c +++ b/src/window.c @@ -89,16 +89,14 @@ void _glfwInputWindowPos(_GLFWwindow* window, int x, int y) // We then check if windowMon is really the monitor where // the window is located and if not we set the monitor for // that window to the other monitor including width, height - // and refreshrate. At last we simply set the position vars - // to 0, because the relative position of a fullscreen window - // content area moving from one monitor to another is always 0, 0 + // and refreshrate. detectedMon = glfwGetMonitorFromWindow((GLFWwindow*) window); if (detectedMon != NULL && windowMon != detectedMon) { vidmode = glfwGetVideoMode(detectedMon); if (vidmode != NULL) { - glfwSetWindowMonitor((GLFWwindow*) window, detectedMon, x = 0, y = 0, vidmode->width, vidmode->height, vidmode->refreshRate); + glfwSetWindowMonitor((GLFWwindow*) window, detectedMon, 0, 0, vidmode->width, vidmode->height, vidmode->refreshRate); } } }