From e51fed9d1bac040196c53a5840638fa886aa1d13 Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Mon, 29 Jun 2020 12:31:13 +0300 Subject: [PATCH] [3.2] Add window click-through support. --- core/bind/core_bind.cpp | 6 ++++++ core/bind/core_bind.h | 1 + core/os/os.h | 1 + doc/classes/OS.xml | 22 ++++++++++++++++++++++ platform/osx/os_osx.h | 2 ++ platform/osx/os_osx.mm | 25 +++++++++++++++++++++++++ platform/windows/os_windows.cpp | 32 ++++++++++++++++++++++++++++++++ platform/windows/os_windows.h | 4 ++++ platform/x11/detect.py | 6 ++++++ platform/x11/os_x11.cpp | 28 ++++++++++++++++++++++++++++ platform/x11/os_x11.h | 1 + 11 files changed, 128 insertions(+) diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 1d2c39ed6fa..6325a08187d 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -215,6 +215,11 @@ void _OS::set_window_title(const String &p_title) { OS::get_singleton()->set_window_title(p_title); } +void _OS::set_window_mouse_passthrough(const PoolVector2Array &p_region) { + + OS::get_singleton()->set_window_mouse_passthrough(p_region); +} + int _OS::get_mouse_button_state() const { return OS::get_singleton()->get_mouse_button_state(); @@ -1307,6 +1312,7 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("has_touchscreen_ui_hint"), &_OS::has_touchscreen_ui_hint); ClassDB::bind_method(D_METHOD("set_window_title", "title"), &_OS::set_window_title); + ClassDB::bind_method(D_METHOD("set_window_mouse_passthrough", "region"), &_OS::set_window_mouse_passthrough); ClassDB::bind_method(D_METHOD("set_low_processor_usage_mode", "enable"), &_OS::set_low_processor_usage_mode); ClassDB::bind_method(D_METHOD("is_in_low_processor_usage_mode"), &_OS::is_in_low_processor_usage_mode); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index d880ac145a4..9d00e27a43f 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -150,6 +150,7 @@ public: Point2 get_mouse_position() const; void set_window_title(const String &p_title); + void set_window_mouse_passthrough(const PoolVector2Array &p_region); int get_mouse_button_state() const; void set_clipboard(const String &p_text); diff --git a/core/os/os.h b/core/os/os.h index 160282042cf..f97d89e7116 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -174,6 +174,7 @@ public: virtual Point2 get_mouse_position() const = 0; virtual int get_mouse_button_state() const = 0; virtual void set_window_title(const String &p_title) = 0; + virtual void set_window_mouse_passthrough(const PoolVector2Array &p_region){}; virtual void set_clipboard(const String &p_text); virtual String get_clipboard() const; diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 72c1c55dbf0..93a01c845b0 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -966,6 +966,28 @@ [b]Note:[/b] This method is implemented on Linux, macOS and Windows. + + + + + + + Sets a polygonal region of the window which accepts mouse events. Mouse events outside the region will be passed through. + Passing an empty array will disable passthrough support (all mouse events will be intercepted by the window, which is the default behavior). + [codeblock] + # Set region, using Path2D node. + OS.set_window_mouse_passthrough($Path2D.curve.get_baked_points()) + + # Set region, using Polygon2D node. + OS.set_window_mouse_passthrough($Polygon2D.polygon) + + # Reset region to default. + OS.set_window_mouse_passthrough([]) + [/codeblock] + [b]Note:[/b] On Windows, the portion of a window that lies outside the region is not drawn, while on Linux and macOS it is. + [b]Note:[/b] This method is implemented on Linux, macOS and Windows. + + diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index 8187ca0b8e4..24fa3f977d6 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -114,6 +114,7 @@ public: NSOpenGLPixelFormat *pixelFormat; NSOpenGLContext *context; + Vector mpath; bool layered_window; CursorShape cursor_shape; @@ -208,6 +209,7 @@ public: virtual int get_mouse_button_state() const; void update_real_mouse_position(); virtual void set_window_title(const String &p_title); + virtual void set_window_mouse_passthrough(const PoolVector2Array &p_region); virtual Size2 get_window_size() const; virtual Size2 get_real_window_size() const; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 142a9ec3f62..fea9ee34393 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -30,6 +30,7 @@ #include "os_osx.h" +#include "core/math/geometry.h" #include "core/os/keyboard.h" #include "core/print_string.h" #include "core/version_generated.gen.h" @@ -2136,6 +2137,13 @@ void OS_OSX::set_window_title(const String &p_title) { [window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]]; } +void OS_OSX::set_window_mouse_passthrough(const PoolVector2Array &p_region) { + mpath.clear(); + for (int i = 0; i < p_region.size(); i++) { + mpath.push_back(p_region[i]); + } +} + void OS_OSX::set_native_icon(const String &p_filename) { FileAccess *f = FileAccess::open(p_filename, FileAccess::READ); @@ -3053,6 +3061,23 @@ void OS_OSX::process_events() { } process_key_events(); + if (mpath.size() > 0) { + const Vector2 mpos = get_mouse_pos([window_object mouseLocationOutsideOfEventStream]); + if (Geometry::is_point_in_polygon(mpos, mpath)) { + if ([window_object ignoresMouseEvents]) { + [window_object setIgnoresMouseEvents:NO]; + } + } else { + if (![window_object ignoresMouseEvents]) { + [window_object setIgnoresMouseEvents:YES]; + } + } + } else { + if ([window_object ignoresMouseEvents]) { + [window_object setIgnoresMouseEvents:NO]; + } + } + [autoreleasePool drain]; autoreleasePool = [[NSAutoreleasePool alloc] init]; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index b7766dccb51..0007155e0ee 100755 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -34,6 +34,7 @@ #include "os_windows.h" #include "core/io/marshalls.h" +#include "core/math/geometry.h" #include "core/version_generated.gen.h" #include "drivers/gles2/rasterizer_gles2.h" #include "drivers/gles3/rasterizer_gles3.h" @@ -1952,6 +1953,36 @@ void OS_Windows::set_window_title(const String &p_title) { SetWindowTextW(hWnd, p_title.c_str()); } +void OS_Windows::set_window_mouse_passthrough(const PoolVector2Array &p_region) { + mpath.clear(); + for (int i = 0; i < p_region.size(); i++) { + mpath.push_back(p_region[i]); + } + _update_window_mouse_passthrough(); +} + +void OS_Windows::_update_window_mouse_passthrough() { + if (mpath.size() == 0) { + SetWindowRgn(hWnd, NULL, TRUE); + } else { + POINT *points = (POINT *)memalloc(sizeof(POINT) * mpath.size()); + for (int i = 0; i < mpath.size(); i++) { + if (video_mode.borderless_window) { + points[i].x = mpath[i].x; + points[i].y = mpath[i].y; + } else { + points[i].x = mpath[i].x + GetSystemMetrics(SM_CXSIZEFRAME); + points[i].y = mpath[i].y + GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYCAPTION); + } + } + + HRGN region = CreatePolygonRgn(points, mpath.size(), ALTERNATE); + SetWindowRgn(hWnd, region, TRUE); + DeleteObject(region); + memfree(points); + } +} + void OS_Windows::set_video_mode(const VideoMode &p_video_mode, int p_screen) { } @@ -2339,6 +2370,7 @@ void OS_Windows::set_borderless_window(bool p_borderless) { preserve_window_size = true; _update_window_style(); + _update_window_mouse_passthrough(); } bool OS_Windows::get_borderless_window() { diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 69767b6764b..6ddb3c3ac6d 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -309,6 +309,8 @@ class OS_Windows : public OS { int pressrc; HINSTANCE hInstance; // Holds The Instance Of The Application HWND hWnd; + + Vector mpath; Point2 last_pos; bool layered_window; @@ -371,6 +373,7 @@ class OS_Windows : public OS { void _touch_event(bool p_pressed, float p_x, float p_y, int idx); void _update_window_style(bool p_repaint = true, bool p_maximized = false); + void _update_window_mouse_passthrough(); void _set_mouse_mode_impl(MouseMode p_mode); @@ -422,6 +425,7 @@ public: void update_real_mouse_position(); virtual int get_mouse_button_state() const; virtual void set_window_title(const String &p_title); + virtual void set_window_mouse_passthrough(const PoolVector2Array &p_region); virtual void set_video_mode(const VideoMode &p_video_mode, int p_screen = 0); virtual VideoMode get_video_mode(int p_screen = 0) const; diff --git a/platform/x11/detect.py b/platform/x11/detect.py index 716930db236..8087a08617d 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -36,6 +36,11 @@ def can_build(): print("xinerama not found.. x11 disabled.") return False + x11_error = os.system("pkg-config xext --modversion > /dev/null ") + if x11_error: + print("xext not found.. x11 disabled.") + return False + x11_error = os.system("pkg-config xrandr --modversion > /dev/null ") if x11_error: print("xrandr not found.. x11 disabled.") @@ -197,6 +202,7 @@ def configure(env): env.ParseConfig("pkg-config x11 --cflags --libs") env.ParseConfig("pkg-config xcursor --cflags --libs") env.ParseConfig("pkg-config xinerama --cflags --libs") + env.ParseConfig("pkg-config xext --cflags --libs") env.ParseConfig("pkg-config xrandr --cflags --libs") env.ParseConfig("pkg-config xrender --cflags --libs") env.ParseConfig("pkg-config xi --cflags --libs") diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 5a71131d86d..a9d63bda45a 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -52,6 +52,7 @@ #include #include #include +#include // ICCCM #define WM_NormalState 1L // window normal state @@ -976,6 +977,33 @@ void OS_X11::set_window_title(const String &p_title) { XChangeProperty(x11_display, x11_window, _net_wm_name, utf8_string, 8, PropModeReplace, (unsigned char *)p_title.utf8().get_data(), p_title.utf8().length()); } +void OS_X11::set_window_mouse_passthrough(const PoolVector2Array &p_region) { + int event_base, error_base; + const Bool ext_okay = XShapeQueryExtension(x11_display, &event_base, &error_base); + if (ext_okay) { + Region region; + if (p_region.size() == 0) { + region = XCreateRegion(); + XRectangle rect; + rect.x = 0; + rect.y = 0; + rect.width = get_real_window_size().x; + rect.height = get_real_window_size().y; + XUnionRectWithRegion(&rect, region, region); + } else { + XPoint *points = (XPoint *)memalloc(sizeof(XPoint) * p_region.size()); + for (int i = 0; i < p_region.size(); i++) { + points[i].x = p_region[i].x; + points[i].y = p_region[i].y; + } + region = XPolygonRegion(points, p_region.size(), EvenOddRule); + memfree(points); + } + XShapeCombineRegion(x11_display, x11_window, ShapeInput, 0, 0, region, ShapeSet); + XDestroyRegion(region); + } +} + void OS_X11::set_video_mode(const VideoMode &p_video_mode, int p_screen) { } diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index 20d8e96b6d1..c1ff3b35417 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -238,6 +238,7 @@ public: virtual Point2 get_mouse_position() const; virtual int get_mouse_button_state() const; virtual void set_window_title(const String &p_title); + virtual void set_window_mouse_passthrough(const PoolVector2Array &p_region); virtual void set_icon(const Ref &p_icon);