diff --git a/README.md b/README.md index cf4643e5..34ec2b3a 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,7 @@ information on what to include when reporting a bug. - Added definition of `GLAPIENTRY` to public header - Added `GLFW_TRANSPARENT_FRAMEBUFFER` window hint and attribute for controlling per-pixel framebuffer transparency (#197,#663,#715,#723,#1078) +- Added `GLFW_HOVERED` window attribute for polling cursor hover state (#1166) - Added `GLFW_CENTER_CURSOR` window hint for controlling cursor centering (#749,#842) - Added `GLFW_JOYSTICK_HAT_BUTTONS` init hint (#889) diff --git a/docs/input.dox b/docs/input.dox index a72ba150..1d8439d5 100644 --- a/docs/input.dox +++ b/docs/input.dox @@ -419,6 +419,16 @@ void cursor_enter_callback(GLFWwindow* window, int entered) } @endcode +You can query whether the cursor is currently inside the client area of the +window with the [GLFW_HOVERED](@ref GLFW_HOVERED_attrib) window attribute. + +@code +if (glfwGetWindowAttrib(window, GLFW_HOVERED)) +{ + highlight_interface(); +} +@endcode + @subsection input_mouse_button Mouse button input diff --git a/docs/window.dox b/docs/window.dox index 2f11f135..9e97ce3b 100644 --- a/docs/window.dox +++ b/docs/window.dox @@ -1220,6 +1220,11 @@ See @ref window_iconify for details. __GLFW_MAXIMIZED__ indicates whether the specified window is maximized. See @ref window_maximize for details. +@anchor GLFW_HOVERED_attrib +__GLFW_HOVERED__ indicates whether the cursor is currently directly over the +client area of the window, with no other windows between. See @ref cursor_enter +for details. + @anchor GLFW_VISIBLE_attrib __GLFW_VISIBLE__ indicates whether the specified window is visible. See @ref window_hide for details. diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 167d99f2..237dd55e 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -814,6 +814,7 @@ extern "C" { * [window attribute](@ref GLFW_TRANSPARENT_FRAMEBUFFER_attrib). */ #define GLFW_TRANSPARENT_FRAMEBUFFER 0x0002000A +#define GLFW_HOVERED 0x0002000B /*! @brief Framebuffer bit depth hint. * diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 5b62bb59..10d4a0c1 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1488,6 +1488,20 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return [window->ns.object isZoomed]; } +int _glfwPlatformWindowHovered(_GLFWwindow* window) +{ + const NSPoint point = [NSEvent mouseLocation]; + + if ([NSWindow windowNumberAtPoint:point belowWindowWithWindowNumber:0] != + [window->ns.object windowNumber]) + { + return GLFW_FALSE; + } + + return NSPointInRect(point, + [window->ns.object convertRectToScreen:[window->ns.view bounds]]); +} + int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) { return ![window->ns.object isOpaque] && ![window->ns.view isOpaque]; diff --git a/src/internal.h b/src/internal.h index 4de1b9f5..ea9c4ed1 100644 --- a/src/internal.h +++ b/src/internal.h @@ -690,6 +690,7 @@ int _glfwPlatformWindowFocused(_GLFWwindow* window); int _glfwPlatformWindowIconified(_GLFWwindow* window); int _glfwPlatformWindowVisible(_GLFWwindow* window); int _glfwPlatformWindowMaximized(_GLFWwindow* window); +int _glfwPlatformWindowHovered(_GLFWwindow* window); int _glfwPlatformFramebufferTransparent(_GLFWwindow* window); float _glfwPlatformGetWindowOpacity(_GLFWwindow* window); void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled); diff --git a/src/mir_window.c b/src/mir_window.c index ad06cb07..a0d17db2 100644 --- a/src/mir_window.c +++ b/src/mir_window.c @@ -627,6 +627,13 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return mir_window_get_state(window->mir.window) == mir_window_state_maximized; } +int _glfwPlatformWindowHovered(_GLFWwindow* window) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); + return GLFW_FALSE; +} + int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) { _glfwInputError(GLFW_PLATFORM_ERROR, diff --git a/src/null_window.c b/src/null_window.c index 6eb4ac6e..6a54cfe5 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -165,6 +165,11 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return GLFW_FALSE; } +int _glfwPlatformWindowHovered(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) { return GLFW_FALSE; diff --git a/src/win32_window.c b/src/win32_window.c index 3e3fb63b..fbb4f2eb 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -1578,6 +1578,11 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return IsZoomed(window->win32.handle); } +int _glfwPlatformWindowHovered(_GLFWwindow* window) +{ + return cursorInClientArea(window); +} + int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) { return window->win32.transparent && _glfwIsCompositionEnabledWin32(); diff --git a/src/window.c b/src/window.c index ae365560..e7352f47 100644 --- a/src/window.c +++ b/src/window.c @@ -785,6 +785,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) return _glfwPlatformWindowVisible(window); case GLFW_MAXIMIZED: return _glfwPlatformWindowMaximized(window); + case GLFW_HOVERED: + return _glfwPlatformWindowHovered(window); case GLFW_TRANSPARENT_FRAMEBUFFER: return _glfwPlatformFramebufferTransparent(window); case GLFW_RESIZABLE: diff --git a/src/wl_init.c b/src/wl_init.c index 8a5d98a4..78d5901e 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -54,6 +54,8 @@ static void pointerHandleEnter(void* data, _glfw.wl.pointerSerial = serial; _glfw.wl.pointerFocus = window; + window->wl.hovered = GLFW_TRUE; + _glfwPlatformSetCursor(window, window->wl.currentCursor); _glfwInputCursorEnter(window, GLFW_TRUE); } @@ -68,6 +70,8 @@ static void pointerHandleLeave(void* data, if (!window) return; + window->wl.hovered = GLFW_FALSE; + _glfw.wl.pointerSerial = serial; _glfw.wl.pointerFocus = NULL; _glfwInputCursorEnter(window, GLFW_FALSE); diff --git a/src/wl_platform.h b/src/wl_platform.h index c3cebecf..53499812 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -117,6 +117,7 @@ typedef struct _GLFWwindowWayland int width, height; GLFWbool visible; GLFWbool maximized; + GLFWbool hovered; GLFWbool transparent; struct wl_surface* surface; struct wl_egl_window* native; diff --git a/src/wl_window.c b/src/wl_window.c index 9759ba26..daebf672 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -692,6 +692,11 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return window->wl.maximized; } +int _glfwPlatformWindowHovered(_GLFWwindow* window) +{ + return window->wl.hovered; +} + int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) { return window->wl.transparent; diff --git a/src/x11_window.c b/src/x11_window.c index a7600558..1520b825 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -2453,6 +2453,28 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return maximized; } +int _glfwPlatformWindowHovered(_GLFWwindow* window) +{ + Window w = _glfw.x11.root; + while (w) + { + Window root; + int rootX, rootY, childX, childY; + unsigned int mask; + + if (!XQueryPointer(_glfw.x11.display, w, + &root, &w, &rootX, &rootY, &childX, &childY, &mask)) + { + return GLFW_FALSE; + } + + if (w == window->x11.handle) + return GLFW_TRUE; + } + + return GLFW_FALSE; +} + int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) { if (!window->x11.transparent)