浏览代码

Fix window_get_current_screen for X11 display server

This method used to check which screen contains the top-left corner of
the window (and default to the first screen in case none is found),
which is not accurate in some cases.

Now the area of overlap with each screen is calculated, so we can get
the best candidate based on the window's position.

This makes window_get_current_screen consistent with Windows platform,
and fixes an issue where popups appear on the main screen when the main
window is slightly moved outside of the desktop on the top or left.
PouleyKetchoupp 5 年之前
父节点
当前提交
b34480b645
共有 2 个文件被更改,包括 51 次插入26 次删除
  1. 49 26
      platform/linuxbsd/display_server_x11.cpp
  2. 2 0
      platform/linuxbsd/display_server_x11.h

+ 49 - 26
platform/linuxbsd/display_server_x11.cpp

@@ -683,35 +683,49 @@ int DisplayServerX11::get_screen_count() const {
 	return count;
 	return count;
 }
 }
 
 
-Point2i DisplayServerX11::screen_get_position(int p_screen) const {
-	_THREAD_SAFE_METHOD_
+Rect2i DisplayServerX11::_screen_get_rect(int p_screen) const {
+	Rect2i rect(0, 0, 0, 0);
 
 
 	if (p_screen == SCREEN_OF_MAIN_WINDOW) {
 	if (p_screen == SCREEN_OF_MAIN_WINDOW) {
 		p_screen = window_get_current_screen();
 		p_screen = window_get_current_screen();
 	}
 	}
 
 
-	// Using Xinerama Extension
-	int event_base, error_base;
-	const Bool ext_okay = XineramaQueryExtension(x11_display, &event_base, &error_base);
-	if (!ext_okay) {
-		return Point2i(0, 0);
-	}
+	ERR_FAIL_COND_V(p_screen < 0, rect);
 
 
-	int count;
-	XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
+	// Using Xinerama Extension.
+	int event_base, error_base;
+	if (XineramaQueryExtension(x11_display, &event_base, &error_base)) {
+		int count;
+		XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
+
+		// Check if screen is valid.
+		if (p_screen < count) {
+			rect.position.x = xsi[p_screen].x_org;
+			rect.position.y = xsi[p_screen].y_org;
+			rect.size.width = xsi[p_screen].width;
+			rect.size.height = xsi[p_screen].height;
+		} else {
+			ERR_PRINT("Invalid screen index: " + itos(p_screen) + "(count: " + itos(count) + ").");
+		}
 
 
-	// Check if screen is valid
-	ERR_FAIL_INDEX_V(p_screen, count, Point2i(0, 0));
+		if (xsi) {
+			XFree(xsi);
+		}
+	}
 
 
-	Point2i position = Point2i(xsi[p_screen].x_org, xsi[p_screen].y_org);
+	return rect;
+}
 
 
-	XFree(xsi);
+Point2i DisplayServerX11::screen_get_position(int p_screen) const {
+	_THREAD_SAFE_METHOD_
 
 
-	return position;
+	return _screen_get_rect(p_screen).position;
 }
 }
 
 
 Size2i DisplayServerX11::screen_get_size(int p_screen) const {
 Size2i DisplayServerX11::screen_get_size(int p_screen) const {
-	return screen_get_usable_rect(p_screen).size;
+	_THREAD_SAFE_METHOD_
+
+	return _screen_get_rect(p_screen).size;
 }
 }
 
 
 Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
 Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
@@ -1011,22 +1025,31 @@ void DisplayServerX11::window_set_drop_files_callback(const Callable &p_callable
 int DisplayServerX11::window_get_current_screen(WindowID p_window) const {
 int DisplayServerX11::window_get_current_screen(WindowID p_window) const {
 	_THREAD_SAFE_METHOD_
 	_THREAD_SAFE_METHOD_
 
 
-	ERR_FAIL_COND_V(!windows.has(p_window), -1);
+	int count = get_screen_count();
+	if (count < 2) {
+		// Early exit with single monitor.
+		return 0;
+	}
+
+	ERR_FAIL_COND_V(!windows.has(p_window), 0);
 	const WindowData &wd = windows[p_window];
 	const WindowData &wd = windows[p_window];
 
 
-	int x, y;
-	Window child;
-	XTranslateCoordinates(x11_display, wd.x11_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child);
+	const Rect2i window_rect(wd.position, wd.size);
 
 
-	int count = get_screen_count();
+	// Find which monitor has the largest overlap with the given window.
+	int screen_index = 0;
+	int max_area = 0;
 	for (int i = 0; i < count; i++) {
 	for (int i = 0; i < count; i++) {
-		Point2i pos = screen_get_position(i);
-		Size2i size = screen_get_size(i);
-		if ((x >= pos.x && x < pos.x + size.width) && (y >= pos.y && y < pos.y + size.height)) {
-			return i;
+		Rect2i screen_rect = _screen_get_rect(i);
+		Rect2i intersection = screen_rect.intersection(window_rect);
+		int area = intersection.get_area();
+		if (area > max_area) {
+			max_area = area;
+			screen_index = i;
 		}
 		}
 	}
 	}
-	return 0;
+
+	return screen_index;
 }
 }
 
 
 void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window) {
 void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window) {

+ 2 - 0
platform/linuxbsd/display_server_x11.h

@@ -197,6 +197,8 @@ class DisplayServerX11 : public DisplayServer {
 
 
 	bool _refresh_device_info();
 	bool _refresh_device_info();
 
 
+	Rect2i _screen_get_rect(int p_screen) const;
+
 	MouseButton _get_mouse_button_state(MouseButton p_x11_button, int p_x11_type);
 	MouseButton _get_mouse_button_state(MouseButton p_x11_button, int p_x11_type);
 	void _get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state);
 	void _get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state);
 	void _flush_mouse_motion();
 	void _flush_mouse_motion();