diff options
Diffstat (limited to 'platform/linuxbsd')
| -rw-r--r-- | platform/linuxbsd/display_server_x11.cpp | 552 | ||||
| -rw-r--r-- | platform/linuxbsd/display_server_x11.h | 6 | ||||
| -rw-r--r-- | platform/linuxbsd/joypad_linux.cpp | 11 | ||||
| -rw-r--r-- | platform/linuxbsd/os_linuxbsd.cpp | 4 | ||||
| -rw-r--r-- | platform/linuxbsd/os_linuxbsd.h | 2 | ||||
| -rw-r--r-- | platform/linuxbsd/platform_config.h | 2 | 
6 files changed, 313 insertions, 264 deletions
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp index 4aec6d256c..fe9e253cc9 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/display_server_x11.cpp @@ -39,10 +39,6 @@  #include "main/main.h"  #include "scene/resources/texture.h" -#if defined(OPENGL_ENABLED) -#include "drivers/gles2/rasterizer_gles2.h" -#endif -  #if defined(VULKAN_ENABLED)  #include "servers/rendering/rasterizer_rd/rasterizer_rd.h"  #endif @@ -87,6 +83,13 @@  #define VALUATOR_TILTX 3  #define VALUATOR_TILTY 4 +//#define DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED +#ifdef DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED +#define DEBUG_LOG_X11(...) printf(__VA_ARGS__) +#else +#define DEBUG_LOG_X11(...) +#endif +  static const double abs_resolution_mult = 10000.0;  static const double abs_resolution_range_mult = 10.0; @@ -701,6 +704,8 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {  	WindowData &wd = windows[p_id]; +	DEBUG_LOG_X11("delete_sub_window: %lu (%u) \n", wd.x11_window, p_id); +  	while (wd.transient_children.size()) {  		window_set_transient(wd.transient_children.front()->get(), INVALID_WINDOW_ID);  	} @@ -737,15 +742,31 @@ ObjectID DisplayServerX11::window_get_attached_instance_id(WindowID p_window) co  }  DisplayServerX11::WindowID DisplayServerX11::get_window_at_screen_position(const Point2i &p_position) const { -#warning This is an incorrect implementation, if windows overlap, it should return the topmost visible one or none if occluded by a foreign window - +	WindowID found_window = INVALID_WINDOW_ID; +	WindowID parent_window = INVALID_WINDOW_ID; +	unsigned int focus_order = 0;  	for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { -		Rect2i win_rect = Rect2i(window_get_position(E->key()), window_get_size(E->key())); +		const WindowData &wd = E->get(); + +		// Discard windows with no focus. +		if (wd.focus_order == 0) { +			continue; +		} + +		// Find topmost window which contains the given position. +		WindowID window_id = E->key(); +		Rect2i win_rect = Rect2i(window_get_position(window_id), window_get_size(window_id));  		if (win_rect.has_point(p_position)) { -			return E->key(); +			// For siblings, pick the window which was focused last. +			if ((parent_window != wd.transient_parent) || (wd.focus_order > focus_order)) { +				found_window = window_id; +				parent_window = wd.transient_parent; +				focus_order = wd.focus_order; +			}  		}  	} -	return INVALID_WINDOW_ID; + +	return found_window;  }  void DisplayServerX11::window_set_title(const String &p_title, WindowID p_window) { @@ -854,24 +875,34 @@ void DisplayServerX11::window_set_transient(WindowID p_window, WindowID p_parent  	ERR_FAIL_COND(!windows.has(p_window));  	WindowData &wd_window = windows[p_window]; -	ERR_FAIL_COND(wd_window.transient_parent == p_parent); +	WindowID prev_parent = wd_window.transient_parent; +	ERR_FAIL_COND(prev_parent == p_parent);  	ERR_FAIL_COND_MSG(wd_window.on_top, "Windows with the 'on top' can't become transient.");  	if (p_parent == INVALID_WINDOW_ID) {  		//remove transient -		ERR_FAIL_COND(wd_window.transient_parent == INVALID_WINDOW_ID); -		ERR_FAIL_COND(!windows.has(wd_window.transient_parent)); +		ERR_FAIL_COND(prev_parent == INVALID_WINDOW_ID); +		ERR_FAIL_COND(!windows.has(prev_parent)); -		WindowData &wd_parent = windows[wd_window.transient_parent]; +		WindowData &wd_parent = windows[prev_parent];  		wd_window.transient_parent = INVALID_WINDOW_ID;  		wd_parent.transient_children.erase(p_window);  		XSetTransientForHint(x11_display, wd_window.x11_window, None); + +		// Set focus to parent sub window to avoid losing all focus with nested menus. +		// RevertToPointerRoot is used to make sure we don't lose all focus in case +		// a subwindow and its parent are both destroyed. +		if (wd_window.menu_type && !wd_window.no_focus) { +			if (!wd_parent.no_focus) { +				XSetInputFocus(x11_display, wd_parent.x11_window, RevertToPointerRoot, CurrentTime); +			} +		}  	} else {  		ERR_FAIL_COND(!windows.has(p_parent)); -		ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent"); +		ERR_FAIL_COND_MSG(prev_parent != INVALID_WINDOW_ID, "Window already has a transient parent");  		WindowData &wd_parent = windows[p_parent];  		wd_window.transient_parent = p_parent; @@ -881,6 +912,46 @@ void DisplayServerX11::window_set_transient(WindowID p_window, WindowID p_parent  	}  } +// Helper method. Assumes that the window id has already been checked and exists. +void DisplayServerX11::_update_size_hints(WindowID p_window) { +	WindowData &wd = windows[p_window]; +	WindowMode window_mode = window_get_mode(p_window); +	XSizeHints *xsh = XAllocSizeHints(); + +	// Always set the position and size hints - they should be synchronized with the actual values after the window is mapped anyway +	xsh->flags |= PPosition | PSize; +	xsh->x = wd.position.x; +	xsh->y = wd.position.y; +	xsh->width = wd.size.width; +	xsh->height = wd.size.height; + +	if (window_mode == WINDOW_MODE_FULLSCREEN) { +		// Do not set any other hints to prevent the window manager from ignoring the fullscreen flags +	} else if (window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) { +		// If resizing is disabled, use the forced size +		xsh->flags |= PMinSize | PMaxSize; +		xsh->min_width = wd.size.x; +		xsh->max_width = wd.size.x; +		xsh->min_height = wd.size.y; +		xsh->max_height = wd.size.y; +	} else { +		// Otherwise, just respect min_size and max_size +		if (wd.min_size != Size2i()) { +			xsh->flags |= PMinSize; +			xsh->min_width = wd.min_size.x; +			xsh->min_height = wd.min_size.y; +		} +		if (wd.max_size != Size2i()) { +			xsh->flags |= PMaxSize; +			xsh->max_width = wd.max_size.x; +			xsh->max_height = wd.max_size.y; +		} +	} + +	XSetWMNormalHints(x11_display, wd.x11_window, xsh); +	XFree(xsh); +} +  Point2i DisplayServerX11::window_get_position(WindowID p_window) const {  	_THREAD_SAFE_METHOD_ @@ -934,25 +1005,8 @@ void DisplayServerX11::window_set_max_size(const Size2i p_size, WindowID p_windo  	}  	wd.max_size = p_size; -	if (!window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) { -		XSizeHints *xsh; -		xsh = XAllocSizeHints(); -		xsh->flags = 0L; -		if (wd.min_size != Size2i()) { -			xsh->flags |= PMinSize; -			xsh->min_width = wd.min_size.x; -			xsh->min_height = wd.min_size.y; -		} -		if (wd.max_size != Size2i()) { -			xsh->flags |= PMaxSize; -			xsh->max_width = wd.max_size.x; -			xsh->max_height = wd.max_size.y; -		} -		XSetWMNormalHints(x11_display, wd.x11_window, xsh); -		XFree(xsh); - -		XFlush(x11_display); -	} +	_update_size_hints(p_window); +	XFlush(x11_display);  }  Size2i DisplayServerX11::window_get_max_size(WindowID p_window) const { @@ -976,25 +1030,8 @@ void DisplayServerX11::window_set_min_size(const Size2i p_size, WindowID p_windo  	}  	wd.min_size = p_size; -	if (!window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) { -		XSizeHints *xsh; -		xsh = XAllocSizeHints(); -		xsh->flags = 0L; -		if (wd.min_size != Size2i()) { -			xsh->flags |= PMinSize; -			xsh->min_width = wd.min_size.x; -			xsh->min_height = wd.min_size.y; -		} -		if (wd.max_size != Size2i()) { -			xsh->flags |= PMaxSize; -			xsh->max_width = wd.max_size.x; -			xsh->max_height = wd.max_size.y; -		} -		XSetWMNormalHints(x11_display, wd.x11_window, xsh); -		XFree(xsh); - -		XFlush(x11_display); -	} +	_update_size_hints(p_window); +	XFlush(x11_display);  }  Size2i DisplayServerX11::window_get_min_size(WindowID p_window) const { @@ -1027,37 +1064,15 @@ void DisplayServerX11::window_set_size(const Size2i p_size, WindowID p_window) {  	int old_w = xwa.width;  	int old_h = xwa.height; -	// If window resizable is disabled we need to update the attributes first -	XSizeHints *xsh; -	xsh = XAllocSizeHints(); -	if (!window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) { -		xsh->flags = PMinSize | PMaxSize; -		xsh->min_width = size.x; -		xsh->max_width = size.x; -		xsh->min_height = size.y; -		xsh->max_height = size.y; -	} else { -		xsh->flags = 0L; -		if (wd.min_size != Size2i()) { -			xsh->flags |= PMinSize; -			xsh->min_width = wd.min_size.x; -			xsh->min_height = wd.min_size.y; -		} -		if (wd.max_size != Size2i()) { -			xsh->flags |= PMaxSize; -			xsh->max_width = wd.max_size.x; -			xsh->max_height = wd.max_size.y; -		} -	} -	XSetWMNormalHints(x11_display, wd.x11_window, xsh); -	XFree(xsh); +	// Update our videomode width and height +	wd.size = size; + +	// Update the size hints first to make sure the window size can be set +	_update_size_hints(p_window);  	// Resize the window  	XResizeWindow(x11_display, wd.x11_window, size.x, size.y); -	// Update our videomode width and height -	wd.size = size; -  	for (int timeout = 0; timeout < 50; ++timeout) {  		XSync(x11_display, False);  		XGetWindowAttributes(x11_display, wd.x11_window, &xwa); @@ -1213,14 +1228,9 @@ void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) {  		XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);  	} -	if (p_enabled && window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) { +	if (p_enabled) {  		// Set the window as resizable to prevent window managers to ignore the fullscreen state flag. -		XSizeHints *xsh; - -		xsh = XAllocSizeHints(); -		xsh->flags = 0L; -		XSetWMNormalHints(x11_display, wd.x11_window, xsh); -		XFree(xsh); +		_update_size_hints(p_window);  	}  	// Using EWMH -- Extended Window Manager Hints @@ -1248,30 +1258,7 @@ void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) {  	if (!p_enabled) {  		// Reset the non-resizable flags if we un-set these before. -		Size2i size = window_get_size(p_window); -		XSizeHints *xsh; -		xsh = XAllocSizeHints(); -		if (window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) { -			xsh->flags = PMinSize | PMaxSize; -			xsh->min_width = size.x; -			xsh->max_width = size.x; -			xsh->min_height = size.y; -			xsh->max_height = size.y; -		} else { -			xsh->flags = 0L; -			if (wd.min_size != Size2i()) { -				xsh->flags |= PMinSize; -				xsh->min_width = wd.min_size.x; -				xsh->min_height = wd.min_size.y; -			} -			if (wd.max_size != Size2i()) { -				xsh->flags |= PMaxSize; -				xsh->max_width = wd.max_size.x; -				xsh->max_height = wd.max_size.y; -			} -		} -		XSetWMNormalHints(x11_display, wd.x11_window, xsh); -		XFree(xsh); +		_update_size_hints(p_window);  		// put back or remove decorations according to the last set borderless state  		Hints hints; @@ -1329,13 +1316,13 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) {  		} break;  		case WINDOW_MODE_FULLSCREEN: {  			//Remove full-screen +			wd.fullscreen = false; +  			_set_wm_fullscreen(p_window, false);  			//un-maximize required for always on top  			bool on_top = window_get_flag(WINDOW_FLAG_ALWAYS_ON_TOP, p_window); -			wd.fullscreen = false; -  			window_set_position(wd.last_position_before_fs, p_window);  			if (on_top) { @@ -1381,15 +1368,16 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) {  		} break;  		case WINDOW_MODE_FULLSCREEN: {  			wd.last_position_before_fs = wd.position; +  			if (window_get_flag(WINDOW_FLAG_ALWAYS_ON_TOP, p_window)) {  				_set_wm_maximized(p_window, true);  			} -			_set_wm_fullscreen(p_window, true); +  			wd.fullscreen = true; +			_set_wm_fullscreen(p_window, true);  		} break;  		case WINDOW_MODE_MAXIMIZED: {  			_set_wm_maximized(p_window, true); -  		} break;  	}  } @@ -1456,37 +1444,11 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo  	switch (p_flag) {  		case WINDOW_FLAG_RESIZE_DISABLED: { -			XSizeHints *xsh; -			xsh = XAllocSizeHints(); -			if (p_enabled) { -				Size2i size = window_get_size(p_window); - -				xsh->flags = PMinSize | PMaxSize; -				xsh->min_width = size.x; -				xsh->max_width = size.x; -				xsh->min_height = size.y; -				xsh->max_height = size.y; -			} else { -				xsh->flags = 0L; -				if (wd.min_size != Size2i()) { -					xsh->flags |= PMinSize; -					xsh->min_width = wd.min_size.x; -					xsh->min_height = wd.min_size.y; -				} -				if (wd.max_size != Size2i()) { -					xsh->flags |= PMaxSize; -					xsh->max_width = wd.max_size.x; -					xsh->max_height = wd.max_size.y; -				} -			} - -			XSetWMNormalHints(x11_display, wd.x11_window, xsh); -			XFree(xsh); -  			wd.resize_disabled = p_enabled; -			XFlush(x11_display); +			_update_size_hints(p_window); +			XFlush(x11_display);  		} break;  		case WINDOW_FLAG_BORDERLESS: {  			Hints hints; @@ -2366,6 +2328,11 @@ void DisplayServerX11::_send_window_event(const WindowData &wd, WindowEvent p_ev  void DisplayServerX11::process_events() {  	_THREAD_SAFE_METHOD_ +#ifdef DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED +	static int frame = 0; +	++frame; +#endif +  	if (app_focused) {  		//verify that one of the windows has focus, else send focus out notification  		bool focus_found = false; @@ -2382,6 +2349,7 @@ void DisplayServerX11::process_events() {  			if (delta > 250) {  				//X11 can go between windows and have no focus for a while, when creating them or something else. Use this as safety to avoid unnecessary focus in/outs.  				if (OS::get_singleton()->get_main_loop()) { +					DEBUG_LOG_X11("All focus lost, triggering NOTIFICATION_APPLICATION_FOCUS_OUT\n");  					OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);  				}  				app_focused = false; @@ -2404,6 +2372,10 @@ void DisplayServerX11::process_events() {  		XEvent event;  		XNextEvent(x11_display, &event); +		if (XFilterEvent(&event, None)) { +			continue; +		} +  		WindowID window_id = MAIN_WINDOW_ID;  		// Assign the event to the relevant window @@ -2414,10 +2386,6 @@ void DisplayServerX11::process_events() {  			}  		} -		if (XFilterEvent(&event, None)) { -			continue; -		} -  		if (XGetEventData(x11_display, &event.xcookie)) {  			if (event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {  				XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data; @@ -2580,32 +2548,67 @@ void DisplayServerX11::process_events() {  		XFreeEventData(x11_display, &event.xcookie);  		switch (event.type) { -			case Expose: +			case MapNotify: { +				DEBUG_LOG_X11("[%u] MapNotify window=%lu (%u) \n", frame, event.xmap.window, window_id); + +				const WindowData &wd = windows[window_id]; + +				// Set focus when menu window is started. +				// RevertToPointerRoot is used to make sure we don't lose all focus in case +				// a subwindow and its parent are both destroyed. +				if (wd.menu_type && !wd.no_focus) { +					XSetInputFocus(x11_display, wd.x11_window, RevertToPointerRoot, CurrentTime); +				} +			} break; + +			case Expose: { +				DEBUG_LOG_X11("[%u] Expose window=%lu (%u), count='%u' \n", frame, event.xexpose.window, window_id, event.xexpose.count); +  				Main::force_redraw(); -				break; +			} break; + +			case NoExpose: { +				DEBUG_LOG_X11("[%u] NoExpose drawable=%lu (%u) \n", frame, event.xnoexpose.drawable, window_id); -			case NoExpose:  				windows[window_id].minimized = true; -				break; +			} break;  			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);  			} break; +  			case LeaveNotify: { +				DEBUG_LOG_X11("[%u] LeaveNotify window=%lu (%u), mode='%u' \n", frame, event.xcrossing.window, window_id, event.xcrossing.mode); +  				if (!mouse_mode_grab) {  					_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);  				}  			} break; +  			case EnterNotify: { +				DEBUG_LOG_X11("[%u] EnterNotify window=%lu (%u), mode='%u' \n", frame, event.xcrossing.window, window_id, event.xcrossing.mode); +  				if (!mouse_mode_grab) {  					_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);  				}  			} break; -			case FocusIn: -				windows[window_id].focused = true; -				_send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_IN); + +			case FocusIn: { +				DEBUG_LOG_X11("[%u] FocusIn window=%lu (%u), mode='%u' \n", frame, event.xfocus.window, window_id, event.xfocus.mode); + +				WindowData &wd = windows[window_id]; + +				wd.focused = true; + +				// Keep track of focus order for overlapping windows. +				static unsigned int focus_order = 0; +				wd.focus_order = ++focus_order; + +				_send_window_event(wd, WINDOW_EVENT_FOCUS_IN);  				if (mouse_mode_grab) {  					// Show and update the cursor if confined and the window regained focus. @@ -2629,8 +2632,8 @@ void DisplayServerX11::process_events() {  					XIGrabDevice(x11_display, xi.touch_devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &xi.touch_event_mask);  				}*/  #endif -				if (windows[window_id].xic) { -					XSetICFocus(windows[window_id].xic); +				if (wd.xic) { +					XSetICFocus(wd.xic);  				}  				if (!app_focused) { @@ -2639,12 +2642,17 @@ void DisplayServerX11::process_events() {  					}  					app_focused = true;  				} -				break; +			} break; + +			case FocusOut: { +				DEBUG_LOG_X11("[%u] FocusOut window=%lu (%u), mode='%u' \n", frame, event.xfocus.window, window_id, event.xfocus.mode); + +				WindowData &wd = windows[window_id]; + +				wd.focused = false; -			case FocusOut: -				windows[window_id].focused = false;  				Input::get_singleton()->release_pressed_events(); -				_send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_OUT); +				_send_window_event(wd, WINDOW_EVENT_FOCUS_OUT);  				if (mouse_mode_grab) {  					for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { @@ -2673,14 +2681,26 @@ void DisplayServerX11::process_events() {  				}  				xi.state.clear();  #endif -				if (windows[window_id].xic) { -					XSetICFocus(windows[window_id].xic); +				if (wd.xic) { +					XSetICFocus(wd.xic); +				} +			} break; + +			case ConfigureNotify: { +				DEBUG_LOG_X11("[%u] ConfigureNotify window=%lu (%u), event=%lu, above=%lu, override_redirect=%u \n", frame, event.xconfigure.window, window_id, event.xconfigure.event, event.xconfigure.above, event.xconfigure.override_redirect); + +				const WindowData &wd = windows[window_id]; + +				// Set focus when menu window is re-used. +				// RevertToPointerRoot is used to make sure we don't lose all focus in case +				// a subwindow and its parent are both destroyed. +				if (wd.menu_type && !wd.no_focus) { +					XSetInputFocus(x11_display, wd.x11_window, RevertToPointerRoot, CurrentTime);  				} -				break; -			case ConfigureNotify:  				_window_changed(&event); -				break; +			} break; +  			case ButtonPress:  			case ButtonRelease: {  				/* exit in case of a mouse button press */ @@ -2707,7 +2727,18 @@ void DisplayServerX11::process_events() {  				mb->set_pressed((event.type == ButtonPress)); +				const WindowData &wd = windows[window_id]; +  				if (event.type == ButtonPress) { +					DEBUG_LOG_X11("[%u] ButtonPress window=%lu (%u), button_index=%u \n", frame, event.xbutton.window, window_id, mb->get_button_index()); + +					// Ensure window focus on click. +					// RevertToPointerRoot is used to make sure we don't lose all focus in case +					// a subwindow and its parent are both destroyed. +					if (!wd.no_focus) { +						XSetInputFocus(x11_display, wd.x11_window, RevertToPointerRoot, CurrentTime); +					} +  					uint64_t diff = OS::get_singleton()->get_ticks_usec() / 1000 - last_click_ms;  					if (mb->get_button_index() == last_click_button_index) { @@ -2726,6 +2757,33 @@ void DisplayServerX11::process_events() {  						last_click_ms += diff;  						last_click_pos = Point2i(event.xbutton.x, event.xbutton.y);  					} +				} else { +					DEBUG_LOG_X11("[%u] ButtonRelease window=%lu (%u), button_index=%u \n", frame, event.xbutton.window, window_id, mb->get_button_index()); + +					if (!wd.focused) { +						// Propagate the event to the focused window, +						// because it's received only on the topmost window. +						// Note: This is needed for drag & drop to work between windows, +						// because the engine expects events to keep being processed +						// on the same window dragging started. +						for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { +							const WindowData &wd_other = E->get(); +							WindowID window_id_other = E->key(); +							if (wd_other.focused) { +								if (window_id_other != window_id) { +									int x, y; +									Window child; +									XTranslateCoordinates(x11_display, wd.x11_window, wd_other.x11_window, event.xbutton.x, event.xbutton.y, &x, &y, &child); + +									mb->set_window_id(window_id_other); +									mb->set_position(Vector2(x, y)); +									mb->set_global_position(mb->get_position()); +									Input::get_singleton()->accumulate_input_event(mb); +								} +								break; +							} +						} +					}  				}  				Input::get_singleton()->accumulate_input_event(mb); @@ -2775,6 +2833,9 @@ void DisplayServerX11::process_events() {  					break;  				} +				const WindowData &wd = windows[window_id]; +				bool focused = wd.focused; +  				if (mouse_mode == MOUSE_MODE_CAPTURED) {  					if (xi.relative_motion.x == 0 && xi.relative_motion.y == 0) {  						break; @@ -2783,7 +2844,7 @@ void DisplayServerX11::process_events() {  					Point2i new_center = pos;  					pos = last_mouse_pos + xi.relative_motion;  					center = new_center; -					do_mouse_warp = windows[window_id].focused; // warp the cursor if we're focused in +					do_mouse_warp = focused; // warp the cursor if we're focused in  				}  				if (!last_mouse_pos_valid) { @@ -2825,14 +2886,11 @@ void DisplayServerX11::process_events() {  				}  				mm->set_tilt(xi.tilt); -				// Make the absolute position integral so it doesn't look _too_ weird :) -				Point2i posi(pos); -  				_get_key_modifier_state(event.xmotion.state, mm);  				mm->set_button_mask(mouse_get_button_state()); -				mm->set_position(posi); -				mm->set_global_position(posi); -				Input::get_singleton()->set_mouse_position(posi); +				mm->set_position(pos); +				mm->set_global_position(pos); +				Input::get_singleton()->set_mouse_position(pos);  				mm->set_speed(Input::get_singleton()->get_last_mouse_speed());  				mm->set_relative(rel); @@ -2843,8 +2901,32 @@ void DisplayServerX11::process_events() {  				// Don't propagate the motion event unless we have focus  				// this is so that the relative motion doesn't get messed up  				// after we regain focus. -				if (windows[window_id].focused || !mouse_mode_grab) { +				if (focused) {  					Input::get_singleton()->accumulate_input_event(mm); +				} else { +					// Propagate the event to the focused window, +					// because it's received only on the topmost window. +					// Note: This is needed for drag & drop to work between windows, +					// because the engine expects events to keep being processed +					// on the same window dragging started. +					for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { +						const WindowData &wd_other = E->get(); +						if (wd_other.focused) { +							int x, y; +							Window child; +							XTranslateCoordinates(x11_display, wd.x11_window, wd_other.x11_window, event.xmotion.x, event.xmotion.y, &x, &y, &child); + +							Point2i pos_focused(x, y); + +							mm->set_window_id(E->key()); +							mm->set_position(pos_focused); +							mm->set_global_position(pos_focused); +							mm->set_speed(Input::get_singleton()->get_last_mouse_speed()); +							Input::get_singleton()->accumulate_input_event(mm); + +							break; +						} +					}  				}  			} break; @@ -3221,11 +3303,38 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u  	unsigned long valuemask = CWBorderPixel | CWColormap | CWEventMask; -	WindowID id; +	WindowID id = window_id_counter++; +	WindowData &wd = windows[id]; + +	if ((id != MAIN_WINDOW_ID) && (p_flags & WINDOW_FLAG_BORDERLESS_BIT)) { +		wd.menu_type = true; +	} + +	if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) { +		wd.menu_type = true; +		wd.no_focus = true; +	} + +	// Setup for menu subwindows: +	// - override_redirect forces the WM not to interfere with the window, to avoid delays due to +	//   handling decorations and placement. +	//   On the other hand, focus changes need to be handled manually when this is set. +	// - save_under is a hint for the WM to keep the content of windows behind to avoid repaint. +	if (wd.menu_type) { +		windowAttributes.override_redirect = True; +		windowAttributes.save_under = True; +		valuemask |= CWOverrideRedirect | CWSaveUnder; +	} +  	{ -		WindowData wd;  		wd.x11_window = XCreateWindow(x11_display, RootWindow(x11_display, visualInfo->screen), p_rect.position.x, p_rect.position.y, p_rect.size.width > 0 ? p_rect.size.width : 1, p_rect.size.height > 0 ? p_rect.size.height : 1, 0, visualInfo->depth, InputOutput, visualInfo->visual, valuemask, &windowAttributes); +		// Enable receiving notification when the window is initialized (MapNotify) +		// so the focus can be set at the right time. +		if (wd.menu_type && !wd.no_focus) { +			XSelectInput(x11_display, wd.x11_window, StructureNotifyMask); +		} +  		//associate PID  		// make PID known to X11  		{ @@ -3300,87 +3409,30 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u  		_update_context(wd); -		id = window_id_counter++; - -		windows[id] = wd; - -		{ -			if (p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT) { -				XSizeHints *xsh; -				xsh = XAllocSizeHints(); - -				xsh->flags = PMinSize | PMaxSize; -				xsh->min_width = p_rect.size.width; -				xsh->max_width = p_rect.size.width; -				xsh->min_height = p_rect.size.height; -				xsh->max_height = p_rect.size.height; - -				XSetWMNormalHints(x11_display, wd.x11_window, xsh); -				XFree(xsh); -			} - -			bool make_utility = false; - -			if (p_flags & WINDOW_FLAG_BORDERLESS_BIT) { -				Hints hints; -				Atom property; -				hints.flags = 2; -				hints.decorations = 0; -				property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); -				XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); - -				make_utility = true; -			} -			if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) { -				make_utility = true; -			} - -			if (make_utility) { -				//this one seems to disable the fade animations for regular windows -				//but has the drawback that will not get focus by default, so -				//we need to force it, unless no focus requested - -				Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_UTILITY", False); -				Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False); - -				XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1); - -				if (!(p_flags & WINDOW_FLAG_NO_FOCUS_BIT)) { -					//but as utility appears unfocused, it needs to be forcefuly focused, unless no focus requested -					XEvent xev; -					Atom net_active_window = XInternAtom(x11_display, "_NET_ACTIVE_WINDOW", False); - -					memset(&xev, 0, sizeof(xev)); -					xev.type = ClientMessage; -					xev.xclient.window = wd.x11_window; -					xev.xclient.message_type = net_active_window; -					xev.xclient.format = 32; -					xev.xclient.data.l[0] = 1; -					xev.xclient.data.l[1] = CurrentTime; - -					XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); -				} -			} else { -				Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_NORMAL", False); -				Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False); - -				XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1); -			} +		if (p_flags & WINDOW_FLAG_BORDERLESS_BIT) { +			Hints hints; +			Atom property; +			hints.flags = 2; +			hints.decorations = 0; +			property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); +			XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);  		} -		if (id != MAIN_WINDOW_ID) { -			XSizeHints my_hints = XSizeHints(); +		if (wd.menu_type) { +			// Set Utility type to disable fade animations. +			Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_UTILITY", False); +			Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False); -			my_hints.flags = PPosition | PSize; /* I want to specify position and size */ -			my_hints.x = p_rect.position.x; /* The origin and size coords I want */ -			my_hints.y = p_rect.position.y; -			my_hints.width = p_rect.size.width; -			my_hints.height = p_rect.size.height; +			XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1); +		} else { +			Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_NORMAL", False); +			Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False); -			XSetNormalHints(x11_display, wd.x11_window, &my_hints); -			XMoveWindow(x11_display, wd.x11_window, p_rect.position.x, p_rect.position.y); +			XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);  		} +		_update_size_hints(id); +  #if defined(VULKAN_ENABLED)  		if (context_vulkan) {  			Error err = context_vulkan->window_create(id, wd.x11_window, x11_display, p_rect.size.width, p_rect.size.height); @@ -3397,8 +3449,6 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u  		XFree(visualInfo);  	} -	WindowData &wd = windows[id]; -  	window_set_mode(p_mode, id);  	//sync size diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h index 0ba1359145..57cee910a0 100644 --- a/platform/linuxbsd/display_server_x11.h +++ b/platform/linuxbsd/display_server_x11.h @@ -132,6 +132,9 @@ class DisplayServerX11 : public DisplayServer {  		ObjectID instance_id; +		bool menu_type = false; +		bool no_focus = false; +  		//better to guess on the fly, given WM can change it  		//WindowMode mode;  		bool fullscreen = false; //OS can't exit from this mode @@ -141,6 +144,8 @@ class DisplayServerX11 : public DisplayServer {  		Vector2i last_position_before_fs;  		bool focused = false;  		bool minimized = false; + +		unsigned int focus_order = 0;  	};  	Map<WindowID, WindowData> windows; @@ -235,6 +240,7 @@ 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; +	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); diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp index 5edaf35c50..fda1358dfd 100644 --- a/platform/linuxbsd/joypad_linux.cpp +++ b/platform/linuxbsd/joypad_linux.cpp @@ -311,16 +311,9 @@ void JoypadLinux::open_joypad(const char *p_path) {  			return;  		} -		//check if the device supports basic gamepad events, prevents certain keyboards from -		//being detected as joypads +		// Check if the device supports basic gamepad events  		if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) && -					(test_bit(ABS_X, absbit) || test_bit(ABS_Y, absbit) || test_bit(ABS_HAT0X, absbit) || -							test_bit(ABS_GAS, absbit) || test_bit(ABS_RUDDER, absbit)) && -					(test_bit(BTN_A, keybit) || test_bit(BTN_THUMBL, keybit) || -							test_bit(BTN_TRIGGER, keybit) || test_bit(BTN_1, keybit))) && -				!(test_bit(EV_ABS, evbit) && -						test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit) && -						test_bit(ABS_RX, absbit) && test_bit(ABS_RY, absbit))) { +					test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit))) {  			close(fd);  			return;  		} diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp index 8c6f3b1167..e00a32e3ba 100644 --- a/platform/linuxbsd/os_linuxbsd.cpp +++ b/platform/linuxbsd/os_linuxbsd.cpp @@ -88,7 +88,9 @@ void OS_LinuxBSD::finalize() {  #endif  #ifdef JOYDEV_ENABLED -	memdelete(joypad); +	if (joypad) { +		memdelete(joypad); +	}  #endif  } diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h index 4295721c68..cd4fbd9db5 100644 --- a/platform/linuxbsd/os_linuxbsd.h +++ b/platform/linuxbsd/os_linuxbsd.h @@ -48,7 +48,7 @@ class OS_LinuxBSD : public OS_Unix {  	bool force_quit;  #ifdef JOYDEV_ENABLED -	JoypadLinux *joypad; +	JoypadLinux *joypad = nullptr;  #endif  #ifdef ALSA_ENABLED diff --git a/platform/linuxbsd/platform_config.h b/platform/linuxbsd/platform_config.h index ac30519132..764666681f 100644 --- a/platform/linuxbsd/platform_config.h +++ b/platform/linuxbsd/platform_config.h @@ -35,5 +35,3 @@  #include <stdlib.h>  #define PTHREAD_BSD_SET_NAME  #endif - -#define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h"  |