1 /*************************************************************************/
2 /*  os_x11.cpp                                                           */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
10 /*                                                                       */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the       */
13 /* "Software"), to deal in the Software without restriction, including   */
14 /* without limitation the rights to use, copy, modify, merge, publish,   */
15 /* distribute, sublicense, and/or sell copies of the Software, and to    */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions:                                             */
18 /*                                                                       */
19 /* The above copyright notice and this permission notice shall be        */
20 /* included in all copies or substantial portions of the Software.       */
21 /*                                                                       */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
29 /*************************************************************************/
30 
31 #include "os_x11.h"
32 
33 #include "core/os/dir_access.h"
34 #include "core/print_string.h"
35 #include "detect_prime.h"
36 #include "drivers/gles2/rasterizer_gles2.h"
37 #include "drivers/gles3/rasterizer_gles3.h"
38 #include "key_mapping_x11.h"
39 #include "main/main.h"
40 #include "servers/visual/visual_server_raster.h"
41 #include "servers/visual/visual_server_wrap_mt.h"
42 
43 #ifdef HAVE_MNTENT
44 #include <mntent.h>
45 #endif
46 
47 #include <errno.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 
52 #include <X11/Xatom.h>
53 #include <X11/Xutil.h>
54 #include <X11/extensions/Xinerama.h>
55 
56 // ICCCM
57 #define WM_NormalState 1L // window normal state
58 #define WM_IconicState 3L // window minimized
59 // EWMH
60 #define _NET_WM_STATE_REMOVE 0L // remove/unset property
61 #define _NET_WM_STATE_ADD 1L // add/set property
62 #define _NET_WM_STATE_TOGGLE 2L // toggle property
63 
64 #include <dlfcn.h>
65 #include <fcntl.h>
66 #include <sys/stat.h>
67 #include <sys/types.h>
68 #include <unistd.h>
69 
70 //stupid linux.h
71 #ifdef KEY_TAB
72 #undef KEY_TAB
73 #endif
74 
75 #undef CursorShape
76 
77 #include <X11/XKBlib.h>
78 
79 // 2.2 is the first release with multitouch
80 #define XINPUT_CLIENT_VERSION_MAJOR 2
81 #define XINPUT_CLIENT_VERSION_MINOR 2
82 
83 #define VALUATOR_ABSX 0
84 #define VALUATOR_ABSY 1
85 #define VALUATOR_PRESSURE 2
86 #define VALUATOR_TILTX 3
87 #define VALUATOR_TILTY 4
88 
89 static const double abs_resolution_mult = 10000.0;
90 static const double abs_resolution_range_mult = 10.0;
91 
initialize_core()92 void OS_X11::initialize_core() {
93 
94 	crash_handler.initialize();
95 
96 	OS_Unix::initialize_core();
97 }
98 
get_current_video_driver() const99 int OS_X11::get_current_video_driver() const {
100 	return video_driver_index;
101 }
102 
initialize(const VideoMode & p_desired,int p_video_driver,int p_audio_driver)103 Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
104 
105 	long im_event_mask = 0;
106 	last_button_state = 0;
107 
108 	xmbstring = NULL;
109 	x11_window = 0;
110 	last_click_ms = 0;
111 	last_click_button_index = -1;
112 	last_click_pos = Point2(-100, -100);
113 	args = OS::get_singleton()->get_cmdline_args();
114 	current_videomode = p_desired;
115 	main_loop = NULL;
116 	last_timestamp = 0;
117 	last_mouse_pos_valid = false;
118 	last_keyrelease_time = 0;
119 	xdnd_version = 0;
120 
121 	if (get_render_thread_mode() == RENDER_SEPARATE_THREAD) {
122 		XInitThreads();
123 	}
124 
125 	/** XLIB INITIALIZATION **/
126 	x11_display = XOpenDisplay(NULL);
127 
128 	if (!x11_display) {
129 		ERR_PRINT("X11 Display is not available");
130 		return ERR_UNAVAILABLE;
131 	}
132 
133 	char *modifiers = NULL;
134 	Bool xkb_dar = False;
135 	XAutoRepeatOn(x11_display);
136 	xkb_dar = XkbSetDetectableAutoRepeat(x11_display, True, NULL);
137 
138 	// Try to support IME if detectable auto-repeat is supported
139 	if (xkb_dar == True) {
140 
141 #ifdef X_HAVE_UTF8_STRING
142 		// Xutf8LookupString will be used later instead of XmbLookupString before
143 		// the multibyte sequences can be converted to unicode string.
144 		modifiers = XSetLocaleModifiers("");
145 #endif
146 	}
147 
148 	if (modifiers == NULL) {
149 		if (is_stdout_verbose()) {
150 			WARN_PRINT("IME is disabled");
151 		}
152 		XSetLocaleModifiers("@im=none");
153 		WARN_PRINT("Error setting locale modifiers");
154 	}
155 
156 	const char *err;
157 	xrr_get_monitors = NULL;
158 	xrr_free_monitors = NULL;
159 	int xrandr_major = 0;
160 	int xrandr_minor = 0;
161 	int event_base, error_base;
162 	xrandr_ext_ok = XRRQueryExtension(x11_display, &event_base, &error_base);
163 	xrandr_handle = dlopen("libXrandr.so.2", RTLD_LAZY);
164 	if (!xrandr_handle) {
165 		err = dlerror();
166 		fprintf(stderr, "could not load libXrandr.so.2, Error: %s\n", err);
167 	} else {
168 		XRRQueryVersion(x11_display, &xrandr_major, &xrandr_minor);
169 		if (((xrandr_major << 8) | xrandr_minor) >= 0x0105) {
170 			xrr_get_monitors = (xrr_get_monitors_t)dlsym(xrandr_handle, "XRRGetMonitors");
171 			if (!xrr_get_monitors) {
172 				err = dlerror();
173 				fprintf(stderr, "could not find symbol XRRGetMonitors\nError: %s\n", err);
174 			} else {
175 				xrr_free_monitors = (xrr_free_monitors_t)dlsym(xrandr_handle, "XRRFreeMonitors");
176 				if (!xrr_free_monitors) {
177 					err = dlerror();
178 					fprintf(stderr, "could not find XRRFreeMonitors\nError: %s\n", err);
179 					xrr_get_monitors = NULL;
180 				}
181 			}
182 		}
183 	}
184 
185 	if (!refresh_device_info()) {
186 		OS::get_singleton()->alert("Your system does not support XInput 2.\n"
187 								   "Please upgrade your distribution.",
188 				"Unable to initialize XInput");
189 		return ERR_UNAVAILABLE;
190 	}
191 
192 	xim = XOpenIM(x11_display, NULL, NULL, NULL);
193 
194 	if (xim == NULL) {
195 		WARN_PRINT("XOpenIM failed");
196 		xim_style = 0L;
197 	} else {
198 		::XIMCallback im_destroy_callback;
199 		im_destroy_callback.client_data = (::XPointer)(this);
200 		im_destroy_callback.callback = (::XIMProc)(xim_destroy_callback);
201 		if (XSetIMValues(xim, XNDestroyCallback, &im_destroy_callback,
202 					NULL) != NULL) {
203 			WARN_PRINT("Error setting XIM destroy callback");
204 		}
205 
206 		::XIMStyles *xim_styles = NULL;
207 		xim_style = 0L;
208 		char *imvalret = XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL);
209 		if (imvalret != NULL || xim_styles == NULL) {
210 			fprintf(stderr, "Input method doesn't support any styles\n");
211 		}
212 
213 		if (xim_styles) {
214 			xim_style = 0L;
215 			for (int i = 0; i < xim_styles->count_styles; i++) {
216 
217 				if (xim_styles->supported_styles[i] ==
218 						(XIMPreeditNothing | XIMStatusNothing)) {
219 
220 					xim_style = xim_styles->supported_styles[i];
221 					break;
222 				}
223 			}
224 
225 			XFree(xim_styles);
226 		}
227 		XFree(imvalret);
228 	}
229 
230 /*
231 	char* windowid = getenv("GODOT_WINDOWID");
232 	if (windowid) {
233 
234 		//freopen("/home/punto/stdout", "w", stdout);
235 		//reopen("/home/punto/stderr", "w", stderr);
236 		x11_window = atol(windowid);
237 
238 		XWindowAttributes xwa;
239 		XGetWindowAttributes(x11_display,x11_window,&xwa);
240 
241 		current_videomode.width = xwa.width;
242 		current_videomode.height = xwa.height;
243 	};
244 	*/
245 
246 // maybe contextgl wants to be in charge of creating the window
247 #if defined(OPENGL_ENABLED)
248 	if (getenv("DRI_PRIME") == NULL) {
249 		int use_prime = -1;
250 
251 		if (getenv("PRIMUS_DISPLAY") ||
252 				getenv("PRIMUS_libGLd") ||
253 				getenv("PRIMUS_libGLa") ||
254 				getenv("PRIMUS_libGL") ||
255 				getenv("PRIMUS_LOAD_GLOBAL") ||
256 				getenv("BUMBLEBEE_SOCKET")) {
257 
258 			print_verbose("Optirun/primusrun detected. Skipping GPU detection");
259 			use_prime = 0;
260 		}
261 
262 		if (getenv("LD_LIBRARY_PATH")) {
263 			String ld_library_path(getenv("LD_LIBRARY_PATH"));
264 			Vector<String> libraries = ld_library_path.split(":");
265 
266 			for (int i = 0; i < libraries.size(); ++i) {
267 				if (FileAccess::exists(libraries[i] + "/libGL.so.1") ||
268 						FileAccess::exists(libraries[i] + "/libGL.so")) {
269 
270 					print_verbose("Custom libGL override detected. Skipping GPU detection");
271 					use_prime = 0;
272 				}
273 			}
274 		}
275 
276 		if (use_prime == -1) {
277 			print_verbose("Detecting GPUs, set DRI_PRIME in the environment to override GPU detection logic.");
278 			use_prime = detect_prime();
279 		}
280 
281 		if (use_prime) {
282 			print_line("Found discrete GPU, setting DRI_PRIME=1 to use it.");
283 			print_line("Note: Set DRI_PRIME=0 in the environment to disable Godot from using the discrete GPU.");
284 			setenv("DRI_PRIME", "1", 1);
285 		}
286 	}
287 
288 	ContextGL_X11::ContextType opengl_api_type = ContextGL_X11::GLES_3_0_COMPATIBLE;
289 
290 	if (p_video_driver == VIDEO_DRIVER_GLES2) {
291 		opengl_api_type = ContextGL_X11::GLES_2_0_COMPATIBLE;
292 	}
293 
294 	bool editor = Engine::get_singleton()->is_editor_hint();
295 	bool gl_initialization_error = false;
296 
297 	context_gl = NULL;
298 	while (!context_gl) {
299 		context_gl = memnew(ContextGL_X11(x11_display, x11_window, current_videomode, opengl_api_type));
300 
301 		if (context_gl->initialize() != OK) {
302 			memdelete(context_gl);
303 			context_gl = NULL;
304 
305 			if (GLOBAL_GET("rendering/quality/driver/fallback_to_gles2") || editor) {
306 				if (p_video_driver == VIDEO_DRIVER_GLES2) {
307 					gl_initialization_error = true;
308 					break;
309 				}
310 
311 				p_video_driver = VIDEO_DRIVER_GLES2;
312 				opengl_api_type = ContextGL_X11::GLES_2_0_COMPATIBLE;
313 			} else {
314 				gl_initialization_error = true;
315 				break;
316 			}
317 		}
318 	}
319 
320 	while (true) {
321 		if (opengl_api_type == ContextGL_X11::GLES_3_0_COMPATIBLE) {
322 			if (RasterizerGLES3::is_viable() == OK) {
323 				RasterizerGLES3::register_config();
324 				RasterizerGLES3::make_current();
325 				break;
326 			} else {
327 				if (GLOBAL_GET("rendering/quality/driver/fallback_to_gles2") || editor) {
328 					p_video_driver = VIDEO_DRIVER_GLES2;
329 					opengl_api_type = ContextGL_X11::GLES_2_0_COMPATIBLE;
330 					continue;
331 				} else {
332 					gl_initialization_error = true;
333 					break;
334 				}
335 			}
336 		}
337 
338 		if (opengl_api_type == ContextGL_X11::GLES_2_0_COMPATIBLE) {
339 			if (RasterizerGLES2::is_viable() == OK) {
340 				RasterizerGLES2::register_config();
341 				RasterizerGLES2::make_current();
342 				break;
343 			} else {
344 				gl_initialization_error = true;
345 				break;
346 			}
347 		}
348 	}
349 
350 	if (gl_initialization_error) {
351 		OS::get_singleton()->alert("Your video card driver does not support any of the supported OpenGL versions.\n"
352 								   "Please update your drivers or if you have a very old or integrated GPU upgrade it.",
353 				"Unable to initialize Video driver");
354 		return ERR_UNAVAILABLE;
355 	}
356 
357 	video_driver_index = p_video_driver;
358 
359 	context_gl->set_use_vsync(current_videomode.use_vsync);
360 
361 #endif
362 
363 	visual_server = memnew(VisualServerRaster);
364 	if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
365 		visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD));
366 	}
367 
368 	if (current_videomode.maximized) {
369 		current_videomode.maximized = false;
370 		set_window_maximized(true);
371 		// borderless fullscreen window mode
372 	} else if (current_videomode.fullscreen) {
373 		current_videomode.fullscreen = false;
374 		set_window_fullscreen(true);
375 	} else if (current_videomode.borderless_window) {
376 		Hints hints;
377 		Atom property;
378 		hints.flags = 2;
379 		hints.decorations = 0;
380 		property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
381 		XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
382 	}
383 
384 	// make PID known to X11
385 	{
386 		const long pid = this->get_process_id();
387 		Atom net_wm_pid = XInternAtom(x11_display, "_NET_WM_PID", False);
388 		XChangeProperty(x11_display, x11_window, net_wm_pid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
389 	}
390 
391 	// disable resizable window
392 	if (!current_videomode.resizable && !current_videomode.fullscreen) {
393 		XSizeHints *xsh;
394 		xsh = XAllocSizeHints();
395 		xsh->flags = PMinSize | PMaxSize;
396 		XWindowAttributes xwa;
397 		if (current_videomode.fullscreen) {
398 			XGetWindowAttributes(x11_display, DefaultRootWindow(x11_display), &xwa);
399 		} else {
400 			XGetWindowAttributes(x11_display, x11_window, &xwa);
401 		}
402 		xsh->min_width = xwa.width;
403 		xsh->max_width = xwa.width;
404 		xsh->min_height = xwa.height;
405 		xsh->max_height = xwa.height;
406 		XSetWMNormalHints(x11_display, x11_window, xsh);
407 		XFree(xsh);
408 	}
409 
410 	if (current_videomode.always_on_top) {
411 		current_videomode.always_on_top = false;
412 		set_window_always_on_top(true);
413 	}
414 
415 	ERR_FAIL_COND_V(!visual_server, ERR_UNAVAILABLE);
416 	ERR_FAIL_COND_V(x11_window == 0, ERR_UNAVAILABLE);
417 
418 	XSetWindowAttributes new_attr;
419 
420 	new_attr.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask |
421 						  ButtonReleaseMask | EnterWindowMask |
422 						  LeaveWindowMask | PointerMotionMask |
423 						  Button1MotionMask |
424 						  Button2MotionMask | Button3MotionMask |
425 						  Button4MotionMask | Button5MotionMask |
426 						  ButtonMotionMask | KeymapStateMask |
427 						  ExposureMask | VisibilityChangeMask |
428 						  StructureNotifyMask |
429 						  SubstructureNotifyMask | SubstructureRedirectMask |
430 						  FocusChangeMask | PropertyChangeMask |
431 						  ColormapChangeMask | OwnerGrabButtonMask |
432 						  im_event_mask;
433 
434 	XChangeWindowAttributes(x11_display, x11_window, CWEventMask, &new_attr);
435 
436 	static unsigned char all_mask_data[XIMaskLen(XI_LASTEVENT)] = {};
437 	static unsigned char all_master_mask_data[XIMaskLen(XI_LASTEVENT)] = {};
438 
439 	xi.all_event_mask.deviceid = XIAllDevices;
440 	xi.all_event_mask.mask_len = sizeof(all_mask_data);
441 	xi.all_event_mask.mask = all_mask_data;
442 
443 	xi.all_master_event_mask.deviceid = XIAllMasterDevices;
444 	xi.all_master_event_mask.mask_len = sizeof(all_master_mask_data);
445 	xi.all_master_event_mask.mask = all_master_mask_data;
446 
447 	XISetMask(xi.all_event_mask.mask, XI_HierarchyChanged);
448 	XISetMask(xi.all_master_event_mask.mask, XI_DeviceChanged);
449 	XISetMask(xi.all_master_event_mask.mask, XI_RawMotion);
450 
451 #ifdef TOUCH_ENABLED
452 	if (xi.touch_devices.size()) {
453 		XISetMask(xi.all_event_mask.mask, XI_TouchBegin);
454 		XISetMask(xi.all_event_mask.mask, XI_TouchUpdate);
455 		XISetMask(xi.all_event_mask.mask, XI_TouchEnd);
456 		XISetMask(xi.all_event_mask.mask, XI_TouchOwnership);
457 	}
458 #endif
459 
460 	XISelectEvents(x11_display, x11_window, &xi.all_event_mask, 1);
461 	XISelectEvents(x11_display, DefaultRootWindow(x11_display), &xi.all_master_event_mask, 1);
462 
463 	// Disabled by now since grabbing also blocks mouse events
464 	// (they are received as extended events instead of standard events)
465 	/*XIClearMask(xi.touch_event_mask.mask, XI_TouchOwnership);
466 
467 	// Grab touch devices to avoid OS gesture interference
468 	for (int i = 0; i < xi.touch_devices.size(); ++i) {
469 		XIGrabDevice(x11_display, xi.touch_devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &xi.touch_event_mask);
470 	}*/
471 
472 	/* set the titlebar name */
473 	XStoreName(x11_display, x11_window, "Godot");
474 
475 	wm_delete = XInternAtom(x11_display, "WM_DELETE_WINDOW", true);
476 	XSetWMProtocols(x11_display, x11_window, &wm_delete, 1);
477 
478 	im_active = false;
479 	im_position = Vector2();
480 
481 	if (xim && xim_style) {
482 
483 		xic = XCreateIC(xim, XNInputStyle, xim_style, XNClientWindow, x11_window, XNFocusWindow, x11_window, (char *)NULL);
484 		if (XGetICValues(xic, XNFilterEvents, &im_event_mask, NULL) != NULL) {
485 			WARN_PRINT("XGetICValues couldn't obtain XNFilterEvents value");
486 			XDestroyIC(xic);
487 			xic = NULL;
488 		}
489 		if (xic) {
490 			XUnsetICFocus(xic);
491 		} else {
492 			WARN_PRINT("XCreateIC couldn't create xic");
493 		}
494 	} else {
495 
496 		xic = NULL;
497 		WARN_PRINT("XCreateIC couldn't create xic");
498 	}
499 
500 	cursor_size = XcursorGetDefaultSize(x11_display);
501 	cursor_theme = XcursorGetTheme(x11_display);
502 
503 	if (!cursor_theme) {
504 		print_verbose("XcursorGetTheme could not get cursor theme");
505 		cursor_theme = "default";
506 	}
507 
508 	for (int i = 0; i < CURSOR_MAX; i++) {
509 
510 		cursors[i] = None;
511 		img[i] = NULL;
512 	}
513 
514 	current_cursor = CURSOR_ARROW;
515 
516 	for (int i = 0; i < CURSOR_MAX; i++) {
517 
518 		static const char *cursor_file[] = {
519 			"left_ptr",
520 			"xterm",
521 			"hand2",
522 			"cross",
523 			"watch",
524 			"left_ptr_watch",
525 			"fleur",
526 			"hand1",
527 			"X_cursor",
528 			"sb_v_double_arrow",
529 			"sb_h_double_arrow",
530 			"size_bdiag",
531 			"size_fdiag",
532 			"hand1",
533 			"sb_v_double_arrow",
534 			"sb_h_double_arrow",
535 			"question_arrow"
536 		};
537 
538 		img[i] = XcursorLibraryLoadImage(cursor_file[i], cursor_theme, cursor_size);
539 		if (img[i]) {
540 			cursors[i] = XcursorImageLoadCursor(x11_display, img[i]);
541 		} else {
542 			print_verbose("Failed loading custom cursor: " + String(cursor_file[i]));
543 		}
544 	}
545 
546 	{
547 		// Creating an empty/transparent cursor
548 
549 		// Create 1x1 bitmap
550 		Pixmap cursormask = XCreatePixmap(x11_display,
551 				RootWindow(x11_display, DefaultScreen(x11_display)), 1, 1, 1);
552 
553 		// Fill with zero
554 		XGCValues xgc;
555 		xgc.function = GXclear;
556 		GC gc = XCreateGC(x11_display, cursormask, GCFunction, &xgc);
557 		XFillRectangle(x11_display, cursormask, gc, 0, 0, 1, 1);
558 
559 		// Color value doesn't matter. Mask zero means no foreground or background will be drawn
560 		XColor col = {};
561 
562 		Cursor cursor = XCreatePixmapCursor(x11_display,
563 				cursormask, // source (using cursor mask as placeholder, since it'll all be ignored)
564 				cursormask, // mask
565 				&col, &col, 0, 0);
566 
567 		XFreePixmap(x11_display, cursormask);
568 		XFreeGC(x11_display, gc);
569 
570 		if (cursor == None) {
571 			ERR_PRINT("FAILED CREATING CURSOR");
572 		}
573 
574 		null_cursor = cursor;
575 	}
576 	set_cursor_shape(CURSOR_BUSY);
577 
578 	//Set Xdnd (drag & drop) support
579 	Atom XdndAware = XInternAtom(x11_display, "XdndAware", False);
580 	Atom version = 5;
581 	XChangeProperty(x11_display, x11_window, XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&version, 1);
582 
583 	xdnd_enter = XInternAtom(x11_display, "XdndEnter", False);
584 	xdnd_position = XInternAtom(x11_display, "XdndPosition", False);
585 	xdnd_status = XInternAtom(x11_display, "XdndStatus", False);
586 	xdnd_action_copy = XInternAtom(x11_display, "XdndActionCopy", False);
587 	xdnd_drop = XInternAtom(x11_display, "XdndDrop", False);
588 	xdnd_finished = XInternAtom(x11_display, "XdndFinished", False);
589 	xdnd_selection = XInternAtom(x11_display, "XdndSelection", False);
590 	requested = None;
591 
592 	visual_server->init();
593 
594 	AudioDriverManager::initialize(p_audio_driver);
595 
596 	input = memnew(InputDefault);
597 
598 	window_has_focus = true; // Set focus to true at init
599 #ifdef JOYDEV_ENABLED
600 	joypad = memnew(JoypadLinux(input));
601 #endif
602 	_ensure_user_data_dir();
603 
604 	power_manager = memnew(PowerX11);
605 
606 	if (p_desired.layered) {
607 		set_window_per_pixel_transparency_enabled(true);
608 	}
609 
610 	XEvent xevent;
611 	while (XPending(x11_display) > 0) {
612 		XNextEvent(x11_display, &xevent);
613 		if (xevent.type == ConfigureNotify) {
614 			_window_changed(&xevent);
615 		}
616 	}
617 
618 	update_real_mouse_position();
619 
620 	return OK;
621 }
622 
refresh_device_info()623 bool OS_X11::refresh_device_info() {
624 	int event_base, error_base;
625 
626 	print_verbose("XInput: Refreshing devices.");
627 
628 	if (!XQueryExtension(x11_display, "XInputExtension", &xi.opcode, &event_base, &error_base)) {
629 		print_verbose("XInput extension not available. Please upgrade your distribution.");
630 		return false;
631 	}
632 
633 	int xi_major_query = XINPUT_CLIENT_VERSION_MAJOR;
634 	int xi_minor_query = XINPUT_CLIENT_VERSION_MINOR;
635 
636 	if (XIQueryVersion(x11_display, &xi_major_query, &xi_minor_query) != Success) {
637 		print_verbose(vformat("XInput 2 not available (server supports %d.%d).", xi_major_query, xi_minor_query));
638 		xi.opcode = 0;
639 		return false;
640 	}
641 
642 	if (xi_major_query < XINPUT_CLIENT_VERSION_MAJOR || (xi_major_query == XINPUT_CLIENT_VERSION_MAJOR && xi_minor_query < XINPUT_CLIENT_VERSION_MINOR)) {
643 		print_verbose(vformat("XInput %d.%d not available (server supports %d.%d). Touch input unavailable.",
644 				XINPUT_CLIENT_VERSION_MAJOR, XINPUT_CLIENT_VERSION_MINOR, xi_major_query, xi_minor_query));
645 	}
646 
647 	xi.absolute_devices.clear();
648 	xi.touch_devices.clear();
649 
650 	int dev_count;
651 	XIDeviceInfo *info = XIQueryDevice(x11_display, XIAllDevices, &dev_count);
652 
653 	for (int i = 0; i < dev_count; i++) {
654 		XIDeviceInfo *dev = &info[i];
655 		if (!dev->enabled)
656 			continue;
657 		if (!(dev->use == XIMasterPointer || dev->use == XIFloatingSlave))
658 			continue;
659 
660 		bool direct_touch = false;
661 		bool absolute_mode = false;
662 		int resolution_x = 0;
663 		int resolution_y = 0;
664 		double abs_x_min = 0;
665 		double abs_x_max = 0;
666 		double abs_y_min = 0;
667 		double abs_y_max = 0;
668 		double pressure_min = 0;
669 		double pressure_max = 0;
670 		double tilt_x_min = 0;
671 		double tilt_x_max = 0;
672 		double tilt_y_min = 0;
673 		double tilt_y_max = 0;
674 		for (int j = 0; j < dev->num_classes; j++) {
675 #ifdef TOUCH_ENABLED
676 			if (dev->classes[j]->type == XITouchClass && ((XITouchClassInfo *)dev->classes[j])->mode == XIDirectTouch) {
677 				direct_touch = true;
678 			}
679 #endif
680 			if (dev->classes[j]->type == XIValuatorClass) {
681 				XIValuatorClassInfo *class_info = (XIValuatorClassInfo *)dev->classes[j];
682 
683 				if (class_info->number == VALUATOR_ABSX && class_info->mode == XIModeAbsolute) {
684 					resolution_x = class_info->resolution;
685 					abs_x_min = class_info->min;
686 					abs_y_max = class_info->max;
687 					absolute_mode = true;
688 				} else if (class_info->number == VALUATOR_ABSY && class_info->mode == XIModeAbsolute) {
689 					resolution_y = class_info->resolution;
690 					abs_y_min = class_info->min;
691 					abs_y_max = class_info->max;
692 					absolute_mode = true;
693 				} else if (class_info->number == VALUATOR_PRESSURE && class_info->mode == XIModeAbsolute) {
694 					pressure_min = class_info->min;
695 					pressure_max = class_info->max;
696 				} else if (class_info->number == VALUATOR_TILTX && class_info->mode == XIModeAbsolute) {
697 					tilt_x_min = class_info->min;
698 					tilt_x_max = class_info->max;
699 				} else if (class_info->number == VALUATOR_TILTY && class_info->mode == XIModeAbsolute) {
700 					tilt_x_min = class_info->min;
701 					tilt_x_max = class_info->max;
702 				}
703 			}
704 		}
705 		if (direct_touch) {
706 			xi.touch_devices.push_back(dev->deviceid);
707 			print_verbose("XInput: Using touch device: " + String(dev->name));
708 		}
709 		if (absolute_mode) {
710 			// If no resolution was reported, use the min/max ranges.
711 			if (resolution_x <= 0) {
712 				resolution_x = (abs_x_max - abs_x_min) * abs_resolution_range_mult;
713 			}
714 			if (resolution_y <= 0) {
715 				resolution_y = (abs_y_max - abs_y_min) * abs_resolution_range_mult;
716 			}
717 			xi.absolute_devices[dev->deviceid] = Vector2(abs_resolution_mult / resolution_x, abs_resolution_mult / resolution_y);
718 			print_verbose("XInput: Absolute pointing device: " + String(dev->name));
719 		}
720 
721 		xi.pressure = 0;
722 		xi.pen_pressure_range[dev->deviceid] = Vector2(pressure_min, pressure_max);
723 		xi.pen_tilt_x_range[dev->deviceid] = Vector2(tilt_x_min, tilt_x_max);
724 		xi.pen_tilt_y_range[dev->deviceid] = Vector2(tilt_y_min, tilt_y_max);
725 	}
726 
727 	XIFreeDeviceInfo(info);
728 #ifdef TOUCH_ENABLED
729 	if (!xi.touch_devices.size()) {
730 		print_verbose("XInput: No touch devices found.");
731 	}
732 #endif
733 
734 	return true;
735 }
736 
xim_destroy_callback(::XIM im,::XPointer client_data,::XPointer call_data)737 void OS_X11::xim_destroy_callback(::XIM im, ::XPointer client_data,
738 		::XPointer call_data) {
739 
740 	WARN_PRINT("Input method stopped");
741 	OS_X11 *os = reinterpret_cast<OS_X11 *>(client_data);
742 	os->xim = NULL;
743 	os->xic = NULL;
744 }
745 
set_ime_active(const bool p_active)746 void OS_X11::set_ime_active(const bool p_active) {
747 
748 	im_active = p_active;
749 
750 	if (!xic)
751 		return;
752 
753 	if (p_active) {
754 		XSetICFocus(xic);
755 		set_ime_position(im_position);
756 	} else {
757 		XUnsetICFocus(xic);
758 	}
759 }
760 
set_ime_position(const Point2 & p_pos)761 void OS_X11::set_ime_position(const Point2 &p_pos) {
762 
763 	im_position = p_pos;
764 
765 	if (!xic)
766 		return;
767 
768 	::XPoint spot;
769 	spot.x = short(p_pos.x);
770 	spot.y = short(p_pos.y);
771 	XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
772 	XSetICValues(xic, XNPreeditAttributes, preedit_attr, NULL);
773 	XFree(preedit_attr);
774 }
775 
get_unique_id() const776 String OS_X11::get_unique_id() const {
777 
778 	static String machine_id;
779 	if (machine_id.empty()) {
780 		if (FileAccess *f = FileAccess::open("/etc/machine-id", FileAccess::READ)) {
781 			while (machine_id.empty() && !f->eof_reached()) {
782 				machine_id = f->get_line().strip_edges();
783 			}
784 			f->close();
785 			memdelete(f);
786 		}
787 	}
788 	return machine_id;
789 }
790 
finalize()791 void OS_X11::finalize() {
792 
793 	if (main_loop)
794 		memdelete(main_loop);
795 	main_loop = NULL;
796 
797 	/*
798 	if (debugger_connection_console) {
799 		memdelete(debugger_connection_console);
800 	}
801 	*/
802 #ifdef ALSAMIDI_ENABLED
803 	driver_alsamidi.close();
804 #endif
805 
806 #ifdef JOYDEV_ENABLED
807 	memdelete(joypad);
808 #endif
809 
810 	xi.touch_devices.clear();
811 	xi.state.clear();
812 
813 	memdelete(input);
814 
815 	cursors_cache.clear();
816 	visual_server->finish();
817 	memdelete(visual_server);
818 	//memdelete(rasterizer);
819 
820 	memdelete(power_manager);
821 
822 	if (xrandr_handle)
823 		dlclose(xrandr_handle);
824 
825 	XUnmapWindow(x11_display, x11_window);
826 	XDestroyWindow(x11_display, x11_window);
827 
828 #if defined(OPENGL_ENABLED)
829 	memdelete(context_gl);
830 #endif
831 	for (int i = 0; i < CURSOR_MAX; i++) {
832 		if (cursors[i] != None)
833 			XFreeCursor(x11_display, cursors[i]);
834 		if (img[i] != NULL)
835 			XcursorImageDestroy(img[i]);
836 	};
837 
838 	if (xic) {
839 		XDestroyIC(xic);
840 	}
841 	if (xim) {
842 		XCloseIM(xim);
843 	}
844 
845 	XCloseDisplay(x11_display);
846 	if (xmbstring)
847 		memfree(xmbstring);
848 
849 	args.clear();
850 }
851 
set_mouse_mode(MouseMode p_mode)852 void OS_X11::set_mouse_mode(MouseMode p_mode) {
853 
854 	if (p_mode == mouse_mode)
855 		return;
856 
857 	if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED)
858 		XUngrabPointer(x11_display, CurrentTime);
859 
860 	// The only modes that show a cursor are VISIBLE and CONFINED
861 	bool showCursor = (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED);
862 
863 	if (showCursor) {
864 		XDefineCursor(x11_display, x11_window, cursors[current_cursor]); // show cursor
865 	} else {
866 		XDefineCursor(x11_display, x11_window, null_cursor); // hide cursor
867 	}
868 
869 	mouse_mode = p_mode;
870 
871 	if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) {
872 
873 		//flush pending motion events
874 		flush_mouse_motion();
875 
876 		if (XGrabPointer(
877 					x11_display, x11_window, True,
878 					ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
879 					GrabModeAsync, GrabModeAsync, x11_window, None, CurrentTime) != GrabSuccess) {
880 			ERR_PRINT("NO GRAB");
881 		}
882 
883 		if (mouse_mode == MOUSE_MODE_CAPTURED) {
884 			center.x = current_videomode.width / 2;
885 			center.y = current_videomode.height / 2;
886 
887 			XWarpPointer(x11_display, None, x11_window,
888 					0, 0, 0, 0, (int)center.x, (int)center.y);
889 
890 			input->set_mouse_position(center);
891 		}
892 	} else {
893 		do_mouse_warp = false;
894 	}
895 
896 	XFlush(x11_display);
897 }
898 
warp_mouse_position(const Point2 & p_to)899 void OS_X11::warp_mouse_position(const Point2 &p_to) {
900 
901 	if (mouse_mode == MOUSE_MODE_CAPTURED) {
902 
903 		last_mouse_pos = p_to;
904 	} else {
905 
906 		/*XWindowAttributes xwa;
907 		XGetWindowAttributes(x11_display, x11_window, &xwa);
908 		printf("%d %d\n", xwa.x, xwa.y); needed? */
909 
910 		XWarpPointer(x11_display, None, x11_window,
911 				0, 0, 0, 0, (int)p_to.x, (int)p_to.y);
912 	}
913 }
914 
flush_mouse_motion()915 void OS_X11::flush_mouse_motion() {
916 	while (true) {
917 		if (XPending(x11_display) > 0) {
918 			XEvent event;
919 			XPeekEvent(x11_display, &event);
920 
921 			if (XGetEventData(x11_display, &event.xcookie) && event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
922 				XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
923 
924 				if (event_data->evtype == XI_RawMotion) {
925 					XNextEvent(x11_display, &event);
926 				} else {
927 					break;
928 				}
929 			} else {
930 				break;
931 			}
932 		} else {
933 			break;
934 		}
935 	}
936 
937 	xi.relative_motion.x = 0;
938 	xi.relative_motion.y = 0;
939 }
940 
get_mouse_mode() const941 OS::MouseMode OS_X11::get_mouse_mode() const {
942 	return mouse_mode;
943 }
944 
get_mouse_button_state() const945 int OS_X11::get_mouse_button_state() const {
946 	return last_button_state;
947 }
948 
get_mouse_position() const949 Point2 OS_X11::get_mouse_position() const {
950 	return last_mouse_pos;
951 }
952 
get_window_per_pixel_transparency_enabled() const953 bool OS_X11::get_window_per_pixel_transparency_enabled() const {
954 
955 	if (!is_layered_allowed()) return false;
956 	return layered_window;
957 }
958 
set_window_per_pixel_transparency_enabled(bool p_enabled)959 void OS_X11::set_window_per_pixel_transparency_enabled(bool p_enabled) {
960 
961 	if (!is_layered_allowed()) return;
962 	if (layered_window != p_enabled) {
963 		if (p_enabled) {
964 			layered_window = true;
965 		} else {
966 			layered_window = false;
967 		}
968 	}
969 }
970 
set_window_title(const String & p_title)971 void OS_X11::set_window_title(const String &p_title) {
972 	XStoreName(x11_display, x11_window, p_title.utf8().get_data());
973 
974 	Atom _net_wm_name = XInternAtom(x11_display, "_NET_WM_NAME", false);
975 	Atom utf8_string = XInternAtom(x11_display, "UTF8_STRING", false);
976 	XChangeProperty(x11_display, x11_window, _net_wm_name, utf8_string, 8, PropModeReplace, (unsigned char *)p_title.utf8().get_data(), p_title.utf8().length());
977 }
978 
set_video_mode(const VideoMode & p_video_mode,int p_screen)979 void OS_X11::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
980 }
981 
get_video_mode(int p_screen) const982 OS::VideoMode OS_X11::get_video_mode(int p_screen) const {
983 	return current_videomode;
984 }
985 
get_fullscreen_mode_list(List<VideoMode> * p_list,int p_screen) const986 void OS_X11::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
987 }
988 
set_wm_fullscreen(bool p_enabled)989 void OS_X11::set_wm_fullscreen(bool p_enabled) {
990 	if (p_enabled && !get_borderless_window()) {
991 		// remove decorations if the window is not already borderless
992 		Hints hints;
993 		Atom property;
994 		hints.flags = 2;
995 		hints.decorations = 0;
996 		property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
997 		XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
998 	}
999 
1000 	if (p_enabled && !is_window_resizable()) {
1001 		// Set the window as resizable to prevent window managers to ignore the fullscreen state flag.
1002 		XSizeHints *xsh;
1003 
1004 		xsh = XAllocSizeHints();
1005 		xsh->flags = 0L;
1006 		XSetWMNormalHints(x11_display, x11_window, xsh);
1007 		XFree(xsh);
1008 	}
1009 
1010 	// Using EWMH -- Extended Window Manager Hints
1011 	XEvent xev;
1012 	Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
1013 	Atom wm_fullscreen = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", False);
1014 
1015 	memset(&xev, 0, sizeof(xev));
1016 	xev.type = ClientMessage;
1017 	xev.xclient.window = x11_window;
1018 	xev.xclient.message_type = wm_state;
1019 	xev.xclient.format = 32;
1020 	xev.xclient.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
1021 	xev.xclient.data.l[1] = wm_fullscreen;
1022 	xev.xclient.data.l[2] = 0;
1023 
1024 	XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
1025 
1026 	// set bypass compositor hint
1027 	Atom bypass_compositor = XInternAtom(x11_display, "_NET_WM_BYPASS_COMPOSITOR", False);
1028 	unsigned long compositing_disable_on = p_enabled ? 1 : 0;
1029 	XChangeProperty(x11_display, x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1);
1030 
1031 	XFlush(x11_display);
1032 
1033 	if (!p_enabled) {
1034 		// Reset the non-resizable flags if we un-set these before.
1035 		Size2 size = get_window_size();
1036 		XSizeHints *xsh;
1037 		xsh = XAllocSizeHints();
1038 		if (!is_window_resizable()) {
1039 			xsh->flags = PMinSize | PMaxSize;
1040 			xsh->min_width = size.x;
1041 			xsh->max_width = size.x;
1042 			xsh->min_height = size.y;
1043 			xsh->max_height = size.y;
1044 		} else {
1045 			xsh->flags = 0L;
1046 			if (min_size != Size2()) {
1047 				xsh->flags |= PMinSize;
1048 				xsh->min_width = min_size.x;
1049 				xsh->min_height = min_size.y;
1050 			}
1051 			if (max_size != Size2()) {
1052 				xsh->flags |= PMaxSize;
1053 				xsh->max_width = max_size.x;
1054 				xsh->max_height = max_size.y;
1055 			}
1056 		}
1057 		XSetWMNormalHints(x11_display, x11_window, xsh);
1058 		XFree(xsh);
1059 
1060 		// put back or remove decorations according to the last set borderless state
1061 		Hints hints;
1062 		Atom property;
1063 		hints.flags = 2;
1064 		hints.decorations = current_videomode.borderless_window ? 0 : 1;
1065 		property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
1066 		XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
1067 	}
1068 }
1069 
set_wm_above(bool p_enabled)1070 void OS_X11::set_wm_above(bool p_enabled) {
1071 	Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
1072 	Atom wm_above = XInternAtom(x11_display, "_NET_WM_STATE_ABOVE", False);
1073 
1074 	XClientMessageEvent xev;
1075 	memset(&xev, 0, sizeof(xev));
1076 	xev.type = ClientMessage;
1077 	xev.window = x11_window;
1078 	xev.message_type = wm_state;
1079 	xev.format = 32;
1080 	xev.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
1081 	xev.data.l[1] = wm_above;
1082 	xev.data.l[3] = 1;
1083 	XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&xev);
1084 }
1085 
get_screen_count() const1086 int OS_X11::get_screen_count() const {
1087 	// Using Xinerama Extension
1088 	int event_base, error_base;
1089 	const Bool ext_okay = XineramaQueryExtension(x11_display, &event_base, &error_base);
1090 	if (!ext_okay) return 0;
1091 
1092 	int count;
1093 	XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
1094 	XFree(xsi);
1095 	return count;
1096 }
1097 
get_current_screen() const1098 int OS_X11::get_current_screen() const {
1099 	int x, y;
1100 	Window child;
1101 	XTranslateCoordinates(x11_display, x11_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child);
1102 
1103 	int count = get_screen_count();
1104 	for (int i = 0; i < count; i++) {
1105 		Point2i pos = get_screen_position(i);
1106 		Size2i size = get_screen_size(i);
1107 		if ((x >= pos.x && x < pos.x + size.width) && (y >= pos.y && y < pos.y + size.height))
1108 			return i;
1109 	}
1110 	return 0;
1111 }
1112 
set_current_screen(int p_screen)1113 void OS_X11::set_current_screen(int p_screen) {
1114 	int count = get_screen_count();
1115 	if (p_screen >= count) return;
1116 
1117 	if (current_videomode.fullscreen) {
1118 		Point2i position = get_screen_position(p_screen);
1119 		Size2i size = get_screen_size(p_screen);
1120 
1121 		XMoveResizeWindow(x11_display, x11_window, position.x, position.y, size.x, size.y);
1122 	} else {
1123 		if (p_screen != get_current_screen()) {
1124 			Point2i position = get_screen_position(p_screen);
1125 			XMoveWindow(x11_display, x11_window, position.x, position.y);
1126 		}
1127 	}
1128 }
1129 
get_screen_position(int p_screen) const1130 Point2 OS_X11::get_screen_position(int p_screen) const {
1131 	if (p_screen == -1) {
1132 		p_screen = get_current_screen();
1133 	}
1134 
1135 	// Using Xinerama Extension
1136 	int event_base, error_base;
1137 	const Bool ext_okay = XineramaQueryExtension(x11_display, &event_base, &error_base);
1138 	if (!ext_okay) {
1139 		return Point2i(0, 0);
1140 	}
1141 
1142 	int count;
1143 	XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
1144 	if (p_screen >= count) {
1145 		return Point2i(0, 0);
1146 	}
1147 
1148 	Point2i position = Point2i(xsi[p_screen].x_org, xsi[p_screen].y_org);
1149 
1150 	XFree(xsi);
1151 
1152 	return position;
1153 }
1154 
get_screen_size(int p_screen) const1155 Size2 OS_X11::get_screen_size(int p_screen) const {
1156 	if (p_screen == -1) {
1157 		p_screen = get_current_screen();
1158 	}
1159 
1160 	// Using Xinerama Extension
1161 	int event_base, error_base;
1162 	const Bool ext_okay = XineramaQueryExtension(x11_display, &event_base, &error_base);
1163 	if (!ext_okay) return Size2i(0, 0);
1164 
1165 	int count;
1166 	XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
1167 	if (p_screen >= count) return Size2i(0, 0);
1168 
1169 	Size2i size = Point2i(xsi[p_screen].width, xsi[p_screen].height);
1170 	XFree(xsi);
1171 	return size;
1172 }
1173 
get_screen_dpi(int p_screen) const1174 int OS_X11::get_screen_dpi(int p_screen) const {
1175 	if (p_screen == -1) {
1176 		p_screen = get_current_screen();
1177 	}
1178 
1179 	//invalid screen?
1180 	ERR_FAIL_INDEX_V(p_screen, get_screen_count(), 0);
1181 
1182 	//Get physical monitor Dimensions through XRandR and calculate dpi
1183 	Size2 sc = get_screen_size(p_screen);
1184 	if (xrandr_ext_ok) {
1185 		int count = 0;
1186 		if (xrr_get_monitors) {
1187 			xrr_monitor_info *monitors = xrr_get_monitors(x11_display, x11_window, true, &count);
1188 			if (p_screen < count) {
1189 				double xdpi = sc.width / (double)monitors[p_screen].mwidth * 25.4;
1190 				double ydpi = sc.height / (double)monitors[p_screen].mheight * 25.4;
1191 				xrr_free_monitors(monitors);
1192 				return (xdpi + ydpi) / 2;
1193 			}
1194 			xrr_free_monitors(monitors);
1195 		} else if (p_screen == 0) {
1196 			XRRScreenSize *sizes = XRRSizes(x11_display, 0, &count);
1197 			if (sizes) {
1198 				double xdpi = sc.width / (double)sizes[0].mwidth * 25.4;
1199 				double ydpi = sc.height / (double)sizes[0].mheight * 25.4;
1200 				return (xdpi + ydpi) / 2;
1201 			}
1202 		}
1203 	}
1204 
1205 	int width_mm = DisplayWidthMM(x11_display, p_screen);
1206 	int height_mm = DisplayHeightMM(x11_display, p_screen);
1207 	double xdpi = (width_mm ? sc.width / (double)width_mm * 25.4 : 0);
1208 	double ydpi = (height_mm ? sc.height / (double)height_mm * 25.4 : 0);
1209 	if (xdpi || ydpi)
1210 		return (xdpi + ydpi) / (xdpi && ydpi ? 2 : 1);
1211 
1212 	//could not get dpi
1213 	return 96;
1214 }
1215 
get_window_position() const1216 Point2 OS_X11::get_window_position() const {
1217 	int x, y;
1218 	Window child;
1219 	XTranslateCoordinates(x11_display, x11_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child);
1220 	return Point2i(x, y);
1221 }
1222 
set_window_position(const Point2 & p_position)1223 void OS_X11::set_window_position(const Point2 &p_position) {
1224 	int x = 0;
1225 	int y = 0;
1226 	if (!get_borderless_window()) {
1227 		//exclude window decorations
1228 		XSync(x11_display, False);
1229 		Atom prop = XInternAtom(x11_display, "_NET_FRAME_EXTENTS", True);
1230 		if (prop != None) {
1231 			Atom type;
1232 			int format;
1233 			unsigned long len;
1234 			unsigned long remaining;
1235 			unsigned char *data = NULL;
1236 			if (XGetWindowProperty(x11_display, x11_window, prop, 0, 4, False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) {
1237 				if (format == 32 && len == 4) {
1238 					long *extents = (long *)data;
1239 					x = extents[0];
1240 					y = extents[2];
1241 				}
1242 				XFree(data);
1243 			}
1244 		}
1245 	}
1246 	XMoveWindow(x11_display, x11_window, p_position.x - x, p_position.y - y);
1247 	update_real_mouse_position();
1248 }
1249 
get_window_size() const1250 Size2 OS_X11::get_window_size() const {
1251 	// Use current_videomode width and height instead of XGetWindowAttributes
1252 	// since right after a XResizeWindow the attributes may not be updated yet
1253 	return Size2i(current_videomode.width, current_videomode.height);
1254 }
1255 
get_real_window_size() const1256 Size2 OS_X11::get_real_window_size() const {
1257 	XWindowAttributes xwa;
1258 	XSync(x11_display, False);
1259 	XGetWindowAttributes(x11_display, x11_window, &xwa);
1260 	int w = xwa.width;
1261 	int h = xwa.height;
1262 	Atom prop = XInternAtom(x11_display, "_NET_FRAME_EXTENTS", True);
1263 	if (prop != None) {
1264 		Atom type;
1265 		int format;
1266 		unsigned long len;
1267 		unsigned long remaining;
1268 		unsigned char *data = NULL;
1269 		if (XGetWindowProperty(x11_display, x11_window, prop, 0, 4, False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) {
1270 			if (format == 32 && len == 4) {
1271 				long *extents = (long *)data;
1272 				w += extents[0] + extents[1]; // left, right
1273 				h += extents[2] + extents[3]; // top, bottom
1274 			}
1275 			XFree(data);
1276 		}
1277 	}
1278 	return Size2(w, h);
1279 }
1280 
get_max_window_size() const1281 Size2 OS_X11::get_max_window_size() const {
1282 	return max_size;
1283 }
1284 
get_min_window_size() const1285 Size2 OS_X11::get_min_window_size() const {
1286 	return min_size;
1287 }
1288 
set_min_window_size(const Size2 p_size)1289 void OS_X11::set_min_window_size(const Size2 p_size) {
1290 
1291 	if ((p_size != Size2()) && (max_size != Size2()) && ((p_size.x > max_size.x) || (p_size.y > max_size.y))) {
1292 		ERR_PRINT("Minimum window size can't be larger than maximum window size!");
1293 		return;
1294 	}
1295 	min_size = p_size;
1296 
1297 	if (is_window_resizable()) {
1298 		XSizeHints *xsh;
1299 		xsh = XAllocSizeHints();
1300 		xsh->flags = 0L;
1301 		if (min_size != Size2()) {
1302 			xsh->flags |= PMinSize;
1303 			xsh->min_width = min_size.x;
1304 			xsh->min_height = min_size.y;
1305 		}
1306 		if (max_size != Size2()) {
1307 			xsh->flags |= PMaxSize;
1308 			xsh->max_width = max_size.x;
1309 			xsh->max_height = max_size.y;
1310 		}
1311 		XSetWMNormalHints(x11_display, x11_window, xsh);
1312 		XFree(xsh);
1313 
1314 		XFlush(x11_display);
1315 	}
1316 }
1317 
set_max_window_size(const Size2 p_size)1318 void OS_X11::set_max_window_size(const Size2 p_size) {
1319 
1320 	if ((p_size != Size2()) && ((p_size.x < min_size.x) || (p_size.y < min_size.y))) {
1321 		ERR_PRINT("Maximum window size can't be smaller than minimum window size!");
1322 		return;
1323 	}
1324 	max_size = p_size;
1325 
1326 	if (is_window_resizable()) {
1327 		XSizeHints *xsh;
1328 		xsh = XAllocSizeHints();
1329 		xsh->flags = 0L;
1330 		if (min_size != Size2()) {
1331 			xsh->flags |= PMinSize;
1332 			xsh->min_width = min_size.x;
1333 			xsh->min_height = min_size.y;
1334 		}
1335 		if (max_size != Size2()) {
1336 			xsh->flags |= PMaxSize;
1337 			xsh->max_width = max_size.x;
1338 			xsh->max_height = max_size.y;
1339 		}
1340 		XSetWMNormalHints(x11_display, x11_window, xsh);
1341 		XFree(xsh);
1342 
1343 		XFlush(x11_display);
1344 	}
1345 }
1346 
set_window_size(const Size2 p_size)1347 void OS_X11::set_window_size(const Size2 p_size) {
1348 
1349 	if (current_videomode.width == p_size.width && current_videomode.height == p_size.height)
1350 		return;
1351 
1352 	XWindowAttributes xwa;
1353 	XSync(x11_display, False);
1354 	XGetWindowAttributes(x11_display, x11_window, &xwa);
1355 	int old_w = xwa.width;
1356 	int old_h = xwa.height;
1357 
1358 	// If window resizable is disabled we need to update the attributes first
1359 	XSizeHints *xsh;
1360 	xsh = XAllocSizeHints();
1361 	if (!is_window_resizable()) {
1362 		xsh->flags = PMinSize | PMaxSize;
1363 		xsh->min_width = p_size.x;
1364 		xsh->max_width = p_size.x;
1365 		xsh->min_height = p_size.y;
1366 		xsh->max_height = p_size.y;
1367 	} else {
1368 		xsh->flags = 0L;
1369 		if (min_size != Size2()) {
1370 			xsh->flags |= PMinSize;
1371 			xsh->min_width = min_size.x;
1372 			xsh->min_height = min_size.y;
1373 		}
1374 		if (max_size != Size2()) {
1375 			xsh->flags |= PMaxSize;
1376 			xsh->max_width = max_size.x;
1377 			xsh->max_height = max_size.y;
1378 		}
1379 	}
1380 	XSetWMNormalHints(x11_display, x11_window, xsh);
1381 	XFree(xsh);
1382 
1383 	// Resize the window
1384 	XResizeWindow(x11_display, x11_window, p_size.x, p_size.y);
1385 
1386 	// Update our videomode width and height
1387 	current_videomode.width = p_size.x;
1388 	current_videomode.height = p_size.y;
1389 
1390 	for (int timeout = 0; timeout < 50; ++timeout) {
1391 		XSync(x11_display, False);
1392 		XGetWindowAttributes(x11_display, x11_window, &xwa);
1393 
1394 		if (old_w != xwa.width || old_h != xwa.height)
1395 			break;
1396 
1397 		usleep(10000);
1398 	}
1399 }
1400 
set_window_fullscreen(bool p_enabled)1401 void OS_X11::set_window_fullscreen(bool p_enabled) {
1402 
1403 	if (current_videomode.fullscreen == p_enabled)
1404 		return;
1405 
1406 	if (layered_window)
1407 		set_window_per_pixel_transparency_enabled(false);
1408 
1409 	if (p_enabled && current_videomode.always_on_top) {
1410 		// Fullscreen + Always-on-top requires a maximized window on some window managers (Metacity)
1411 		set_window_maximized(true);
1412 	}
1413 	set_wm_fullscreen(p_enabled);
1414 	if (!p_enabled && current_videomode.always_on_top) {
1415 		// Restore
1416 		set_window_maximized(false);
1417 	}
1418 	if (!p_enabled) {
1419 		set_window_position(last_position_before_fs);
1420 	} else {
1421 		last_position_before_fs = get_window_position();
1422 	}
1423 	current_videomode.fullscreen = p_enabled;
1424 }
1425 
is_window_fullscreen() const1426 bool OS_X11::is_window_fullscreen() const {
1427 	return current_videomode.fullscreen;
1428 }
1429 
set_window_resizable(bool p_enabled)1430 void OS_X11::set_window_resizable(bool p_enabled) {
1431 
1432 	XSizeHints *xsh;
1433 	xsh = XAllocSizeHints();
1434 	if (!p_enabled) {
1435 		Size2 size = get_window_size();
1436 
1437 		xsh->flags = PMinSize | PMaxSize;
1438 		xsh->min_width = size.x;
1439 		xsh->max_width = size.x;
1440 		xsh->min_height = size.y;
1441 		xsh->max_height = size.y;
1442 	} else {
1443 		xsh->flags = 0L;
1444 		if (min_size != Size2()) {
1445 			xsh->flags |= PMinSize;
1446 			xsh->min_width = min_size.x;
1447 			xsh->min_height = min_size.y;
1448 		}
1449 		if (max_size != Size2()) {
1450 			xsh->flags |= PMaxSize;
1451 			xsh->max_width = max_size.x;
1452 			xsh->max_height = max_size.y;
1453 		}
1454 	}
1455 
1456 	XSetWMNormalHints(x11_display, x11_window, xsh);
1457 	XFree(xsh);
1458 
1459 	current_videomode.resizable = p_enabled;
1460 
1461 	XFlush(x11_display);
1462 }
1463 
is_window_resizable() const1464 bool OS_X11::is_window_resizable() const {
1465 	return current_videomode.resizable;
1466 }
1467 
set_window_minimized(bool p_enabled)1468 void OS_X11::set_window_minimized(bool p_enabled) {
1469 	// Using ICCCM -- Inter-Client Communication Conventions Manual
1470 	XEvent xev;
1471 	Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False);
1472 
1473 	memset(&xev, 0, sizeof(xev));
1474 	xev.type = ClientMessage;
1475 	xev.xclient.window = x11_window;
1476 	xev.xclient.message_type = wm_change;
1477 	xev.xclient.format = 32;
1478 	xev.xclient.data.l[0] = p_enabled ? WM_IconicState : WM_NormalState;
1479 
1480 	XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
1481 
1482 	Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
1483 	Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False);
1484 
1485 	memset(&xev, 0, sizeof(xev));
1486 	xev.type = ClientMessage;
1487 	xev.xclient.window = x11_window;
1488 	xev.xclient.message_type = wm_state;
1489 	xev.xclient.format = 32;
1490 	xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
1491 	xev.xclient.data.l[1] = wm_hidden;
1492 
1493 	XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
1494 }
1495 
is_window_minimized() const1496 bool OS_X11::is_window_minimized() const {
1497 	// Using ICCCM -- Inter-Client Communication Conventions Manual
1498 	Atom property = XInternAtom(x11_display, "WM_STATE", True);
1499 	Atom type;
1500 	int format;
1501 	unsigned long len;
1502 	unsigned long remaining;
1503 	unsigned char *data = NULL;
1504 	bool retval = false;
1505 
1506 	int result = XGetWindowProperty(
1507 			x11_display,
1508 			x11_window,
1509 			property,
1510 			0,
1511 			32,
1512 			False,
1513 			AnyPropertyType,
1514 			&type,
1515 			&format,
1516 			&len,
1517 			&remaining,
1518 			&data);
1519 
1520 	if (result == Success) {
1521 		long *state = (long *)data;
1522 		if (state[0] == WM_IconicState) {
1523 			retval = true;
1524 		}
1525 		XFree(data);
1526 	}
1527 
1528 	return retval;
1529 }
1530 
set_window_maximized(bool p_enabled)1531 void OS_X11::set_window_maximized(bool p_enabled) {
1532 	if (is_window_maximized() == p_enabled)
1533 		return;
1534 
1535 	// Using EWMH -- Extended Window Manager Hints
1536 	XEvent xev;
1537 	Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
1538 	Atom wm_max_horz = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
1539 	Atom wm_max_vert = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
1540 
1541 	memset(&xev, 0, sizeof(xev));
1542 	xev.type = ClientMessage;
1543 	xev.xclient.window = x11_window;
1544 	xev.xclient.message_type = wm_state;
1545 	xev.xclient.format = 32;
1546 	xev.xclient.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
1547 	xev.xclient.data.l[1] = wm_max_horz;
1548 	xev.xclient.data.l[2] = wm_max_vert;
1549 
1550 	XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
1551 
1552 	if (p_enabled && is_window_maximize_allowed()) {
1553 		// Wait for effective resizing (so the GLX context is too).
1554 		// Give up after 0.5s, it's not going to happen on this WM.
1555 		// https://github.com/godotengine/godot/issues/19978
1556 		for (int attempt = 0; !is_window_maximized() && attempt < 50; attempt++) {
1557 			usleep(10000);
1558 		}
1559 	}
1560 
1561 	maximized = p_enabled;
1562 }
1563 
1564 // Just a helper to reduce code duplication in `is_window_maximize_allowed`
1565 // and `is_window_maximized`.
window_maximize_check(const char * p_atom_name) const1566 bool OS_X11::window_maximize_check(const char *p_atom_name) const {
1567 	Atom property = XInternAtom(x11_display, p_atom_name, False);
1568 	Atom type;
1569 	int format;
1570 	unsigned long len;
1571 	unsigned long remaining;
1572 	unsigned char *data = NULL;
1573 	bool retval = false;
1574 
1575 	int result = XGetWindowProperty(
1576 			x11_display,
1577 			x11_window,
1578 			property,
1579 			0,
1580 			1024,
1581 			False,
1582 			XA_ATOM,
1583 			&type,
1584 			&format,
1585 			&len,
1586 			&remaining,
1587 			&data);
1588 
1589 	if (result == Success) {
1590 		Atom *atoms = (Atom *)data;
1591 		Atom wm_act_max_horz = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_HORZ", False);
1592 		Atom wm_act_max_vert = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_VERT", False);
1593 		bool found_wm_act_max_horz = false;
1594 		bool found_wm_act_max_vert = false;
1595 
1596 		for (uint64_t i = 0; i < len; i++) {
1597 			if (atoms[i] == wm_act_max_horz)
1598 				found_wm_act_max_horz = true;
1599 			if (atoms[i] == wm_act_max_vert)
1600 				found_wm_act_max_vert = true;
1601 
1602 			if (found_wm_act_max_horz || found_wm_act_max_vert) {
1603 				retval = true;
1604 				break;
1605 			}
1606 		}
1607 
1608 		XFree(data);
1609 	}
1610 
1611 	return retval;
1612 }
1613 
is_window_maximize_allowed() const1614 bool OS_X11::is_window_maximize_allowed() const {
1615 	return window_maximize_check("_NET_WM_ALLOWED_ACTIONS");
1616 }
1617 
is_window_maximized() const1618 bool OS_X11::is_window_maximized() const {
1619 	// Using EWMH -- Extended Window Manager Hints
1620 	return window_maximize_check("_NET_WM_STATE");
1621 }
1622 
set_window_always_on_top(bool p_enabled)1623 void OS_X11::set_window_always_on_top(bool p_enabled) {
1624 	if (is_window_always_on_top() == p_enabled)
1625 		return;
1626 
1627 	if (p_enabled && current_videomode.fullscreen) {
1628 		// Fullscreen + Always-on-top requires a maximized window on some window managers (Metacity)
1629 		set_window_maximized(true);
1630 	}
1631 	set_wm_above(p_enabled);
1632 	if (!p_enabled && !current_videomode.fullscreen) {
1633 		// Restore
1634 		set_window_maximized(false);
1635 	}
1636 
1637 	current_videomode.always_on_top = p_enabled;
1638 }
1639 
is_window_always_on_top() const1640 bool OS_X11::is_window_always_on_top() const {
1641 	return current_videomode.always_on_top;
1642 }
1643 
is_window_focused() const1644 bool OS_X11::is_window_focused() const {
1645 	return window_focused;
1646 }
1647 
set_borderless_window(bool p_borderless)1648 void OS_X11::set_borderless_window(bool p_borderless) {
1649 
1650 	if (get_borderless_window() == p_borderless)
1651 		return;
1652 
1653 	current_videomode.borderless_window = p_borderless;
1654 
1655 	Hints hints;
1656 	Atom property;
1657 	hints.flags = 2;
1658 	hints.decorations = current_videomode.borderless_window ? 0 : 1;
1659 	property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
1660 	XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
1661 
1662 	// Preserve window size
1663 	set_window_size(Size2(current_videomode.width, current_videomode.height));
1664 }
1665 
get_borderless_window()1666 bool OS_X11::get_borderless_window() {
1667 
1668 	bool borderless = current_videomode.borderless_window;
1669 	Atom prop = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
1670 	if (prop != None) {
1671 
1672 		Atom type;
1673 		int format;
1674 		unsigned long len;
1675 		unsigned long remaining;
1676 		unsigned char *data = NULL;
1677 		if (XGetWindowProperty(x11_display, x11_window, prop, 0, sizeof(Hints), False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) {
1678 			if (data && (format == 32) && (len >= 5)) {
1679 				borderless = !((Hints *)data)->decorations;
1680 			}
1681 			XFree(data);
1682 		}
1683 	}
1684 	return borderless;
1685 }
1686 
request_attention()1687 void OS_X11::request_attention() {
1688 	// Using EWMH -- Extended Window Manager Hints
1689 	//
1690 	// Sets the _NET_WM_STATE_DEMANDS_ATTENTION atom for WM_STATE
1691 	// Will be unset by the window manager after user react on the request for attention
1692 
1693 	XEvent xev;
1694 	Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
1695 	Atom wm_attention = XInternAtom(x11_display, "_NET_WM_STATE_DEMANDS_ATTENTION", False);
1696 
1697 	memset(&xev, 0, sizeof(xev));
1698 	xev.type = ClientMessage;
1699 	xev.xclient.window = x11_window;
1700 	xev.xclient.message_type = wm_state;
1701 	xev.xclient.format = 32;
1702 	xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
1703 	xev.xclient.data.l[1] = wm_attention;
1704 
1705 	XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
1706 	XFlush(x11_display);
1707 }
1708 
get_key_modifier_state(unsigned int p_x11_state,Ref<InputEventWithModifiers> state)1709 void OS_X11::get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state) {
1710 
1711 	state->set_shift((p_x11_state & ShiftMask));
1712 	state->set_control((p_x11_state & ControlMask));
1713 	state->set_alt((p_x11_state & Mod1Mask /*|| p_x11_state&Mod5Mask*/)); //altgr should not count as alt
1714 	state->set_metakey((p_x11_state & Mod4Mask));
1715 }
1716 
get_mouse_button_state(unsigned int p_x11_button,int p_x11_type)1717 unsigned int OS_X11::get_mouse_button_state(unsigned int p_x11_button, int p_x11_type) {
1718 
1719 	unsigned int mask = 1 << (p_x11_button - 1);
1720 
1721 	if (p_x11_type == ButtonPress) {
1722 		last_button_state |= mask;
1723 	} else {
1724 		last_button_state &= ~mask;
1725 	}
1726 
1727 	return last_button_state;
1728 }
1729 
handle_key_event(XKeyEvent * p_event,bool p_echo)1730 void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
1731 
1732 	// X11 functions don't know what const is
1733 	XKeyEvent *xkeyevent = p_event;
1734 
1735 	// This code was pretty difficult to write.
1736 	// The docs stink and every toolkit seems to
1737 	// do it in a different way.
1738 
1739 	/* Phase 1, obtain a proper keysym */
1740 
1741 	// This was also very difficult to figure out.
1742 	// You'd expect you could just use Keysym provided by
1743 	// XKeycodeToKeysym to obtain internationalized
1744 	// input.. WRONG!!
1745 	// you must use XLookupString (???) which not only wastes
1746 	// cycles generating an unnecessary string, but also
1747 	// still works in half the cases. (won't handle deadkeys)
1748 	// For more complex input methods (deadkeys and more advanced)
1749 	// you have to use XmbLookupString (??).
1750 	// So.. then you have to chosse which of both results
1751 	// you want to keep.
1752 	// This is a real bizarreness and cpu waster.
1753 
1754 	KeySym keysym_keycode = 0; // keysym used to find a keycode
1755 	KeySym keysym_unicode = 0; // keysym used to find unicode
1756 
1757 	// XLookupString returns keysyms usable as nice scancodes/
1758 	char str[256 + 1];
1759 	XKeyEvent xkeyevent_no_mod = *xkeyevent;
1760 	xkeyevent_no_mod.state &= ~ShiftMask;
1761 	xkeyevent_no_mod.state &= ~ControlMask;
1762 	XLookupString(xkeyevent, str, 256, &keysym_unicode, NULL);
1763 	XLookupString(&xkeyevent_no_mod, NULL, 0, &keysym_keycode, NULL);
1764 
1765 	// Meanwhile, XLookupString returns keysyms useful for unicode.
1766 
1767 	if (!xmbstring) {
1768 		// keep a temporary buffer for the string
1769 		xmbstring = (char *)memalloc(sizeof(char) * 8);
1770 		xmblen = 8;
1771 	}
1772 
1773 	if (xkeyevent->type == KeyPress && xic) {
1774 
1775 		Status status;
1776 #ifdef X_HAVE_UTF8_STRING
1777 		int utf8len = 8;
1778 		char *utf8string = (char *)memalloc(sizeof(char) * utf8len);
1779 		int utf8bytes = Xutf8LookupString(xic, xkeyevent, utf8string,
1780 				utf8len - 1, &keysym_unicode, &status);
1781 		if (status == XBufferOverflow) {
1782 			utf8len = utf8bytes + 1;
1783 			utf8string = (char *)memrealloc(utf8string, utf8len);
1784 			utf8bytes = Xutf8LookupString(xic, xkeyevent, utf8string,
1785 					utf8len - 1, &keysym_unicode, &status);
1786 		}
1787 		utf8string[utf8bytes] = '\0';
1788 
1789 		if (status == XLookupChars) {
1790 			bool keypress = xkeyevent->type == KeyPress;
1791 			unsigned int keycode = KeyMappingX11::get_keycode(keysym_keycode);
1792 			if (keycode >= 'a' && keycode <= 'z')
1793 				keycode -= 'a' - 'A';
1794 
1795 			String tmp;
1796 			tmp.parse_utf8(utf8string, utf8bytes);
1797 			for (int i = 0; i < tmp.length(); i++) {
1798 				Ref<InputEventKey> k;
1799 				k.instance();
1800 				if (keycode == 0 && tmp[i] == 0) {
1801 					continue;
1802 				}
1803 
1804 				get_key_modifier_state(xkeyevent->state, k);
1805 
1806 				k->set_unicode(tmp[i]);
1807 
1808 				k->set_pressed(keypress);
1809 
1810 				k->set_scancode(keycode);
1811 
1812 				k->set_echo(false);
1813 
1814 				if (k->get_scancode() == KEY_BACKTAB) {
1815 					//make it consistent across platforms.
1816 					k->set_scancode(KEY_TAB);
1817 					k->set_shift(true);
1818 				}
1819 
1820 				input->accumulate_input_event(k);
1821 			}
1822 			memfree(utf8string);
1823 			return;
1824 		}
1825 		memfree(utf8string);
1826 #else
1827 		do {
1828 
1829 			int mnbytes = XmbLookupString(xic, xkeyevent, xmbstring, xmblen - 1, &keysym_unicode, &status);
1830 			xmbstring[mnbytes] = '\0';
1831 
1832 			if (status == XBufferOverflow) {
1833 				xmblen = mnbytes + 1;
1834 				xmbstring = (char *)memrealloc(xmbstring, xmblen);
1835 			}
1836 		} while (status == XBufferOverflow);
1837 #endif
1838 	}
1839 
1840 	/* Phase 2, obtain a pigui keycode from the keysym */
1841 
1842 	// KeyMappingX11 just translated the X11 keysym to a PIGUI
1843 	// keysym, so it works in all platforms the same.
1844 
1845 	unsigned int keycode = KeyMappingX11::get_keycode(keysym_keycode);
1846 
1847 	/* Phase 3, obtain a unicode character from the keysym */
1848 
1849 	// KeyMappingX11 also translates keysym to unicode.
1850 	// It does a binary search on a table to translate
1851 	// most properly.
1852 	unsigned int unicode = keysym_unicode > 0 ? KeyMappingX11::get_unicode_from_keysym(keysym_unicode) : 0;
1853 
1854 	/* Phase 4, determine if event must be filtered */
1855 
1856 	// This seems to be a side-effect of using XIM.
1857 	// XEventFilter looks like a core X11 function,
1858 	// but it's actually just used to see if we must
1859 	// ignore a deadkey, or events XIM determines
1860 	// must not reach the actual gui.
1861 	// Guess it was a design problem of the extension
1862 
1863 	bool keypress = xkeyevent->type == KeyPress;
1864 
1865 	if (keycode == 0 && unicode == 0)
1866 		return;
1867 
1868 	/* Phase 5, determine modifier mask */
1869 
1870 	// No problems here, except I had no way to
1871 	// know Mod1 was ALT and Mod4 was META (applekey/winkey)
1872 	// just tried Mods until i found them.
1873 
1874 	//print_verbose("mod1: "+itos(xkeyevent->state&Mod1Mask)+" mod 5: "+itos(xkeyevent->state&Mod5Mask));
1875 
1876 	Ref<InputEventKey> k;
1877 	k.instance();
1878 
1879 	get_key_modifier_state(xkeyevent->state, k);
1880 
1881 	/* Phase 6, determine echo character */
1882 
1883 	// Echo characters in X11 are a keyrelease and a keypress
1884 	// one after the other with the (almot) same timestamp.
1885 	// To detect them, i use XPeekEvent and check that their
1886 	// difference in time is below a threshold.
1887 
1888 	if (xkeyevent->type != KeyPress) {
1889 
1890 		p_echo = false;
1891 
1892 		// make sure there are events pending,
1893 		// so this call won't block.
1894 		if (XPending(x11_display) > 0) {
1895 			XEvent peek_event;
1896 			XPeekEvent(x11_display, &peek_event);
1897 
1898 			// I'm using a threshold of 5 msecs,
1899 			// since sometimes there seems to be a little
1900 			// jitter. I'm still not convinced that all this approach
1901 			// is correct, but the xorg developers are
1902 			// not very helpful today.
1903 
1904 			::Time tresh = ABSDIFF(peek_event.xkey.time, xkeyevent->time);
1905 			if (peek_event.type == KeyPress && tresh < 5) {
1906 				KeySym rk;
1907 				XLookupString((XKeyEvent *)&peek_event, str, 256, &rk, NULL);
1908 				if (rk == keysym_keycode) {
1909 					XEvent event;
1910 					XNextEvent(x11_display, &event); //erase next event
1911 					handle_key_event((XKeyEvent *)&event, true);
1912 					return; //ignore current, echo next
1913 				}
1914 			}
1915 
1916 			// use the time from peek_event so it always works
1917 		}
1918 
1919 		// save the time to check for echo when keypress happens
1920 	}
1921 
1922 	/* Phase 7, send event to Window */
1923 
1924 	k->set_pressed(keypress);
1925 
1926 	if (keycode >= 'a' && keycode <= 'z')
1927 		keycode -= 'a' - 'A';
1928 
1929 	k->set_scancode(keycode);
1930 	k->set_unicode(unicode);
1931 	k->set_echo(p_echo);
1932 
1933 	if (k->get_scancode() == KEY_BACKTAB) {
1934 		//make it consistent across platforms.
1935 		k->set_scancode(KEY_TAB);
1936 		k->set_shift(true);
1937 	}
1938 
1939 	//don't set mod state if modifier keys are released by themselves
1940 	//else event.is_action() will not work correctly here
1941 	if (!k->is_pressed()) {
1942 		if (k->get_scancode() == KEY_SHIFT)
1943 			k->set_shift(false);
1944 		else if (k->get_scancode() == KEY_CONTROL)
1945 			k->set_control(false);
1946 		else if (k->get_scancode() == KEY_ALT)
1947 			k->set_alt(false);
1948 		else if (k->get_scancode() == KEY_META)
1949 			k->set_metakey(false);
1950 	}
1951 
1952 	bool last_is_pressed = Input::get_singleton()->is_key_pressed(k->get_scancode());
1953 	if (k->is_pressed()) {
1954 		if (last_is_pressed) {
1955 			k->set_echo(true);
1956 		}
1957 	}
1958 
1959 	//printf("key: %x\n",k->get_scancode());
1960 	input->accumulate_input_event(k);
1961 }
1962 
1963 struct Property {
1964 	unsigned char *data;
1965 	int format, nitems;
1966 	Atom type;
1967 };
1968 
read_property(Display * p_display,Window p_window,Atom p_property)1969 static Property read_property(Display *p_display, Window p_window, Atom p_property) {
1970 
1971 	Atom actual_type;
1972 	int actual_format;
1973 	unsigned long nitems;
1974 	unsigned long bytes_after;
1975 	unsigned char *ret = 0;
1976 
1977 	int read_bytes = 1024;
1978 
1979 	//Keep trying to read the property until there are no
1980 	//bytes unread.
1981 	do {
1982 		if (ret != 0)
1983 			XFree(ret);
1984 
1985 		XGetWindowProperty(p_display, p_window, p_property, 0, read_bytes, False, AnyPropertyType,
1986 				&actual_type, &actual_format, &nitems, &bytes_after,
1987 				&ret);
1988 
1989 		read_bytes *= 2;
1990 
1991 	} while (bytes_after != 0);
1992 
1993 	Property p = { ret, actual_format, (int)nitems, actual_type };
1994 
1995 	return p;
1996 }
1997 
pick_target_from_list(Display * p_display,Atom * p_list,int p_count)1998 static Atom pick_target_from_list(Display *p_display, Atom *p_list, int p_count) {
1999 
2000 	static const char *target_type = "text/uri-list";
2001 
2002 	for (int i = 0; i < p_count; i++) {
2003 
2004 		Atom atom = p_list[i];
2005 
2006 		if (atom != None && String(XGetAtomName(p_display, atom)) == target_type)
2007 			return atom;
2008 	}
2009 	return None;
2010 }
2011 
pick_target_from_atoms(Display * p_disp,Atom p_t1,Atom p_t2,Atom p_t3)2012 static Atom pick_target_from_atoms(Display *p_disp, Atom p_t1, Atom p_t2, Atom p_t3) {
2013 
2014 	static const char *target_type = "text/uri-list";
2015 	if (p_t1 != None && String(XGetAtomName(p_disp, p_t1)) == target_type)
2016 		return p_t1;
2017 
2018 	if (p_t2 != None && String(XGetAtomName(p_disp, p_t2)) == target_type)
2019 		return p_t2;
2020 
2021 	if (p_t3 != None && String(XGetAtomName(p_disp, p_t3)) == target_type)
2022 		return p_t3;
2023 
2024 	return None;
2025 }
2026 
_window_changed(XEvent * event)2027 void OS_X11::_window_changed(XEvent *event) {
2028 
2029 	if (xic) {
2030 		//  Not portable.
2031 		set_ime_position(Point2(0, 1));
2032 	}
2033 	if ((event->xconfigure.width == current_videomode.width) &&
2034 			(event->xconfigure.height == current_videomode.height))
2035 		return;
2036 
2037 	current_videomode.width = event->xconfigure.width;
2038 	current_videomode.height = event->xconfigure.height;
2039 }
2040 
process_xevents()2041 void OS_X11::process_xevents() {
2042 
2043 	//printf("checking events %i\n", XPending(x11_display));
2044 
2045 	do_mouse_warp = false;
2046 
2047 	// Is the current mouse mode one where it needs to be grabbed.
2048 	bool mouse_mode_grab = mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED;
2049 
2050 	xi.pressure = 0;
2051 	xi.tilt = Vector2();
2052 	xi.pressure_supported = false;
2053 
2054 	while (XPending(x11_display) > 0) {
2055 		XEvent event;
2056 		XNextEvent(x11_display, &event);
2057 
2058 		if (XFilterEvent(&event, None)) {
2059 			continue;
2060 		}
2061 
2062 		if (XGetEventData(x11_display, &event.xcookie)) {
2063 
2064 			if (event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
2065 
2066 				XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
2067 				int index = event_data->detail;
2068 				Vector2 pos = Vector2(event_data->event_x, event_data->event_y);
2069 
2070 				switch (event_data->evtype) {
2071 					case XI_HierarchyChanged:
2072 					case XI_DeviceChanged: {
2073 						refresh_device_info();
2074 					} break;
2075 					case XI_RawMotion: {
2076 						XIRawEvent *raw_event = (XIRawEvent *)event_data;
2077 						int device_id = raw_event->deviceid;
2078 
2079 						// Determine the axis used (called valuators in XInput for some forsaken reason)
2080 						//  Mask is a bitmask indicating which axes are involved.
2081 						//  We are interested in the values of axes 0 and 1.
2082 						if (raw_event->valuators.mask_len <= 0) {
2083 							break;
2084 						}
2085 
2086 						const double *values = raw_event->raw_values;
2087 
2088 						double rel_x = 0.0;
2089 						double rel_y = 0.0;
2090 
2091 						if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_ABSX)) {
2092 							rel_x = *values;
2093 							values++;
2094 						}
2095 
2096 						if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_ABSY)) {
2097 							rel_y = *values;
2098 							values++;
2099 						}
2100 
2101 						if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_PRESSURE)) {
2102 							Map<int, Vector2>::Element *pen_pressure = xi.pen_pressure_range.find(device_id);
2103 							if (pen_pressure) {
2104 								Vector2 pen_pressure_range = pen_pressure->value();
2105 								if (pen_pressure_range != Vector2()) {
2106 									xi.pressure_supported = true;
2107 									xi.pressure = (*values - pen_pressure_range[0]) /
2108 												  (pen_pressure_range[1] - pen_pressure_range[0]);
2109 								}
2110 							}
2111 
2112 							values++;
2113 						}
2114 
2115 						if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_TILTX)) {
2116 							Map<int, Vector2>::Element *pen_tilt_x = xi.pen_tilt_x_range.find(device_id);
2117 							if (pen_tilt_x) {
2118 								Vector2 pen_tilt_x_range = pen_tilt_x->value();
2119 								if (pen_tilt_x_range != Vector2()) {
2120 									xi.tilt.x = ((*values - pen_tilt_x_range[0]) / (pen_tilt_x_range[1] - pen_tilt_x_range[0])) * 2 - 1;
2121 								}
2122 							}
2123 
2124 							values++;
2125 						}
2126 
2127 						if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_TILTY)) {
2128 							Map<int, Vector2>::Element *pen_tilt_y = xi.pen_tilt_y_range.find(device_id);
2129 							if (pen_tilt_y) {
2130 								Vector2 pen_tilt_y_range = pen_tilt_y->value();
2131 								if (pen_tilt_y_range != Vector2()) {
2132 									xi.tilt.y = ((*values - pen_tilt_y_range[0]) / (pen_tilt_y_range[1] - pen_tilt_y_range[0])) * 2 - 1;
2133 								}
2134 							}
2135 
2136 							values++;
2137 						}
2138 
2139 						// https://bugs.freedesktop.org/show_bug.cgi?id=71609
2140 						// http://lists.libsdl.org/pipermail/commits-libsdl.org/2015-June/000282.html
2141 						if (raw_event->time == xi.last_relative_time && rel_x == xi.relative_motion.x && rel_y == xi.relative_motion.y) {
2142 							break; // Flush duplicate to avoid overly fast motion
2143 						}
2144 
2145 						xi.old_raw_pos.x = xi.raw_pos.x;
2146 						xi.old_raw_pos.y = xi.raw_pos.y;
2147 						xi.raw_pos.x = rel_x;
2148 						xi.raw_pos.y = rel_y;
2149 
2150 						Map<int, Vector2>::Element *abs_info = xi.absolute_devices.find(device_id);
2151 
2152 						if (abs_info) {
2153 							// Absolute mode device
2154 							Vector2 mult = abs_info->value();
2155 
2156 							xi.relative_motion.x += (xi.raw_pos.x - xi.old_raw_pos.x) * mult.x;
2157 							xi.relative_motion.y += (xi.raw_pos.y - xi.old_raw_pos.y) * mult.y;
2158 						} else {
2159 							// Relative mode device
2160 							xi.relative_motion.x = xi.raw_pos.x;
2161 							xi.relative_motion.y = xi.raw_pos.y;
2162 						}
2163 
2164 						xi.last_relative_time = raw_event->time;
2165 					} break;
2166 #ifdef TOUCH_ENABLED
2167 					case XI_TouchBegin: // Fall-through
2168 							// Disabled hand-in-hand with the grabbing
2169 							//XIAllowTouchEvents(x11_display, event_data->deviceid, event_data->detail, x11_window, XIAcceptTouch);
2170 
2171 					case XI_TouchEnd: {
2172 
2173 						bool is_begin = event_data->evtype == XI_TouchBegin;
2174 
2175 						Ref<InputEventScreenTouch> st;
2176 						st.instance();
2177 						st->set_index(index);
2178 						st->set_position(pos);
2179 						st->set_pressed(is_begin);
2180 
2181 						if (is_begin) {
2182 							if (xi.state.has(index)) // Defensive
2183 								break;
2184 							xi.state[index] = pos;
2185 							if (xi.state.size() == 1) {
2186 								// X11 may send a motion event when a touch gesture begins, that would result
2187 								// in a spurious mouse motion event being sent to Godot; remember it to be able to filter it out
2188 								xi.mouse_pos_to_filter = pos;
2189 							}
2190 							input->accumulate_input_event(st);
2191 						} else {
2192 							if (!xi.state.has(index)) // Defensive
2193 								break;
2194 							xi.state.erase(index);
2195 							input->accumulate_input_event(st);
2196 						}
2197 					} break;
2198 
2199 					case XI_TouchUpdate: {
2200 
2201 						Map<int, Vector2>::Element *curr_pos_elem = xi.state.find(index);
2202 						if (!curr_pos_elem) { // Defensive
2203 							break;
2204 						}
2205 
2206 						if (curr_pos_elem->value() != pos) {
2207 
2208 							Ref<InputEventScreenDrag> sd;
2209 							sd.instance();
2210 							sd->set_index(index);
2211 							sd->set_position(pos);
2212 							sd->set_relative(pos - curr_pos_elem->value());
2213 							input->accumulate_input_event(sd);
2214 
2215 							curr_pos_elem->value() = pos;
2216 						}
2217 					} break;
2218 #endif
2219 				}
2220 			}
2221 		}
2222 		XFreeEventData(x11_display, &event.xcookie);
2223 
2224 		switch (event.type) {
2225 			case Expose:
2226 				Main::force_redraw();
2227 				break;
2228 
2229 			case NoExpose:
2230 				minimized = true;
2231 				break;
2232 
2233 			case VisibilityNotify: {
2234 				XVisibilityEvent *visibility = (XVisibilityEvent *)&event;
2235 				minimized = (visibility->state == VisibilityFullyObscured);
2236 			} break;
2237 			case LeaveNotify: {
2238 				if (main_loop && !mouse_mode_grab)
2239 					main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT);
2240 
2241 			} break;
2242 			case EnterNotify: {
2243 				if (main_loop && !mouse_mode_grab)
2244 					main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
2245 			} break;
2246 			case FocusIn:
2247 				minimized = false;
2248 				window_has_focus = true;
2249 				main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
2250 				window_focused = true;
2251 
2252 				if (mouse_mode_grab) {
2253 					// Show and update the cursor if confined and the window regained focus.
2254 					if (mouse_mode == MOUSE_MODE_CONFINED)
2255 						XUndefineCursor(x11_display, x11_window);
2256 					else if (mouse_mode == MOUSE_MODE_CAPTURED) // or re-hide it in captured mode
2257 						XDefineCursor(x11_display, x11_window, null_cursor);
2258 
2259 					XGrabPointer(
2260 							x11_display, x11_window, True,
2261 							ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
2262 							GrabModeAsync, GrabModeAsync, x11_window, None, CurrentTime);
2263 				}
2264 #ifdef TOUCH_ENABLED
2265 				// Grab touch devices to avoid OS gesture interference
2266 				/*for (int i = 0; i < xi.touch_devices.size(); ++i) {
2267 					XIGrabDevice(x11_display, xi.touch_devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &xi.touch_event_mask);
2268 				}*/
2269 #endif
2270 				if (xic) {
2271 					XSetICFocus(xic);
2272 				}
2273 				break;
2274 
2275 			case FocusOut:
2276 				window_has_focus = false;
2277 				input->release_pressed_events();
2278 				main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
2279 				window_focused = false;
2280 
2281 				if (mouse_mode_grab) {
2282 					//dear X11, I try, I really try, but you never work, you do whathever you want.
2283 					if (mouse_mode == MOUSE_MODE_CAPTURED) {
2284 						// Show the cursor if we're in captured mode so it doesn't look weird.
2285 						XUndefineCursor(x11_display, x11_window);
2286 					}
2287 					XUngrabPointer(x11_display, CurrentTime);
2288 				}
2289 #ifdef TOUCH_ENABLED
2290 				// Ungrab touch devices so input works as usual while we are unfocused
2291 				/*for (int i = 0; i < xi.touch_devices.size(); ++i) {
2292 					XIUngrabDevice(x11_display, xi.touch_devices[i], CurrentTime);
2293 				}*/
2294 
2295 				// Release every pointer to avoid sticky points
2296 				for (Map<int, Vector2>::Element *E = xi.state.front(); E; E = E->next()) {
2297 
2298 					Ref<InputEventScreenTouch> st;
2299 					st.instance();
2300 					st->set_index(E->key());
2301 					st->set_position(E->get());
2302 					input->accumulate_input_event(st);
2303 				}
2304 				xi.state.clear();
2305 #endif
2306 				if (xic) {
2307 					XUnsetICFocus(xic);
2308 				}
2309 				break;
2310 
2311 			case ConfigureNotify:
2312 				_window_changed(&event);
2313 				break;
2314 			case ButtonPress:
2315 			case ButtonRelease: {
2316 
2317 				/* exit in case of a mouse button press */
2318 				last_timestamp = event.xbutton.time;
2319 				if (mouse_mode == MOUSE_MODE_CAPTURED) {
2320 					event.xbutton.x = last_mouse_pos.x;
2321 					event.xbutton.y = last_mouse_pos.y;
2322 				}
2323 
2324 				Ref<InputEventMouseButton> mb;
2325 				mb.instance();
2326 
2327 				get_key_modifier_state(event.xbutton.state, mb);
2328 				mb->set_button_index(event.xbutton.button);
2329 				if (mb->get_button_index() == 2)
2330 					mb->set_button_index(3);
2331 				else if (mb->get_button_index() == 3)
2332 					mb->set_button_index(2);
2333 				mb->set_button_mask(get_mouse_button_state(mb->get_button_index(), event.xbutton.type));
2334 				mb->set_position(Vector2(event.xbutton.x, event.xbutton.y));
2335 				mb->set_global_position(mb->get_position());
2336 
2337 				mb->set_pressed((event.type == ButtonPress));
2338 
2339 				if (event.type == ButtonPress) {
2340 
2341 					uint64_t diff = get_ticks_usec() / 1000 - last_click_ms;
2342 
2343 					if (mb->get_button_index() == last_click_button_index) {
2344 
2345 						if (diff < 400 && Point2(last_click_pos).distance_to(Point2(event.xbutton.x, event.xbutton.y)) < 5) {
2346 
2347 							last_click_ms = 0;
2348 							last_click_pos = Point2(-100, -100);
2349 							last_click_button_index = -1;
2350 							mb->set_doubleclick(true);
2351 						}
2352 
2353 					} else if (mb->get_button_index() < 4 || mb->get_button_index() > 7) {
2354 						last_click_button_index = mb->get_button_index();
2355 					}
2356 
2357 					if (!mb->is_doubleclick()) {
2358 						last_click_ms += diff;
2359 						last_click_pos = Point2(event.xbutton.x, event.xbutton.y);
2360 					}
2361 				}
2362 
2363 				input->accumulate_input_event(mb);
2364 
2365 			} break;
2366 			case MotionNotify: {
2367 
2368 				// The X11 API requires filtering one-by-one through the motion
2369 				// notify events, in order to figure out which event is the one
2370 				// generated by warping the mouse pointer.
2371 
2372 				while (true) {
2373 					if (mouse_mode == MOUSE_MODE_CAPTURED && event.xmotion.x == current_videomode.width / 2 && event.xmotion.y == current_videomode.height / 2) {
2374 						//this is likely the warp event since it was warped here
2375 						center = Vector2(event.xmotion.x, event.xmotion.y);
2376 						break;
2377 					}
2378 
2379 					if (XPending(x11_display) > 0) {
2380 						XEvent tevent;
2381 						XPeekEvent(x11_display, &tevent);
2382 						if (tevent.type == MotionNotify) {
2383 							XNextEvent(x11_display, &event);
2384 						} else {
2385 							break;
2386 						}
2387 					} else {
2388 						break;
2389 					}
2390 				}
2391 
2392 				last_timestamp = event.xmotion.time;
2393 
2394 				// Motion is also simple.
2395 				// A little hack is in order
2396 				// to be able to send relative motion events.
2397 				Point2 pos(event.xmotion.x, event.xmotion.y);
2398 
2399 				// Avoidance of spurious mouse motion (see handling of touch)
2400 				bool filter = false;
2401 				// Adding some tolerance to match better Point2i to Vector2
2402 				if (xi.state.size() && Vector2(pos).distance_squared_to(xi.mouse_pos_to_filter) < 2) {
2403 					filter = true;
2404 				}
2405 				// Invalidate to avoid filtering a possible legitimate similar event coming later
2406 				xi.mouse_pos_to_filter = Vector2(1e10, 1e10);
2407 				if (filter) {
2408 					break;
2409 				}
2410 
2411 				if (mouse_mode == MOUSE_MODE_CAPTURED) {
2412 					if (xi.relative_motion.x == 0 && xi.relative_motion.y == 0) {
2413 						break;
2414 					}
2415 
2416 					Point2i new_center = pos;
2417 					pos = last_mouse_pos + xi.relative_motion;
2418 					center = new_center;
2419 					do_mouse_warp = window_has_focus; // warp the cursor if we're focused in
2420 				}
2421 
2422 				if (!last_mouse_pos_valid) {
2423 
2424 					last_mouse_pos = pos;
2425 					last_mouse_pos_valid = true;
2426 				}
2427 
2428 				// Hackish but relative mouse motion is already handled in the RawMotion event.
2429 				//  RawMotion does not provide the absolute mouse position (whereas MotionNotify does).
2430 				//  Therefore, RawMotion cannot be the authority on absolute mouse position.
2431 				//  RawMotion provides more precision than MotionNotify, which doesn't sense subpixel motion.
2432 				//  Therefore, MotionNotify cannot be the authority on relative mouse motion.
2433 				//  This means we need to take a combined approach...
2434 				Point2 rel;
2435 
2436 				// Only use raw input if in capture mode. Otherwise use the classic behavior.
2437 				if (mouse_mode == MOUSE_MODE_CAPTURED) {
2438 					rel = xi.relative_motion;
2439 				} else {
2440 					rel = pos - last_mouse_pos;
2441 				}
2442 
2443 				// Reset to prevent lingering motion
2444 				xi.relative_motion.x = 0;
2445 				xi.relative_motion.y = 0;
2446 
2447 				if (mouse_mode == MOUSE_MODE_CAPTURED) {
2448 					pos = Point2i(current_videomode.width / 2, current_videomode.height / 2);
2449 				}
2450 
2451 				Ref<InputEventMouseMotion> mm;
2452 				mm.instance();
2453 
2454 				if (xi.pressure_supported) {
2455 					mm->set_pressure(xi.pressure);
2456 				} else {
2457 					mm->set_pressure((get_mouse_button_state() & (1 << (BUTTON_LEFT - 1))) ? 1.0f : 0.0f);
2458 				}
2459 				mm->set_tilt(xi.tilt);
2460 
2461 				// Make the absolute position integral so it doesn't look _too_ weird :)
2462 				Point2i posi(pos);
2463 
2464 				get_key_modifier_state(event.xmotion.state, mm);
2465 				mm->set_button_mask(get_mouse_button_state());
2466 				mm->set_position(posi);
2467 				mm->set_global_position(posi);
2468 				input->set_mouse_position(posi);
2469 				mm->set_speed(input->get_last_mouse_speed());
2470 
2471 				mm->set_relative(rel);
2472 
2473 				last_mouse_pos = pos;
2474 
2475 				// printf("rel: %d,%d\n", rel.x, rel.y );
2476 				// Don't propagate the motion event unless we have focus
2477 				// this is so that the relative motion doesn't get messed up
2478 				// after we regain focus.
2479 				if (window_has_focus || !mouse_mode_grab)
2480 					input->accumulate_input_event(mm);
2481 
2482 			} break;
2483 			case KeyPress:
2484 			case KeyRelease: {
2485 
2486 				last_timestamp = event.xkey.time;
2487 
2488 				// key event is a little complex, so
2489 				// it will be handled in its own function.
2490 				handle_key_event((XKeyEvent *)&event);
2491 			} break;
2492 			case SelectionRequest: {
2493 
2494 				XSelectionRequestEvent *req;
2495 				XEvent e, respond;
2496 				e = event;
2497 
2498 				req = &(e.xselectionrequest);
2499 				if (req->target == XInternAtom(x11_display, "UTF8_STRING", 0) ||
2500 						req->target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) ||
2501 						req->target == XInternAtom(x11_display, "TEXT", 0) ||
2502 						req->target == XA_STRING ||
2503 						req->target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) ||
2504 						req->target == XInternAtom(x11_display, "text/plain", 0)) {
2505 					CharString clip = OS::get_clipboard().utf8();
2506 					XChangeProperty(x11_display,
2507 							req->requestor,
2508 							req->property,
2509 							req->target,
2510 							8,
2511 							PropModeReplace,
2512 							(unsigned char *)clip.get_data(),
2513 							clip.length());
2514 					respond.xselection.property = req->property;
2515 				} else if (req->target == XInternAtom(x11_display, "TARGETS", 0)) {
2516 
2517 					Atom data[7];
2518 					data[0] = XInternAtom(x11_display, "TARGETS", 0);
2519 					data[1] = XInternAtom(x11_display, "UTF8_STRING", 0);
2520 					data[2] = XInternAtom(x11_display, "COMPOUND_TEXT", 0);
2521 					data[3] = XInternAtom(x11_display, "TEXT", 0);
2522 					data[4] = XA_STRING;
2523 					data[5] = XInternAtom(x11_display, "text/plain;charset=utf-8", 0);
2524 					data[6] = XInternAtom(x11_display, "text/plain", 0);
2525 
2526 					XChangeProperty(x11_display,
2527 							req->requestor,
2528 							req->property,
2529 							XA_ATOM,
2530 							32,
2531 							PropModeReplace,
2532 							(unsigned char *)&data,
2533 							sizeof(data) / sizeof(data[0]));
2534 					respond.xselection.property = req->property;
2535 
2536 				} else {
2537 					char *targetname = XGetAtomName(x11_display, req->target);
2538 					printf("No Target '%s'\n", targetname);
2539 					if (targetname)
2540 						XFree(targetname);
2541 					respond.xselection.property = None;
2542 				}
2543 
2544 				respond.xselection.type = SelectionNotify;
2545 				respond.xselection.display = req->display;
2546 				respond.xselection.requestor = req->requestor;
2547 				respond.xselection.selection = req->selection;
2548 				respond.xselection.target = req->target;
2549 				respond.xselection.time = req->time;
2550 				XSendEvent(x11_display, req->requestor, True, NoEventMask, &respond);
2551 				XFlush(x11_display);
2552 			} break;
2553 
2554 			case SelectionNotify:
2555 
2556 				if (event.xselection.target == requested) {
2557 
2558 					Property p = read_property(x11_display, x11_window, XInternAtom(x11_display, "PRIMARY", 0));
2559 
2560 					Vector<String> files = String((char *)p.data).split("\n", false);
2561 					for (int i = 0; i < files.size(); i++) {
2562 						files.write[i] = files[i].replace("file://", "").http_unescape().strip_edges();
2563 					}
2564 					main_loop->drop_files(files);
2565 
2566 					//Reply that all is well.
2567 					XClientMessageEvent m;
2568 					memset(&m, 0, sizeof(m));
2569 					m.type = ClientMessage;
2570 					m.display = x11_display;
2571 					m.window = xdnd_source_window;
2572 					m.message_type = xdnd_finished;
2573 					m.format = 32;
2574 					m.data.l[0] = x11_window;
2575 					m.data.l[1] = 1;
2576 					m.data.l[2] = xdnd_action_copy; //We only ever copy.
2577 
2578 					XSendEvent(x11_display, xdnd_source_window, False, NoEventMask, (XEvent *)&m);
2579 				}
2580 				break;
2581 
2582 			case ClientMessage:
2583 
2584 				if ((unsigned int)event.xclient.data.l[0] == (unsigned int)wm_delete)
2585 					main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
2586 
2587 				else if ((unsigned int)event.xclient.message_type == (unsigned int)xdnd_enter) {
2588 
2589 					//File(s) have been dragged over the window, check for supported target (text/uri-list)
2590 					xdnd_version = (event.xclient.data.l[1] >> 24);
2591 					Window source = event.xclient.data.l[0];
2592 					bool more_than_3 = event.xclient.data.l[1] & 1;
2593 					if (more_than_3) {
2594 						Property p = read_property(x11_display, source, XInternAtom(x11_display, "XdndTypeList", False));
2595 						requested = pick_target_from_list(x11_display, (Atom *)p.data, p.nitems);
2596 					} else
2597 						requested = pick_target_from_atoms(x11_display, event.xclient.data.l[2], event.xclient.data.l[3], event.xclient.data.l[4]);
2598 				} else if ((unsigned int)event.xclient.message_type == (unsigned int)xdnd_position) {
2599 
2600 					//xdnd position event, reply with an XDND status message
2601 					//just depending on type of data for now
2602 					XClientMessageEvent m;
2603 					memset(&m, 0, sizeof(m));
2604 					m.type = ClientMessage;
2605 					m.display = event.xclient.display;
2606 					m.window = event.xclient.data.l[0];
2607 					m.message_type = xdnd_status;
2608 					m.format = 32;
2609 					m.data.l[0] = x11_window;
2610 					m.data.l[1] = (requested != None);
2611 					m.data.l[2] = 0; //empty rectangle
2612 					m.data.l[3] = 0;
2613 					m.data.l[4] = xdnd_action_copy;
2614 
2615 					XSendEvent(x11_display, event.xclient.data.l[0], False, NoEventMask, (XEvent *)&m);
2616 					XFlush(x11_display);
2617 				} else if ((unsigned int)event.xclient.message_type == (unsigned int)xdnd_drop) {
2618 
2619 					if (requested != None) {
2620 						xdnd_source_window = event.xclient.data.l[0];
2621 						if (xdnd_version >= 1)
2622 							XConvertSelection(x11_display, xdnd_selection, requested, XInternAtom(x11_display, "PRIMARY", 0), x11_window, event.xclient.data.l[2]);
2623 						else
2624 							XConvertSelection(x11_display, xdnd_selection, requested, XInternAtom(x11_display, "PRIMARY", 0), x11_window, CurrentTime);
2625 					} else {
2626 						//Reply that we're not interested.
2627 						XClientMessageEvent m;
2628 						memset(&m, 0, sizeof(m));
2629 						m.type = ClientMessage;
2630 						m.display = event.xclient.display;
2631 						m.window = event.xclient.data.l[0];
2632 						m.message_type = xdnd_finished;
2633 						m.format = 32;
2634 						m.data.l[0] = x11_window;
2635 						m.data.l[1] = 0;
2636 						m.data.l[2] = None; //Failed.
2637 						XSendEvent(x11_display, event.xclient.data.l[0], False, NoEventMask, (XEvent *)&m);
2638 					}
2639 				}
2640 				break;
2641 			default:
2642 				break;
2643 		}
2644 	}
2645 
2646 	XFlush(x11_display);
2647 
2648 	if (do_mouse_warp) {
2649 
2650 		XWarpPointer(x11_display, None, x11_window,
2651 				0, 0, 0, 0, (int)current_videomode.width / 2, (int)current_videomode.height / 2);
2652 
2653 		/*
2654 		Window root, child;
2655 		int root_x, root_y;
2656 		int win_x, win_y;
2657 		unsigned int mask;
2658 		XQueryPointer( x11_display, x11_window, &root, &child, &root_x, &root_y, &win_x, &win_y, &mask );
2659 
2660 		printf("Root: %d,%d\n", root_x, root_y);
2661 		printf("Win: %d,%d\n", win_x, win_y);
2662 		*/
2663 	}
2664 
2665 	input->flush_accumulated_events();
2666 }
2667 
get_main_loop() const2668 MainLoop *OS_X11::get_main_loop() const {
2669 
2670 	return main_loop;
2671 }
2672 
delete_main_loop()2673 void OS_X11::delete_main_loop() {
2674 
2675 	if (main_loop)
2676 		memdelete(main_loop);
2677 	main_loop = NULL;
2678 }
2679 
set_main_loop(MainLoop * p_main_loop)2680 void OS_X11::set_main_loop(MainLoop *p_main_loop) {
2681 
2682 	main_loop = p_main_loop;
2683 	input->set_main_loop(p_main_loop);
2684 }
2685 
can_draw() const2686 bool OS_X11::can_draw() const {
2687 
2688 	return !minimized;
2689 };
2690 
set_clipboard(const String & p_text)2691 void OS_X11::set_clipboard(const String &p_text) {
2692 
2693 	OS::set_clipboard(p_text);
2694 
2695 	XSetSelectionOwner(x11_display, XA_PRIMARY, x11_window, CurrentTime);
2696 	XSetSelectionOwner(x11_display, XInternAtom(x11_display, "CLIPBOARD", 0), x11_window, CurrentTime);
2697 };
2698 
_get_clipboard_impl(Atom p_source,Window x11_window,::Display * x11_display,String p_internal_clipboard,Atom target)2699 static String _get_clipboard_impl(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard, Atom target) {
2700 
2701 	String ret;
2702 
2703 	Atom type;
2704 	Atom selection = XA_PRIMARY;
2705 	int format, result;
2706 	unsigned long len, bytes_left, dummy;
2707 	unsigned char *data;
2708 	Window Sown = XGetSelectionOwner(x11_display, p_source);
2709 
2710 	if (Sown == x11_window) {
2711 
2712 		return p_internal_clipboard;
2713 	};
2714 
2715 	if (Sown != None) {
2716 		XConvertSelection(x11_display, p_source, target, selection,
2717 				x11_window, CurrentTime);
2718 		XFlush(x11_display);
2719 		while (true) {
2720 			XEvent event;
2721 			XNextEvent(x11_display, &event);
2722 			if (event.type == SelectionNotify && event.xselection.requestor == x11_window) {
2723 				break;
2724 			};
2725 		};
2726 
2727 		//
2728 		// Do not get any data, see how much data is there
2729 		//
2730 		XGetWindowProperty(x11_display, x11_window,
2731 				selection, // Tricky..
2732 				0, 0, // offset - len
2733 				0, // Delete 0==FALSE
2734 				AnyPropertyType, //flag
2735 				&type, // return type
2736 				&format, // return format
2737 				&len, &bytes_left, //that
2738 				&data);
2739 		// DATA is There
2740 		if (bytes_left > 0) {
2741 			result = XGetWindowProperty(x11_display, x11_window,
2742 					selection, 0, bytes_left, 0,
2743 					AnyPropertyType, &type, &format,
2744 					&len, &dummy, &data);
2745 			if (result == Success) {
2746 				ret.parse_utf8((const char *)data);
2747 			} else
2748 				printf("FAIL\n");
2749 			XFree(data);
2750 		}
2751 	}
2752 
2753 	return ret;
2754 }
2755 
_get_clipboard(Atom p_source,Window x11_window,::Display * x11_display,String p_internal_clipboard)2756 static String _get_clipboard(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard) {
2757 	String ret;
2758 	Atom utf8_atom = XInternAtom(x11_display, "UTF8_STRING", True);
2759 	if (utf8_atom != None) {
2760 		ret = _get_clipboard_impl(p_source, x11_window, x11_display, p_internal_clipboard, utf8_atom);
2761 	}
2762 	if (ret == "") {
2763 		ret = _get_clipboard_impl(p_source, x11_window, x11_display, p_internal_clipboard, XA_STRING);
2764 	}
2765 	return ret;
2766 }
2767 
get_clipboard() const2768 String OS_X11::get_clipboard() const {
2769 
2770 	String ret;
2771 	ret = _get_clipboard(XInternAtom(x11_display, "CLIPBOARD", 0), x11_window, x11_display, OS::get_clipboard());
2772 
2773 	if (ret == "") {
2774 		ret = _get_clipboard(XA_PRIMARY, x11_window, x11_display, OS::get_clipboard());
2775 	};
2776 
2777 	return ret;
2778 }
2779 
get_name() const2780 String OS_X11::get_name() const {
2781 
2782 	return "X11";
2783 }
2784 
shell_open(String p_uri)2785 Error OS_X11::shell_open(String p_uri) {
2786 
2787 	Error ok;
2788 	List<String> args;
2789 	args.push_back(p_uri);
2790 	ok = execute("xdg-open", args, false);
2791 	if (ok == OK)
2792 		return OK;
2793 	ok = execute("gnome-open", args, false);
2794 	if (ok == OK)
2795 		return OK;
2796 	ok = execute("kde-open", args, false);
2797 	return ok;
2798 }
2799 
_check_internal_feature_support(const String & p_feature)2800 bool OS_X11::_check_internal_feature_support(const String &p_feature) {
2801 
2802 	return p_feature == "pc";
2803 }
2804 
get_config_path() const2805 String OS_X11::get_config_path() const {
2806 
2807 	if (has_environment("XDG_CONFIG_HOME")) {
2808 		return get_environment("XDG_CONFIG_HOME");
2809 	} else if (has_environment("HOME")) {
2810 		return get_environment("HOME").plus_file(".config");
2811 	} else {
2812 		return ".";
2813 	}
2814 }
2815 
get_data_path() const2816 String OS_X11::get_data_path() const {
2817 
2818 	if (has_environment("XDG_DATA_HOME")) {
2819 		return get_environment("XDG_DATA_HOME");
2820 	} else if (has_environment("HOME")) {
2821 		return get_environment("HOME").plus_file(".local/share");
2822 	} else {
2823 		return get_config_path();
2824 	}
2825 }
2826 
get_cache_path() const2827 String OS_X11::get_cache_path() const {
2828 
2829 	if (has_environment("XDG_CACHE_HOME")) {
2830 		return get_environment("XDG_CACHE_HOME");
2831 	} else if (has_environment("HOME")) {
2832 		return get_environment("HOME").plus_file(".cache");
2833 	} else {
2834 		return get_config_path();
2835 	}
2836 }
2837 
get_system_dir(SystemDir p_dir) const2838 String OS_X11::get_system_dir(SystemDir p_dir) const {
2839 
2840 	String xdgparam;
2841 
2842 	switch (p_dir) {
2843 		case SYSTEM_DIR_DESKTOP: {
2844 
2845 			xdgparam = "DESKTOP";
2846 		} break;
2847 		case SYSTEM_DIR_DCIM: {
2848 
2849 			xdgparam = "PICTURES";
2850 
2851 		} break;
2852 		case SYSTEM_DIR_DOCUMENTS: {
2853 
2854 			xdgparam = "DOCUMENTS";
2855 
2856 		} break;
2857 		case SYSTEM_DIR_DOWNLOADS: {
2858 
2859 			xdgparam = "DOWNLOAD";
2860 
2861 		} break;
2862 		case SYSTEM_DIR_MOVIES: {
2863 
2864 			xdgparam = "VIDEOS";
2865 
2866 		} break;
2867 		case SYSTEM_DIR_MUSIC: {
2868 
2869 			xdgparam = "MUSIC";
2870 
2871 		} break;
2872 		case SYSTEM_DIR_PICTURES: {
2873 
2874 			xdgparam = "PICTURES";
2875 
2876 		} break;
2877 		case SYSTEM_DIR_RINGTONES: {
2878 
2879 			xdgparam = "MUSIC";
2880 
2881 		} break;
2882 	}
2883 
2884 	String pipe;
2885 	List<String> arg;
2886 	arg.push_back(xdgparam);
2887 	Error err = const_cast<OS_X11 *>(this)->execute("xdg-user-dir", arg, true, NULL, &pipe);
2888 	if (err != OK)
2889 		return ".";
2890 	return pipe.strip_edges();
2891 }
2892 
move_window_to_foreground()2893 void OS_X11::move_window_to_foreground() {
2894 
2895 	XEvent xev;
2896 	Atom net_active_window = XInternAtom(x11_display, "_NET_ACTIVE_WINDOW", False);
2897 
2898 	memset(&xev, 0, sizeof(xev));
2899 	xev.type = ClientMessage;
2900 	xev.xclient.window = x11_window;
2901 	xev.xclient.message_type = net_active_window;
2902 	xev.xclient.format = 32;
2903 	xev.xclient.data.l[0] = 1;
2904 	xev.xclient.data.l[1] = CurrentTime;
2905 
2906 	XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
2907 	XFlush(x11_display);
2908 }
2909 
set_cursor_shape(CursorShape p_shape)2910 void OS_X11::set_cursor_shape(CursorShape p_shape) {
2911 
2912 	ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
2913 
2914 	if (p_shape == current_cursor) {
2915 		return;
2916 	}
2917 
2918 	if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
2919 		if (cursors[p_shape] != None) {
2920 			XDefineCursor(x11_display, x11_window, cursors[p_shape]);
2921 		} else if (cursors[CURSOR_ARROW] != None) {
2922 			XDefineCursor(x11_display, x11_window, cursors[CURSOR_ARROW]);
2923 		}
2924 	}
2925 
2926 	current_cursor = p_shape;
2927 }
2928 
get_cursor_shape() const2929 OS::CursorShape OS_X11::get_cursor_shape() const {
2930 
2931 	return current_cursor;
2932 }
2933 
set_custom_mouse_cursor(const RES & p_cursor,CursorShape p_shape,const Vector2 & p_hotspot)2934 void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
2935 
2936 	if (p_cursor.is_valid()) {
2937 
2938 		Map<CursorShape, Vector<Variant> >::Element *cursor_c = cursors_cache.find(p_shape);
2939 
2940 		if (cursor_c) {
2941 			if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) {
2942 				set_cursor_shape(p_shape);
2943 				return;
2944 			}
2945 
2946 			cursors_cache.erase(p_shape);
2947 		}
2948 
2949 		Ref<Texture> texture = p_cursor;
2950 		Ref<AtlasTexture> atlas_texture = p_cursor;
2951 		Ref<Image> image;
2952 		Size2 texture_size;
2953 		Rect2 atlas_rect;
2954 
2955 		if (texture.is_valid()) {
2956 			image = texture->get_data();
2957 		}
2958 
2959 		if (!image.is_valid() && atlas_texture.is_valid()) {
2960 			texture = atlas_texture->get_atlas();
2961 
2962 			atlas_rect.size.width = texture->get_width();
2963 			atlas_rect.size.height = texture->get_height();
2964 			atlas_rect.position.x = atlas_texture->get_region().position.x;
2965 			atlas_rect.position.y = atlas_texture->get_region().position.y;
2966 
2967 			texture_size.width = atlas_texture->get_region().size.x;
2968 			texture_size.height = atlas_texture->get_region().size.y;
2969 		} else if (image.is_valid()) {
2970 			texture_size.width = texture->get_width();
2971 			texture_size.height = texture->get_height();
2972 		}
2973 
2974 		ERR_FAIL_COND(!texture.is_valid());
2975 		ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0);
2976 		ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
2977 		ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
2978 
2979 		image = texture->get_data();
2980 
2981 		ERR_FAIL_COND(!image.is_valid());
2982 
2983 		// Create the cursor structure
2984 		XcursorImage *cursor_image = XcursorImageCreate(texture_size.width, texture_size.height);
2985 		XcursorUInt image_size = texture_size.width * texture_size.height;
2986 		XcursorDim size = sizeof(XcursorPixel) * image_size;
2987 
2988 		cursor_image->version = 1;
2989 		cursor_image->size = size;
2990 		cursor_image->xhot = p_hotspot.x;
2991 		cursor_image->yhot = p_hotspot.y;
2992 
2993 		// allocate memory to contain the whole file
2994 		cursor_image->pixels = (XcursorPixel *)memalloc(size);
2995 
2996 		image->lock();
2997 
2998 		for (XcursorPixel index = 0; index < image_size; index++) {
2999 			int row_index = floor(index / texture_size.width) + atlas_rect.position.y;
3000 			int column_index = (index % int(texture_size.width)) + atlas_rect.position.x;
3001 
3002 			if (atlas_texture.is_valid()) {
3003 				column_index = MIN(column_index, atlas_rect.size.width - 1);
3004 				row_index = MIN(row_index, atlas_rect.size.height - 1);
3005 			}
3006 
3007 			*(cursor_image->pixels + index) = image->get_pixel(column_index, row_index).to_argb32();
3008 		}
3009 
3010 		image->unlock();
3011 
3012 		ERR_FAIL_COND(cursor_image->pixels == NULL);
3013 
3014 		// Save it for a further usage
3015 		cursors[p_shape] = XcursorImageLoadCursor(x11_display, cursor_image);
3016 
3017 		Vector<Variant> params;
3018 		params.push_back(p_cursor);
3019 		params.push_back(p_hotspot);
3020 		cursors_cache.insert(p_shape, params);
3021 
3022 		if (p_shape == current_cursor) {
3023 			if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
3024 				XDefineCursor(x11_display, x11_window, cursors[p_shape]);
3025 			}
3026 		}
3027 
3028 		memfree(cursor_image->pixels);
3029 		XcursorImageDestroy(cursor_image);
3030 	} else {
3031 		// Reset to default system cursor
3032 		if (img[p_shape]) {
3033 			cursors[p_shape] = XcursorImageLoadCursor(x11_display, img[p_shape]);
3034 		}
3035 
3036 		CursorShape c = current_cursor;
3037 		current_cursor = CURSOR_MAX;
3038 		set_cursor_shape(c);
3039 
3040 		cursors_cache.erase(p_shape);
3041 	}
3042 }
3043 
release_rendering_thread()3044 void OS_X11::release_rendering_thread() {
3045 
3046 #if defined(OPENGL_ENABLED)
3047 	context_gl->release_current();
3048 #endif
3049 }
3050 
make_rendering_thread()3051 void OS_X11::make_rendering_thread() {
3052 
3053 #if defined(OPENGL_ENABLED)
3054 	context_gl->make_current();
3055 #endif
3056 }
3057 
swap_buffers()3058 void OS_X11::swap_buffers() {
3059 
3060 #if defined(OPENGL_ENABLED)
3061 	context_gl->swap_buffers();
3062 #endif
3063 }
3064 
alert(const String & p_alert,const String & p_title)3065 void OS_X11::alert(const String &p_alert, const String &p_title) {
3066 	const char *message_programs[] = { "zenity", "kdialog", "Xdialog", "xmessage" };
3067 
3068 	String path = get_environment("PATH");
3069 	Vector<String> path_elems = path.split(":", false);
3070 	String program;
3071 
3072 	for (int i = 0; i < path_elems.size(); i++) {
3073 		for (uint64_t k = 0; k < sizeof(message_programs) / sizeof(char *); k++) {
3074 			String tested_path = path_elems[i].plus_file(message_programs[k]);
3075 
3076 			if (FileAccess::exists(tested_path)) {
3077 				program = tested_path;
3078 				break;
3079 			}
3080 		}
3081 
3082 		if (program.length())
3083 			break;
3084 	}
3085 
3086 	List<String> args;
3087 
3088 	if (program.ends_with("zenity")) {
3089 		args.push_back("--error");
3090 		args.push_back("--width");
3091 		args.push_back("500");
3092 		args.push_back("--title");
3093 		args.push_back(p_title);
3094 		args.push_back("--text");
3095 		args.push_back(p_alert);
3096 	}
3097 
3098 	if (program.ends_with("kdialog")) {
3099 		args.push_back("--error");
3100 		args.push_back(p_alert);
3101 		args.push_back("--title");
3102 		args.push_back(p_title);
3103 	}
3104 
3105 	if (program.ends_with("Xdialog")) {
3106 		args.push_back("--title");
3107 		args.push_back(p_title);
3108 		args.push_back("--msgbox");
3109 		args.push_back(p_alert);
3110 		args.push_back("0");
3111 		args.push_back("0");
3112 	}
3113 
3114 	if (program.ends_with("xmessage")) {
3115 		args.push_back("-center");
3116 		args.push_back("-title");
3117 		args.push_back(p_title);
3118 		args.push_back(p_alert);
3119 	}
3120 
3121 	if (program.length()) {
3122 		execute(program, args, true);
3123 	} else {
3124 		print_line(p_alert);
3125 	}
3126 }
3127 
3128 bool g_set_icon_error = false;
set_icon_errorhandler(Display * dpy,XErrorEvent * ev)3129 int set_icon_errorhandler(Display *dpy, XErrorEvent *ev) {
3130 	g_set_icon_error = true;
3131 	return 0;
3132 }
3133 
set_icon(const Ref<Image> & p_icon)3134 void OS_X11::set_icon(const Ref<Image> &p_icon) {
3135 	int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&set_icon_errorhandler);
3136 
3137 	Atom net_wm_icon = XInternAtom(x11_display, "_NET_WM_ICON", False);
3138 
3139 	if (p_icon.is_valid()) {
3140 		Ref<Image> img = p_icon->duplicate();
3141 		img->convert(Image::FORMAT_RGBA8);
3142 
3143 		while (true) {
3144 			int w = img->get_width();
3145 			int h = img->get_height();
3146 
3147 			if (g_set_icon_error) {
3148 				g_set_icon_error = false;
3149 
3150 				WARN_PRINT("Icon too large, attempting to resize icon.");
3151 
3152 				int new_width, new_height;
3153 				if (w > h) {
3154 					new_width = w / 2;
3155 					new_height = h * new_width / w;
3156 				} else {
3157 					new_height = h / 2;
3158 					new_width = w * new_height / h;
3159 				}
3160 
3161 				w = new_width;
3162 				h = new_height;
3163 
3164 				if (!w || !h) {
3165 					WARN_PRINT("Unable to set icon.");
3166 					break;
3167 				}
3168 
3169 				img->resize(w, h, Image::INTERPOLATE_CUBIC);
3170 			}
3171 
3172 			// We're using long to have wordsize (32Bit build -> 32 Bits, 64 Bit build -> 64 Bits
3173 			Vector<long> pd;
3174 
3175 			pd.resize(2 + w * h);
3176 
3177 			pd.write[0] = w;
3178 			pd.write[1] = h;
3179 
3180 			PoolVector<uint8_t>::Read r = img->get_data().read();
3181 
3182 			long *wr = &pd.write[2];
3183 			uint8_t const *pr = r.ptr();
3184 
3185 			for (int i = 0; i < w * h; i++) {
3186 				long v = 0;
3187 				//    A             R             G            B
3188 				v |= pr[3] << 24 | pr[0] << 16 | pr[1] << 8 | pr[2];
3189 				*wr++ = v;
3190 				pr += 4;
3191 			}
3192 
3193 			XChangeProperty(x11_display, x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pd.ptr(), pd.size());
3194 
3195 			if (!g_set_icon_error)
3196 				break;
3197 		}
3198 	} else {
3199 		XDeleteProperty(x11_display, x11_window, net_wm_icon);
3200 	}
3201 
3202 	XFlush(x11_display);
3203 	XSetErrorHandler(oldHandler);
3204 }
3205 
force_process_input()3206 void OS_X11::force_process_input() {
3207 	process_xevents(); // get rid of pending events
3208 #ifdef JOYDEV_ENABLED
3209 	joypad->process_joypads();
3210 #endif
3211 }
3212 
run()3213 void OS_X11::run() {
3214 
3215 	force_quit = false;
3216 
3217 	if (!main_loop)
3218 		return;
3219 
3220 	main_loop->init();
3221 
3222 	//uint64_t last_ticks=get_ticks_usec();
3223 
3224 	//int frames=0;
3225 	//uint64_t frame=0;
3226 
3227 	while (!force_quit) {
3228 
3229 		process_xevents(); // get rid of pending events
3230 #ifdef JOYDEV_ENABLED
3231 		joypad->process_joypads();
3232 #endif
3233 		if (Main::iteration())
3234 			break;
3235 	};
3236 
3237 	main_loop->finish();
3238 }
3239 
is_joy_known(int p_device)3240 bool OS_X11::is_joy_known(int p_device) {
3241 	return input->is_joy_mapped(p_device);
3242 }
3243 
get_joy_guid(int p_device) const3244 String OS_X11::get_joy_guid(int p_device) const {
3245 	return input->get_joy_guid_remapped(p_device);
3246 }
3247 
_set_use_vsync(bool p_enable)3248 void OS_X11::_set_use_vsync(bool p_enable) {
3249 #if defined(OPENGL_ENABLED)
3250 	if (context_gl)
3251 		context_gl->set_use_vsync(p_enable);
3252 #endif
3253 }
3254 /*
3255 bool OS_X11::is_vsync_enabled() const {
3256 
3257 	if (context_gl)
3258 		return context_gl->is_using_vsync();
3259 
3260 	return true;
3261 }
3262 */
set_context(int p_context)3263 void OS_X11::set_context(int p_context) {
3264 
3265 	XClassHint *classHint = XAllocClassHint();
3266 
3267 	if (classHint) {
3268 
3269 		CharString name_str;
3270 		switch (p_context) {
3271 			case CONTEXT_EDITOR:
3272 				name_str = "Godot_Editor";
3273 				break;
3274 			case CONTEXT_PROJECTMAN:
3275 				name_str = "Godot_ProjectList";
3276 				break;
3277 			case CONTEXT_ENGINE:
3278 				name_str = "Godot_Engine";
3279 				break;
3280 		}
3281 
3282 		CharString class_str;
3283 		if (p_context == CONTEXT_ENGINE) {
3284 			String config_name = GLOBAL_GET("application/config/name");
3285 			if (config_name.length() == 0) {
3286 				class_str = "Godot_Engine";
3287 			} else {
3288 				class_str = config_name.utf8();
3289 			}
3290 		} else {
3291 			class_str = "Godot";
3292 		}
3293 
3294 		classHint->res_class = class_str.ptrw();
3295 		classHint->res_name = name_str.ptrw();
3296 
3297 		XSetClassHint(x11_display, x11_window, classHint);
3298 		XFree(classHint);
3299 	}
3300 }
3301 
get_power_state()3302 OS::PowerState OS_X11::get_power_state() {
3303 	return power_manager->get_power_state();
3304 }
3305 
get_power_seconds_left()3306 int OS_X11::get_power_seconds_left() {
3307 	return power_manager->get_power_seconds_left();
3308 }
3309 
get_power_percent_left()3310 int OS_X11::get_power_percent_left() {
3311 	return power_manager->get_power_percent_left();
3312 }
3313 
disable_crash_handler()3314 void OS_X11::disable_crash_handler() {
3315 	crash_handler.disable();
3316 }
3317 
is_disable_crash_handler() const3318 bool OS_X11::is_disable_crash_handler() const {
3319 	return crash_handler.is_disabled();
3320 }
3321 
get_mountpoint(const String & p_path)3322 static String get_mountpoint(const String &p_path) {
3323 	struct stat s;
3324 	if (stat(p_path.utf8().get_data(), &s)) {
3325 		return "";
3326 	}
3327 
3328 #ifdef HAVE_MNTENT
3329 	dev_t dev = s.st_dev;
3330 	FILE *fd = setmntent("/proc/mounts", "r");
3331 	if (!fd) {
3332 		return "";
3333 	}
3334 
3335 	struct mntent mnt;
3336 	char buf[1024];
3337 	size_t buflen = 1024;
3338 	while (getmntent_r(fd, &mnt, buf, buflen)) {
3339 		if (!stat(mnt.mnt_dir, &s) && s.st_dev == dev) {
3340 			endmntent(fd);
3341 			return String(mnt.mnt_dir);
3342 		}
3343 	}
3344 
3345 	endmntent(fd);
3346 #endif
3347 	return "";
3348 }
3349 
move_to_trash(const String & p_path)3350 Error OS_X11::move_to_trash(const String &p_path) {
3351 	String trash_can = "";
3352 	String mnt = get_mountpoint(p_path);
3353 
3354 	// If there is a directory "[Mountpoint]/.Trash-[UID]/files", use it as the trash can.
3355 	if (mnt != "") {
3356 		String path(mnt + "/.Trash-" + itos(getuid()) + "/files");
3357 		struct stat s;
3358 		if (!stat(path.utf8().get_data(), &s)) {
3359 			trash_can = path;
3360 		}
3361 	}
3362 
3363 	// Otherwise, if ${XDG_DATA_HOME} is defined, use "${XDG_DATA_HOME}/Trash/files" as the trash can.
3364 	if (trash_can == "") {
3365 		char *dhome = getenv("XDG_DATA_HOME");
3366 		if (dhome) {
3367 			trash_can = String(dhome) + "/Trash/files";
3368 		}
3369 	}
3370 
3371 	// Otherwise, if ${HOME} is defined, use "${HOME}/.local/share/Trash/files" as the trash can.
3372 	if (trash_can == "") {
3373 		char *home = getenv("HOME");
3374 		if (home) {
3375 			trash_can = String(home) + "/.local/share/Trash/files";
3376 		}
3377 	}
3378 
3379 	// Issue an error if none of the previous locations is appropriate for the trash can.
3380 	if (trash_can == "") {
3381 		ERR_PRINTS("move_to_trash: Could not determine the trash can location");
3382 		return FAILED;
3383 	}
3384 
3385 	// Create needed directories for decided trash can location.
3386 	DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
3387 	Error err = dir_access->make_dir_recursive(trash_can);
3388 	memdelete(dir_access);
3389 
3390 	// Issue an error if trash can is not created proprely.
3391 	if (err != OK) {
3392 		ERR_PRINTS("move_to_trash: Could not create the trash can \"" + trash_can + "\"");
3393 		return err;
3394 	}
3395 
3396 	// The trash can is successfully created, now move the given resource to it.
3397 	// Do not use DirAccess:rename() because it can't move files across multiple mountpoints.
3398 	List<String> mv_args;
3399 	mv_args.push_back(p_path);
3400 	mv_args.push_back(trash_can);
3401 	int retval;
3402 	err = execute("mv", mv_args, true, NULL, NULL, &retval);
3403 
3404 	// Issue an error if "mv" failed to move the given resource to the trash can.
3405 	if (err != OK || retval != 0) {
3406 		ERR_PRINTS("move_to_trash: Could not move the resource \"" + p_path + "\" to the trash can \"" + trash_can + "\"");
3407 		return FAILED;
3408 	}
3409 
3410 	return OK;
3411 }
3412 
get_latin_keyboard_variant() const3413 OS::LatinKeyboardVariant OS_X11::get_latin_keyboard_variant() const {
3414 
3415 	XkbDescRec *xkbdesc = XkbAllocKeyboard();
3416 	ERR_FAIL_COND_V(!xkbdesc, LATIN_KEYBOARD_QWERTY);
3417 
3418 	XkbGetNames(x11_display, XkbSymbolsNameMask, xkbdesc);
3419 	ERR_FAIL_COND_V(!xkbdesc->names, LATIN_KEYBOARD_QWERTY);
3420 	ERR_FAIL_COND_V(!xkbdesc->names->symbols, LATIN_KEYBOARD_QWERTY);
3421 
3422 	char *layout = XGetAtomName(x11_display, xkbdesc->names->symbols);
3423 	ERR_FAIL_COND_V(!layout, LATIN_KEYBOARD_QWERTY);
3424 
3425 	Vector<String> info = String(layout).split("+");
3426 	ERR_FAIL_INDEX_V(1, info.size(), LATIN_KEYBOARD_QWERTY);
3427 
3428 	if (info[1].find("colemak") != -1) {
3429 		return LATIN_KEYBOARD_COLEMAK;
3430 	} else if (info[1].find("qwertz") != -1) {
3431 		return LATIN_KEYBOARD_QWERTZ;
3432 	} else if (info[1].find("azerty") != -1) {
3433 		return LATIN_KEYBOARD_AZERTY;
3434 	} else if (info[1].find("qzerty") != -1) {
3435 		return LATIN_KEYBOARD_QZERTY;
3436 	} else if (info[1].find("dvorak") != -1) {
3437 		return LATIN_KEYBOARD_DVORAK;
3438 	} else if (info[1].find("neo") != -1) {
3439 		return LATIN_KEYBOARD_NEO;
3440 	}
3441 
3442 	return LATIN_KEYBOARD_QWERTY;
3443 }
3444 
keyboard_get_layout_count() const3445 int OS_X11::keyboard_get_layout_count() const {
3446 	int _group_count = 0;
3447 	XkbDescRec *kbd = XkbAllocKeyboard();
3448 	if (kbd) {
3449 		kbd->dpy = x11_display;
3450 		XkbGetControls(x11_display, XkbAllControlsMask, kbd);
3451 		XkbGetNames(x11_display, XkbSymbolsNameMask, kbd);
3452 
3453 		const Atom *groups = kbd->names->groups;
3454 		if (kbd->ctrls != NULL) {
3455 			_group_count = kbd->ctrls->num_groups;
3456 		} else {
3457 			while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) {
3458 				_group_count++;
3459 			}
3460 		}
3461 		XkbFreeKeyboard(kbd, 0, true);
3462 	}
3463 	return _group_count;
3464 }
3465 
keyboard_get_current_layout() const3466 int OS_X11::keyboard_get_current_layout() const {
3467 	XkbStateRec state;
3468 	XkbGetState(x11_display, XkbUseCoreKbd, &state);
3469 	return state.group;
3470 }
3471 
keyboard_set_current_layout(int p_index)3472 void OS_X11::keyboard_set_current_layout(int p_index) {
3473 	ERR_FAIL_INDEX(p_index, keyboard_get_layout_count());
3474 	XkbLockGroup(x11_display, XkbUseCoreKbd, p_index);
3475 }
3476 
keyboard_get_layout_language(int p_index) const3477 String OS_X11::keyboard_get_layout_language(int p_index) const {
3478 	String ret;
3479 	XkbDescRec *kbd = XkbAllocKeyboard();
3480 	if (kbd) {
3481 		kbd->dpy = x11_display;
3482 		XkbGetControls(x11_display, XkbAllControlsMask, kbd);
3483 		XkbGetNames(x11_display, XkbSymbolsNameMask, kbd);
3484 		XkbGetNames(x11_display, XkbGroupNamesMask, kbd);
3485 
3486 		int _group_count = 0;
3487 		const Atom *groups = kbd->names->groups;
3488 		if (kbd->ctrls != NULL) {
3489 			_group_count = kbd->ctrls->num_groups;
3490 		} else {
3491 			while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) {
3492 				_group_count++;
3493 			}
3494 		}
3495 
3496 		Atom names = kbd->names->symbols;
3497 		if (names != None) {
3498 			char *name = XGetAtomName(x11_display, names);
3499 			Vector<String> info = String(name).split("+");
3500 			if (p_index >= 0 && p_index < _group_count) {
3501 				if (p_index + 1 < info.size()) {
3502 					ret = info[p_index + 1]; // Skip "pc" at the start and "inet"/"group" at the end of symbols.
3503 				} else {
3504 					ret = "en"; // No symbol for layout fallback to "en".
3505 				}
3506 			} else {
3507 				ERR_PRINT("Index " + itos(p_index) + "is out of bounds (" + itos(_group_count) + ").");
3508 			}
3509 			XFree(name);
3510 		}
3511 		XkbFreeKeyboard(kbd, 0, true);
3512 	}
3513 	return ret.substr(0, 2);
3514 }
3515 
keyboard_get_layout_name(int p_index) const3516 String OS_X11::keyboard_get_layout_name(int p_index) const {
3517 	String ret;
3518 	XkbDescRec *kbd = XkbAllocKeyboard();
3519 	if (kbd) {
3520 		kbd->dpy = x11_display;
3521 		XkbGetControls(x11_display, XkbAllControlsMask, kbd);
3522 		XkbGetNames(x11_display, XkbSymbolsNameMask, kbd);
3523 		XkbGetNames(x11_display, XkbGroupNamesMask, kbd);
3524 
3525 		int _group_count = 0;
3526 		const Atom *groups = kbd->names->groups;
3527 		if (kbd->ctrls != NULL) {
3528 			_group_count = kbd->ctrls->num_groups;
3529 		} else {
3530 			while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) {
3531 				_group_count++;
3532 			}
3533 		}
3534 
3535 		if (p_index >= 0 && p_index < _group_count) {
3536 			char *full_name = XGetAtomName(x11_display, groups[p_index]);
3537 			ret.parse_utf8(full_name);
3538 			XFree(full_name);
3539 		} else {
3540 			ERR_PRINT("Index " + itos(p_index) + "is out of bounds (" + itos(_group_count) + ").");
3541 		}
3542 		XkbFreeKeyboard(kbd, 0, true);
3543 	}
3544 	return ret;
3545 }
3546 
update_real_mouse_position()3547 void OS_X11::update_real_mouse_position() {
3548 	Window root_return, child_return;
3549 	int root_x, root_y, win_x, win_y;
3550 	unsigned int mask_return;
3551 
3552 	Bool xquerypointer_result = XQueryPointer(x11_display, x11_window, &root_return, &child_return, &root_x, &root_y,
3553 			&win_x, &win_y, &mask_return);
3554 
3555 	if (xquerypointer_result) {
3556 		if (win_x > 0 && win_y > 0 && win_x <= current_videomode.width && win_y <= current_videomode.height) {
3557 
3558 			last_mouse_pos.x = win_x;
3559 			last_mouse_pos.y = win_y;
3560 			last_mouse_pos_valid = true;
3561 			input->set_mouse_position(last_mouse_pos);
3562 		}
3563 	}
3564 }
3565 
OS_X11()3566 OS_X11::OS_X11() {
3567 
3568 #ifdef PULSEAUDIO_ENABLED
3569 	AudioDriverManager::add_driver(&driver_pulseaudio);
3570 #endif
3571 
3572 #ifdef ALSA_ENABLED
3573 	AudioDriverManager::add_driver(&driver_alsa);
3574 #endif
3575 
3576 	xi.opcode = 0;
3577 	xi.last_relative_time = 0;
3578 	layered_window = false;
3579 	minimized = false;
3580 	window_focused = true;
3581 	xim_style = 0L;
3582 	mouse_mode = MOUSE_MODE_VISIBLE;
3583 	last_position_before_fs = Vector2();
3584 }
3585