Browse Source

Merge pull request #40205 from bruvzg/click-through-3

[3.2] Add mouse event pass-through support for window.
Rémi Verschelde 5 years ago
parent
commit
4ad74609ce

+ 6 - 0
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);

+ 1 - 0
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);

+ 1 - 0
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;

+ 22 - 0
doc/classes/OS.xml

@@ -966,6 +966,28 @@
 				[b]Note:[/b] This method is implemented on Linux, macOS and Windows.
 			</description>
 		</method>
+		<method name="set_window_mouse_passthrough">
+			<return type="void">
+			</return>
+			<argument index="0" name="region" type="PoolVector2Array">
+			</argument>
+			<description>
+				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.
+			</description>
+		</method>
 		<method name="set_window_title">
 			<return type="void">
 			</return>

+ 2 - 0
platform/osx/os_osx.h

@@ -114,6 +114,7 @@ public:
 	NSOpenGLPixelFormat *pixelFormat;
 	NSOpenGLContext *context;
 
+	Vector<Vector2> 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;

+ 25 - 0
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];
 

+ 32 - 0
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() {

+ 4 - 0
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<Vector2> 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;

+ 6 - 0
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.")
@@ -213,6 +218,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")

+ 28 - 0
platform/x11/os_x11.cpp

@@ -52,6 +52,7 @@
 #include <X11/Xatom.h>
 #include <X11/Xutil.h>
 #include <X11/extensions/Xinerama.h>
+#include <X11/extensions/shape.h>
 
 // ICCCM
 #define WM_NormalState 1L // window normal state
@@ -996,6 +997,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) {
 }
 

+ 1 - 0
platform/x11/os_x11.h

@@ -254,6 +254,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<Image> &p_icon);