1 /* RetroArch - A frontend for libretro.
2 * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
3 * Copyright (C) 2011-2017 - Daniel De Matteis
4 *
5 * RetroArch is free software: you can redistribute it and/or modify it under the terms
6 * of the GNU General Public License as published by the Free Software Found-
7 * ation, either version 3 of the License, or (at your option) any later version.
8 *
9 * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along with RetroArch.
14 * If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 #include <stdlib.h>
18 #include <string.h>
19 #include <math.h>
20
21 #include <sys/types.h>
22 #include <sys/wait.h>
23
24 #include <errno.h>
25 #include <unistd.h>
26
27 #include <X11/Xatom.h>
28
29 #ifdef HAVE_CONFIG_H
30 #include "../../config.h"
31 #endif
32
33 #ifdef HAVE_XINERAMA
34 #include <X11/extensions/Xinerama.h>
35 #endif
36
37 #include "x11_common.h"
38
39 #include <X11/extensions/xf86vmode.h>
40
41 #include <encodings/utf.h>
42 #include <compat/strl.h>
43
44 #ifdef HAVE_DBUS
45 #include "dbus_common.h"
46 #endif
47
48 #include "../../frontend/frontend_driver.h"
49 #include "../../input/input_driver.h"
50 #include "../../input/input_keymaps.h"
51 #include "../../input/common/input_x11_common.h"
52 #include "../../configuration.h"
53 #include "../../verbosity.h"
54
55 #define _NET_WM_STATE_ADD 1
56 #define MOVERESIZE_GRAVITY_CENTER 5
57 #define MOVERESIZE_X_SHIFT 8
58 #define MOVERESIZE_Y_SHIFT 9
59
60 #define V_DBLSCAN 0x20
61
62 /* TODO/FIXME - globals */
63 bool g_x11_entered = false;
64 Display *g_x11_dpy = NULL;
65 unsigned g_x11_screen = 0;
66 Window g_x11_win = None;
67 Colormap g_x11_cmap;
68
69 /* TODO/FIXME - static globals */
70 static XF86VidModeModeInfo desktop_mode;
71 static bool xdg_screensaver_available = true;
72 static bool g_x11_has_focus = false;
73 static bool g_x11_true_full = false;
74 static XConfigureEvent g_x11_xce = {0};
75 static Atom XA_NET_WM_STATE;
76 static Atom XA_NET_WM_STATE_FULLSCREEN;
77 static Atom XA_NET_MOVERESIZE_WINDOW;
78 static Atom g_x11_quit_atom;
79 static XIM g_x11_xim;
80 static XIC g_x11_xic;
81
x11_hide_mouse(Display * dpy,Window win)82 static void x11_hide_mouse(Display *dpy, Window win)
83 {
84 Cursor no_ptr;
85 Pixmap bm_no;
86 XColor black, dummy;
87 static char bm_no_data[] = {0, 0, 0, 0, 0, 0, 0, 0};
88 Colormap colormap = DefaultColormap(dpy, DefaultScreen(dpy));
89
90 if (!XAllocNamedColor(dpy, colormap, "black", &black, &dummy))
91 return;
92
93 bm_no = XCreateBitmapFromData(dpy, win, bm_no_data, 8, 8);
94 no_ptr = XCreatePixmapCursor(dpy, bm_no, bm_no, &black, &black, 0, 0);
95
96 XDefineCursor(dpy, win, no_ptr);
97 XFreeCursor(dpy, no_ptr);
98
99 if (bm_no != None)
100 XFreePixmap(dpy, bm_no);
101
102 XFreeColors(dpy, colormap, &black.pixel, 1, 0);
103 }
104
x11_show_mouse(Display * dpy,Window win,bool state)105 void x11_show_mouse(Display *dpy, Window win, bool state)
106 {
107 if (state)
108 XUndefineCursor(dpy, win);
109 else
110 x11_hide_mouse(dpy, win);
111 }
112
x11_set_net_wm_fullscreen(Display * dpy,Window win)113 void x11_set_net_wm_fullscreen(Display *dpy, Window win)
114 {
115 XEvent xev = {0};
116
117 XA_NET_WM_STATE = XInternAtom(dpy, "_NET_WM_STATE", False);
118 XA_NET_WM_STATE_FULLSCREEN = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
119
120 xev.xclient.type = ClientMessage;
121 xev.xclient.send_event = True;
122 xev.xclient.message_type = XA_NET_WM_STATE;
123 xev.xclient.window = win;
124 xev.xclient.format = 32;
125 xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
126 xev.xclient.data.l[1] = XA_NET_WM_STATE_FULLSCREEN;
127
128 XSendEvent(dpy, DefaultRootWindow(dpy), False,
129 SubstructureRedirectMask | SubstructureNotifyMask,
130 &xev);
131 }
132
133 /* Try to be nice to tiling WMs if possible. */
134
x11_move_window(Display * dpy,Window win,int x,int y,unsigned width,unsigned height)135 void x11_move_window(Display *dpy, Window win, int x, int y,
136 unsigned width, unsigned height)
137 {
138 XEvent xev = {0};
139
140 XA_NET_MOVERESIZE_WINDOW = XInternAtom(dpy,
141 "_NET_MOVERESIZE_WINDOW", False);
142
143 xev.xclient.type = ClientMessage;
144 xev.xclient.send_event = True;
145 xev.xclient.message_type = XA_NET_MOVERESIZE_WINDOW;
146 xev.xclient.window = win;
147 xev.xclient.format = 32;
148 xev.xclient.data.l[0] = (1 << MOVERESIZE_X_SHIFT)
149 | (1 << MOVERESIZE_Y_SHIFT);
150 xev.xclient.data.l[1] = x;
151 xev.xclient.data.l[2] = y;
152
153 XSendEvent(dpy, DefaultRootWindow(dpy), False,
154 SubstructureRedirectMask | SubstructureNotifyMask,
155 &xev);
156 }
157
x11_set_window_class(Display * dpy,Window win)158 static void x11_set_window_class(Display *dpy, Window win)
159 {
160 XClassHint hint;
161
162 hint.res_name = (char*)"retroarch"; /* Broken header. */
163 hint.res_class = (char*)"retroarch";
164 XSetClassHint(dpy, win, &hint);
165 }
166
x11_set_window_pid(Display * dpy,Window win)167 static void x11_set_window_pid(Display *dpy, Window win)
168 {
169 long scret = 0;
170 char *hostname = NULL;
171 pid_t pid = getpid();
172
173 XChangeProperty(dpy, win, XInternAtom(dpy, "_NET_WM_PID", False),
174 XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
175
176 errno = 0;
177 if ((scret = sysconf(_SC_HOST_NAME_MAX)) == -1 && errno)
178 return;
179 if (!(hostname = (char*)malloc(scret + 1)))
180 return;
181
182 if (gethostname(hostname, scret + 1) == -1)
183 RARCH_WARN("Failed to get hostname.\n");
184 else
185 {
186 XChangeProperty(dpy, win, XA_WM_CLIENT_MACHINE, XA_STRING, 8,
187 PropModeReplace, (unsigned char *)hostname, strlen(hostname));
188 }
189 free(hostname);
190 }
191
x11_set_window_attr(Display * dpy,Window win)192 void x11_set_window_attr(Display *dpy, Window win)
193 {
194 x11_set_window_class(dpy, win);
195 x11_set_window_pid(dpy, win);
196 }
197
xdg_screensaver_inhibit(Window wnd)198 static void xdg_screensaver_inhibit(Window wnd)
199 {
200 int ret;
201 char cmd[64];
202 char title[128];
203
204 cmd[0] = '\0';
205 title[0] = '\0';
206
207 RARCH_LOG("[X11]: Suspending screensaver (X11, xdg-screensaver).\n");
208
209 if (g_x11_dpy && g_x11_win)
210 {
211 /* Make sure the window has a title, even if it's a bogus one, otherwise
212 * xdg-screensaver will fail and report to stderr, framing RA for its bug.
213 * A single space character is used so that the title bar stays visibly
214 * the same, as if there's no title at all. */
215 video_driver_get_window_title(title, sizeof(title));
216 if (strlen(title) == 0)
217 snprintf(title, sizeof(title), " ");
218 XChangeProperty(g_x11_dpy, g_x11_win, XA_WM_NAME, XA_STRING,
219 8, PropModeReplace, (const unsigned char*) title,
220 strlen(title));
221 }
222
223 snprintf(cmd, sizeof(cmd), "xdg-screensaver suspend 0x%x", (int)wnd);
224
225 ret = system(cmd);
226 if (ret == -1)
227 {
228 xdg_screensaver_available = false;
229 RARCH_WARN("Failed to launch xdg-screensaver.\n");
230 }
231 else if (WEXITSTATUS(ret))
232 {
233 xdg_screensaver_available = false;
234 RARCH_WARN("Could not suspend screen saver.\n");
235 }
236 }
237
x11_suspend_screensaver(Window wnd,bool enable)238 void x11_suspend_screensaver(Window wnd, bool enable)
239 {
240 #ifdef HAVE_DBUS
241 if (dbus_suspend_screensaver(enable))
242 return;
243 #endif
244 if (enable)
245 if (xdg_screensaver_available)
246 xdg_screensaver_inhibit(wnd);
247 }
248
x11_get_refresh_rate(void * data)249 float x11_get_refresh_rate(void *data)
250 {
251 XWindowAttributes attr;
252 XF86VidModeModeLine modeline;
253 Screen *screen;
254 int screenid;
255 int dotclock;
256 float refresh;
257
258 if (!g_x11_dpy || g_x11_win == None)
259 return 0.0f;
260
261 if (!XGetWindowAttributes(g_x11_dpy, g_x11_win, &attr))
262 return 0.0f;
263
264 screen = attr.screen;
265 screenid = XScreenNumberOfScreen(screen);
266
267 XF86VidModeGetModeLine(g_x11_dpy, screenid, &dotclock, &modeline);
268
269 /* non-native modes like 1080p on a 4K display might use DoubleScan */
270 if (modeline.flags & V_DBLSCAN)
271 dotclock /= 2;
272
273 refresh = (float)dotclock * 1000.0f / modeline.htotal / modeline.vtotal;
274
275 return refresh;
276 }
277
get_video_mode(Display * dpy,unsigned width,unsigned height,XF86VidModeModeInfo * mode,XF86VidModeModeInfo * desktop_mode)278 static bool get_video_mode(
279 Display *dpy, unsigned width, unsigned height,
280 XF86VidModeModeInfo *mode, XF86VidModeModeInfo *desktop_mode)
281 {
282 int i, num_modes = 0;
283 bool ret = false;
284 float refresh_mod = 0.0f;
285 float minimum_fps_diff = 0.0f;
286 XF86VidModeModeInfo **modes = NULL;
287 settings_t *settings = config_get_ptr();
288 unsigned black_frame_insertion = settings->uints.video_black_frame_insertion;
289 float video_refresh_rate = settings->floats.video_refresh_rate;
290
291 XF86VidModeGetAllModeLines(dpy, DefaultScreen(dpy), &num_modes, &modes);
292
293 if (!num_modes)
294 {
295 XFree(modes);
296 return false;
297 }
298
299 *desktop_mode = *modes[0];
300
301 /* If we use black frame insertion, we fake a 60 Hz monitor
302 * for 120 Hz one, etc, so try to match that. */
303 refresh_mod = 1.0f / (black_frame_insertion + 1.0f);
304
305 for (i = 0; i < num_modes; i++)
306 {
307 float refresh, diff;
308 const XF86VidModeModeInfo *m = modes[i];
309
310 if (!m)
311 continue;
312
313 if (m->hdisplay != width)
314 continue;
315 if (m->vdisplay != height)
316 continue;
317
318 refresh = refresh_mod * m->dotclock * 1000.0f / (m->htotal * m->vtotal);
319 diff = fabsf(refresh - video_refresh_rate);
320
321 if (!ret || diff < minimum_fps_diff)
322 {
323 *mode = *m;
324 minimum_fps_diff = diff;
325 }
326 ret = true;
327 }
328
329 XFree(modes);
330 return ret;
331 }
332
x11_enter_fullscreen(Display * dpy,unsigned width,unsigned height)333 bool x11_enter_fullscreen(
334 Display *dpy, unsigned width,
335 unsigned height)
336 {
337 XF86VidModeModeInfo mode;
338
339 if (!get_video_mode(dpy, width, height, &mode, &desktop_mode))
340 return false;
341
342 if (!XF86VidModeSwitchToMode(dpy, DefaultScreen(dpy), &mode))
343 return false;
344
345 XF86VidModeSetViewPort(dpy, DefaultScreen(dpy), 0, 0);
346 return true;
347 }
348
x11_exit_fullscreen(Display * dpy)349 void x11_exit_fullscreen(Display *dpy)
350 {
351 XF86VidModeSwitchToMode(dpy, DefaultScreen(dpy), &desktop_mode);
352 XF86VidModeSetViewPort(dpy, DefaultScreen(dpy), 0, 0);
353 }
354
x11_create_input_context(Display * dpy,Window win,XIM * xim,XIC * xic)355 bool x11_create_input_context(Display *dpy, Window win, XIM *xim, XIC *xic)
356 {
357 x11_destroy_input_context(xim, xic);
358
359 g_x11_has_focus = true;
360 *xim = XOpenIM(dpy, NULL, NULL, NULL);
361
362 if (!*xim)
363 {
364 RARCH_ERR("[X11]: Failed to open input method.\n");
365 return false;
366 }
367
368 *xic = XCreateIC(*xim, XNInputStyle,
369 XIMPreeditNothing | XIMStatusNothing, XNClientWindow, win, NULL);
370
371 if (!*xic)
372 {
373 RARCH_ERR("[X11]: Failed to create input context.\n");
374 return false;
375 }
376
377 XSetICFocus(*xic);
378 return true;
379 }
380
x11_destroy_input_context(XIM * xim,XIC * xic)381 void x11_destroy_input_context(XIM *xim, XIC *xic)
382 {
383 if (*xic)
384 {
385 XDestroyIC(*xic);
386 *xic = NULL;
387 }
388
389 if (*xim)
390 {
391 XCloseIM(*xim);
392 *xim = NULL;
393 }
394 }
395
x11_get_metrics(void * data,enum display_metric_types type,float * value)396 bool x11_get_metrics(void *data,
397 enum display_metric_types type, float *value)
398 {
399 unsigned screen_no = 0;
400 Display *dpy = (Display*)XOpenDisplay(NULL);
401 int pixels_x = DisplayWidth(dpy, screen_no);
402 int pixels_y = DisplayHeight(dpy, screen_no);
403 int physical_width = DisplayWidthMM(dpy, screen_no);
404 int physical_height = DisplayHeightMM(dpy, screen_no);
405
406 (void)pixels_y;
407
408 XCloseDisplay(dpy);
409
410 switch (type)
411 {
412 case DISPLAY_METRIC_PIXEL_WIDTH:
413 *value = (float)pixels_x;
414 break;
415 case DISPLAY_METRIC_PIXEL_HEIGHT:
416 *value = (float)pixels_y;
417 break;
418 case DISPLAY_METRIC_MM_WIDTH:
419 *value = (float)physical_width;
420 break;
421 case DISPLAY_METRIC_MM_HEIGHT:
422 *value = (float)physical_height;
423 break;
424 case DISPLAY_METRIC_DPI:
425 *value = ((((float)pixels_x) * 25.4) / ((float)physical_width));
426 break;
427 case DISPLAY_METRIC_NONE:
428 default:
429 *value = 0;
430 return false;
431 }
432
433 return true;
434 }
435
x11_handle_key_event(unsigned keycode,XEvent * event,XIC ic,bool filter)436 static void x11_handle_key_event(unsigned keycode, XEvent *event, XIC ic, bool filter)
437 {
438 int i;
439 Status status;
440 uint32_t chars[32];
441 unsigned key = 0;
442 uint16_t mod = 0;
443 unsigned state = event->xkey.state;
444 bool down = event->type == KeyPress;
445 int num = 0;
446 KeySym keysym = 0;
447
448 chars[0] = '\0';
449
450 /* this code generates the localized chars using keysyms */
451 if (!filter)
452 {
453 if (down)
454 {
455 char keybuf[32];
456
457 keybuf[0] = '\0';
458 #ifdef X_HAVE_UTF8_STRING
459 status = 0;
460
461 /* XwcLookupString doesn't seem to work. */
462 num = Xutf8LookupString(ic, &event->xkey, keybuf,
463 ARRAY_SIZE(keybuf), &keysym, &status);
464
465 /* libc functions need UTF-8 locale to work properly,
466 * which makes mbrtowc a bit impractical.
467 *
468 * Use custom UTF8 -> UTF-32 conversion. */
469 num = utf8_conv_utf32(chars, ARRAY_SIZE(chars), keybuf, num);
470 #else
471 (void)ic;
472 num = XLookupString(&event->xkey, keybuf,
473 sizeof(keybuf), &keysym, NULL); /* ASCII only. */
474 for (i = 0; i < num; i++)
475 chars[i] = keybuf[i] & 0x7f;
476 #endif
477 }
478 else
479 keysym = XLookupKeysym(&event->xkey, (state & ShiftMask) || (state & LockMask));
480 }
481
482 /* We can't feed uppercase letters to the keycode translator. Seems like a bad idea
483 * to feed it keysyms anyway, so here is a little hack... */
484 if (keysym >= XK_A && keysym <= XK_Z)
485 keysym += XK_z - XK_Z;
486
487 /* Get the real keycode,
488 that correctly ignores international layouts as windows code does. */
489 key = input_keymaps_translate_keysym_to_rk(keycode);
490
491 if (state & ShiftMask)
492 mod |= RETROKMOD_SHIFT;
493 if (state & LockMask)
494 mod |= RETROKMOD_CAPSLOCK;
495 if (state & ControlMask)
496 mod |= RETROKMOD_CTRL;
497 if (state & Mod1Mask)
498 mod |= RETROKMOD_ALT;
499 if (state & Mod2Mask)
500 mod |= RETROKMOD_NUMLOCK;
501 if (state & Mod4Mask)
502 mod |= RETROKMOD_META;
503
504 input_keyboard_event(down, key, chars[0], mod, RETRO_DEVICE_KEYBOARD);
505
506 for (i = 1; i < num; i++)
507 input_keyboard_event(down, RETROK_UNKNOWN,
508 chars[i], mod, RETRO_DEVICE_KEYBOARD);
509 }
510
x11_alive(void * data)511 bool x11_alive(void *data)
512 {
513 while (XPending(g_x11_dpy))
514 {
515 XEvent event;
516 bool filter = false;
517 unsigned keycode = 0;
518
519 /* Can get events from older windows. Check this. */
520 XNextEvent(g_x11_dpy, &event);
521
522 /* IMPORTANT - Get keycode before XFilterEvent
523 because the event is localizated after the call */
524 keycode = event.xkey.keycode;
525 filter = XFilterEvent(&event, g_x11_win);
526
527 switch (event.type)
528 {
529 case ClientMessage:
530 if (event.xclient.window == g_x11_win &&
531 (Atom)event.xclient.data.l[0] == g_x11_quit_atom)
532 frontend_driver_set_signal_handler_state(1);
533 break;
534
535 case DestroyNotify:
536 if (event.xdestroywindow.window == g_x11_win)
537 frontend_driver_set_signal_handler_state(1);
538 break;
539
540 case MapNotify:
541 if (event.xmap.window == g_x11_win)
542 g_x11_has_focus = true;
543 break;
544
545 case UnmapNotify:
546 if (event.xunmap.window == g_x11_win)
547 g_x11_has_focus = false;
548 break;
549
550 case ConfigureNotify:
551 if (event.xconfigure.window == g_x11_win)
552 g_x11_xce = event.xconfigure;
553 break;
554
555 case ButtonPress:
556 switch (event.xbutton.button)
557 {
558 case 1: /* Left click */
559 #if 0
560 RARCH_LOG("Click occurred : [%d, %d]\n",
561 event.xbutton.x_root,
562 event.xbutton.y_root);
563 #endif
564 break;
565 case 2: /* Grabbed */
566 /* Middle click */
567 break;
568 case 3: /* Right click */
569 break;
570 case 4: /* Grabbed */
571 /* Scroll up */
572 case 5: /* Scroll down */
573 case 6: /* Scroll wheel left */
574 case 7: /* Scroll wheel right */
575 x_input_poll_wheel(&event.xbutton, true);
576 break;
577 }
578 break;
579
580 case EnterNotify:
581 g_x11_entered = true;
582 break;
583
584 case LeaveNotify:
585 g_x11_entered = false;
586 break;
587
588 case ButtonRelease:
589 break;
590
591 case KeyRelease:
592 /* When you receive a key release and the next event is a key press
593 of the same key combination, then it's auto-repeat and the
594 key wasn't actually released. */
595 if(XEventsQueued(g_x11_dpy, QueuedAfterReading))
596 {
597 XEvent next_event;
598 XPeekEvent(g_x11_dpy, &next_event);
599 if (next_event.type == KeyPress &&
600 next_event.xkey.time == event.xkey.time &&
601 next_event.xkey.keycode == event.xkey.keycode)
602 {
603 break; /* Key wasn't actually released */
604 }
605 }
606 case KeyPress:
607 if (event.xkey.window == g_x11_win)
608 x11_handle_key_event(keycode, &event, g_x11_xic, filter);
609 break;
610 }
611 }
612
613 return !((bool)frontend_driver_get_signal_handler_state());
614 }
615
x11_check_window(void * data,bool * quit,bool * resize,unsigned * width,unsigned * height)616 void x11_check_window(void *data, bool *quit,
617 bool *resize, unsigned *width, unsigned *height)
618 {
619 unsigned new_width = *width;
620 unsigned new_height = *height;
621
622 x11_get_video_size(data, &new_width, &new_height);
623
624 if (new_width != *width || new_height != *height)
625 {
626 *width = new_width;
627 *height = new_height;
628 *resize = true;
629 }
630
631 x11_alive(data);
632
633 *quit = (bool)frontend_driver_get_signal_handler_state();
634 }
635
x11_get_video_size(void * data,unsigned * width,unsigned * height)636 void x11_get_video_size(void *data, unsigned *width, unsigned *height)
637 {
638 if (!g_x11_dpy || g_x11_win == None)
639 {
640 Display *dpy = (Display*)XOpenDisplay(NULL);
641 *width = 0;
642 *height = 0;
643
644 if (dpy)
645 {
646 int screen = DefaultScreen(dpy);
647 *width = DisplayWidth(dpy, screen);
648 *height = DisplayHeight(dpy, screen);
649 XCloseDisplay(dpy);
650 }
651 }
652 else
653 {
654 if (g_x11_xce.width != 0 && g_x11_xce.height != 0)
655 {
656 *width = g_x11_xce.width;
657 *height = g_x11_xce.height;
658 }
659 else
660 {
661 XWindowAttributes target;
662 XGetWindowAttributes(g_x11_dpy, g_x11_win, &target);
663
664 *width = target.width;
665 *height = target.height;
666 }
667 }
668 }
669
x11_has_focus_internal(void * data)670 bool x11_has_focus_internal(void *data)
671 {
672 return g_x11_has_focus;
673 }
674
x11_has_focus(void * data)675 bool x11_has_focus(void *data)
676 {
677 Window win;
678 int rev;
679
680 XGetInputFocus(g_x11_dpy, &win, &rev);
681
682 return (win == g_x11_win && g_x11_has_focus) || g_x11_true_full;
683 }
684
x11_connect(void)685 bool x11_connect(void)
686 {
687 frontend_driver_destroy_signal_handler_state();
688
689 /* Keep one g_x11_dpy alive the entire process lifetime.
690 * This is necessary for nVidia's EGL implementation for now. */
691 if (!g_x11_dpy)
692 {
693 g_x11_dpy = XOpenDisplay(NULL);
694 if (!g_x11_dpy)
695 return false;
696 }
697
698 #ifdef HAVE_DBUS
699 dbus_ensure_connection();
700 #endif
701
702 memset(&g_x11_xce, 0, sizeof(XConfigureEvent));
703
704 return true;
705 }
706
x11_update_title(void * data)707 void x11_update_title(void *data)
708 {
709 char title[128];
710
711 title[0] = '\0';
712
713 video_driver_get_window_title(title, sizeof(title));
714
715 if (title[0])
716 XChangeProperty(g_x11_dpy, g_x11_win, XA_WM_NAME, XA_STRING,
717 8, PropModeReplace, (const unsigned char*)title,
718 strlen(title));
719 }
720
x11_input_ctx_new(bool true_full)721 bool x11_input_ctx_new(bool true_full)
722 {
723 if (!x11_create_input_context(g_x11_dpy, g_x11_win,
724 &g_x11_xim, &g_x11_xic))
725 return false;
726
727 video_driver_display_type_set(RARCH_DISPLAY_X11);
728 video_driver_display_set((uintptr_t)g_x11_dpy);
729 video_driver_window_set((uintptr_t)g_x11_win);
730 g_x11_true_full = true_full;
731 return true;
732 }
733
x11_input_ctx_destroy(void)734 void x11_input_ctx_destroy(void)
735 {
736 x11_destroy_input_context(&g_x11_xim, &g_x11_xic);
737 }
738
x11_window_destroy(bool fullscreen)739 void x11_window_destroy(bool fullscreen)
740 {
741 if (g_x11_win)
742 XUnmapWindow(g_x11_dpy, g_x11_win);
743 if (!fullscreen)
744 XDestroyWindow(g_x11_dpy, g_x11_win);
745 g_x11_win = None;
746
747 #ifdef HAVE_DBUS
748 dbus_screensaver_uninhibit();
749 dbus_close_connection();
750 #endif
751 }
752
x11_colormap_destroy(void)753 void x11_colormap_destroy(void)
754 {
755 if (!g_x11_cmap)
756 return;
757
758 XFreeColormap(g_x11_dpy, g_x11_cmap);
759 g_x11_cmap = None;
760 }
761
x11_install_quit_atom(void)762 void x11_install_quit_atom(void)
763 {
764 g_x11_quit_atom = XInternAtom(g_x11_dpy,
765 "WM_DELETE_WINDOW", False);
766 if (g_x11_quit_atom)
767 XSetWMProtocols(g_x11_dpy, g_x11_win, &g_x11_quit_atom, 1);
768 }
769
x11_wait_notify(Display * d,XEvent * e,char * arg)770 static Bool x11_wait_notify(Display *d, XEvent *e, char *arg)
771 {
772 return e->type == MapNotify && e->xmap.window == g_x11_win;
773 }
774
x11_event_queue_check(XEvent * event)775 void x11_event_queue_check(XEvent *event)
776 {
777 XIfEvent(g_x11_dpy, event, x11_wait_notify, NULL);
778 }
779
x11_check_atom_supported(Display * dpy,Atom atom)780 static bool x11_check_atom_supported(Display *dpy, Atom atom)
781 {
782 Atom XA_NET_SUPPORTED = XInternAtom(dpy, "_NET_SUPPORTED", True);
783 Atom type;
784 int format;
785 unsigned long nitems;
786 unsigned long bytes_after;
787 Atom *prop;
788 int i;
789
790 if (XA_NET_SUPPORTED == None)
791 return false;
792
793 XGetWindowProperty(dpy, DefaultRootWindow(dpy), XA_NET_SUPPORTED,
794 0, UINT_MAX, False, XA_ATOM, &type, &format,&nitems,
795 &bytes_after, (unsigned char **) &prop);
796
797 if (!prop || type != XA_ATOM)
798 return false;
799
800 for (i = 0; i < nitems; i++)
801 {
802 if (prop[i] == atom)
803 {
804 XFree(prop);
805 return true;
806 }
807 }
808
809 XFree(prop);
810
811 return false;
812 }
813
x11_has_net_wm_fullscreen(Display * dpy)814 bool x11_has_net_wm_fullscreen(Display *dpy)
815 {
816 XA_NET_WM_STATE_FULLSCREEN = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
817
818 return x11_check_atom_supported(dpy, XA_NET_WM_STATE_FULLSCREEN);
819 }
820
x11_get_wm_name(Display * dpy)821 char *x11_get_wm_name(Display *dpy)
822 {
823 Atom type;
824 int format;
825 Window window;
826 Atom XA_NET_SUPPORTING_WM_CHECK = XInternAtom(g_x11_dpy, "_NET_SUPPORTING_WM_CHECK", False);
827 Atom XA_NET_WM_NAME = XInternAtom(g_x11_dpy, "_NET_WM_NAME", False);
828 Atom XA_UTF8_STRING = XInternAtom(g_x11_dpy, "UTF8_STRING", False);
829 unsigned long nitems = 0;
830 unsigned long bytes_after = 0;
831 char *title = NULL;
832 unsigned char *propdata = NULL;
833
834 if (!XA_NET_SUPPORTING_WM_CHECK || !XA_NET_WM_NAME)
835 return NULL;
836
837 if (!(XGetWindowProperty(dpy,
838 DefaultRootWindow(dpy),
839 XA_NET_SUPPORTING_WM_CHECK,
840 0,
841 1,
842 False,
843 XA_WINDOW,
844 &type,
845 &format,
846 &nitems,
847 &bytes_after,
848 &propdata) == Success &&
849 propdata))
850 return NULL;
851
852 window = ((Window *) propdata)[0];
853
854 XFree(propdata);
855
856 if (!(XGetWindowProperty(dpy,
857 window,
858 XA_NET_WM_NAME,
859 0,
860 8192,
861 False,
862 XA_UTF8_STRING,
863 &type,
864 &format,
865 &nitems,
866 &bytes_after,
867 &propdata) == Success
868 && propdata))
869 return NULL;
870
871 title = strdup((char *) propdata);
872 XFree(propdata);
873
874 return title;
875 }
876