Browse Source

Merge pull request #110990 from timothyqiu/x11-min-max

X11: Fix minimization of maximized windows
Thaddeus Crews 2 months ago
parent
commit
4ea49aecaf

+ 78 - 45
platform/linuxbsd/x11/display_server_x11.cpp

@@ -1917,9 +1917,22 @@ void DisplayServerX11::show_window(WindowID p_id) {
 
 
 	DEBUG_LOG_X11("show_window: %lu (%u) \n", wd.x11_window, p_id);
 	DEBUG_LOG_X11("show_window: %lu (%u) \n", wd.x11_window, p_id);
 
 
+	// Setup initial minimize/maximize state.
+	// `_NET_WM_STATE` can be set directly when the window is unmapped.
+	LocalVector<Atom> hints;
+	if (wd.maximized) {
+		hints.push_back(XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_VERT", False));
+		hints.push_back(XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False));
+	}
+	if (wd.minimized) {
+		hints.push_back(XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False));
+	}
+	XChangeProperty(x11_display, wd.x11_window, XInternAtom(x11_display, "_NET_WM_STATE", False), XA_ATOM, 32, PropModeReplace, (unsigned char *)hints.ptr(), hints.size());
+
 	XMapWindow(x11_display, wd.x11_window);
 	XMapWindow(x11_display, wd.x11_window);
 	XSync(x11_display, False);
 	XSync(x11_display, False);
-	_validate_mode_on_map(p_id);
+
+	_validate_fullscreen_on_map(p_id);
 
 
 	if (p_id == MAIN_WINDOW_ID) {
 	if (p_id == MAIN_WINDOW_ID) {
 		// Get main window size for boot splash drawing.
 		// Get main window size for boot splash drawing.
@@ -2448,6 +2461,52 @@ void DisplayServerX11::_update_actions_hints(WindowID p_window) {
 	}
 	}
 }
 }
 
 
+void DisplayServerX11::_update_wm_state_hints(WindowID p_window) {
+	WindowData &wd = windows[p_window];
+
+	Atom type;
+	int format;
+	unsigned long len;
+	unsigned long remaining;
+	unsigned char *data = nullptr;
+
+	int result = XGetWindowProperty(
+			x11_display,
+			wd.x11_window,
+			XInternAtom(x11_display, "_NET_WM_STATE", False),
+			0,
+			1024,
+			False,
+			XA_ATOM,
+			&type,
+			&format,
+			&len,
+			&remaining,
+			&data);
+	if (result != Success) {
+		return;
+	}
+
+	LocalVector<Atom> hints;
+	if (data) {
+		hints.resize(len);
+		Atom *atoms = (Atom *)data;
+		for (unsigned long i = 0; i < len; i++) {
+			hints[i] = atoms[i];
+		}
+		XFree(data);
+	}
+
+	Atom fullscreen_atom = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", False);
+	Atom maximized_horz_atom = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
+	Atom maximized_vert_atom = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
+	Atom hidden_atom = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False);
+
+	wd.fullscreen = hints.has(fullscreen_atom);
+	wd.maximized = hints.has(maximized_horz_atom) && hints.has(maximized_vert_atom);
+	wd.minimized = hints.has(hidden_atom);
+}
+
 Point2i DisplayServerX11::window_get_position(WindowID p_window) const {
 Point2i DisplayServerX11::window_get_position(WindowID p_window) const {
 	_THREAD_SAFE_METHOD_
 	_THREAD_SAFE_METHOD_
 
 
@@ -2855,15 +2914,11 @@ bool DisplayServerX11::_window_fullscreen_check(WindowID p_window) const {
 	return retval;
 	return retval;
 }
 }
 
 
-void DisplayServerX11::_validate_mode_on_map(WindowID p_window) {
+void DisplayServerX11::_validate_fullscreen_on_map(WindowID p_window) {
 	// Check if we applied any window modes that didn't take effect while unmapped
 	// Check if we applied any window modes that didn't take effect while unmapped
 	const WindowData &wd = windows[p_window];
 	const WindowData &wd = windows[p_window];
 	if (wd.fullscreen && !_window_fullscreen_check(p_window)) {
 	if (wd.fullscreen && !_window_fullscreen_check(p_window)) {
 		_set_wm_fullscreen(p_window, true, wd.exclusive_fullscreen);
 		_set_wm_fullscreen(p_window, true, wd.exclusive_fullscreen);
-	} else if (wd.maximized && !_window_maximize_check(p_window, "_NET_WM_STATE")) {
-		_set_wm_maximized(p_window, true);
-	} else if (wd.minimized && !_window_minimize_check(p_window)) {
-		_set_wm_minimized(p_window, true);
 	}
 	}
 
 
 	if (wd.on_top) {
 	if (wd.on_top) {
@@ -3062,13 +3117,15 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) {
 
 
 		} break;
 		} break;
 		case WINDOW_MODE_MAXIMIZED: {
 		case WINDOW_MODE_MAXIMIZED: {
-			_set_wm_maximized(p_window, false);
+			// Varies between target modes, so do nothing here.
 		} break;
 		} break;
 	}
 	}
 
 
 	switch (p_mode) {
 	switch (p_mode) {
 		case WINDOW_MODE_WINDOWED: {
 		case WINDOW_MODE_WINDOWED: {
-			//do nothing
+			if (wd.maximized) {
+				_set_wm_maximized(p_window, false);
+			}
 		} break;
 		} break;
 		case WINDOW_MODE_MINIMIZED: {
 		case WINDOW_MODE_MINIMIZED: {
 			_set_wm_minimized(p_window, true);
 			_set_wm_minimized(p_window, true);
@@ -3102,28 +3159,21 @@ DisplayServer::WindowMode DisplayServerX11::window_get_mode(WindowID p_window) c
 	ERR_FAIL_COND_V(!windows.has(p_window), WINDOW_MODE_WINDOWED);
 	ERR_FAIL_COND_V(!windows.has(p_window), WINDOW_MODE_WINDOWED);
 	const WindowData &wd = windows[p_window];
 	const WindowData &wd = windows[p_window];
 
 
-	if (wd.fullscreen) { //if fullscreen, it's not in another mode
+	if (_window_minimize_check(p_window)) {
+		return WINDOW_MODE_MINIMIZED;
+	}
+
+	if (wd.fullscreen) {
 		if (wd.exclusive_fullscreen) {
 		if (wd.exclusive_fullscreen) {
 			return WINDOW_MODE_EXCLUSIVE_FULLSCREEN;
 			return WINDOW_MODE_EXCLUSIVE_FULLSCREEN;
-		} else {
-			return WINDOW_MODE_FULLSCREEN;
 		}
 		}
+		return WINDOW_MODE_FULLSCREEN;
 	}
 	}
 
 
-	// Test maximized.
-	// Using EWMH -- Extended Window Manager Hints
 	if (_window_maximize_check(p_window, "_NET_WM_STATE")) {
 	if (_window_maximize_check(p_window, "_NET_WM_STATE")) {
 		return WINDOW_MODE_MAXIMIZED;
 		return WINDOW_MODE_MAXIMIZED;
 	}
 	}
 
 
-	{
-		if (_window_minimize_check(p_window)) {
-			return WINDOW_MODE_MINIMIZED;
-		}
-	}
-
-	// All other discarded, return windowed.
-
 	return WINDOW_MODE_WINDOWED;
 	return WINDOW_MODE_WINDOWED;
 }
 }
 
 
@@ -4402,11 +4452,6 @@ void DisplayServerX11::_window_changed(XEvent *event) {
 		return;
 		return;
 	}
 	}
 
 
-	// Query display server about a possible new window state.
-	wd.fullscreen = _window_fullscreen_check(window_id);
-	wd.maximized = _window_maximize_check(window_id, "_NET_WM_STATE") && !wd.fullscreen;
-	wd.minimized = _window_minimize_check(window_id) && !wd.fullscreen && !wd.maximized;
-
 	// Readjusting the window position if the window is being reparented by the window manager for decoration
 	// Readjusting the window position if the window is being reparented by the window manager for decoration
 	Window root, parent, *children;
 	Window root, parent, *children;
 	unsigned int nchildren;
 	unsigned int nchildren;
@@ -5037,7 +5082,7 @@ void DisplayServerX11::process_events() {
 				}
 				}
 
 
 				// Have we failed to set fullscreen while the window was unmapped?
 				// Have we failed to set fullscreen while the window was unmapped?
-				_validate_mode_on_map(window_id);
+				_validate_fullscreen_on_map(window_id);
 
 
 				// On KDE Plasma, when the parent window of an embedded process is restored after being minimized,
 				// On KDE Plasma, when the parent window of an embedded process is restored after being minimized,
 				// only the embedded window receives the Map notification, causing it to
 				// only the embedded window receives the Map notification, causing it to
@@ -5058,24 +5103,6 @@ void DisplayServerX11::process_events() {
 				Main::force_redraw();
 				Main::force_redraw();
 			} break;
 			} break;
 
 
-			case NoExpose: {
-				DEBUG_LOG_X11("[%u] NoExpose drawable=%lu (%u) \n", frame, event.xnoexpose.drawable, window_id);
-				if (ime_window_event) {
-					break;
-				}
-
-				windows[window_id].minimized = true;
-			} break;
-
-			case VisibilityNotify: {
-				DEBUG_LOG_X11("[%u] VisibilityNotify window=%lu (%u), state=%u \n", frame, event.xvisibility.window, window_id, event.xvisibility.state);
-				if (ime_window_event) {
-					break;
-				}
-
-				windows[window_id].minimized = _window_minimize_check(window_id);
-			} break;
-
 			case LeaveNotify: {
 			case LeaveNotify: {
 				DEBUG_LOG_X11("[%u] LeaveNotify window=%lu (%u), mode='%u' \n", frame, event.xcrossing.window, window_id, event.xcrossing.mode);
 				DEBUG_LOG_X11("[%u] LeaveNotify window=%lu (%u), mode='%u' \n", frame, event.xcrossing.window, window_id, event.xcrossing.mode);
 				if (ime_window_event) {
 				if (ime_window_event) {
@@ -5219,6 +5246,12 @@ void DisplayServerX11::process_events() {
 				_window_changed(&event);
 				_window_changed(&event);
 			} break;
 			} break;
 
 
+			case PropertyNotify: {
+				if (event.xproperty.atom == XInternAtom(x11_display, "_NET_WM_STATE", False)) {
+					_update_wm_state_hints(window_id);
+				}
+			} break;
+
 			case ButtonPress:
 			case ButtonPress:
 			case ButtonRelease: {
 			case ButtonRelease: {
 				if (ime_window_event || ignore_events) {
 				if (ime_window_event || ignore_events) {

+ 2 - 1
platform/linuxbsd/x11/display_server_x11.h

@@ -352,9 +352,10 @@ class DisplayServerX11 : public DisplayServer {
 	bool _window_maximize_check(WindowID p_window, const char *p_atom_name) const;
 	bool _window_maximize_check(WindowID p_window, const char *p_atom_name) const;
 	bool _window_fullscreen_check(WindowID p_window) const;
 	bool _window_fullscreen_check(WindowID p_window) const;
 	bool _window_minimize_check(WindowID p_window) const;
 	bool _window_minimize_check(WindowID p_window) const;
-	void _validate_mode_on_map(WindowID p_window);
+	void _validate_fullscreen_on_map(WindowID p_window);
 	void _update_size_hints(WindowID p_window);
 	void _update_size_hints(WindowID p_window);
 	void _update_actions_hints(WindowID p_window);
 	void _update_actions_hints(WindowID p_window);
+	void _update_wm_state_hints(WindowID p_window);
 	void _set_wm_fullscreen(WindowID p_window, bool p_enabled, bool p_exclusive);
 	void _set_wm_fullscreen(WindowID p_window, bool p_enabled, bool p_exclusive);
 	void _set_wm_maximized(WindowID p_window, bool p_enabled);
 	void _set_wm_maximized(WindowID p_window, bool p_enabled);
 	void _set_wm_minimized(WindowID p_window, bool p_enabled);
 	void _set_wm_minimized(WindowID p_window, bool p_enabled);