Browse Source

Fix minimize/maximize not taking effect in X11.

Attempts to construct an X11 window in an initial state of
minimized/maximized would fail due to the window being unmapped.
We simply check for failed mode changes during an unmap and reapply
them if necessary.
MatthewZelriche 3 years ago
parent
commit
87937bcf87
2 changed files with 92 additions and 94 deletions
  1. 88 93
      platform/linuxbsd/display_server_x11.cpp
  2. 4 1
      platform/linuxbsd/display_server_x11.h

+ 88 - 93
platform/linuxbsd/display_server_x11.cpp

@@ -1823,6 +1823,47 @@ bool DisplayServerX11::_window_maximize_check(WindowID p_window, const char *p_a
 	return retval;
 }
 
+bool DisplayServerX11::_window_minimize_check(WindowID p_window) const {
+	const WindowData &wd = windows[p_window];
+
+	// Using ICCCM -- Inter-Client Communication Conventions Manual
+	Atom property = XInternAtom(x11_display, "WM_STATE", True);
+	if (property == None) {
+		return false;
+	}
+
+	Atom type;
+	int format;
+	unsigned long len;
+	unsigned long remaining;
+	unsigned char *data = nullptr;
+
+	int result = XGetWindowProperty(
+			x11_display,
+			wd.x11_window,
+			property,
+			0,
+			32,
+			False,
+			AnyPropertyType,
+			&type,
+			&format,
+			&len,
+			&remaining,
+			&data);
+
+	if (result == Success && data) {
+		long *state = (long *)data;
+		if (state[0] == WM_IconicState) {
+			XFree(data);
+			return true;
+		}
+		XFree(data);
+	}
+
+	return false;
+}
+
 bool DisplayServerX11::_window_fullscreen_check(WindowID p_window) const {
 	ERR_FAIL_COND_V(!windows.has(p_window), false);
 	const WindowData &wd = windows[p_window];
@@ -1869,11 +1910,15 @@ bool DisplayServerX11::_window_fullscreen_check(WindowID p_window) const {
 	return retval;
 }
 
-void DisplayServerX11::_validate_fullscreen_on_map(WindowID p_window) {
+void DisplayServerX11::_validate_mode_on_map(WindowID p_window) {
+	// Check if we applied any window modes that didn't take effect while unmapped
 	const WindowData &wd = windows[p_window];
 	if (wd.fullscreen && !_window_fullscreen_check(p_window)) {
-		// Unmapped fullscreen attempt didn't take effect, redo...
 		_set_wm_fullscreen(p_window, true);
+	} 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);
 	}
 }
 
@@ -1911,6 +1956,37 @@ void DisplayServerX11::_set_wm_maximized(WindowID p_window, bool p_enabled) {
 			usleep(10000);
 		}
 	}
+	wd.maximized = p_enabled;
+}
+
+void DisplayServerX11::_set_wm_minimized(WindowID p_window, bool p_enabled) {
+	WindowData &wd = windows[p_window];
+	// Using ICCCM -- Inter-Client Communication Conventions Manual
+	XEvent xev;
+	Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False);
+
+	memset(&xev, 0, sizeof(xev));
+	xev.type = ClientMessage;
+	xev.xclient.window = wd.x11_window;
+	xev.xclient.message_type = wm_change;
+	xev.xclient.format = 32;
+	xev.xclient.data.l[0] = p_enabled ? WM_IconicState : WM_NormalState;
+
+	XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+
+	Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
+	Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False);
+
+	memset(&xev, 0, sizeof(xev));
+	xev.type = ClientMessage;
+	xev.xclient.window = wd.x11_window;
+	xev.xclient.message_type = wm_state;
+	xev.xclient.format = 32;
+	xev.xclient.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
+	xev.xclient.data.l[1] = wm_hidden;
+
+	XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+	wd.minimized = p_enabled;
 }
 
 void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) {
@@ -1992,32 +2068,7 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) {
 			//do nothing
 		} break;
 		case WINDOW_MODE_MINIMIZED: {
-			//Un-Minimize
-			// Using ICCCM -- Inter-Client Communication Conventions Manual
-			XEvent xev;
-			Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False);
-
-			memset(&xev, 0, sizeof(xev));
-			xev.type = ClientMessage;
-			xev.xclient.window = wd.x11_window;
-			xev.xclient.message_type = wm_change;
-			xev.xclient.format = 32;
-			xev.xclient.data.l[0] = WM_NormalState;
-
-			XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
-
-			Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
-			Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False);
-
-			memset(&xev, 0, sizeof(xev));
-			xev.type = ClientMessage;
-			xev.xclient.window = wd.x11_window;
-			xev.xclient.message_type = wm_state;
-			xev.xclient.format = 32;
-			xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
-			xev.xclient.data.l[1] = wm_hidden;
-
-			XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+			_set_wm_minimized(p_window, false);
 		} break;
 		case WINDOW_MODE_EXCLUSIVE_FULLSCREEN:
 		case WINDOW_MODE_FULLSCREEN: {
@@ -2046,31 +2097,7 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) {
 			//do nothing
 		} break;
 		case WINDOW_MODE_MINIMIZED: {
-			// Using ICCCM -- Inter-Client Communication Conventions Manual
-			XEvent xev;
-			Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False);
-
-			memset(&xev, 0, sizeof(xev));
-			xev.type = ClientMessage;
-			xev.xclient.window = wd.x11_window;
-			xev.xclient.message_type = wm_change;
-			xev.xclient.format = 32;
-			xev.xclient.data.l[0] = WM_IconicState;
-
-			XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
-
-			Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
-			Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False);
-
-			memset(&xev, 0, sizeof(xev));
-			xev.type = ClientMessage;
-			xev.xclient.window = wd.x11_window;
-			xev.xclient.message_type = wm_state;
-			xev.xclient.format = 32;
-			xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
-			xev.xclient.data.l[1] = wm_hidden;
-
-			XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+			_set_wm_minimized(p_window, true);
 		} break;
 		case WINDOW_MODE_EXCLUSIVE_FULLSCREEN:
 		case WINDOW_MODE_FULLSCREEN: {
@@ -2105,40 +2132,9 @@ DisplayServer::WindowMode DisplayServerX11::window_get_mode(WindowID p_window) c
 		return WINDOW_MODE_MAXIMIZED;
 	}
 
-	{ // Test minimized.
-		// Using ICCCM -- Inter-Client Communication Conventions Manual
-		Atom property = XInternAtom(x11_display, "WM_STATE", True);
-		if (property == None) {
-			return WINDOW_MODE_WINDOWED;
-		}
-
-		Atom type;
-		int format;
-		unsigned long len;
-		unsigned long remaining;
-		unsigned char *data = nullptr;
-
-		int result = XGetWindowProperty(
-				x11_display,
-				wd.x11_window,
-				property,
-				0,
-				32,
-				False,
-				AnyPropertyType,
-				&type,
-				&format,
-				&len,
-				&remaining,
-				&data);
-
-		if (result == Success && data) {
-			long *state = (long *)data;
-			if (state[0] == WM_IconicState) {
-				XFree(data);
-				return WINDOW_MODE_MINIMIZED;
-			}
-			XFree(data);
+	{
+		if (_window_minimize_check(p_window)) {
+			return WINDOW_MODE_MINIMIZED;
 		}
 	}
 
@@ -3658,9 +3654,6 @@ void DisplayServerX11::process_events() {
 
 				const WindowData &wd = windows[window_id];
 
-				// Have we failed to set fullscreen while the window was unmapped?
-				_validate_fullscreen_on_map(window_id);
-
 				XWindowAttributes xwa;
 				XSync(x11_display, False);
 				XGetWindowAttributes(x11_display, wd.x11_window, &xwa);
@@ -3671,6 +3664,9 @@ void DisplayServerX11::process_events() {
 				if ((xwa.map_state == IsViewable) && !wd.no_focus && !wd.is_popup) {
 					XSetInputFocus(x11_display, wd.x11_window, RevertToPointerRoot, CurrentTime);
 				}
+
+				// Have we failed to set fullscreen while the window was unmapped?
+				_validate_mode_on_map(window_id);
 			} break;
 
 			case Expose: {
@@ -3690,8 +3686,7 @@ void DisplayServerX11::process_events() {
 			case VisibilityNotify: {
 				DEBUG_LOG_X11("[%u] VisibilityNotify window=%lu (%u), state=%u \n", frame, event.xvisibility.window, window_id, event.xvisibility.state);
 
-				XVisibilityEvent *visibility = (XVisibilityEvent *)&event;
-				windows[window_id].minimized = (visibility->state == VisibilityFullyObscured);
+				windows[window_id].minimized = _window_minimize_check(window_id);
 			} break;
 
 			case LeaveNotify: {
@@ -5003,7 +4998,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
 			_window_changed(&xevent);
 		} else if (xevent.type == MapNotify) {
 			// Have we failed to set fullscreen while the window was unmapped?
-			_validate_fullscreen_on_map(main_window);
+			_validate_mode_on_map(main_window);
 		}
 	}
 

+ 4 - 1
platform/linuxbsd/display_server_x11.h

@@ -152,6 +152,7 @@ class DisplayServerX11 : public DisplayServer {
 		Vector2i last_position_before_fs;
 		bool focused = true;
 		bool minimized = false;
+		bool maximized = false;
 		bool is_popup = false;
 
 		Rect2i parent_safe_rect;
@@ -268,10 +269,12 @@ class DisplayServerX11 : public DisplayServer {
 	void _update_real_mouse_position(const WindowData &wd);
 	bool _window_maximize_check(WindowID p_window, const char *p_atom_name) const;
 	bool _window_fullscreen_check(WindowID p_window) const;
-	void _validate_fullscreen_on_map(WindowID p_window);
+	bool _window_minimize_check(WindowID p_window) const;
+	void _validate_mode_on_map(WindowID p_window);
 	void _update_size_hints(WindowID p_window);
 	void _set_wm_fullscreen(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 _update_context(WindowData &wd);