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