From a51ee1665dec35a250e38a0fdad197530950bce9 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 27 Sep 2012 03:36:13 -0400 Subject: [PATCH] X11: Attempt to go fullscreen the way SDL 1.2 did it. Ideally this code is never used, but as a legacy fallback, it could be useful. --- src/video/x11/SDL_x11sym.h | 5 + src/video/x11/SDL_x11window.c | 237 ++++++++++++++++++++++++++++++---- src/video/x11/SDL_x11window.h | 1 + 3 files changed, 218 insertions(+), 25 deletions(-) diff --git a/src/video/x11/SDL_x11sym.h b/src/video/x11/SDL_x11sym.h index b71d30b0b..493a14e63 100755 --- a/src/video/x11/SDL_x11sym.h +++ b/src/video/x11/SDL_x11sym.h @@ -30,6 +30,7 @@ SDL_X11_SYM(int,XAutoRepeatOff,(Display* a),(a),return) SDL_X11_SYM(int,XChangePointerControl,(Display* a,Bool b,Bool c,int d,int e,int f),(a,b,c,d,e,f),return) SDL_X11_SYM(int,XChangeProperty,(Display* a,Window b,Atom c,Atom d,int e,int f,_Xconst unsigned char* g,int h),(a,b,c,d,e,f,g,h),return) SDL_X11_SYM(Bool,XCheckIfEvent,(Display* a,XEvent *b,Bool (*c)(Display*,XEvent*,XPointer),XPointer d),(a,b,c,d),return) +SDL_X11_SYM(int,XClearWindow,(Display* a,Window b),(a,b),return) SDL_X11_SYM(int,XCloseDisplay,(Display* a),(a),return) SDL_X11_SYM(int,XConvertSelection,(Display* a,Atom b,Atom c,Atom d,Window e,Time f),(a,b,c,d,e,f),return) SDL_X11_SYM(Pixmap,XCreateBitmapFromData,(Display *dpy,Drawable d,_Xconst char *data,unsigned int width,unsigned int height),(dpy,d,data,width,height),return) @@ -69,6 +70,7 @@ SDL_X11_SYM(int,XGrabServer,(Display* a),(a),return) SDL_X11_SYM(Status,XIconifyWindow,(Display* a,Window b,int c),(a,b,c),return) SDL_X11_SYM(KeyCode,XKeysymToKeycode,(Display* a,KeySym b),(a,b),return) SDL_X11_SYM(char*,XKeysymToString,(KeySym a),(a),return) +SDL_X11_SYM(int,XInstallColormap,(Display* a,Colormap b),(a,b),return) SDL_X11_SYM(Atom,XInternAtom,(Display* a,_Xconst char* b,Bool c),(a,b,c),return) SDL_X11_SYM(XPixmapFormatValues*,XListPixmapFormats,(Display* a,int* b),(a,b),return) SDL_X11_SYM(KeySym,XLookupKeysym,(XKeyEvent* a,int b),(a,b),return) @@ -85,6 +87,7 @@ SDL_X11_SYM(int,XPutImage,(Display* a,Drawable b,GC c,XImage* d,int e,int f,int SDL_X11_SYM(int,XQueryKeymap,(Display* a,char *b),(a,b),return) SDL_X11_SYM(Bool,XQueryPointer,(Display* a,Window b,Window* c,Window* d,int* e,int* f,int* g,int* h,unsigned int* i),(a,b,c,d,e,f,g,h,i),return) SDL_X11_SYM(int,XRaiseWindow,(Display* a,Window b),(a,b),return) +SDL_X11_SYM(int,XReparentWindow,(Display* a,Window b,Window c,int d,int e),(a,b,c,d,e),return) SDL_X11_SYM(int,XResetScreenSaver,(Display* a),(a),return) SDL_X11_SYM(int,XResizeWindow,(Display* a,Window b,unsigned int c,unsigned int d),(a,b,c,d),return) SDL_X11_SYM(int,XSelectInput,(Display* a,Window b,long c),(a,b,c),return) @@ -95,6 +98,7 @@ SDL_X11_SYM(int,XSetInputFocus,(Display *a,Window b,int c,Time d),(a,b,c,d),retu SDL_X11_SYM(int,XSetSelectionOwner,(Display* a,Atom b,Window c,Time d),(a,b,c,d),return) SDL_X11_SYM(int,XSetTransientForHint,(Display* a,Window b,Window c),(a,b,c),return) SDL_X11_SYM(void,XSetTextProperty,(Display* a,Window b,XTextProperty* c,Atom d),(a,b,c,d),) +SDL_X11_SYM(int,XSetWindowBackground,(Display* a,Window b,unsigned long c),(a,b,c),return) SDL_X11_SYM(void,XSetWMProperties,(Display* a,Window b,XTextProperty* c,XTextProperty* d,char** e,int f,XSizeHints* g,XWMHints* h,XClassHint* i),(a,b,c,d,e,f,g,h,i),) SDL_X11_SYM(void,XSetWMNormalHints,(Display* a,Window b,XSizeHints* c),(a,b,c),) SDL_X11_SYM(Status,XSetWMProtocols,(Display* a,Window b,Atom* c,int d),(a,b,c,d),return) @@ -256,6 +260,7 @@ SDL_X11_SYM(Bool,XF86VidModeGetViewPort,(Display *a,int b,int *c,int *d),(a,b,c, SDL_X11_SYM(Bool,XF86VidModeQueryExtension,(Display *a,int *b,int *c),(a,b,c),return) SDL_X11_SYM(Bool,XF86VidModeQueryVersion,(Display *a,int *b,int *c),(a,b,c),return) SDL_X11_SYM(Bool,XF86VidModeSwitchToMode,(Display *a,int b,XF86VidModeModeInfo *c),(a,b,c),return) +SDL_X11_SYM(Bool,XF86VidModeLockModeSwitch,(Display *a,int b,int c),(a,b,c),return) #endif /* *INDENT-ON* */ diff --git a/src/video/x11/SDL_x11window.c b/src/video/x11/SDL_x11window.c index 4ee61e1ef..9bb1efd1b 100755 --- a/src/video/x11/SDL_x11window.c +++ b/src/video/x11/SDL_x11window.c @@ -38,22 +38,38 @@ #include "SDL_timer.h" #include "SDL_syswm.h" +#include "SDL_assert.h" #define _NET_WM_STATE_REMOVE 0l #define _NET_WM_STATE_ADD 1l #define _NET_WM_STATE_TOGGLE 2l -static SDL_bool -X11_IsWindowOldFullscreen(_THIS, SDL_Window * window) +static Bool isMapNotify(Display *dpy, XEvent *ev, XPointer win) { - SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; + return ev->type == MapNotify && ev->xmap.window == *((Window*)win); +} +static Bool isUnmapNotify(Display *dpy, XEvent *ev, XPointer win) +{ + return ev->type == UnmapNotify && ev->xunmap.window == *((Window*)win); +} +static Bool isConfigureNotify(Display *dpy, XEvent *ev, XPointer win) +{ + return ev->type == ConfigureNotify && ev->xunmap.window == *((Window*)win); +} +static Bool isFocusOut(Display *dpy, XEvent *ev, XPointer win) +{ + return ev->type == FocusOut && ev->xunmap.window == *((Window*)win); +} +static Bool isFocusIn(Display *dpy, XEvent *ev, XPointer win) +{ + return ev->type == FocusIn && ev->xunmap.window == *((Window*)win); +} - /* ICCCM2.0-compliant window managers can handle fullscreen windows */ - if ((window->flags & SDL_WINDOW_FULLSCREEN) && !videodata->net_wm) { - return SDL_TRUE; - } else { - return SDL_FALSE; - } +static SDL_bool +X11_IsWindowLegacyFullscreen(_THIS, SDL_Window * window) +{ + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + return (data->fswindow != 0); } static SDL_bool @@ -724,19 +740,6 @@ X11_SetWindowSize(_THIS, SDL_Window * window) XFlush(display); } -static Bool isMapNotify(Display *dpy, XEvent *ev, XPointer win) -{ - return ev->type == MapNotify && ev->xmap.window == *((Window*)win); -} -static Bool isUnmapNotify(Display *dpy, XEvent *ev, XPointer win) -{ - return ev->type == UnmapNotify && ev->xunmap.window == *((Window*)win); -} -static Bool isConfigureNotify(Display *dpy, XEvent *ev, XPointer win) -{ - return ev->type == ConfigureNotify && ev->xunmap.window == *((Window*)win); -} - void X11_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered) { @@ -907,8 +910,9 @@ isActionAllowed(SDL_WindowData *data, Atom action) return ret; } -void -X11_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * _display, SDL_bool fullscreen) +/* This asks the Window Manager to handle fullscreen for us. Most don't do it right, though. */ +static void +X11_SetWindowFullscreenViaWM(_THIS, SDL_Window * window, SDL_VideoDisplay * _display, SDL_bool fullscreen) { SDL_WindowData *data = (SDL_WindowData *) window->driverdata; SDL_DisplayData *displaydata = (SDL_DisplayData *) _display->driverdata; @@ -977,6 +981,189 @@ X11_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * _display, XFlush(display); } +static __inline__ int +maxint(const int a, const int b) +{ + return (a > b ? a : b); +} + + +/* This handles fullscreen itself, outside the Window Manager. */ +static void +X11_BeginWindowFullscreenLegacy(_THIS, SDL_Window * window, SDL_VideoDisplay * _display) +{ + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + SDL_DisplayData *displaydata = (SDL_DisplayData *) _display->driverdata; + Visual *visual = data->visual; + Display *display = data->videodata->display; + const int screen = displaydata->screen; + Window root = RootWindow(display, screen); + const int def_vis = (visual == DefaultVisual(display, screen)); + const int w = maxint(window->w, _display->current_mode.w); + const int h = maxint(window->h, _display->current_mode.h); + unsigned long xattrmask = 0; + XSetWindowAttributes xattr; + XEvent ev; + int x = 0; + int y = 0; + + if ( data->fswindow ) { + return; /* already fullscreen, I hope. */ + } + + /* Ungrab the input so that we can move the mouse around */ + XUngrabPointer(display, CurrentTime); + + #if SDL_VIDEO_DRIVER_X11_XINERAMA + /* !!! FIXME: there was some Xinerama code in 1.2 here to set x,y to the origin of a specific screen. */ + #endif + + SDL_zero(xattr); + xattr.override_redirect = True; + xattrmask |= CWOverrideRedirect; + xattr.background_pixel = def_vis ? BlackPixel(display, screen) : 0; + xattrmask |= CWBackPixel; + xattr.border_pixel = 0; + xattrmask |= CWBorderPixel; + xattr.colormap = data->colormap; + xattrmask |= CWColormap; + + data->fswindow = XCreateWindow(display, root, x, y, w, h, 0, + displaydata->depth, InputOutput, + visual, xattrmask, &xattr); + + XSelectInput(display, data->fswindow, StructureNotifyMask); + + XSetWindowBackground(display, data->fswindow, 0); + XClearWindow(display, data->fswindow); + + XMapRaised(display, data->fswindow); + + /* Wait to be mapped, filter Unmap event out if it arrives. */ + XIfEvent(display, &ev, &isMapNotify, (XPointer)&data->fswindow); + XCheckIfEvent(display, &ev, &isUnmapNotify, (XPointer)&data->fswindow); + +#if SDL_VIDEO_DRIVER_X11_XVIDMODE + if ( displaydata->use_vidmode ) { + XF86VidModeLockModeSwitch(display, screen, True); + } +#endif + + XInstallColormap(display, data->colormap); + + SetWindowBordered(display, displaydata->screen, data->xwindow, SDL_FALSE); + XFlush(display); + //XIfEvent(display, &ev, &isConfigureNotify, (XPointer)&data->xwindow); + + /* Center actual window within our cover-the-screen window. */ + x += (w - window->w) / 2; + y += (h - window->h) / 2; + XReparentWindow(display, data->xwindow, data->fswindow, x, y); + XRaiseWindow(display, data->xwindow); + + /* Make sure the fswindow is in view by warping mouse to the corner */ + XWarpPointer(display, None, root, 0, 0, 0, 0, 0, 0); + XFlush(display); + + /* Center mouse in the window. */ + x += (window->w / 2); + y += (window->h / 2); + XWarpPointer(display, None, root, 0, 0, 0, 0, x, y); + + /* Wait to be mapped, filter Unmap event out if it arrives. */ + XIfEvent(display, &ev, &isMapNotify, (XPointer)&data->xwindow); + + /* Wait to be visible, or XSetInputFocus() triggers an X error. */ + while (SDL_TRUE) { + XWindowAttributes attr; + XSync(display, False); + XGetWindowAttributes(display, data->xwindow, &attr); + if (attr.map_state == IsViewable) + break; + } + + XSetInputFocus(display, data->xwindow, RevertToParent, CurrentTime); + window->flags |= SDL_WINDOW_INPUT_FOCUS; + SDL_SetKeyboardFocus(data->window); + + X11_SetWindowGrab(_this, window); + + XSync(display, False); +} + +static void +X11_EndWindowFullscreenLegacy(_THIS, SDL_Window * window, SDL_VideoDisplay * _display) +{ + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + SDL_DisplayData *displaydata = (SDL_DisplayData *) _display->driverdata; + Display *display = data->videodata->display; + const int screen = displaydata->screen; + Window root = RootWindow(display, screen); + XEvent ev; + + if (!data->fswindow) + return; /* already not fullscreen, I hope. */ + + XReparentWindow(display, data->xwindow, root, window->x, window->y); + +#if SDL_VIDEO_DRIVER_X11_VIDMODE + if ( displaydata->use_vidmode ) { + XF86VidModeLockModeSwitch(display, screen, False); + } +#endif + + XUnmapWindow(display, data->fswindow); + /* Wait to be unmapped. */ + XIfEvent(display, &ev, &isUnmapNotify, (XPointer)&data->fswindow); + XDestroyWindow(display, data->fswindow); + data->fswindow = 0; + + /* catch these events so we know the window is back in business. */ + XIfEvent(display, &ev, &isUnmapNotify, (XPointer)&data->xwindow); + XIfEvent(display, &ev, &isMapNotify, (XPointer)&data->xwindow); + + XSync(display, True); /* Flush spurious mode change events */ + + X11_SetWindowGrab(_this, window); + + SetWindowBordered(display, screen, data->xwindow, + (window->flags & SDL_WINDOW_BORDERLESS) == 0); + + XFlush(display); +} + + +void +X11_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * _display, SDL_bool fullscreen) +{ + /* !!! FIXME: SDL_Hint? */ + SDL_bool legacy = SDL_FALSE; + const char *env = SDL_getenv("SDL_VIDEO_X11_LEGACY_FULLSCREEN"); + if (env) { + legacy = SDL_atoi(env); + } else { + SDL_DisplayData *displaydata = (SDL_DisplayData *) _display->driverdata; + if ( displaydata->use_vidmode ) { + legacy = SDL_TRUE; /* the new stuff only works with XRandR. */ + } else { + /* !!! FIXME: look at the window manager name, and blacklist certain ones? */ + /* http://stackoverflow.com/questions/758648/find-the-name-of-the-x-window-manager */ + legacy = SDL_FALSE; /* try the new way. */ + } + } + + if (legacy) { + if (fullscreen) { + X11_BeginWindowFullscreenLegacy(_this, window, _display); + } else { + X11_EndWindowFullscreenLegacy(_this, window, _display); + } + } else { + X11_SetWindowFullscreenViaWM(_this, window, _display, fullscreen); + } +} + + int X11_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp) { @@ -1054,7 +1241,7 @@ X11_SetWindowGrab(_THIS, SDL_Window * window) SDL_bool oldstyle_fullscreen; /* ICCCM2.0-compliant window managers can handle fullscreen windows */ - oldstyle_fullscreen = X11_IsWindowOldFullscreen(_this, window); + oldstyle_fullscreen = X11_IsWindowLegacyFullscreen(_this, window); if (((window->flags & SDL_WINDOW_INPUT_GRABBED) || oldstyle_fullscreen) && (window->flags & SDL_WINDOW_INPUT_FOCUS)) { diff --git a/src/video/x11/SDL_x11window.h b/src/video/x11/SDL_x11window.h index 36db96c5c..a92e9c3a6 100755 --- a/src/video/x11/SDL_x11window.h +++ b/src/video/x11/SDL_x11window.h @@ -27,6 +27,7 @@ typedef struct { SDL_Window *window; Window xwindow; + Window fswindow; /* used if we can't have the WM handle fullscreen. */ Visual *visual; Colormap colormap; #ifndef NO_SHARED_MEMORY