diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index dad45a496..bb653ebe7 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -832,6 +832,13 @@ extern "C" { */ #define GLFW_FOCUS_ON_SHOW 0x0002000C +/*! @brief Forward mouse input to window behind. + * + * Mouse input forwarding[window hint](@ref GLFW_MOUSE_PASSTHRU_hint) or + * [window attribute](@ref GLFW_MOUSE_PASSTHRU_attrib). + */ +#define GLFW_MOUSE_PASSTHRU 0x0002000D + /*! @brief Framebuffer bit depth hint. * * Framebuffer bit depth [hint](@ref GLFW_RED_BITS). @@ -3499,6 +3506,7 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib); * [GLFW_FLOATING](@ref GLFW_FLOATING_attrib), * [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib) and * [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_attrib). + * [GLFW_MOUSE_PASSTHRU](@ref GLFW_MOUSE_PASSTHRU_attrib) * * Some of these attributes are ignored for full screen windows. The new * value will take effect if the window is later made windowed. diff --git a/src/cocoa_window.m b/src/cocoa_window.m index dd8879b51..81603048d 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1356,6 +1356,14 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) } // autoreleasepool } +void _glfwPlatformSetWindowMousePassthru(_GLFWwindow* window, GLFWbool enabled) +{ + window->mousePassthru = enabled; + @autoreleasepool { + [window->ns.object setIgnoresMouseEvents:enabled]; + } +} + float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) { @autoreleasepool { diff --git a/src/internal.h b/src/internal.h index acdae22d4..f84ea5db8 100644 --- a/src/internal.h +++ b/src/internal.h @@ -265,6 +265,7 @@ struct _GLFWwndconfig GLFWbool maximized; GLFWbool centerCursor; GLFWbool focusOnShow; + GLFWbool mousePassthru; GLFWbool scaleToMonitor; struct { GLFWbool retina; @@ -372,6 +373,7 @@ struct _GLFWwindow GLFWbool autoIconify; GLFWbool floating; GLFWbool focusOnShow; + GLFWbool mousePassthru; GLFWbool shouldClose; void* userPointer; GLFWvidmode videoMode; @@ -669,6 +671,7 @@ float _glfwPlatformGetWindowOpacity(_GLFWwindow* window); void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled); +void _glfwPlatformSetWindowMousePassthru(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity); void _glfwPlatformPollEvents(void); diff --git a/src/null_window.c b/src/null_window.c index 936400d39..40a060d84 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -189,6 +189,10 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) { } +void _glfwPlatformSetWindowMousePassthru(_GLFWwindow* window, GLFWbool enabled) +{ +} + float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) { return 1.f; diff --git a/src/win32_window.c b/src/win32_window.c index fbb05b6ac..0e7a1e2db 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -1195,6 +1195,13 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, DragFinish(drop); return 0; } + + case WM_NCHITTEST: + { + if (window->mousePassthru) + return HTTRANSPARENT; + break; + } } return DefWindowProcW(hWnd, uMsg, wParam, lParam); @@ -1847,6 +1854,11 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); } +void _glfwPlatformSetWindowMousePassthru(_GLFWwindow* window, GLFWbool enabled) +{ + window->mousePassthru = enabled; +} + float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) { BYTE alpha; diff --git a/src/window.c b/src/window.c index fa604d01d..1dc2f2b70 100644 --- a/src/window.c +++ b/src/window.c @@ -243,6 +243,8 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, } } + _glfwPlatformSetWindowMousePassthru(window, wndconfig.mousePassthru); + return (GLFWwindow*) window; } @@ -375,6 +377,9 @@ GLFWAPI void glfwWindowHint(int hint, int value) case GLFW_FOCUS_ON_SHOW: _glfw.hints.window.focusOnShow = value ? GLFW_TRUE : GLFW_FALSE; return; + case GLFW_MOUSE_PASSTHRU: + _glfw.hints.window.mousePassthru = value ? GLFW_TRUE : GLFW_FALSE; + return; case GLFW_CLIENT_API: _glfw.hints.context.client = value; return; @@ -819,6 +824,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) return _glfwPlatformWindowHovered(window); case GLFW_FOCUS_ON_SHOW: return window->focusOnShow; + case GLFW_MOUSE_PASSTHRU: + return window->mousePassthru; case GLFW_TRANSPARENT_FRAMEBUFFER: return _glfwPlatformFramebufferTransparent(window); case GLFW_RESIZABLE: @@ -897,6 +904,8 @@ GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value) } else if (attrib == GLFW_FOCUS_ON_SHOW) window->focusOnShow = value; + else if (attrib == GLFW_MOUSE_PASSTHRU) + _glfwPlatformSetWindowMousePassthru(window, value); else _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); } diff --git a/src/wl_window.c b/src/wl_window.c index 200b004ca..4ea74ef45 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1140,6 +1140,23 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) "Wayland: Window attribute setting not implemented yet"); } +void _glfwPlatformSetWindowMousePassthru(_GLFWwindow* window, GLFWbool enabled) +{ + if (enabled == window->mousePassthru) + return; + + if (enabled) + { + struct wl_region* region = wl_compositor_create_region(_glfw.wl.compositor); + wl_surface_set_input_region(window->wl.surface, region); + wl_region_destroy(region); + } + else + wl_surface_set_input_region(window->wl.surface, 0); + wl_surface_commit(window->wl.surface); + window->mousePassthru = enabled; +} + float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) { return 1.f; diff --git a/src/x11_init.c b/src/x11_init.c index ae76d36be..3dda45c4a 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -707,6 +707,33 @@ static GLFWbool initExtensions(void) } } +#if defined(__CYGWIN__) + _glfw.x11.xshape.handle = _glfw_dlopen("libXext-6.so"); +#else + _glfw.x11.xshape.handle = _glfw_dlopen("libXext.so.6"); +#endif + if (_glfw.x11.xshape.handle) + { + _glfw.x11.xshape.QueryExtension = (PFN_XShapeQueryExtension) + _glfw_dlsym(_glfw.x11.xshape.handle, "XShapeQueryExtension"); + _glfw.x11.xshape.ShapeCombineRegion = (PFN_XShapeCombineRegion) + _glfw_dlsym(_glfw.x11.xshape.handle, "XShapeCombineRegion"); + _glfw.x11.xshape.QueryVersion = (PFN_XShapeQueryVersion) + _glfw_dlsym(_glfw.x11.xshape.handle, "XShapeQueryVersion"); + + if (XShapeQueryExtension(_glfw.x11.display, + &_glfw.x11.xshape.errorBase, + &_glfw.x11.xshape.eventBase)) + { + if (XShapeQueryVersion(_glfw.x11.display, + &_glfw.x11.xshape.major, + &_glfw.x11.xshape.minor)) + { + _glfw.x11.xshape.available = GLFW_TRUE; + } + } + } + // Update the key code LUT // FIXME: We should listen to XkbMapNotify events to track changes to // the keyboard mapping. diff --git a/src/x11_platform.h b/src/x11_platform.h index 33145e63e..59f75f0ba 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -123,6 +123,13 @@ typedef XRenderPictFormat* (* PFN_XRenderFindVisualFormat)(Display*,Visual const #define XRenderQueryVersion _glfw.x11.xrender.QueryVersion #define XRenderFindVisualFormat _glfw.x11.xrender.FindVisualFormat +typedef Bool (* PFN_XShapeQueryExtension)(Display*,int*,int*); +typedef Status (* PFN_XShapeQueryVersion)(Display*dpy,int*,int*); +typedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int); +#define XShapeQueryExtension _glfw.x11.xshape.QueryExtension +#define XShapeQueryVersion _glfw.x11.xshape.QueryVersion +#define XShapeCombineRegion _glfw.x11.xshape.ShapeCombineRegion + typedef VkFlags VkXlibSurfaceCreateFlagsKHR; typedef VkFlags VkXcbSurfaceCreateFlagsKHR; @@ -404,6 +411,18 @@ typedef struct _GLFWlibraryX11 PFN_XRenderFindVisualFormat FindVisualFormat; } xrender; + struct { + GLFWbool available; + void* handle; + int major; + int minor; + int eventBase; + int errorBase; + PFN_XShapeQueryExtension QueryExtension; + PFN_XShapeCombineRegion ShapeCombineRegion; + PFN_XShapeQueryVersion QueryVersion; + } xshape; + } _GLFWlibraryX11; // X11-specific per-monitor data diff --git a/src/x11_window.c b/src/x11_window.c index d8355ae8c..cd3e4cbad 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -2630,6 +2630,32 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) XFlush(_glfw.x11.display); } +void _glfwPlatformSetWindowMousePassthru(_GLFWwindow* window, GLFWbool enabled) +{ + if (!_glfw.x11.xshape.available) + return; + + if (enabled == window->mousePassthru) + return; + + int width = 0; + int height = 0; + if (!enabled) + _glfwPlatformGetWindowSize(window, &width, &height); + + XRectangle rect; + rect.x = 0; + rect.y = 0; + rect.width = (unsigned short)width; + rect.height = (unsigned short)height; + + Region region = XCreateRegion(); + XUnionRectWithRegion(&rect, region, region); + XShapeCombineRegion(_glfw.x11.display, window->x11.handle, 2/*ShapeInput*/, 0, 0, region, 0/*ShapeSet*/); + XDestroyRegion(region); + window->mousePassthru = enabled; +} + float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) { float opacity = 1.f;