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);