Add basic Xinerama support to X11.

This commit is contained in:
Themaister 2012-10-12 19:05:29 +02:00
parent b80227cf81
commit 9208663c53
8 changed files with 260 additions and 24 deletions

View File

@ -216,8 +216,8 @@ endif
ifeq ($(HAVE_X11), 1)
OBJ += input/x11_input.o gfx/context/x11_common.o
LIBS += $(X11_LIBS) $(XEXT_LIBS) $(XF86VM_LIBS)
DEFINES += $(X11_CFLAGS) $(XEXT_CFLAGS) $(XF86VM_CFLAGS)
LIBS += $(X11_LIBS) $(XEXT_LIBS) $(XF86VM_LIBS) $(XINERAMA_LIBS)
DEFINES += $(X11_CFLAGS) $(XEXT_CFLAGS) $(XF86VM_CFLAGS) $(XINERAMA_CFLAGS)
endif
ifeq ($(HAVE_CG), 1)

View File

@ -33,6 +33,7 @@ static Window g_win;
static Colormap g_cmap;
static Atom g_quit_atom;
static bool g_has_focus;
static unsigned g_screen;
static GLXContext g_ctx;
static GLXFBConfig g_fbc;
@ -259,28 +260,63 @@ static bool gfx_ctx_set_video_mode(
}
}
int x_off = 0;
int y_off = 0;
#ifdef HAVE_XINERAMA
if (fullscreen || g_screen != 0)
{
unsigned new_width = width;
unsigned new_height = height;
if (x11_get_xinerama_coord(g_dpy, g_screen, &x_off, &y_off, &new_width, &new_height))
RARCH_LOG("[GLX]: Using Xinerama on screen #%u.\n", g_screen);
else
RARCH_LOG("[GLX]: Xinerama is not active on screen.\n");
if (fullscreen)
{
width = new_width;
height = new_height;
}
}
#endif
RARCH_LOG("[GLX]: X = %d, Y = %d, W = %u, H = %u.\n",
x_off, y_off, width, height);
g_win = XCreateWindow(g_dpy, RootWindow(g_dpy, vi->screen),
0, 0, width ? width : 200, height ? height : 200, 0,
x_off, y_off, width, height, 0,
vi->depth, InputOutput, vi->visual,
CWBorderPixel | CWColormap | CWEventMask | (true_full ? CWOverrideRedirect : 0), &swa);
XSetWindowBackground(g_dpy, g_win, 0);
gfx_ctx_update_window_title(true);
x11_hide_mouse(g_dpy, g_win);
if (fullscreen)
x11_hide_mouse(g_dpy, g_win);
if (true_full)
{
RARCH_LOG("[GLX]: Using true fullscreen.\n");
XMapRaised(g_dpy, g_win);
XGrabKeyboard(g_dpy, g_win, True, GrabModeAsync, GrabModeAsync, CurrentTime);
}
else if (fullscreen) // We attempted true fullscreen, but failed. Attempt using windowed fullscreen.
{
XMapRaised(g_dpy, g_win);
RARCH_WARN("[GLX]: Using windowed fullscreen.\n");
RARCH_LOG("[GLX]: Using windowed fullscreen.\n");
// We have to move the window to the screen we want to go fullscreen on first.
// x_off and y_off usually get ignored in XCreateWindow().
x11_move_window(g_dpy, g_win, x_off, y_off, width, height);
x11_windowed_fullscreen(g_dpy, g_win);
}
else
{
XMapWindow(g_dpy, g_win);
// If we want to map the window on a different screen, we'll have to do it by force.
// Otherwise, we should try to let the window manager sort it out.
// x_off and y_off usually get ignored in XCreateWindow().
if (g_screen)
x11_move_window(g_dpy, g_win, x_off, y_off, width, height);
}
XEvent event;
XIfEvent(g_dpy, &event, glx_wait_notify, NULL);
@ -326,7 +362,9 @@ static bool gfx_ctx_set_video_mode(
driver.display_type = RARCH_DISPLAY_X11;
driver.video_display = (uintptr_t)g_dpy;
driver.video_window = (uintptr_t)g_win;
// Always assume that we have focus in true fullscreen.
driver.video_window = true_full ? (uintptr_t)None : (uintptr_t)g_win;
return true;
@ -349,6 +387,22 @@ static void gfx_ctx_destroy(void)
if (g_win)
{
// Save last used monitor for later.
#ifdef HAVE_XINERAMA
XWindowAttributes target;
Window child;
int x = 0, y = 0;
XGetWindowAttributes(g_dpy, g_win, &target);
XTranslateCoordinates(g_dpy, g_win, DefaultRootWindow(g_dpy),
target.x, target.y, &x, &y, &child);
g_screen = x11_get_xinerama_monitor(g_dpy, x, y,
target.width, target.height);
RARCH_LOG("[GLX]: Saved monitor #%u.\n", g_screen);
#endif
XUnmapWindow(g_dpy, g_win);
XDestroyWindow(g_dpy, g_win);
g_win = None;
@ -391,7 +445,7 @@ static bool gfx_ctx_has_focus(void)
int rev;
XGetInputFocus(g_dpy, &win, &rev);
return win == g_win && g_has_focus;
return (win == g_win && g_has_focus) || (g_win == None);
}
static gfx_ctx_proc_t gfx_ctx_get_proc_address(const char *symbol)

View File

@ -46,8 +46,12 @@ void x11_hide_mouse(Display *dpy, Window win)
static Atom XA_NET_WM_STATE;
static Atom XA_NET_WM_STATE_FULLSCREEN;
static Atom XA_NET_MOVERESIZE_WINDOW;
#define XA_INIT(x) XA##x = XInternAtom(dpy, #x, False)
#define _NET_WM_STATE_ADD 1
#define MOVERESIZE_GRAVITY_CENTER 5
#define MOVERESIZE_X_SHIFT 8
#define MOVERESIZE_Y_SHIFT 9
void x11_windowed_fullscreen(Display *dpy, Window win)
{
XA_INIT(_NET_WM_STATE);
@ -55,23 +59,46 @@ void x11_windowed_fullscreen(Display *dpy, Window win)
if (!XA_NET_WM_STATE || !XA_NET_WM_STATE_FULLSCREEN)
{
RARCH_ERR("[X/EGL]: Cannot set windowed fullscreen.\n");
RARCH_ERR("[X11]: Cannot set windowed fullscreen.\n");
return;
}
XEvent xev;
XEvent xev = {0};
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
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;
xev.xclient.data.l[2] = 0;
xev.xclient.data.l[3] = 0;
xev.xclient.data.l[4] = 0;
XSendEvent(dpy, DefaultRootWindow(dpy), False,
SubstructureRedirectMask | SubstructureNotifyMask,
&xev);
}
// Try to be nice to tiling WMs if possible.
void x11_move_window(Display *dpy, Window win, int x, int y,
unsigned width, unsigned height)
{
XA_INIT(_NET_MOVERESIZE_WINDOW);
if (!XA_NET_MOVERESIZE_WINDOW)
{
RARCH_ERR("[X11]: Cannot move window.\n");
return;
}
XEvent xev = {0};
xev.xclient.type = ClientMessage;
xev.xclient.send_event = True;
xev.xclient.message_type = XA_NET_MOVERESIZE_WINDOW;
xev.xclient.window = win;
xev.xclient.format = 32;
xev.xclient.data.l[0] = (1 << MOVERESIZE_X_SHIFT) | (1 << MOVERESIZE_Y_SHIFT);
xev.xclient.data.l[1] = x;
xev.xclient.data.l[2] = y;
XSendEvent(dpy, DefaultRootWindow(dpy), False,
SubstructureRedirectMask | SubstructureNotifyMask,
@ -141,3 +168,80 @@ void x11_exit_fullscreen(Display *dpy, XF86VidModeModeInfo *desktop_mode)
XF86VidModeSetViewPort(dpy, DefaultScreen(dpy), 0, 0);
}
#ifdef HAVE_XINERAMA
static XineramaScreenInfo *x11_query_screens(Display *dpy, int *num_screens)
{
int major, minor;
if (!XineramaQueryExtension(dpy, &major, &minor))
return false;
XineramaQueryVersion(dpy, &major, &minor);
RARCH_LOG("[X11]: Xinerama version: %d.%d.\n", major, minor);
if (!XineramaIsActive(dpy))
return false;
return XineramaQueryScreens(dpy, num_screens);
}
bool x11_get_xinerama_coord(Display *dpy, int screen,
int *x, int *y, unsigned *w, unsigned *h)
{
bool ret = false;
int num_screens = 0;
XineramaScreenInfo *info = x11_query_screens(dpy, &num_screens);
RARCH_LOG("[X11]: Xinerama screens: %d.\n", num_screens);
for (int i = 0; i < num_screens; i++)
{
if (info[i].screen_number == screen)
{
*x = info[i].x_org;
*y = info[i].y_org;
*w = info[i].width;
*h = info[i].height;
ret = true;
break;
}
}
XFree(info);
return ret;
}
unsigned x11_get_xinerama_monitor(Display *dpy, int x, int y,
int w, int h)
{
unsigned monitor = 0;
int largest_area = 0;
int num_screens = 0;
XineramaScreenInfo *info = x11_query_screens(dpy, &num_screens);
RARCH_LOG("[X11]: Xinerama screens: %d.\n", num_screens);
for (int i = 0; i < num_screens; i++)
{
int max_lx = max(x, info[i].x_org);
int min_rx = min(x + w, info[i].x_org + info[i].width);
int max_ty = max(y, info[i].y_org);
int min_by = min(y + h, info[i].y_org + info[i].height);
int len_x = min_rx - max_lx;
int len_y = min_by - max_ty;
if (len_x < 0 || len_y < 0) // The whole window is outside the screen.
continue;
int area = len_x * len_y;
if (area > largest_area)
{
monitor = i;
largest_area = area;
}
}
XFree(info);
return monitor;
}
#endif

View File

@ -17,9 +17,18 @@
#ifndef X11_COMMON_H__
#define X11_COMMON_H__
#ifdef HAVE_CONFIG_H
#include "../../config.h"
#endif
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/xf86vmode.h>
#ifdef HAVE_XINERAMA
#include <X11/extensions/Xinerama.h>
#endif
#include "../../boolean.h"
void x11_hide_mouse(Display *dpy, Window win);
@ -27,6 +36,15 @@ void x11_windowed_fullscreen(Display *dpy, Window win);
void x11_suspend_screensaver(Window win);
bool x11_enter_fullscreen(Display *dpy, unsigned width, unsigned height, XF86VidModeModeInfo *desktop_mode);
void x11_exit_fullscreen(Display *dpy, XF86VidModeModeInfo *desktop_mode);
void x11_move_window(Display *dpy, Window win, int x, int y, unsigned width, unsigned height);
#ifdef HAVE_XINERAMA
bool x11_get_xinerama_coord(Display *dpy, int screen,
int *x, int *y, unsigned *w, unsigned *h);
unsigned x11_get_xinerama_monitor(Display *dpy,
int x, int y, int w, int h);
#endif
#endif

View File

@ -35,6 +35,7 @@ static Window g_win;
static Colormap g_cmap;
static Atom g_quit_atom;
static bool g_has_focus;
static unsigned g_screen;
static EGLContext g_egl_ctx;
static EGLSurface g_egl_surf;
@ -287,8 +288,31 @@ static bool gfx_ctx_set_video_mode(
}
}
int x_off = 0;
int y_off = 0;
#ifdef HAVE_XINERAMA
if (fullscreen || g_screen != 0)
{
unsigned new_width = width;
unsigned new_height = height;
if (x11_get_xinerama_coord(g_dpy, g_screen, &x_off, &y_off, &new_width, &new_height))
RARCH_LOG("[X/EGL]: Using Xinerama on screen #%u.\n", g_screen);
else
RARCH_LOG("[X/EGL]: Xinerama is not active on screen.\n");
if (fullscreen)
{
width = new_width;
height = new_height;
}
}
#endif
RARCH_LOG("[X/EGL]: X = %d, Y = %d, W = %u, H = %u.\n",
x_off, y_off, width, height);
g_win = XCreateWindow(g_dpy, RootWindow(g_dpy, vi->screen),
0, 0, width ? width : 200, height ? height : 200, 0,
x_off, y_off, width, height, 0,
vi->depth, InputOutput, vi->visual,
CWBorderPixel | CWColormap | CWEventMask | (true_full ? CWOverrideRedirect : 0), &swa);
XSetWindowBackground(g_dpy, g_win, 0);
@ -313,21 +337,33 @@ static bool gfx_ctx_set_video_mode(
goto error;
gfx_ctx_update_window_title(true);
x11_hide_mouse(g_dpy, g_win);
if (fullscreen)
x11_hide_mouse(g_dpy, g_win);
if (true_full)
{
RARCH_LOG("[GLX]: Using true fullscreen.\n");
XMapRaised(g_dpy, g_win);
XGrabKeyboard(g_dpy, g_win, True, GrabModeAsync, GrabModeAsync, CurrentTime);
}
else if (fullscreen) // We attempted true fullscreen, but failed. Attempt using windowed fullscreen.
{
XMapRaised(g_dpy, g_win);
RARCH_WARN("[X/EGL]: Using windowed fullscreen.\n");
RARCH_LOG("[X/EGL]: Using windowed fullscreen.\n");
// We have to move the window to the screen we want to go fullscreen on first.
// x_off and y_off usually get ignored in XCreateWindow().
x11_move_window(g_dpy, g_win, x_off, y_off, width, height);
x11_windowed_fullscreen(g_dpy, g_win);
}
else
{
XMapWindow(g_dpy, g_win);
// If we want to map the window on a different screen, we'll have to do it by force.
// Otherwise, we should try to let the window manager sort it out.
// x_off and y_off usually get ignored in XCreateWindow().
if (g_screen)
x11_move_window(g_dpy, g_win, x_off, y_off, width, height);
}
XEvent event;
XIfEvent(g_dpy, &event, egl_wait_notify, NULL);
@ -345,7 +381,9 @@ static bool gfx_ctx_set_video_mode(
driver.display_type = RARCH_DISPLAY_X11;
driver.video_display = (uintptr_t)g_dpy;
driver.video_window = (uintptr_t)g_win;
// Always assume that we have focus in true fullscreen.
driver.video_window = true_full ? (uintptr_t)None : (uintptr_t)g_win;
return true;
@ -379,6 +417,22 @@ static void gfx_ctx_destroy(void)
if (g_win)
{
// Save last used monitor for later.
#ifdef HAVE_XINERAMA
XWindowAttributes target;
Window child;
int x = 0, y = 0;
XGetWindowAttributes(g_dpy, g_win, &target);
XTranslateCoordinates(g_dpy, g_win, RootWindow(g_dpy, DefaultScreen(g_dpy)),
target.x, target.y, &x, &y, &child);
g_screen = x11_get_xinerama_monitor(g_dpy, x, y,
target.width, target.height);
RARCH_LOG("[GLX]: Saved monitor #%u.\n", g_screen);
#endif
XUnmapWindow(g_dpy, g_win);
XDestroyWindow(g_dpy, g_win);
g_win = None;
@ -421,7 +475,7 @@ static bool gfx_ctx_has_focus(void)
int rev;
XGetInputFocus(g_dpy, &win, &rev);
return win == g_win && g_has_focus;
return (win == g_win && g_has_focus) || (g_win == None);
}
static gfx_ctx_proc_t gfx_ctx_get_proc_address(const char *symbol)

View File

@ -277,8 +277,12 @@ static void x_input_poll_mouse(x11_input_t *x11)
x11->mouse_last_x = x11->mouse_x;
x11->mouse_last_y = x11->mouse_y;
Window win = x11->win;
if (win == None)
win = RootWindow(x11->display, DefaultScreen(x11->display));
XQueryPointer(x11->display,
x11->win,
win,
&root_win, &child_win,
&root_x, &root_y,
&win_x, &win_y,
@ -299,7 +303,7 @@ static void x_input_poll(void *data)
int rev = 0;
XGetInputFocus(x11->display, &win, &rev);
if (win == x11->win)
if (win == x11->win || x11->win == None)
XQueryKeymap(x11->display, x11->state);
else
memset(x11->state, 0, sizeof(x11->state));

View File

@ -165,10 +165,11 @@ fi
check_pkgconf FREETYPE freetype2
check_pkgconf X11 x11
[ "$HAVE_X11" = "no" ] && HAVE_XEXT=no && HAVE_XF86VM=no
[ "$HAVE_X11" = "no" ] && HAVE_XEXT=no && HAVE_XF86VM=no && HAVE_XINERAMA=no
check_pkgconf XEXT xext
check_pkgconf XF86VM xxf86vm
check_pkgconf XINERAMA xinerama
if [ "$HAVE_X11" = 'yes' ] && [ "$HAVE_XEXT" = 'yes' ] && [ "$HAVE_XF86VM" = 'yes' ]; then
check_pkgconf XVIDEO xv
else
@ -184,6 +185,6 @@ check_pkgconf PYTHON python3
add_define_make OS "$OS"
# Creates config.mk and config.h.
VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL GLES VG EGL KMS GBM DRM DYLIB GETOPT_LONG THREADS CG XML SDL_IMAGE LIBPNG DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL CONFIGFILE FREETYPE XVIDEO X11 XEXT XF86VM NETPLAY NETWORK_CMD STDIN_CMD COMMAND SOCKET_LEGACY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM FFMPEG_AVCODEC_ENCODE_AUDIO2 FFMPEG_AVCODEC_ENCODE_VIDEO2 SINC FIXED_POINT BSV_MOVIE VIDEOCORE"
VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL GLES VG EGL KMS GBM DRM DYLIB GETOPT_LONG THREADS CG XML SDL_IMAGE LIBPNG DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL CONFIGFILE FREETYPE XVIDEO X11 XEXT XF86VM XINERAMA NETPLAY NETWORK_CMD STDIN_CMD COMMAND SOCKET_LEGACY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM FFMPEG_AVCODEC_ENCODE_AUDIO2 FFMPEG_AVCODEC_ENCODE_VIDEO2 SINC FIXED_POINT BSV_MOVIE VIDEOCORE"
create_config_make config.mk $VARS
create_config_header config.h $VARS

View File

@ -9,6 +9,7 @@ HAVE_CONFIGFILE=yes # Disable support for config file
HAVE_OPENGL=yes # Disable OpenGL support
HAVE_GLES=no # Use GLESv2 instead of desktop GL
HAVE_X11=auto # Disable everything X11.
HAVE_XINERAMA=auto # Disable Xinerama support.
HAVE_KMS=auto # Enable KMS context support
HAVE_EGL=auto # Enable EGL context support
HAVE_VG=auto # Enable OpenVG support