RetroArch/gfx/common/x11_common.c

936 lines
25 KiB
C
Raw Normal View History

2012-09-26 15:52:25 +02:00
/* RetroArch - A frontend for libretro.
2014-01-01 01:50:59 +01:00
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
2017-01-22 13:40:32 +01:00
* Copyright (C) 2011-2017 - Daniel De Matteis
*
2012-09-26 15:52:25 +02:00
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
2015-09-16 11:24:03 +02:00
#include <sys/types.h>
#include <sys/wait.h>
2015-09-05 20:34:22 +02:00
#include <errno.h>
#include <unistd.h>
2015-09-05 20:34:22 +02:00
#include <X11/Xatom.h>
2016-09-08 08:15:40 +02:00
#ifdef HAVE_CONFIG_H
#include "../../config.h"
#endif
2017-05-20 19:44:04 +02:00
#ifdef HAVE_XINERAMA
#include <X11/extensions/Xinerama.h>
#endif
2015-09-05 20:34:22 +02:00
#include "x11_common.h"
#ifdef HAVE_XF86VM
#include <X11/extensions/xf86vmode.h>
#endif
2017-05-20 19:44:04 +02:00
#include <encodings/utf.h>
#include <compat/strl.h>
#include <string/stdstring.h>
2017-05-20 19:44:04 +02:00
2017-12-16 05:16:27 -08:00
#ifdef HAVE_DBUS
2017-03-24 00:15:17 +01:00
#include "dbus_common.h"
2017-12-16 05:16:27 -08:00
#endif
2017-03-24 00:15:17 +01:00
#include "../../frontend/frontend_driver.h"
#include "../../input/input_driver.h"
2017-05-20 19:48:02 +02:00
#include "../../input/input_keymaps.h"
2015-11-29 03:34:09 +01:00
#include "../../input/common/input_x11_common.h"
#include "../../configuration.h"
#include "../../verbosity.h"
2012-09-26 15:52:25 +02:00
2017-03-23 19:36:39 +01:00
#define _NET_WM_STATE_ADD 1
#define MOVERESIZE_GRAVITY_CENTER 5
#define MOVERESIZE_X_SHIFT 8
#define MOVERESIZE_Y_SHIFT 9
2020-08-03 15:48:08 +02:00
#define V_DBLSCAN 0x20
2020-07-07 02:51:39 +02:00
/* TODO/FIXME - globals */
2016-12-04 03:39:51 +01:00
bool g_x11_entered = false;
Display *g_x11_dpy = NULL;
2016-12-15 12:37:14 +01:00
unsigned g_x11_screen = 0;
2020-08-03 15:48:08 +02:00
Window g_x11_win = None;
2015-11-19 12:23:49 +01:00
Colormap g_x11_cmap;
2020-08-03 15:48:08 +02:00
/* TODO/FIXME - static globals */
#ifdef HAVE_XF86VM
2020-08-03 15:48:08 +02:00
static XF86VidModeModeInfo desktop_mode;
#endif
2020-08-03 15:48:08 +02:00
static bool xdg_screensaver_available = true;
static bool g_x11_has_focus = false;
static bool g_x11_true_full = false;
static XConfigureEvent g_x11_xce = {0};
static Atom XA_NET_WM_STATE;
static Atom XA_NET_WM_STATE_FULLSCREEN;
static Atom XA_NET_MOVERESIZE_WINDOW;
2015-11-19 12:18:35 +01:00
static Atom g_x11_quit_atom;
static XIM g_x11_xim;
static XIC g_x11_xic;
static enum retro_key x11_keysym_lut[RETROK_LAST];
static unsigned *x11_keysym_rlut = NULL;
static unsigned x11_keysym_rlut_size = 0;
2013-03-29 13:46:11 +01:00
static void x11_hide_mouse(Display *dpy, Window win)
2012-09-26 15:52:25 +02:00
{
Cursor no_ptr;
Pixmap bm_no;
XColor black, dummy;
2020-07-07 02:51:39 +02:00
static char bm_no_data[] = {0, 0, 0, 0, 0, 0, 0, 0};
Colormap colormap = DefaultColormap(dpy, DefaultScreen(dpy));
2015-06-13 01:18:13 +02:00
2012-09-26 15:52:25 +02:00
if (!XAllocNamedColor(dpy, colormap, "black", &black, &dummy))
return;
bm_no = XCreateBitmapFromData(dpy, win, bm_no_data, 8, 8);
no_ptr = XCreatePixmapCursor(dpy, bm_no, bm_no, &black, &black, 0, 0);
XDefineCursor(dpy, win, no_ptr);
XFreeCursor(dpy, no_ptr);
if (bm_no != None)
XFreePixmap(dpy, bm_no);
XFreeColors(dpy, colormap, &black.pixel, 1, 0);
}
2013-03-29 13:46:11 +01:00
void x11_show_mouse(Display *dpy, Window win, bool state)
{
if (state)
XUndefineCursor(dpy, win);
else
x11_hide_mouse(dpy, win);
}
void x11_set_net_wm_fullscreen(Display *dpy, Window win)
2012-09-26 15:52:25 +02:00
{
2017-12-25 07:36:49 +01:00
XEvent xev = {0};
2015-06-26 17:46:13 +02:00
2017-03-23 19:36:39 +01:00
XA_NET_WM_STATE = XInternAtom(dpy, "_NET_WM_STATE", False);
XA_NET_WM_STATE_FULLSCREEN = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
2012-09-26 15:52:25 +02:00
2017-03-23 19:36:39 +01:00
xev.xclient.type = ClientMessage;
xev.xclient.send_event = True;
xev.xclient.message_type = XA_NET_WM_STATE;
xev.xclient.window = win;
xev.xclient.format = 32;
xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
xev.xclient.data.l[1] = XA_NET_WM_STATE_FULLSCREEN;
2012-10-12 19:05:29 +02:00
XSendEvent(dpy, DefaultRootWindow(dpy), False,
SubstructureRedirectMask | SubstructureNotifyMask,
&xev);
}
2015-01-12 05:05:56 +01:00
/* Try to be nice to tiling WMs if possible. */
2012-10-12 19:05:29 +02:00
void x11_move_window(Display *dpy, Window win, int x, int y,
unsigned width, unsigned height)
{
2017-12-25 07:36:49 +01:00
XEvent xev = {0};
2012-10-12 19:05:29 +02:00
2018-11-26 13:37:35 +01:00
XA_NET_MOVERESIZE_WINDOW = XInternAtom(dpy,
"_NET_MOVERESIZE_WINDOW", False);
2015-01-10 03:30:23 +01:00
2016-02-05 13:36:18 +01:00
xev.xclient.type = ClientMessage;
xev.xclient.send_event = True;
2012-10-12 19:05:29 +02:00
xev.xclient.message_type = XA_NET_MOVERESIZE_WINDOW;
2016-02-05 13:36:18 +01:00
xev.xclient.window = win;
xev.xclient.format = 32;
xev.xclient.data.l[0] = (1 << MOVERESIZE_X_SHIFT)
2016-02-05 13:36:18 +01:00
| (1 << MOVERESIZE_Y_SHIFT);
xev.xclient.data.l[1] = x;
xev.xclient.data.l[2] = y;
2012-09-26 15:52:25 +02:00
XSendEvent(dpy, DefaultRootWindow(dpy), False,
SubstructureRedirectMask | SubstructureNotifyMask,
&xev);
}
2012-10-26 23:01:32 +02:00
static void x11_set_window_class(Display *dpy, Window win)
{
2016-12-19 19:17:23 +01:00
XClassHint hint;
2015-01-10 03:30:23 +01:00
2015-01-12 05:05:56 +01:00
hint.res_name = (char*)"retroarch"; /* Broken header. */
hint.res_class = (char*)"retroarch";
2012-10-26 23:01:32 +02:00
XSetClassHint(dpy, win, &hint);
}
static void x11_set_window_pid(Display *dpy, Window win)
{
2017-12-25 07:36:49 +01:00
long scret = 0;
char *hostname = NULL;
pid_t pid = getpid();
XChangeProperty(dpy, win, XInternAtom(dpy, "_NET_WM_PID", False),
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
errno = 0;
2018-11-26 13:37:35 +01:00
if ((scret = sysconf(_SC_HOST_NAME_MAX)) == -1 && errno)
return;
2020-01-30 16:47:06 +01:00
if (!(hostname = (char*)malloc(scret + 1)))
return;
2018-11-26 13:37:35 +01:00
if (gethostname(hostname, scret + 1) == -1)
RARCH_WARN("Failed to get hostname.\n");
else
{
XChangeProperty(dpy, win, XA_WM_CLIENT_MACHINE, XA_STRING, 8,
PropModeReplace, (unsigned char *)hostname, strlen(hostname));
}
free(hostname);
}
2012-10-26 23:01:32 +02:00
void x11_set_window_attr(Display *dpy, Window win)
{
x11_set_window_class(dpy, win);
x11_set_window_pid(dpy, win);
2012-10-26 23:01:32 +02:00
}
static void xdg_screensaver_inhibit(Window wnd)
2012-09-26 15:52:25 +02:00
{
2017-01-10 21:54:48 +01:00
int ret;
char cmd[64];
char title[128];
2016-10-21 05:57:40 +02:00
cmd[0] = '\0';
title[0] = '\0';
2017-03-24 01:38:23 +01:00
RARCH_LOG("[X11]: Suspending screensaver (X11, xdg-screensaver).\n");
2015-01-18 22:32:14 +01:00
if (g_x11_dpy && g_x11_win)
{
/* Make sure the window has a title, even if it's a bogus one, otherwise
* xdg-screensaver will fail and report to stderr, framing RA for its bug.
* A single space character is used so that the title bar stays visibly
* the same, as if there's no title at all. */
size_t title_len = video_driver_get_window_title(title, sizeof(title));
if (title_len == 0)
title_len = strlcpy(title, " ", sizeof(title));
XChangeProperty(g_x11_dpy, g_x11_win, XA_WM_NAME, XA_STRING,
8, PropModeReplace, (const unsigned char*) title, title_len);
}
snprintf(cmd, sizeof(cmd), "xdg-screensaver suspend 0x%x", (int)wnd);
2012-09-26 15:52:25 +02:00
if ((ret = system(cmd)) == -1)
{
xdg_screensaver_available = false;
RARCH_WARN("Failed to launch xdg-screensaver.\n");
}
else if (WEXITSTATUS(ret))
{
xdg_screensaver_available = false;
RARCH_WARN("Could not suspend screen saver.\n");
}
}
2016-09-09 18:08:54 -07:00
void x11_suspend_screensaver(Window wnd, bool enable)
{
#ifdef HAVE_DBUS
if (dbus_suspend_screensaver(enable))
2016-09-16 13:31:18 +02:00
return;
2016-09-09 18:08:54 -07:00
#endif
2020-05-24 19:38:41 +02:00
if (enable)
if (xdg_screensaver_available)
xdg_screensaver_inhibit(wnd);
2016-09-09 18:08:54 -07:00
}
#ifdef HAVE_XF86VM
float x11_get_refresh_rate(void *data)
{
XWindowAttributes attr;
XF86VidModeModeLine modeline;
Screen *screen;
int screenid;
int dotclock;
2018-04-16 16:56:36 -05:00
if (!g_x11_dpy || g_x11_win == None)
return 0.0f;
if (!XGetWindowAttributes(g_x11_dpy, g_x11_win, &attr))
return 0.0f;
screen = attr.screen;
screenid = XScreenNumberOfScreen(screen);
XF86VidModeGetModeLine(g_x11_dpy, screenid, &dotclock, &modeline);
/* non-native modes like 1080p on a 4K display might use DoubleScan */
if (modeline.flags & V_DBLSCAN)
dotclock /= 2;
return (float)dotclock * 1000.0f / modeline.htotal / modeline.vtotal;
}
static bool get_video_mode(
2017-01-13 16:57:57 +01:00
Display *dpy, unsigned width, unsigned height,
XF86VidModeModeInfo *mode, XF86VidModeModeInfo *x11_desktop_mode)
2012-09-29 10:47:55 +02:00
{
2016-02-05 13:36:18 +01:00
int i, num_modes = 0;
bool ret = false;
2017-12-25 07:36:49 +01:00
float refresh_mod = 0.0f;
2016-02-05 13:36:18 +01:00
float minimum_fps_diff = 0.0f;
2013-10-22 21:26:33 +02:00
XF86VidModeModeInfo **modes = NULL;
settings_t *settings = config_get_ptr();
unsigned black_frame_insertion = settings->uints.video_black_frame_insertion;
float video_refresh_rate = settings->floats.video_refresh_rate;
2015-01-10 03:30:23 +01:00
2012-09-29 10:47:55 +02:00
XF86VidModeGetAllModeLines(dpy, DefaultScreen(dpy), &num_modes, &modes);
if (!num_modes)
{
XFree(modes);
return false;
}
*x11_desktop_mode = *modes[0];
2012-09-29 10:47:55 +02:00
/* If we use black frame insertion, we fake a 60 Hz monitor
* for 120 Hz one, etc, so try to match that. */
refresh_mod = 1.0f / (black_frame_insertion + 1.0f);
2013-10-22 21:26:33 +02:00
for (i = 0; i < num_modes; i++)
2012-09-29 10:47:55 +02:00
{
2015-01-18 21:49:00 +01:00
float refresh, diff;
const XF86VidModeModeInfo *m = modes[i];
2015-01-10 03:30:23 +01:00
if (!m)
continue;
2015-01-18 21:49:00 +01:00
if (m->hdisplay != width)
continue;
if (m->vdisplay != height)
continue;
refresh = refresh_mod * m->dotclock * 1000.0f / (m->htotal * m->vtotal);
diff = fabsf(refresh - video_refresh_rate);
2015-01-18 21:49:00 +01:00
if (!ret || diff < minimum_fps_diff)
2012-09-29 10:47:55 +02:00
{
2015-01-18 21:49:00 +01:00
*mode = *m;
minimum_fps_diff = diff;
2012-09-29 10:47:55 +02:00
}
2015-01-18 21:49:00 +01:00
ret = true;
2012-09-29 10:47:55 +02:00
}
XFree(modes);
return ret;
}
bool x11_enter_fullscreen(
2017-01-13 16:57:57 +01:00
Display *dpy, unsigned width,
unsigned height)
2012-09-29 10:47:55 +02:00
{
XF86VidModeModeInfo mode;
2014-10-27 14:35:23 +01:00
if (!get_video_mode(dpy, width, height, &mode, &desktop_mode))
2015-01-18 21:49:00 +01:00
return false;
2014-10-27 14:35:23 +01:00
2015-01-18 21:49:00 +01:00
if (!XF86VidModeSwitchToMode(dpy, DefaultScreen(dpy), &mode))
return false;
XF86VidModeSetViewPort(dpy, DefaultScreen(dpy), 0, 0);
return true;
2012-09-29 10:47:55 +02:00
}
void x11_exit_fullscreen(Display *dpy)
2012-09-29 10:47:55 +02:00
{
XF86VidModeSwitchToMode(dpy, DefaultScreen(dpy), &desktop_mode);
2012-09-29 10:47:55 +02:00
XF86VidModeSetViewPort(dpy, DefaultScreen(dpy), 0, 0);
}
#endif
2012-09-29 10:47:55 +02:00
static void x11_init_keyboard_lut(void)
{
const struct rarch_key_map *map = rarch_key_map_x11;
const struct rarch_key_map *map_start = rarch_key_map_x11;
memset(x11_keysym_lut, 0, sizeof(x11_keysym_lut));
x11_keysym_rlut_size = 0;
for (; map->rk != RETROK_UNKNOWN; map++)
{
x11_keysym_lut[map->rk] = (enum retro_key)map->sym;
if (map->sym > x11_keysym_rlut_size)
x11_keysym_rlut_size = map->sym;
}
if (x11_keysym_rlut_size < 65536)
{
if (x11_keysym_rlut)
free(x11_keysym_rlut);
x11_keysym_rlut = (unsigned*)calloc(++x11_keysym_rlut_size, sizeof(unsigned));
for (map = map_start; map->rk != RETROK_UNKNOWN; map++)
x11_keysym_rlut[map->sym] = (enum retro_key)map->rk;
}
else
x11_keysym_rlut_size = 0;
}
2022-08-05 02:00:09 +02:00
static void x11_destroy_input_context(XIM *xim, XIC *xic)
{
if (*xic)
{
XDestroyIC(*xic);
*xic = NULL;
}
if (*xim)
{
XCloseIM(*xim);
*xim = NULL;
}
memset(x11_keysym_lut, 0, sizeof(x11_keysym_lut));
if (x11_keysym_rlut)
{
free(x11_keysym_rlut);
x11_keysym_rlut = NULL;
}
x11_keysym_rlut_size = 0;
}
static bool x11_create_input_context(Display *dpy,
Window win, XIM *xim, XIC *xic)
{
x11_destroy_input_context(xim, xic);
x11_init_keyboard_lut();
g_x11_has_focus = true;
if (!(*xim = XOpenIM(dpy, NULL, NULL, NULL)))
{
RARCH_ERR("[X11]: Failed to open input method.\n");
return false;
}
if (!(*xic = XCreateIC(*xim, XNInputStyle,
XIMPreeditNothing | XIMStatusNothing, XNClientWindow, win, NULL)))
{
RARCH_ERR("[X11]: Failed to create input context.\n");
return false;
}
XSetICFocus(*xic);
return true;
}
2015-04-09 05:05:29 +02:00
bool x11_get_metrics(void *data,
enum display_metric_types type, float *value)
{
unsigned screen_no = 0;
Display *dpy = NULL;
2015-04-09 05:05:29 +02:00
switch (type)
{
case DISPLAY_METRIC_PIXEL_WIDTH:
dpy = (Display*)XOpenDisplay(NULL);
*value = (float)DisplayWidth(dpy, screen_no);
XCloseDisplay(dpy);
break;
case DISPLAY_METRIC_PIXEL_HEIGHT:
dpy = (Display*)XOpenDisplay(NULL);
*value = (float)DisplayHeight(dpy, screen_no);
XCloseDisplay(dpy);
break;
2015-04-09 05:05:29 +02:00
case DISPLAY_METRIC_MM_WIDTH:
dpy = (Display*)XOpenDisplay(NULL);
*value = (float)DisplayWidthMM(dpy, screen_no);
XCloseDisplay(dpy);
2015-04-09 05:05:29 +02:00
break;
case DISPLAY_METRIC_MM_HEIGHT:
dpy = (Display*)XOpenDisplay(NULL);
*value = (float)DisplayHeightMM(dpy, screen_no);
XCloseDisplay(dpy);
2015-04-09 05:05:29 +02:00
break;
case DISPLAY_METRIC_DPI:
dpy = (Display*)XOpenDisplay(NULL);
*value = ((((float)DisplayWidth (dpy, screen_no)) * 25.4)
/ ( (float)DisplayWidthMM(dpy, screen_no)));
XCloseDisplay(dpy);
2015-04-09 05:05:29 +02:00
break;
case DISPLAY_METRIC_NONE:
default:
*value = 0;
return false;
}
return true;
}
2015-11-19 10:02:53 +01:00
static enum retro_key x11_translate_keysym_to_rk(unsigned sym)
{
size_t i;
/* Fast path */
if (x11_keysym_rlut && sym < x11_keysym_rlut_size)
return (enum retro_key)x11_keysym_rlut[sym];
/* Slow path */
for (i = 0; i < ARRAY_SIZE(x11_keysym_lut); i++)
{
if (x11_keysym_lut[i] != sym)
continue;
return (enum retro_key)i;
}
return RETROK_UNKNOWN;
}
static void x11_handle_key_event(unsigned keycode, XEvent *event,
XIC ic, bool filter)
2017-05-20 19:44:04 +02:00
{
int i;
Status status;
2017-05-20 19:44:04 +02:00
uint32_t chars[32];
2017-12-25 07:36:49 +01:00
unsigned key = 0;
2017-05-20 19:44:04 +02:00
uint16_t mod = 0;
unsigned state = event->xkey.state;
2017-12-25 07:36:49 +01:00
bool down = event->type == KeyPress;
int num = 0;
KeySym keysym = 0;
2017-12-25 07:36:49 +01:00
chars[0] = '\0';
2017-05-20 19:44:04 +02:00
/* this code generates the localized chars using keysyms */
2017-05-20 19:44:04 +02:00
if (!filter)
{
if (down)
{
char keybuf[32];
keybuf[0] = '\0';
#ifdef X_HAVE_UTF8_STRING
status = 0;
2017-05-20 19:44:04 +02:00
/* XwcLookupString doesn't seem to work. */
2018-04-22 13:05:40 +02:00
num = Xutf8LookupString(ic, &event->xkey, keybuf,
ARRAY_SIZE(keybuf), &keysym, &status);
/* libc functions need UTF-8 locale to work properly,
2017-05-20 19:44:04 +02:00
* which makes mbrtowc a bit impractical.
*
* Use custom UTF8 -> UTF-32 conversion. */
num = utf8_conv_utf32(chars, ARRAY_SIZE(chars), keybuf, num);
#else
2018-04-22 13:05:40 +02:00
num = XLookupString(&event->xkey, keybuf,
sizeof(keybuf), &keysym, NULL); /* ASCII only. */
2017-05-20 19:44:04 +02:00
for (i = 0; i < num; i++)
chars[i] = keybuf[i] & 0x7f;
#endif
}
else
keysym = XLookupKeysym(&event->xkey,
(state & ShiftMask) || (state & LockMask));
2017-05-20 19:44:04 +02:00
}
/* We can't feed uppercase letters to the keycode translator.
* Seems like a bad idea to feed it keysyms anyway, so here
* is a little hack...
**/
2017-05-20 19:44:04 +02:00
if (keysym >= XK_A && keysym <= XK_Z)
keysym += XK_z - XK_Z;
2019-02-03 15:49:35 -08:00
/* Get the real keycode, that correctly ignores international layouts
* as windows code does. */
key = x11_translate_keysym_to_rk(keycode);
2017-05-20 19:44:04 +02:00
if (state & ShiftMask)
mod |= RETROKMOD_SHIFT;
if (state & LockMask)
mod |= RETROKMOD_CAPSLOCK;
if (state & ControlMask)
mod |= RETROKMOD_CTRL;
if (state & Mod1Mask)
mod |= RETROKMOD_ALT;
if (state & Mod2Mask)
mod |= RETROKMOD_NUMLOCK;
2017-05-20 19:44:04 +02:00
if (state & Mod4Mask)
mod |= RETROKMOD_META;
input_keyboard_event(down, key, chars[0], mod, RETRO_DEVICE_KEYBOARD);
for (i = 1; i < num; i++)
input_keyboard_event(down, RETROK_UNKNOWN,
chars[i], mod, RETRO_DEVICE_KEYBOARD);
}
bool x11_alive(void *data)
2015-11-19 10:02:53 +01:00
{
while (XPending(g_x11_dpy))
{
2017-01-10 21:54:48 +01:00
XEvent event;
2016-12-04 03:39:51 +01:00
bool filter = false;
unsigned keycode = 0;
2015-11-19 10:02:53 +01:00
/* Can get events from older windows. Check this. */
XNextEvent(g_x11_dpy, &event);
2019-02-03 15:49:35 -08:00
/* IMPORTANT - Get keycode before XFilterEvent
because the event is localizated after the call */
keycode = event.xkey.keycode;
filter = XFilterEvent(&event, g_x11_win);
2015-11-19 10:02:53 +01:00
switch (event.type)
{
case ClientMessage:
if ( event.xclient.window == g_x11_win &&
2015-11-19 10:05:54 +01:00
(Atom)event.xclient.data.l[0] == g_x11_quit_atom)
2016-07-09 12:09:39 +02:00
frontend_driver_set_signal_handler_state(1);
2015-11-19 10:02:53 +01:00
break;
case DestroyNotify:
if (event.xdestroywindow.window == g_x11_win)
2016-07-09 12:09:39 +02:00
frontend_driver_set_signal_handler_state(1);
2015-11-19 10:02:53 +01:00
break;
case MapNotify:
if (event.xmap.window == g_x11_win)
g_x11_has_focus = true;
break;
case UnmapNotify:
if (event.xunmap.window == g_x11_win)
g_x11_has_focus = false;
break;
case ConfigureNotify:
if (event.xconfigure.window == g_x11_win)
g_x11_xce = event.xconfigure;
break;
2015-11-19 10:02:53 +01:00
case ButtonPress:
2016-10-01 12:12:58 +02:00
switch (event.xbutton.button)
{
case 1: /* Left click */
#if 0
RARCH_LOG("Click occurred : [%d, %d]\n",
event.xbutton.x_root,
event.xbutton.y_root);
#endif
break;
case 2: /* Grabbed */
/* Middle click */
break;
case 3: /* Right click */
break;
case 4: /* Grabbed */
/* Scroll up */
case 5: /* Scroll down */
case 6: /* Scroll wheel left */
case 7: /* Scroll wheel right */
2016-10-01 12:12:58 +02:00
x_input_poll_wheel(&event.xbutton, true);
break;
}
2015-11-19 10:02:53 +01:00
break;
case EnterNotify:
g_x11_entered = true;
break;
case LeaveNotify:
g_x11_entered = false;
break;
2015-11-19 10:02:53 +01:00
case ButtonRelease:
break;
case KeyRelease:
/* When you receive a key release and the next event
* is a key press of the same key combination,
* then it's auto-repeat and the key wasn't
* actually released. */
2019-03-09 19:06:01 +01:00
if(XEventsQueued(g_x11_dpy, QueuedAfterReading))
{
XEvent next_event;
XPeekEvent(g_x11_dpy, &next_event);
if ( next_event.type == KeyPress
&& next_event.xkey.time == event.xkey.time
&& next_event.xkey.keycode == event.xkey.keycode)
2019-03-14 16:09:05 +00:00
break; /* Key wasn't actually released */
2019-03-09 19:06:01 +01:00
}
case KeyPress:
2015-11-19 10:02:53 +01:00
if (event.xkey.window == g_x11_win)
x11_handle_key_event(keycode, &event, g_x11_xic, filter);
2015-11-19 10:02:53 +01:00
break;
}
}
return !((bool)frontend_driver_get_signal_handler_state());
}
void x11_check_window(void *data, bool *quit,
2020-03-06 20:29:15 +01:00
bool *resize, unsigned *width, unsigned *height)
{
unsigned new_width = *width;
unsigned new_height = *height;
x11_get_video_size(data, &new_width, &new_height);
if (new_width != *width || new_height != *height)
{
*width = new_width;
*height = new_height;
*resize = true;
}
x11_alive(data);
*quit = (bool)frontend_driver_get_signal_handler_state();
2015-11-19 10:02:53 +01:00
}
2015-11-19 10:09:19 +01:00
void x11_get_video_size(void *data, unsigned *width, unsigned *height)
2015-11-19 10:09:19 +01:00
{
if (!g_x11_dpy || g_x11_win == None)
{
Display *dpy = (Display*)XOpenDisplay(NULL);
2017-01-10 21:54:48 +01:00
*width = 0;
*height = 0;
2015-11-19 10:09:19 +01:00
if (dpy)
{
int screen = DefaultScreen(dpy);
2017-01-10 21:54:48 +01:00
*width = DisplayWidth(dpy, screen);
*height = DisplayHeight(dpy, screen);
2015-11-19 10:09:19 +01:00
XCloseDisplay(dpy);
}
}
else
{
if (g_x11_xce.width != 0 && g_x11_xce.height != 0)
{
*width = g_x11_xce.width;
*height = g_x11_xce.height;
}
else
{
XWindowAttributes target;
XGetWindowAttributes(g_x11_dpy, g_x11_win, &target);
2015-11-19 10:09:19 +01:00
*width = target.width;
*height = target.height;
}
2015-11-19 10:09:19 +01:00
}
}
2015-11-19 10:13:09 +01:00
bool x11_has_focus_internal(void *data)
{
return g_x11_has_focus;
}
2015-11-19 10:25:07 +01:00
bool x11_has_focus(void *data)
2015-11-19 10:13:09 +01:00
{
Window win;
int rev;
XGetInputFocus(g_x11_dpy, &win, &rev);
return (win == g_x11_win && g_x11_has_focus) || g_x11_true_full;
}
2015-11-19 11:04:17 +01:00
2015-11-19 11:07:52 +01:00
bool x11_connect(void)
{
frontend_driver_destroy_signal_handler_state();
2015-11-19 11:07:52 +01:00
/* Keep one g_x11_dpy alive the entire process lifetime.
* This is necessary for nVidia's EGL implementation for now. */
if (!g_x11_dpy)
if (!(g_x11_dpy = XOpenDisplay(NULL)))
2015-11-19 11:07:52 +01:00
return false;
2017-12-16 05:16:27 -08:00
#ifdef HAVE_DBUS
dbus_ensure_connection();
2017-12-16 05:16:27 -08:00
#endif
WIP: Fixes #2026 Screensaver suspend on Linux via Dbus One some systems (tested with Gnome 3 on Arch Linux) the current method of using `xdg-screensaver` to suspend the screensaver does not work. Instead, using DBus to issue an `Inhibit` request is recommended. The request returns a cookie that needs to be re-used to un-inhibit the screensaver later. Additionally if the DBus connection is closed the current inhibition is discarded. Thus, the DBus connection needs to stay connected for the duration of the screenshot inhibition. The code is heavily inspired from the [SDL 2.x code](http://hg.libsdl.org/SDL/file/default/src/core/linux/SDL_dbus.c#l172). I didn't call the SDL 2 code though since this it to fix the issue with the GL driver, and I assume one would want to have screensaver inhibited even when SDL 2 is not available (but GL is). I've set "WIP" because: * I haven't done C in a long time so my code is probably not great * There's a dependency on DBus which I don't know is acceptable or not * I've put my code where I could to check it works, but `x11_common` may not be the best place * The code need and "init" and "deinit" kind of method as it needs to initialise the DBus connection, and on deinit close it properly. I've used `x11_connect` and `x11_window_destroy` but they don't sound like the best choices. * I'm a bit unclear as to what happens when "suspend screensaver" is ticked on/off in the menu. This doesn't seem to call `x11_suspend_screensaver` everytime, so I'm not sure if there's a hook somewhere (as disabling screensaver suspend in the menu should cause a DBus unhinibit request to be sent). * Should I just call the SDL 2.x code (meaning that the GL driver would depend on SDL 2.x at runtime)? So, first of all are you ok with the approach, and if yes I'd gladly get feedback about the code, how to architecture it and the best place to put it. Thanks!
2016-09-08 15:18:37 -07:00
memset(&g_x11_xce, 0, sizeof(XConfigureEvent));
2015-11-19 11:07:52 +01:00
return true;
}
2015-11-19 11:18:57 +01:00
void x11_update_title(void *data)
2015-11-19 11:18:57 +01:00
{
size_t len;
char title[128];
title[0] = '\0';
len = video_driver_get_window_title(title, sizeof(title));
if (title[0])
XChangeProperty(g_x11_dpy, g_x11_win, XA_WM_NAME, XA_STRING,
8, PropModeReplace, (const unsigned char*)title, len);
2015-11-19 11:18:57 +01:00
}
2015-11-19 12:05:32 +01:00
bool x11_input_ctx_new(bool true_full)
{
2016-02-05 13:36:18 +01:00
if (!x11_create_input_context(g_x11_dpy, g_x11_win,
&g_x11_xim, &g_x11_xic))
return false;
2015-11-19 12:05:32 +01:00
video_driver_display_type_set(RARCH_DISPLAY_X11);
video_driver_display_set((uintptr_t)g_x11_dpy);
video_driver_window_set((uintptr_t)g_x11_win);
2015-11-19 12:05:32 +01:00
g_x11_true_full = true_full;
return true;
}
void x11_input_ctx_destroy(void)
{
x11_destroy_input_context(&g_x11_xim, &g_x11_xic);
}
2015-11-19 11:49:09 +01:00
void x11_window_destroy(bool fullscreen)
{
if (g_x11_win)
XUnmapWindow(g_x11_dpy, g_x11_win);
if (!fullscreen)
XDestroyWindow(g_x11_dpy, g_x11_win);
2015-11-19 11:55:05 +01:00
g_x11_win = None;
WIP: Fixes #2026 Screensaver suspend on Linux via Dbus One some systems (tested with Gnome 3 on Arch Linux) the current method of using `xdg-screensaver` to suspend the screensaver does not work. Instead, using DBus to issue an `Inhibit` request is recommended. The request returns a cookie that needs to be re-used to un-inhibit the screensaver later. Additionally if the DBus connection is closed the current inhibition is discarded. Thus, the DBus connection needs to stay connected for the duration of the screenshot inhibition. The code is heavily inspired from the [SDL 2.x code](http://hg.libsdl.org/SDL/file/default/src/core/linux/SDL_dbus.c#l172). I didn't call the SDL 2 code though since this it to fix the issue with the GL driver, and I assume one would want to have screensaver inhibited even when SDL 2 is not available (but GL is). I've set "WIP" because: * I haven't done C in a long time so my code is probably not great * There's a dependency on DBus which I don't know is acceptable or not * I've put my code where I could to check it works, but `x11_common` may not be the best place * The code need and "init" and "deinit" kind of method as it needs to initialise the DBus connection, and on deinit close it properly. I've used `x11_connect` and `x11_window_destroy` but they don't sound like the best choices. * I'm a bit unclear as to what happens when "suspend screensaver" is ticked on/off in the menu. This doesn't seem to call `x11_suspend_screensaver` everytime, so I'm not sure if there's a hook somewhere (as disabling screensaver suspend in the menu should cause a DBus unhinibit request to be sent). * Should I just call the SDL 2.x code (meaning that the GL driver would depend on SDL 2.x at runtime)? So, first of all are you ok with the approach, and if yes I'd gladly get feedback about the code, how to architecture it and the best place to put it. Thanks!
2016-09-08 15:18:37 -07:00
#ifdef HAVE_DBUS
2016-09-09 18:08:54 -07:00
dbus_screensaver_uninhibit();
dbus_close_connection();
WIP: Fixes #2026 Screensaver suspend on Linux via Dbus One some systems (tested with Gnome 3 on Arch Linux) the current method of using `xdg-screensaver` to suspend the screensaver does not work. Instead, using DBus to issue an `Inhibit` request is recommended. The request returns a cookie that needs to be re-used to un-inhibit the screensaver later. Additionally if the DBus connection is closed the current inhibition is discarded. Thus, the DBus connection needs to stay connected for the duration of the screenshot inhibition. The code is heavily inspired from the [SDL 2.x code](http://hg.libsdl.org/SDL/file/default/src/core/linux/SDL_dbus.c#l172). I didn't call the SDL 2 code though since this it to fix the issue with the GL driver, and I assume one would want to have screensaver inhibited even when SDL 2 is not available (but GL is). I've set "WIP" because: * I haven't done C in a long time so my code is probably not great * There's a dependency on DBus which I don't know is acceptable or not * I've put my code where I could to check it works, but `x11_common` may not be the best place * The code need and "init" and "deinit" kind of method as it needs to initialise the DBus connection, and on deinit close it properly. I've used `x11_connect` and `x11_window_destroy` but they don't sound like the best choices. * I'm a bit unclear as to what happens when "suspend screensaver" is ticked on/off in the menu. This doesn't seem to call `x11_suspend_screensaver` everytime, so I'm not sure if there's a hook somewhere (as disabling screensaver suspend in the menu should cause a DBus unhinibit request to be sent). * Should I just call the SDL 2.x code (meaning that the GL driver would depend on SDL 2.x at runtime)? So, first of all are you ok with the approach, and if yes I'd gladly get feedback about the code, how to architecture it and the best place to put it. Thanks!
2016-09-08 15:18:37 -07:00
#endif
2015-11-19 11:55:05 +01:00
}
void x11_colormap_destroy(void)
{
if (!g_x11_cmap)
return;
XFreeColormap(g_x11_dpy, g_x11_cmap);
g_x11_cmap = None;
2015-11-19 11:49:09 +01:00
}
2015-11-19 12:18:35 +01:00
void x11_install_quit_atom(void)
{
2016-02-05 13:36:18 +01:00
g_x11_quit_atom = XInternAtom(g_x11_dpy,
"WM_DELETE_WINDOW", False);
2015-11-19 12:18:35 +01:00
if (g_x11_quit_atom)
XSetWMProtocols(g_x11_dpy, g_x11_win, &g_x11_quit_atom, 1);
}
2015-11-19 15:05:17 +01:00
static Bool x11_wait_notify(Display *d, XEvent *e, char *arg)
{
return e->type == MapNotify && e->xmap.window == g_x11_win;
}
void x11_event_queue_check(XEvent *event)
{
XIfEvent(g_x11_dpy, event, x11_wait_notify, NULL);
}
2015-12-01 08:49:35 +01:00
static bool x11_check_atom_supported(Display *dpy, Atom atom)
{
Atom XA_NET_SUPPORTED = XInternAtom(dpy, "_NET_SUPPORTED", True);
Atom type;
int format;
unsigned long nitems;
unsigned long bytes_after;
Atom *prop;
int i;
if (XA_NET_SUPPORTED == None)
return false;
2018-04-22 13:05:40 +02:00
XGetWindowProperty(dpy, DefaultRootWindow(dpy), XA_NET_SUPPORTED,
0, UINT_MAX, False, XA_ATOM, &type, &format,&nitems,
&bytes_after, (unsigned char **) &prop);
if (!prop || type != XA_ATOM)
return false;
for (i = 0; i < nitems; i++)
{
if (prop[i] == atom)
{
XFree(prop);
return true;
}
}
XFree(prop);
return false;
}
bool x11_has_net_wm_fullscreen(Display *dpy)
{
XA_NET_WM_STATE_FULLSCREEN = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
return x11_check_atom_supported(dpy, XA_NET_WM_STATE_FULLSCREEN);
}
char *x11_get_wm_name(Display *dpy)
{
Atom type;
int format;
Window window;
2018-11-26 13:54:51 +01:00
Atom XA_NET_SUPPORTING_WM_CHECK = XInternAtom(g_x11_dpy, "_NET_SUPPORTING_WM_CHECK", False);
Atom XA_NET_WM_NAME = XInternAtom(g_x11_dpy, "_NET_WM_NAME", False);
Atom XA_UTF8_STRING = XInternAtom(g_x11_dpy, "UTF8_STRING", False);
unsigned long nitems = 0;
unsigned long bytes_after = 0;
char *title = NULL;
unsigned char *propdata = NULL;
if (!XA_NET_SUPPORTING_WM_CHECK || !XA_NET_WM_NAME)
return NULL;
2018-11-26 13:54:51 +01:00
if (!(XGetWindowProperty(dpy,
DefaultRootWindow(dpy),
XA_NET_SUPPORTING_WM_CHECK,
0,
1,
False,
XA_WINDOW,
&type,
&format,
&nitems,
&bytes_after,
2018-11-26 13:54:51 +01:00
&propdata) == Success &&
propdata))
return NULL;
2018-11-26 13:54:51 +01:00
window = ((Window *) propdata)[0];
XFree(propdata);
2018-11-26 13:54:51 +01:00
if (!(XGetWindowProperty(dpy,
window,
XA_NET_WM_NAME,
0,
8192,
False,
XA_UTF8_STRING,
&type,
&format,
&nitems,
&bytes_after,
2018-11-26 13:54:51 +01:00
&propdata) == Success
&& propdata))
return NULL;
2018-11-26 13:54:51 +01:00
title = strdup((char *) propdata);
XFree(propdata);
return title;
}