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;
}
if (wndconfig->mousePassthrough)
_glfwPlatformSetWindowMousePassthrough(window, GLFW_TRUE);
if (window->monitor)
{
_glfwPlatformShowWindow(window);
@ -1435,6 +1438,13 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
} // autoreleasepool
}
void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enabled)
{
@autoreleasepool {
[window->ns.object setIgnoresMouseEvents:enabled];
}
}
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
{
@autoreleasepool {

View File

@ -721,6 +721,13 @@ extern "C" {
*/
#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.
*
* Framebuffer bit depth [hint](@ref GLFW_RED_BITS).

View File

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

View File

@ -175,13 +175,14 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
window->videoMode.blueBits = fbconfig.blueBits;
window->videoMode.refreshRate = _glfw.hints.refreshRate;
window->monitor = (_GLFWmonitor*) monitor;
window->resizable = wndconfig.resizable;
window->decorated = wndconfig.decorated;
window->autoIconify = wndconfig.autoIconify;
window->floating = wndconfig.floating;
window->focusOnShow = wndconfig.focusOnShow;
window->cursorMode = GLFW_CURSOR_NORMAL;
window->monitor = (_GLFWmonitor*) monitor;
window->resizable = wndconfig.resizable;
window->decorated = wndconfig.decorated;
window->autoIconify = wndconfig.autoIconify;
window->floating = wndconfig.floating;
window->focusOnShow = wndconfig.focusOnShow;
window->mousePassthrough = wndconfig.mousePassthrough;
window->cursorMode = GLFW_CURSOR_NORMAL;
window->doublebuffer = fbconfig.doublebuffer;
@ -331,6 +332,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_PASSTHROUGH:
_glfw.hints.window.mousePassthrough = value ? GLFW_TRUE : GLFW_FALSE;
return;
case GLFW_CLIENT_API:
_glfw.hints.context.client = value;
return;
@ -796,6 +800,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib)
return _glfwPlatformWindowHovered(window);
case GLFW_FOCUS_ON_SHOW:
return window->focusOnShow;
case GLFW_MOUSE_PASSTHROUGH:
return window->mousePassthrough;
case GLFW_TRANSPARENT_FRAMEBUFFER:
return _glfwPlatformFramebufferTransparent(window);
case GLFW_RESIZABLE:
@ -874,6 +880,11 @@ GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value)
}
else if (attrib == GLFW_FOCUS_ON_SHOW)
window->focusOnShow = value;
else if (attrib == GLFW_MOUSE_PASSTHROUGH)
{
window->mousePassthrough = value;
_glfwPlatformSetWindowMousePassthrough(window, value);
}
else
_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
// FIXME: We should listen to XkbMapNotify events to track changes to
// the keyboard mapping.

View File

@ -27,6 +27,9 @@
// The XInput extension provides raw mouse motion input
#include <X11/extensions/XInput2.h>
// The Shape extension provides custom window shapes
#include <X11/extensions/shape.h>
typedef XRRCrtcGamma* (* PFN_XRRAllocGamma)(int);
typedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*);
typedef void (* PFN_XRRFreeGamma)(XRRCrtcGamma*);
@ -109,6 +112,16 @@ 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);
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_time_linbsd.h"
#include "xkb_unicode_linbsd.h"
@ -365,6 +378,19 @@ typedef struct _GLFWlibraryX11
PFN_XRenderQueryVersion QueryVersion;
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;
PFN_XShapeCombineMask ShapeCombineMask;
} xshape;
} _GLFWlibraryX11;
// X11-specific per-monitor data

View File

@ -2056,6 +2056,9 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
return GLFW_FALSE;
}
if (wndconfig->mousePassthrough)
_glfwPlatformSetWindowMousePassthrough(window, GLFW_TRUE);
if (window->monitor)
{
_glfwPlatformShowWindow(window);
@ -2741,6 +2744,25 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
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 opacity = 1.f;

View File

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

View File

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

View File

@ -155,6 +155,10 @@ func (w *Window) platformSetWindowFloating(enabled bool) error {
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) {
// cocoa_window.m:L1462
panic("goglfw: Window.platformGetWindowOpacity is not implemented yet")

View File

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

View File

@ -23,19 +23,20 @@ type initconfig struct {
}
type wndconfig struct {
width int
height int
title string
resizable bool
visible bool
decorated bool
focused bool
autoIconify bool
floating bool
maximized bool
centerCursor bool
focusOnShow bool
scaleToMonitor bool
width int
height int
title string
resizable bool
visible bool
decorated bool
focused bool
autoIconify bool
floating bool
maximized bool
centerCursor bool
focusOnShow bool
mousePassthrough bool
scaleToMonitor bool
}
type ctxconfig struct {
@ -118,17 +119,18 @@ type (
)
type Window struct {
resizable bool
decorated bool
autoIconify bool
floating bool
focusOnShow bool
shouldClose bool
userPointer unsafe.Pointer
doublebuffer bool
videoMode VidMode
monitor *Monitor
cursor *Cursor
resizable bool
decorated bool
autoIconify bool
floating bool
focusOnShow bool
mousePassthrough bool
shouldClose bool
userPointer unsafe.Pointer
doublebuffer bool
videoMode VidMode
monitor *Monitor
cursor *Cursor
minwidth int
minheight int

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 {
w.platformShowWindow()
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)
}
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) {
style, err := _GetWindowLongW(w.platform.handle, _GWL_EXSTYLE)
if err != nil {

View File

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