From 7da3272486c01aec54b69b6d8de527d177665c52 Mon Sep 17 00:00:00 2001 From: Ben Anderson Date: Fri, 2 Dec 2005 21:17:36 +0000 Subject: [PATCH] Add proper fullscreen and resolution changing support using XRandR. This makes XRandR mandatory, but I don't see any reason why we can't do that, and providing alternatives would be difficult at best for little payoff. --- stepmania/autoconf/m4/opengl.m4 | 19 ++++ .../LowLevelWindow/LowLevelWindow_X11.cpp | 96 +++++++++++++++++-- .../arch/LowLevelWindow/LowLevelWindow_X11.h | 3 + stepmania/src/archutils/Unix/X11Helper.cpp | 21 +++- stepmania/src/archutils/Unix/X11Helper.h | 3 +- 5 files changed, 127 insertions(+), 15 deletions(-) diff --git a/stepmania/autoconf/m4/opengl.m4 b/stepmania/autoconf/m4/opengl.m4 index 78f4f247ea..53da89aaa1 100644 --- a/stepmania/autoconf/m4/opengl.m4 +++ b/stepmania/autoconf/m4/opengl.m4 @@ -31,6 +31,25 @@ AC_DEFUN([SM_X_WITH_OPENGL], [$XLIBS]) AC_DEFINE(HAVE_X11, 1, [X11 libraries present]) fi + + # Check for Xrandr + # Can someone fix this for me? This is producing bizarre warnings from + # configure... I have no clue what I'm doing -Ben + AC_CHECK_LIB(Xrandr, XRRSizes, have_xrandr=yes, have_xrandr=no) + AC_CHECK_HEADER(X11/extensions/Xrandr.h, have_xrandr_header=yes, have_xrandr_header=no) + + if test "$have_xrandr_header" = "no"; then + have_xrandr=no + fi + + if test "$have_xrandr" = "no"; then + echo "*** Direct X11 support needs Xrandr libraries and headers." + echo "*** Couldn't find needed headers. Continuing without X11 backend." + $no_x = yes + else + LIBS="$LIBS -lXrandr" + fi + AM_CONDITIONAL(HAVE_X11, test "$no_x" != "yes") AC_SUBST(XCFLAGS) diff --git a/stepmania/src/arch/LowLevelWindow/LowLevelWindow_X11.cpp b/stepmania/src/arch/LowLevelWindow/LowLevelWindow_X11.cpp index 31f7318989..03fcf5d87b 100644 --- a/stepmania/src/arch/LowLevelWindow/LowLevelWindow_X11.cpp +++ b/stepmania/src/arch/LowLevelWindow/LowLevelWindow_X11.cpp @@ -5,12 +5,15 @@ #include "archutils/Unix/X11Helper.h" #include "PrefsManager.h" // XXX #include "RageDisplay.h" // VideoModeParams +#include "DisplayResolutions.h" #include #include // ceil() #define GLX_GLXEXT_PROTOTYPES #include // All sorts of stuff... +#include #include +#include #if defined(HAVE_LIBXTST) #include @@ -36,10 +39,21 @@ LowLevelWindow_X11::LowLevelWindow_X11() LOG->Info( "X server vendor: %s [%i.%i.%i.%i]", XServerVendor( X11Helper::Dpy ), iMajor, iMinor, iRevision, iPatch ); LOG->Info( "Server GLX vendor: %s [%s]", glXQueryServerString( X11Helper::Dpy, iScreen, GLX_VENDOR ), glXQueryServerString( X11Helper::Dpy, iScreen, GLX_VERSION ) ); LOG->Info( "Client GLX vendor: %s [%s]", glXGetClientString( X11Helper::Dpy, GLX_VENDOR ), glXGetClientString( X11Helper::Dpy, GLX_VERSION ) ); + + m_bWasWindowed = true; } LowLevelWindow_X11::~LowLevelWindow_X11() { + // Reset the display + if( !m_bWasWindowed ) + { + XRRScreenConfiguration *screenConfig = XRRGetScreenInfo( X11Helper::Dpy, RootWindow( X11Helper::Dpy, DefaultScreen( X11Helper::Dpy ) ) ); + XRRSetScreenConfig( X11Helper::Dpy, screenConfig, RootWindow( X11Helper::Dpy, DefaultScreen( X11Helper::Dpy ) ), 0, 1, CurrentTime ); + XRRFreeScreenConfigInfo( screenConfig ); + + XUngrabKeyboard( X11Helper::Dpy, CurrentTime ); + } X11Helper::Stop(); // Xlib cleans up the window for us } @@ -74,7 +88,7 @@ CString LowLevelWindow_X11::TryVideoMode( const VideoModeParams &p, bool &bNewDe hints.min_width = hints.max_width = hints.base_width = p.width; hints.min_height = hints.max_height = hints.base_height = p.height; - if( !m_bWindowIsOpen || p.bpp != CurrentParams.bpp ) + if( !m_bWindowIsOpen || p.bpp != CurrentParams.bpp || ( m_bWasWindowed != p.windowed ) ) { // Different depth, or we didn't make a window before. New context. bNewDeviceOut = true; @@ -111,7 +125,9 @@ CString LowLevelWindow_X11::TryVideoMode( const VideoModeParams &p, bool &bNewDe /* Enable StructureNotifyMask, so we receive a MapNotify for the following XMapWindow. */ X11Helper::OpenMask( StructureNotifyMask ); - if( !X11Helper::MakeWindow(xvi->screen, xvi->depth, xvi->visual, p.width, p.height) ) + // I get strange behavior if I add override redirect after creating the window. + // So, let's recreate the window when changing that state. + if( !X11Helper::MakeWindow(xvi->screen, xvi->depth, xvi->visual, p.width, p.height, !p.windowed) ) { return "Failed to create the window."; } @@ -157,6 +173,58 @@ CString LowLevelWindow_X11::TryVideoMode( const VideoModeParams &p, bool &bNewDe bNewDeviceOut = false; } + + XRRScreenConfiguration *screenConfig = XRRGetScreenInfo( X11Helper::Dpy, RootWindow( X11Helper::Dpy, DefaultScreen( X11Helper::Dpy ) ) ); + + if( !p.windowed ) + { + // Find a matching mode. + int sizesXct; + XRRScreenSize *sizesX = XRRSizes( X11Helper::Dpy, DefaultScreen( X11Helper::Dpy ), &sizesXct ); + ASSERT_M( sizesXct != 0, "Couldn't get resolution list from X server" ); + + int sizeMatch = -1; + int i = 0; + while(i < sizesXct) + { + if(sizesX[i].width == p.width && sizesX[i].height == p.height) + { + sizeMatch = i; + break; + } + i++; + } + // Set this mode. + // XXX: This doesn't handle if the config has changed since we queried it (see man Xrandr) + XRRSetScreenConfig( X11Helper::Dpy, screenConfig, RootWindow( X11Helper::Dpy, DefaultScreen( X11Helper::Dpy ) ), sizeMatch, 1, CurrentTime ); + + // Move the window to the corner that the screen focuses in on. + XMoveWindow( X11Helper::Dpy, X11Helper::Win, 0, 0 ); + + + XRaiseWindow( X11Helper::Dpy, X11Helper::Win ); + + if( m_bWasWindowed ) + { + // We want to prevent the WM from catching anything that comes from the keyboard. + XGrabKeyboard( X11Helper::Dpy, X11Helper::Win, True, + GrabModeAsync, GrabModeAsync, CurrentTime ); + m_bWasWindowed = false; + } + } + else + { + if( !m_bWasWindowed ) + { + XRRSetScreenConfig( X11Helper::Dpy, screenConfig, RootWindow( X11Helper::Dpy, DefaultScreen( X11Helper::Dpy ) ), 0, 1, CurrentTime ); + // In windowed mode, we actually want the WM to function normally. + // Release any previous grab. + XUngrabKeyboard( X11Helper::Dpy, CurrentTime ); + m_bWasWindowed = true; + } + } + + XRRFreeScreenConfigInfo( screenConfig ); // Do this before resizing the window so that pane-style WMs (Ion, // ratpoison) don't resize us back inappropriately. @@ -166,13 +234,6 @@ CString LowLevelWindow_X11::TryVideoMode( const VideoModeParams &p, bool &bNewDe // catching WM normal hints changes in mapped windows. XResizeWindow( X11Helper::Dpy, X11Helper::Win, p.width, p.height ); - // Center the window in the display. - int w = DisplayWidth( X11Helper::Dpy, DefaultScreen(X11Helper::Dpy) ); - int h = DisplayHeight( X11Helper::Dpy, DefaultScreen(X11Helper::Dpy) ); - int x = (w - p.width)/2; - int y = (h - p.height)/2; - XMoveWindow( X11Helper::Dpy, X11Helper::Win, x, y ); - CurrentParams = p; return ""; // Success @@ -224,6 +285,23 @@ void LowLevelWindow_X11::SwapBuffers() } } +void LowLevelWindow_X11::GetDisplayResolutions( DisplayResolutions &out ) const +{ + // This _NEEDS_ Xrandr to be present, but feck, who doesn't have it? + + int sizesXct; + XRRScreenSize *sizesX = XRRSizes( X11Helper::Dpy, DefaultScreen( X11Helper::Dpy ), &sizesXct ); + ASSERT_M( sizesXct != 0, "Couldn't get resolution list from X server" ); + + int i = 0; + while(i < sizesXct) + { + DisplayResolution res = { sizesX[i].width, sizesX[i].height }; + out.s.insert( res ); + i++; + } +} + /* * (c) 2005 Ben Anderson * All rights reserved. diff --git a/stepmania/src/arch/LowLevelWindow/LowLevelWindow_X11.h b/stepmania/src/arch/LowLevelWindow/LowLevelWindow_X11.h index ede083ff95..936b8bce77 100644 --- a/stepmania/src/arch/LowLevelWindow/LowLevelWindow_X11.h +++ b/stepmania/src/arch/LowLevelWindow/LowLevelWindow_X11.h @@ -18,9 +18,12 @@ public: void SwapBuffers(); VideoModeParams GetActualVideoModeParams() const { return CurrentParams; } + + void GetDisplayResolutions( DisplayResolutions &out ) const; private: bool m_bWindowIsOpen; + bool m_bWasWindowed; VideoModeParams CurrentParams; }; diff --git a/stepmania/src/archutils/Unix/X11Helper.cpp b/stepmania/src/archutils/Unix/X11Helper.cpp index 61dd575fc1..653aea91af 100644 --- a/stepmania/src/archutils/Unix/X11Helper.cpp +++ b/stepmania/src/archutils/Unix/X11Helper.cpp @@ -1,7 +1,7 @@ #include "global.h" #include "X11Helper.h" -#include // Display, Window +#include #include "RageLog.h" #include "RageDisplay.h" @@ -86,7 +86,7 @@ static bool pApplyMasks() return true; } -bool X11Helper::MakeWindow( int screenNum, int depth, Visual *visual, int width, int height ) +bool X11Helper::MakeWindow( int screenNum, int depth, Visual *visual, int width, int height, bool overrideRedirect ) { vector::iterator i; @@ -116,9 +116,20 @@ bool X11Helper::MakeWindow( int screenNum, int depth, Visual *visual, int width, winAttribs.colormap = XCreateColormap( Dpy, RootWindow(Dpy, screenNum), visual, AllocNone ); - Win = XCreateWindow( Dpy, RootWindow(Dpy, screenNum), 0, 0, width, - height, 0, depth, InputOutput, visual, - CWBorderPixel | CWColormap | CWEventMask, &winAttribs ); + if( overrideRedirect ) + { + winAttribs.override_redirect = True; + Win = XCreateWindow( Dpy, RootWindow(Dpy, screenNum), 0, 0, width, + height, 0, depth, InputOutput, visual, + CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect, &winAttribs ); + } + else + { + Win = XCreateWindow( Dpy, RootWindow(Dpy, screenNum), 0, 0, width, + height, 0, depth, InputOutput, visual, + CWBorderPixel | CWColormap | CWEventMask, &winAttribs ); + } + g_bHaveWin = true; diff --git a/stepmania/src/archutils/Unix/X11Helper.h b/stepmania/src/archutils/Unix/X11Helper.h index 0dd92762bf..d2ee5be971 100644 --- a/stepmania/src/archutils/Unix/X11Helper.h +++ b/stepmania/src/archutils/Unix/X11Helper.h @@ -26,8 +26,9 @@ namespace X11Helper // (Re)create the window on the screen of this number with this depth, // this visual type, this width (optional -- you can resize the window // in your callback later), and this height (optional). + // Also, whether to enable override redirect on the window. bool MakeWindow(int screenNum, int depth, Visual *visual, - int width=32, int height=32); + int width=32, int height=32, bool overrideRedirect=false); // Unmask one X event type mask thingy (XSelectInput() arg 3) on the // current window. Masked/unmasked events will carry between windows.