RetroArch/gfx/common/x11_common.c

688 lines
17 KiB
C
Raw Normal View History

2012-09-26 13:52:25 +00:00
/* RetroArch - A frontend for libretro.
2014-01-01 00:50:59 +00:00
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
2017-01-22 12:40:32 +00:00
* Copyright (C) 2011-2017 - Daniel De Matteis
*
2012-09-26 13:52:25 +00: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 09:24:03 +00:00
#include <sys/types.h>
#include <sys/wait.h>
2015-09-05 18:34:22 +00:00
#include <errno.h>
#include <unistd.h>
2015-09-05 18:34:22 +00:00
#include <X11/Xatom.h>
2016-09-08 06:15:40 +00:00
#ifdef HAVE_CONFIG_H
#include "../../config.h"
#endif
2017-05-20 17:44:04 +00:00
#ifdef HAVE_XINERAMA
#include <X11/extensions/Xinerama.h>
#endif
2015-09-05 18:34:22 +00:00
#include "x11_common.h"
#include <X11/extensions/xf86vmode.h>
2017-05-20 17:44:04 +00:00
#include <encodings/utf.h>
2017-12-16 13:16:27 +00:00
#ifdef HAVE_DBUS
2017-03-23 23:15:17 +00:00
#include "dbus_common.h"
2017-12-16 13:16:27 +00:00
#endif
2017-03-23 23:15:17 +00:00
#include "../../frontend/frontend_driver.h"
#include "../../input/input_driver.h"
2017-05-20 17:48:02 +00:00
#include "../../input/input_keymaps.h"
2015-11-29 02:34:09 +00:00
#include "../../input/common/input_x11_common.h"
2015-11-23 11:03:38 +00:00
#include "../../verbosity.h"
2012-09-26 13:52:25 +00:00
2017-03-23 18:36:39 +00:00
#define _NET_WM_STATE_ADD 1
#define MOVERESIZE_GRAVITY_CENTER 5
#define MOVERESIZE_X_SHIFT 8
#define MOVERESIZE_Y_SHIFT 9
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 22:18:37 +00:00
static XF86VidModeModeInfo desktop_mode;
2016-12-04 02:39:51 +00:00
static bool xdg_screensaver_available = true;
bool g_x11_entered = false;
static bool g_x11_has_focus = false;
static bool g_x11_true_full = false;
Display *g_x11_dpy = NULL;
2016-12-15 11:37:14 +00:00
unsigned g_x11_screen = 0;
2015-11-19 11:23:49 +00:00
Colormap g_x11_cmap;
Window g_x11_win;
static Atom XA_NET_WM_STATE;
static Atom XA_NET_WM_STATE_FULLSCREEN;
static Atom XA_NET_MOVERESIZE_WINDOW;
2015-11-19 11:23:49 +00:00
2015-11-19 11:18:35 +00:00
static Atom g_x11_quit_atom;
static XIM g_x11_xim;
static XIC g_x11_xic;
2013-03-29 12:46:11 +00:00
static void x11_hide_mouse(Display *dpy, Window win)
2012-09-26 13:52:25 +00:00
{
2015-01-10 02:30:23 +00:00
static char bm_no_data[] = {0, 0, 0, 0, 0, 0, 0, 0};
2012-09-26 13:52:25 +00:00
Cursor no_ptr;
Pixmap bm_no;
XColor black, dummy;
2015-01-10 02:30:23 +00:00
Colormap colormap = DefaultColormap(dpy, DefaultScreen(dpy));
2015-06-12 23:18:13 +00:00
2012-09-26 13:52:25 +00: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 12:46:11 +00:00
void x11_show_mouse(Display *dpy, Window win, bool state)
{
if (state)
XUndefineCursor(dpy, win);
else
x11_hide_mouse(dpy, win);
}
2012-09-26 13:52:25 +00:00
void x11_windowed_fullscreen(Display *dpy, Window win)
{
2017-12-25 06:36:49 +00:00
XEvent xev = {0};
2015-06-26 15:46:13 +00:00
2017-03-23 18:36:39 +00: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 13:52:25 +00:00
2017-03-23 18:36:39 +00: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 17:05:29 +00:00
XSendEvent(dpy, DefaultRootWindow(dpy), False,
SubstructureRedirectMask | SubstructureNotifyMask,
&xev);
}
2015-01-12 04:05:56 +00:00
/* Try to be nice to tiling WMs if possible. */
2012-10-12 17:05:29 +00:00
void x11_move_window(Display *dpy, Window win, int x, int y,
unsigned width, unsigned height)
{
2017-12-25 06:36:49 +00:00
XEvent xev = {0};
2012-10-12 17:05:29 +00:00
2017-03-23 18:36:39 +00:00
XA_NET_MOVERESIZE_WINDOW = XInternAtom(dpy, "_NET_MOVERESIZE_WINDOW", False);
2015-01-10 02:30:23 +00:00
2016-02-05 12:36:18 +00:00
xev.xclient.type = ClientMessage;
xev.xclient.send_event = True;
2012-10-12 17:05:29 +00:00
xev.xclient.message_type = XA_NET_MOVERESIZE_WINDOW;
2016-02-05 12:36:18 +00:00
xev.xclient.window = win;
xev.xclient.format = 32;
xev.xclient.data.l[0] = (1 << MOVERESIZE_X_SHIFT)
2016-02-05 12:36:18 +00:00
| (1 << MOVERESIZE_Y_SHIFT);
xev.xclient.data.l[1] = x;
xev.xclient.data.l[2] = y;
2012-09-26 13:52:25 +00:00
XSendEvent(dpy, DefaultRootWindow(dpy), False,
SubstructureRedirectMask | SubstructureNotifyMask,
&xev);
}
2012-10-26 21:01:32 +00:00
static void x11_set_window_class(Display *dpy, Window win)
{
2016-12-19 18:17:23 +00:00
XClassHint hint;
2015-01-10 02:30:23 +00:00
2015-01-12 04:05:56 +00:00
hint.res_name = (char*)"retroarch"; /* Broken header. */
hint.res_class = (char*)"retroarch";
2012-10-26 21:01:32 +00:00
XSetClassHint(dpy, win, &hint);
}
static void x11_set_window_pid(Display *dpy, Window win)
{
2017-12-25 06:36:49 +00: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;
if((scret = sysconf(_SC_HOST_NAME_MAX)) == -1 && errno)
return;
2017-12-31 13:33:36 +00:00
if((hostname = (char*)malloc(scret + 1)) == NULL)
return;
2017-11-30 23:17:50 +00: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 21:01:32 +00: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 21:01:32 +00:00
}
static void xdg_screensaver_inhibit(Window wnd)
2012-09-26 13:52:25 +00:00
{
2017-01-10 20:54:48 +00:00
int ret;
char cmd[64];
2016-10-21 03:57:40 +00:00
cmd[0] = '\0';
2017-03-24 00:38:23 +00:00
RARCH_LOG("[X11]: Suspending screensaver (X11, xdg-screensaver).\n");
2015-01-18 21:32:14 +00:00
snprintf(cmd, sizeof(cmd), "xdg-screensaver suspend 0x%x", (int)wnd);
2012-09-26 13:52:25 +00:00
2015-01-10 02:30:23 +00:00
ret = system(cmd);
if (ret == -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");
}
}
void x11_suspend_screensaver_xdg_screensaver(Window wnd, bool enable)
{
2016-10-15 23:51:07 +00:00
/* Check if screensaver suspend is enabled in config */
if (!enable)
return;
2017-01-10 20:54:48 +00:00
if (xdg_screensaver_available)
xdg_screensaver_inhibit(wnd);
}
2016-09-10 01:08:54 +00:00
void x11_suspend_screensaver(Window wnd, bool enable)
{
#ifdef HAVE_DBUS
if (dbus_suspend_screensaver(enable))
2016-09-16 11:31:18 +00:00
return;
2016-09-10 01:08:54 +00:00
#endif
x11_suspend_screensaver_xdg_screensaver(wnd, enable);
2016-09-10 01:08:54 +00:00
}
2017-01-18 16:41:27 +00:00
static bool get_video_mode(video_frame_info_t *video_info,
2017-01-13 15:57:57 +00:00
Display *dpy, unsigned width, unsigned height,
XF86VidModeModeInfo *mode, XF86VidModeModeInfo *desktop_mode)
2012-09-29 08:47:55 +00:00
{
2016-02-05 12:36:18 +00:00
int i, num_modes = 0;
bool ret = false;
2017-12-25 06:36:49 +00:00
float refresh_mod = 0.0f;
2016-02-05 12:36:18 +00:00
float minimum_fps_diff = 0.0f;
2013-10-22 19:26:33 +00:00
XF86VidModeModeInfo **modes = NULL;
2015-01-10 02:30:23 +00:00
2012-09-29 08:47:55 +00:00
XF86VidModeGetAllModeLines(dpy, DefaultScreen(dpy), &num_modes, &modes);
if (!num_modes)
{
XFree(modes);
return false;
}
*desktop_mode = *modes[0];
/* If we use black frame insertion, we fake a 60 Hz monitor
* for 120 Hz one, etc, so try to match that. */
2017-01-18 16:41:27 +00:00
refresh_mod = video_info->black_frame_insertion ? 0.5f : 1.0f;
2013-10-22 19:26:33 +00:00
for (i = 0; i < num_modes; i++)
2012-09-29 08:47:55 +00:00
{
2015-01-18 20:49:00 +00:00
float refresh, diff;
const XF86VidModeModeInfo *m = modes[i];
2015-01-10 02:30:23 +00:00
if (!m)
continue;
2015-01-18 20:49:00 +00:00
if (m->hdisplay != width)
continue;
if (m->vdisplay != height)
continue;
refresh = refresh_mod * m->dotclock * 1000.0f / (m->htotal * m->vtotal);
2017-01-18 16:41:27 +00:00
diff = fabsf(refresh - video_info->refresh_rate);
2015-01-18 20:49:00 +00:00
if (!ret || diff < minimum_fps_diff)
2012-09-29 08:47:55 +00:00
{
2015-01-18 20:49:00 +00:00
*mode = *m;
minimum_fps_diff = diff;
2012-09-29 08:47:55 +00:00
}
2015-01-18 20:49:00 +00:00
ret = true;
2012-09-29 08:47:55 +00:00
}
XFree(modes);
return ret;
}
2017-01-18 16:41:27 +00:00
bool x11_enter_fullscreen(video_frame_info_t *video_info,
2017-01-13 15:57:57 +00:00
Display *dpy, unsigned width,
unsigned height)
2012-09-29 08:47:55 +00:00
{
XF86VidModeModeInfo mode;
2014-10-27 13:35:23 +00:00
if (!get_video_mode(video_info, dpy, width, height, &mode, &desktop_mode))
2015-01-18 20:49:00 +00:00
return false;
2014-10-27 13:35:23 +00:00
2015-01-18 20:49:00 +00:00
if (!XF86VidModeSwitchToMode(dpy, DefaultScreen(dpy), &mode))
return false;
XF86VidModeSetViewPort(dpy, DefaultScreen(dpy), 0, 0);
return true;
2012-09-29 08:47:55 +00:00
}
void x11_exit_fullscreen(Display *dpy)
2012-09-29 08:47:55 +00:00
{
XF86VidModeSwitchToMode(dpy, DefaultScreen(dpy), &desktop_mode);
2012-09-29 08:47:55 +00:00
XF86VidModeSetViewPort(dpy, DefaultScreen(dpy), 0, 0);
}
bool x11_create_input_context(Display *dpy, Window win, XIM *xim, XIC *xic)
{
x11_destroy_input_context(xim, xic);
g_x11_has_focus = true;
2017-01-10 20:54:48 +00:00
*xim = XOpenIM(dpy, NULL, NULL, NULL);
2015-01-12 04:05:56 +00:00
if (!*xim)
{
RARCH_ERR("[X11]: Failed to open input method.\n");
return false;
}
*xic = XCreateIC(*xim, XNInputStyle,
XIMPreeditNothing | XIMStatusNothing, XNClientWindow, win, NULL);
2015-01-12 04:05:56 +00:00
if (!*xic)
{
RARCH_ERR("[X11]: Failed to create input context.\n");
return false;
}
XSetICFocus(*xic);
return true;
}
void x11_destroy_input_context(XIM *xim, XIC *xic)
{
if (*xic)
{
XDestroyIC(*xic);
*xic = NULL;
}
if (*xim)
{
XCloseIM(*xim);
*xim = NULL;
}
}
2015-04-09 03:05:29 +00:00
bool x11_get_metrics(void *data,
enum display_metric_types type, float *value)
{
unsigned screen_no = 0;
Display *dpy = (Display*)XOpenDisplay(NULL);
2016-09-14 13:56:14 +00:00
int pixels_x = DisplayWidth(dpy, screen_no);
int pixels_y = DisplayHeight(dpy, screen_no);
int physical_width = DisplayWidthMM(dpy, screen_no);
int physical_height = DisplayHeightMM(dpy, screen_no);
2015-04-09 03:05:29 +00:00
(void)pixels_y;
XCloseDisplay(dpy);
switch (type)
{
case DISPLAY_METRIC_MM_WIDTH:
*value = (float)physical_width;
break;
case DISPLAY_METRIC_MM_HEIGHT:
*value = (float)physical_height;
break;
case DISPLAY_METRIC_DPI:
*value = ((((float)pixels_x) * 25.4) / ((float)physical_width));
break;
case DISPLAY_METRIC_NONE:
default:
*value = 0;
return false;
}
return true;
}
2015-11-19 09:02:53 +00:00
2017-05-20 17:44:04 +00:00
static void x11_handle_key_event(XEvent *event, XIC ic, bool filter)
{
int i;
Status status;
2017-05-20 17:44:04 +00:00
uint32_t chars[32];
2017-12-25 06:36:49 +00:00
unsigned key = 0;
2017-05-20 17:44:04 +00:00
uint16_t mod = 0;
unsigned state = event->xkey.state;
2017-12-25 06:36:49 +00:00
bool down = event->type == KeyPress;
int num = 0;
KeySym keysym = 0;
2017-12-25 06:36:49 +00:00
chars[0] = '\0';
2017-05-20 17:44:04 +00:00
if (!filter)
{
if (down)
{
char keybuf[32];
keybuf[0] = '\0';
#ifdef X_HAVE_UTF8_STRING
status = 0;
2017-05-20 17:44:04 +00:00
/* XwcLookupString doesn't seem to work. */
num = Xutf8LookupString(ic, &event->xkey, keybuf, ARRAY_SIZE(keybuf), &keysym, &status);
/* libc functions need UTF-8 locale to work properly,
2017-05-20 17:44:04 +00:00
* which makes mbrtowc a bit impractical.
*
* Use custom UTF8 -> UTF-32 conversion. */
num = utf8_conv_utf32(chars, ARRAY_SIZE(chars), keybuf, num);
#else
(void)ic;
num = XLookupString(&event->xkey, keybuf, sizeof(keybuf), &keysym, NULL); /* ASCII only. */
for (i = 0; i < num; i++)
chars[i] = keybuf[i] & 0x7f;
#endif
}
else
keysym = XLookupKeysym(&event->xkey, (state & ShiftMask) || (state & LockMask));
}
/* 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... */
if (keysym >= XK_A && keysym <= XK_Z)
keysym += XK_z - XK_Z;
key = input_keymaps_translate_keysym_to_rk(keysym);
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 & Mod4Mask)
mod |= RETROKMOD_META;
if (IsKeypadKey(keysym))
mod |= RETROKMOD_NUMLOCK;
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 09:02:53 +00:00
{
while (XPending(g_x11_dpy))
{
2017-01-10 20:54:48 +00:00
XEvent event;
2016-12-04 02:39:51 +00:00
bool filter = false;
2015-11-19 09:02:53 +00:00
/* Can get events from older windows. Check this. */
XNextEvent(g_x11_dpy, &event);
filter = XFilterEvent(&event, g_x11_win);
switch (event.type)
{
case ClientMessage:
if (event.xclient.window == g_x11_win &&
2015-11-19 09:05:54 +00:00
(Atom)event.xclient.data.l[0] == g_x11_quit_atom)
2016-07-09 10:09:39 +00:00
frontend_driver_set_signal_handler_state(1);
2015-11-19 09:02:53 +00:00
break;
case DestroyNotify:
if (event.xdestroywindow.window == g_x11_win)
2016-07-09 10:09:39 +00:00
frontend_driver_set_signal_handler_state(1);
2015-11-19 09:02:53 +00: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 ButtonPress:
2016-10-01 10:12:58 +00: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 */
x_input_poll_wheel(&event.xbutton, true);
break;
}
2015-11-19 09:02:53 +00:00
break;
case EnterNotify:
g_x11_entered = true;
break;
case LeaveNotify:
g_x11_entered = false;
break;
2015-11-19 09:02:53 +00:00
case ButtonRelease:
break;
case KeyPress:
case KeyRelease:
if (event.xkey.window == g_x11_win)
x11_handle_key_event(&event, g_x11_xic, filter);
break;
}
}
return !((bool)frontend_driver_get_signal_handler_state());
}
void x11_check_window(void *data, bool *quit,
bool *resize, unsigned *width, unsigned *height,
bool is_shutdown)
{
unsigned new_width = *width;
unsigned new_height = *height;
x11_get_video_size(data, &new_width, &new_height);
if (new_width != *width || new_height != *height)
{
*resize = true;
*width = new_width;
*height = new_height;
}
x11_alive(data);
*quit = (bool)frontend_driver_get_signal_handler_state();
2015-11-19 09:02:53 +00:00
}
2015-11-19 09:09:19 +00:00
void x11_get_video_size(void *data, unsigned *width, unsigned *height)
2015-11-19 09:09:19 +00:00
{
if (!g_x11_dpy || g_x11_win == None)
{
Display *dpy = (Display*)XOpenDisplay(NULL);
2017-01-10 20:54:48 +00:00
*width = 0;
*height = 0;
2015-11-19 09:09:19 +00:00
if (dpy)
{
int screen = DefaultScreen(dpy);
2017-01-10 20:54:48 +00:00
*width = DisplayWidth(dpy, screen);
*height = DisplayHeight(dpy, screen);
2015-11-19 09:09:19 +00:00
XCloseDisplay(dpy);
}
}
else
{
XWindowAttributes target;
XGetWindowAttributes(g_x11_dpy, g_x11_win, &target);
*width = target.width;
*height = target.height;
}
}
2015-11-19 09:13:09 +00:00
bool x11_has_focus_internal(void *data)
{
return g_x11_has_focus;
}
2015-11-19 09:25:07 +00:00
bool x11_has_focus(void *data)
2015-11-19 09:13:09 +00: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 10:04:17 +00:00
2015-11-19 10:07:52 +00:00
bool x11_connect(void)
{
frontend_driver_destroy_signal_handler_state();
2015-11-19 10:07:52 +00: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)
{
g_x11_dpy = XOpenDisplay(NULL);
if (!g_x11_dpy)
return false;
}
2017-12-16 13:16:27 +00:00
#ifdef HAVE_DBUS
dbus_ensure_connection();
2017-12-16 13:16:27 +00: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 22:18:37 +00:00
2015-11-19 10:07:52 +00:00
return true;
}
2015-11-19 10:18:57 +00:00
2017-05-19 01:34:53 +00:00
void x11_update_title(void *data, void *data2)
2015-11-19 10:18:57 +00:00
{
char title[128];
title[0] = '\0';
video_driver_get_window_title(title, sizeof(title));
2017-01-19 16:58:00 +00:00
if (title[0])
XStoreName(g_x11_dpy, g_x11_win, title);
2015-11-19 10:18:57 +00:00
}
2015-11-19 11:05:32 +00:00
bool x11_input_ctx_new(bool true_full)
{
2016-02-05 12:36:18 +00:00
if (!x11_create_input_context(g_x11_dpy, g_x11_win,
&g_x11_xim, &g_x11_xic))
return false;
2015-11-19 11:05:32 +00: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 11:05:32 +00: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 10:49:09 +00: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 10:55:05 +00: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 22:18:37 +00:00
#ifdef HAVE_DBUS
2016-09-10 01:08:54 +00: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 22:18:37 +00:00
#endif
2015-11-19 10:55:05 +00: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 10:49:09 +00:00
}
2015-11-19 11:18:35 +00:00
void x11_install_quit_atom(void)
{
2016-02-05 12:36:18 +00:00
g_x11_quit_atom = XInternAtom(g_x11_dpy,
"WM_DELETE_WINDOW", False);
2015-11-19 11:18:35 +00:00
if (g_x11_quit_atom)
XSetWMProtocols(g_x11_dpy, g_x11_win, &g_x11_quit_atom, 1);
}
2015-11-19 14:05:17 +00: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 07:49:35 +00:00