internal/glfw, interna/cglfw, internal/goglfw: add MousePassthrough

Work in progress

Updates #2511
This commit is contained in:
Hajime Hoshi 2023-09-18 14:52:25 +09:00
parent 777c575638
commit c8d38f7f25
14 changed files with 217 additions and 38 deletions

View File

@ -917,6 +917,9 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
return GLFW_FALSE; return GLFW_FALSE;
} }
if (wndconfig->mousePassthrough)
_glfwPlatformSetWindowMousePassthrough(window, GLFW_TRUE);
if (window->monitor) if (window->monitor)
{ {
_glfwPlatformShowWindow(window); _glfwPlatformShowWindow(window);
@ -1435,6 +1438,13 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
} // autoreleasepool } // autoreleasepool
} }
void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enabled)
{
@autoreleasepool {
[window->ns.object setIgnoresMouseEvents:enabled];
}
}
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
{ {
@autoreleasepool { @autoreleasepool {

View File

@ -721,6 +721,13 @@ extern "C" {
*/ */
#define GLFW_FOCUS_ON_SHOW 0x0002000C #define GLFW_FOCUS_ON_SHOW 0x0002000C
/*! @brief Mouse input transparency window hint and attribute
*
* Mouse input transparency [window hint](@ref GLFW_MOUSE_PASSTHROUGH_hint) or
* [window attribute](@ref GLFW_MOUSE_PASSTHROUGH_attrib).
*/
#define GLFW_MOUSE_PASSTHROUGH 0x0002000D
/*! @brief Framebuffer bit depth hint. /*! @brief Framebuffer bit depth hint.
* *
* Framebuffer bit depth [hint](@ref GLFW_RED_BITS). * Framebuffer bit depth [hint](@ref GLFW_RED_BITS).

View File

@ -169,6 +169,7 @@ struct _GLFWwndconfig
GLFWbool maximized; GLFWbool maximized;
GLFWbool centerCursor; GLFWbool centerCursor;
GLFWbool focusOnShow; GLFWbool focusOnShow;
GLFWbool mousePassthrough;
GLFWbool scaleToMonitor; GLFWbool scaleToMonitor;
struct { struct {
GLFWbool retina; GLFWbool retina;
@ -276,6 +277,7 @@ struct _GLFWwindow
GLFWbool autoIconify; GLFWbool autoIconify;
GLFWbool floating; GLFWbool floating;
GLFWbool focusOnShow; GLFWbool focusOnShow;
GLFWbool mousePassthrough;
GLFWbool shouldClose; GLFWbool shouldClose;
void* userPointer; void* userPointer;
GLFWbool doublebuffer; GLFWbool doublebuffer;
@ -504,6 +506,7 @@ void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled);
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled);
void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled);
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity); void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity);
void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enabled);
void _glfwPlatformPollEvents(void); void _glfwPlatformPollEvents(void);
void _glfwPlatformWaitEvents(void); void _glfwPlatformWaitEvents(void);

View File

@ -181,6 +181,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
window->autoIconify = wndconfig.autoIconify; window->autoIconify = wndconfig.autoIconify;
window->floating = wndconfig.floating; window->floating = wndconfig.floating;
window->focusOnShow = wndconfig.focusOnShow; window->focusOnShow = wndconfig.focusOnShow;
window->mousePassthrough = wndconfig.mousePassthrough;
window->cursorMode = GLFW_CURSOR_NORMAL; window->cursorMode = GLFW_CURSOR_NORMAL;
window->doublebuffer = fbconfig.doublebuffer; window->doublebuffer = fbconfig.doublebuffer;
@ -331,6 +332,9 @@ GLFWAPI void glfwWindowHint(int hint, int value)
case GLFW_FOCUS_ON_SHOW: case GLFW_FOCUS_ON_SHOW:
_glfw.hints.window.focusOnShow = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.window.focusOnShow = value ? GLFW_TRUE : GLFW_FALSE;
return; return;
case GLFW_MOUSE_PASSTHROUGH:
_glfw.hints.window.mousePassthrough = value ? GLFW_TRUE : GLFW_FALSE;
return;
case GLFW_CLIENT_API: case GLFW_CLIENT_API:
_glfw.hints.context.client = value; _glfw.hints.context.client = value;
return; return;
@ -796,6 +800,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib)
return _glfwPlatformWindowHovered(window); return _glfwPlatformWindowHovered(window);
case GLFW_FOCUS_ON_SHOW: case GLFW_FOCUS_ON_SHOW:
return window->focusOnShow; return window->focusOnShow;
case GLFW_MOUSE_PASSTHROUGH:
return window->mousePassthrough;
case GLFW_TRANSPARENT_FRAMEBUFFER: case GLFW_TRANSPARENT_FRAMEBUFFER:
return _glfwPlatformFramebufferTransparent(window); return _glfwPlatformFramebufferTransparent(window);
case GLFW_RESIZABLE: case GLFW_RESIZABLE:
@ -874,6 +880,11 @@ GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value)
} }
else if (attrib == GLFW_FOCUS_ON_SHOW) else if (attrib == GLFW_FOCUS_ON_SHOW)
window->focusOnShow = value; window->focusOnShow = value;
else if (attrib == GLFW_MOUSE_PASSTHROUGH)
{
window->mousePassthrough = value;
_glfwPlatformSetWindowMousePassthrough(window, value);
}
else else
_glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib);
} }

View File

@ -820,6 +820,37 @@ static GLFWbool initExtensions(void)
} }
} }
#if defined(__CYGWIN__)
_glfw.x11.xshape.handle = _glfw_dlopen("libXext-6.so");
#elif defined(__OpenBSD__) || defined(__NetBSD__)
_glfw.x11.xshape.handle = _glfw_dlopen("libXext.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");
_glfw.x11.xshape.ShapeCombineMask = (PFN_XShapeCombineMask)
_glfw_dlsym(_glfw.x11.xshape.handle, "XShapeCombineMask");
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 // Update the key code LUT
// FIXME: We should listen to XkbMapNotify events to track changes to // FIXME: We should listen to XkbMapNotify events to track changes to
// the keyboard mapping. // the keyboard mapping.

View File

@ -27,6 +27,9 @@
// The XInput extension provides raw mouse motion input // The XInput extension provides raw mouse motion input
#include <X11/extensions/XInput2.h> #include <X11/extensions/XInput2.h>
// The Shape extension provides custom window shapes
#include <X11/extensions/shape.h>
typedef XRRCrtcGamma* (* PFN_XRRAllocGamma)(int); typedef XRRCrtcGamma* (* PFN_XRRAllocGamma)(int);
typedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*); typedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*);
typedef void (* PFN_XRRFreeGamma)(XRRCrtcGamma*); typedef void (* PFN_XRRFreeGamma)(XRRCrtcGamma*);
@ -109,6 +112,16 @@ typedef XRenderPictFormat* (* PFN_XRenderFindVisualFormat)(Display*,Visual const
#define XRenderQueryVersion _glfw.x11.xrender.QueryVersion #define XRenderQueryVersion _glfw.x11.xrender.QueryVersion
#define XRenderFindVisualFormat _glfw.x11.xrender.FindVisualFormat #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);
typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int);
#define XShapeQueryExtension _glfw.x11.xshape.QueryExtension
#define XShapeQueryVersion _glfw.x11.xshape.QueryVersion
#define XShapeCombineRegion _glfw.x11.xshape.ShapeCombineRegion
#define XShapeCombineMask _glfw.x11.xshape.ShapeCombineMask
#include "posix_thread.h" #include "posix_thread.h"
#include "posix_time_linbsd.h" #include "posix_time_linbsd.h"
#include "xkb_unicode_linbsd.h" #include "xkb_unicode_linbsd.h"
@ -365,6 +378,19 @@ typedef struct _GLFWlibraryX11
PFN_XRenderQueryVersion QueryVersion; PFN_XRenderQueryVersion QueryVersion;
PFN_XRenderFindVisualFormat FindVisualFormat; PFN_XRenderFindVisualFormat FindVisualFormat;
} xrender; } xrender;
struct {
GLFWbool available;
void* handle;
int major;
int minor;
int eventBase;
int errorBase;
PFN_XShapeQueryExtension QueryExtension;
PFN_XShapeCombineRegion ShapeCombineRegion;
PFN_XShapeQueryVersion QueryVersion;
PFN_XShapeCombineMask ShapeCombineMask;
} xshape;
} _GLFWlibraryX11; } _GLFWlibraryX11;
// X11-specific per-monitor data // X11-specific per-monitor data

View File

@ -2056,6 +2056,9 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
return GLFW_FALSE; return GLFW_FALSE;
} }
if (wndconfig->mousePassthrough)
_glfwPlatformSetWindowMousePassthrough(window, GLFW_TRUE);
if (window->monitor) if (window->monitor)
{ {
_glfwPlatformShowWindow(window); _glfwPlatformShowWindow(window);
@ -2741,6 +2744,25 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
XFlush(_glfw.x11.display); XFlush(_glfw.x11.display);
} }
void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enabled)
{
if (!_glfw.x11.xshape.available)
return;
if (enabled)
{
Region region = XCreateRegion();
XShapeCombineRegion(_glfw.x11.display, window->x11.handle,
ShapeInput, 0, 0, region, ShapeSet);
XDestroyRegion(region);
}
else
{
XShapeCombineMask(_glfw.x11.display, window->x11.handle,
ShapeInput, 0, 0, None, ShapeSet);
}
}
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
{ {
float opacity = 1.f; float opacity = 1.f;

View File

@ -70,6 +70,7 @@ const (
FocusOnShow = Hint(0x0002000C) FocusOnShow = Hint(0x0002000C)
Iconified = Hint(0x00020002) Iconified = Hint(0x00020002)
Maximized = Hint(0x00020008) Maximized = Hint(0x00020008)
MousePassthrough = Hint(0x0002000D)
OpenGLForwardCompatible = Hint(0x00022006) OpenGLForwardCompatible = Hint(0x00022006)
OpenGLProfile = Hint(0x00022008) OpenGLProfile = Hint(0x00022008)
Resizable = Hint(0x00020003) Resizable = Hint(0x00020003)

View File

@ -333,6 +333,7 @@ const (
_WS_EX_LAYERED = 0x00080000 _WS_EX_LAYERED = 0x00080000
_WS_EX_OVERLAPPEDWINDOW = _WS_EX_WINDOWEDGE | _WS_EX_CLIENTEDGE _WS_EX_OVERLAPPEDWINDOW = _WS_EX_WINDOWEDGE | _WS_EX_CLIENTEDGE
_WS_EX_TOPMOST = 0x00000008 _WS_EX_TOPMOST = 0x00000008
_WS_EX_TRANSPARENT = 0x00000020
_WS_EX_WINDOWEDGE = 0x00000100 _WS_EX_WINDOWEDGE = 0x00000100
_WS_MAXIMIZE = 0x01000000 _WS_MAXIMIZE = 0x01000000
_WS_MAXIMIZEBOX = 0x00010000 _WS_MAXIMIZEBOX = 0x00010000

View File

@ -155,6 +155,10 @@ func (w *Window) platformSetWindowFloating(enabled bool) error {
panic("goglfw: Window.platformSetWindowFloating is not implemented yet") panic("goglfw: Window.platformSetWindowFloating is not implemented yet")
} }
func (w *Window) platformSetWindowMousePassthrough(enabled bool) error {
panic("goglfw: Window.platformSetWindowMousePassthrough is not implemented yet")
}
func (w *Window) platformGetWindowOpacity() (float32, error) { func (w *Window) platformGetWindowOpacity() (float32, error) {
// cocoa_window.m:L1462 // cocoa_window.m:L1462
panic("goglfw: Window.platformGetWindowOpacity is not implemented yet") panic("goglfw: Window.platformGetWindowOpacity is not implemented yet")

View File

@ -58,6 +58,7 @@ const (
TransparentFramebuffer Hint = 0x0002000A TransparentFramebuffer Hint = 0x0002000A
Hovered Hint = 0x0002000B Hovered Hint = 0x0002000B
FocusOnShow Hint = 0x0002000C FocusOnShow Hint = 0x0002000C
MousePassthrough Hint = 0x0002000D
RedBits Hint = 0x00021001 RedBits Hint = 0x00021001
GreenBits Hint = 0x00021002 GreenBits Hint = 0x00021002

View File

@ -35,6 +35,7 @@ type wndconfig struct {
maximized bool maximized bool
centerCursor bool centerCursor bool
focusOnShow bool focusOnShow bool
mousePassthrough bool
scaleToMonitor bool scaleToMonitor bool
} }
@ -123,6 +124,7 @@ type Window struct {
autoIconify bool autoIconify bool
floating bool floating bool
focusOnShow bool focusOnShow bool
mousePassthrough bool
shouldClose bool shouldClose bool
userPointer unsafe.Pointer userPointer unsafe.Pointer
doublebuffer bool doublebuffer bool

View File

@ -1409,6 +1409,12 @@ func (w *Window) platformCreateWindow(wndconfig *wndconfig, ctxconfig *ctxconfig
} }
} }
if wndconfig.mousePassthrough {
if err := w.platformSetWindowMousePassthrough(true); err != nil {
return err
}
}
if w.monitor != nil { if w.monitor != nil {
w.platformShowWindow() w.platformShowWindow()
if err := w.platformFocusWindow(); err != nil { if err := w.platformFocusWindow(); err != nil {
@ -1972,6 +1978,49 @@ func (w *Window) platformSetWindowFloating(enabled bool) error {
return _SetWindowPos(w.platform.handle, after, 0, 0, 0, 0, _SWP_NOACTIVATE|_SWP_NOMOVE|_SWP_NOSIZE) return _SetWindowPos(w.platform.handle, after, 0, 0, 0, 0, _SWP_NOACTIVATE|_SWP_NOMOVE|_SWP_NOSIZE)
} }
func (w *Window) platformSetWindowMousePassthrough(enabled bool) error {
exStyle, err := _GetWindowLongW(w.platform.handle, _GWL_EXSTYLE)
if err != nil {
return err
}
var key _COLORREF
var alpha byte
var flags uint32
if exStyle&_WS_EX_LAYERED != 0 {
var err error
key, alpha, flags, err = _GetLayeredWindowAttributes(w.platform.handle)
if err != nil {
return err
}
}
if enabled {
exStyle |= _WS_EX_TRANSPARENT | _WS_EX_LAYERED
} else {
exStyle &^= _WS_EX_TRANSPARENT
// NOTE: Window opacity also needs the layered window style so do not
// remove it if the window is alpha blended
if exStyle&_WS_EX_LAYERED != 0 {
if flags&_LWA_ALPHA == 0 {
exStyle &^= _WS_EX_LAYERED
}
}
}
if _, err := _SetWindowLongW(w.platform.handle, _GWL_EXSTYLE, exStyle); err != nil {
return err
}
if enabled {
if err := _SetLayeredWindowAttributes(w.platform.handle, key, alpha, flags); err != nil {
return err
}
}
return nil
}
func (w *Window) platformGetWindowOpacity() (float32, error) { func (w *Window) platformGetWindowOpacity() (float32, error) {
style, err := _GetWindowLongW(w.platform.handle, _GWL_EXSTYLE) style, err := _GetWindowLongW(w.platform.handle, _GWL_EXSTYLE)
if err != nil { if err != nil {

View File

@ -126,6 +126,7 @@ func CreateWindow(width, height int, title string, monitor *Monitor, share *Wind
autoIconify: wndconfig.autoIconify, autoIconify: wndconfig.autoIconify,
floating: wndconfig.floating, floating: wndconfig.floating,
focusOnShow: wndconfig.focusOnShow, focusOnShow: wndconfig.focusOnShow,
mousePassthrough: wndconfig.mousePassthrough,
cursorMode: CursorNormal, cursorMode: CursorNormal,
doublebuffer: fbconfig.doublebuffer, doublebuffer: fbconfig.doublebuffer,
@ -252,6 +253,8 @@ func WindowHint(hint Hint, value int) error {
_glfw.hints.window.centerCursor = intToBool(value) _glfw.hints.window.centerCursor = intToBool(value)
case FocusOnShow: case FocusOnShow:
_glfw.hints.window.focusOnShow = intToBool(value) _glfw.hints.window.focusOnShow = intToBool(value)
case MousePassthrough:
_glfw.hints.window.mousePassthrough = intToBool(value)
case ClientAPI: case ClientAPI:
_glfw.hints.context.client = value _glfw.hints.context.client = value
case ContextCreationAPI: case ContextCreationAPI:
@ -597,6 +600,8 @@ func (w *Window) GetAttrib(attrib Hint) (int, error) {
return boolToInt(b), nil return boolToInt(b), nil
case FocusOnShow: case FocusOnShow:
return boolToInt(w.focusOnShow), nil return boolToInt(w.focusOnShow), nil
case MousePassthrough:
return boolToInt(w.mousePassthrough), nil
case TransparentFramebuffer: case TransparentFramebuffer:
return boolToInt(w.platformFramebufferTransparent()), nil return boolToInt(w.platformFramebufferTransparent()), nil
case Resizable: case Resizable:
@ -681,6 +686,12 @@ func (w *Window) SetAttrib(attrib Hint, value int) error {
case FocusOnShow: case FocusOnShow:
w.focusOnShow = bValue w.focusOnShow = bValue
return nil return nil
case MousePassthrough:
w.mousePassthrough = bValue
if err := w.platformSetWindowMousePassthrough(bValue); err != nil {
return err
}
return nil
default: default:
return fmt.Errorf("goglfw: invalid window attribute 0x%08X: %w", attrib, InvalidEnum) return fmt.Errorf("goglfw: invalid window attribute 0x%08X: %w", attrib, InvalidEnum)
} }