Skip to content

Commit

Permalink
internal/glfw, interna/cglfw, internal/goglfw: add MousePassthrough
Browse files Browse the repository at this point in the history
Work in progress

Updates #2511
  • Loading branch information
hajimehoshi committed Sep 18, 2023
1 parent 777c575 commit c8d38f7
Show file tree
Hide file tree
Showing 14 changed files with 217 additions and 38 deletions.
10 changes: 10 additions & 0 deletions internal/cglfw/cocoa_window_darwin.m
Expand Up @@ -917,6 +917,9 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
return GLFW_FALSE;
}

if (wndconfig->mousePassthrough)
_glfwPlatformSetWindowMousePassthrough(window, GLFW_TRUE);

if (window->monitor)
{
_glfwPlatformShowWindow(window);
Expand Down Expand Up @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions internal/cglfw/glfw3.h
Expand Up @@ -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).
Expand Down
3 changes: 3 additions & 0 deletions internal/cglfw/internal.h
Expand Up @@ -169,6 +169,7 @@ struct _GLFWwndconfig
GLFWbool maximized;
GLFWbool centerCursor;
GLFWbool focusOnShow;
GLFWbool mousePassthrough;
GLFWbool scaleToMonitor;
struct {
GLFWbool retina;
Expand Down Expand Up @@ -276,6 +277,7 @@ struct _GLFWwindow
GLFWbool autoIconify;
GLFWbool floating;
GLFWbool focusOnShow;
GLFWbool mousePassthrough;
GLFWbool shouldClose;
void* userPointer;
GLFWbool doublebuffer;
Expand Down Expand Up @@ -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);
Expand Down
25 changes: 18 additions & 7 deletions internal/cglfw/window.c
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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);
}
Expand Down
31 changes: 31 additions & 0 deletions internal/cglfw/x11_init_linbsd.c
Expand Up @@ -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.
Expand Down
26 changes: 26 additions & 0 deletions internal/cglfw/x11_platform_linbsd.h
Expand Up @@ -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*);
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions internal/cglfw/x11_window_linbsd.c
Expand Up @@ -2056,6 +2056,9 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
return GLFW_FALSE;
}

if (wndconfig->mousePassthrough)
_glfwPlatformSetWindowMousePassthrough(window, GLFW_TRUE);

if (window->monitor)
{
_glfwPlatformShowWindow(window);
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions internal/glfw/const.go
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions internal/goglfw/api_windows.go
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions internal/goglfw/cocoawindow_darwin.go
Expand Up @@ -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")
Expand Down
1 change: 1 addition & 0 deletions internal/goglfw/glfw3h.go
Expand Up @@ -58,6 +58,7 @@ const (
TransparentFramebuffer Hint = 0x0002000A
Hovered Hint = 0x0002000B
FocusOnShow Hint = 0x0002000C
MousePassthrough Hint = 0x0002000D

RedBits Hint = 0x00021001
GreenBits Hint = 0x00021002
Expand Down
50 changes: 26 additions & 24 deletions internal/goglfw/internal.go
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
49 changes: 49 additions & 0 deletions internal/goglfw/win32window_windows.go
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit c8d38f7

Please sign in to comment.