Xrandr support in the X11 target.
authorRyan C. Gordon <icculus@icculus.org>
Wed, 22 Mar 2006 11:13:58 +0000
changeset 1589 34cca785be57
parent 1588 e20dcef5647c
child 1590 96d8c3fec479
Xrandr support in the X11 target. Fixes Bugzilla #109, and as a bonus, Bugzilla #145, too!
configure.in
docs/html/sdlenvvars.html
include/SDL_config.h.in
src/video/x11/SDL_x11dyn.c
src/video/x11/SDL_x11dyn.h
src/video/x11/SDL_x11modes.c
src/video/x11/SDL_x11sym.h
src/video/x11/SDL_x11video.h
--- a/configure.in	Wed Mar 22 09:18:10 2006 +0000
+++ b/configure.in	Wed Mar 22 11:13:58 2006 +0000
@@ -789,9 +789,11 @@
                           , enable_x11_shared=yes)
 
             case "$host" in
-                *-*-darwin*)
+                *-*-darwin*) # Latest Mac OS X actually ships with Xrandr/Xrender libs...
                     x11_lib='/usr/X11R6/lib/libX11.6.dylib'
                     x11ext_lib='/usr/X11R6/lib/libXext.6.dylib'
+                    xrandr_lib='/usr/X11R6/lib/libXrandr.2.dylib'
+                    xrender_lib='/usr/X11R6/lib/libXrender.1.dylib'
                     ;;
                 *-*-osf*)
                     x11_lib='libX11.so'
@@ -810,6 +812,12 @@
                         if test "x$x11ext_lib" = "x"; then
                             x11ext_lib=[`ls $path/libXext.so.[0-9] 2>/dev/null | sort -r | sed 's/.*\/\(.*\)/\1/; q'`]
                         fi
+                        if test "x$xrender_lib" = "x"; then
+                            xrender_lib=[`ls $path/libXrender.so.[0-9] 2>/dev/null | sort -r | sed 's/.*\/\(.*\)/\1/; q'`]
+                        fi
+                        if test "x$xrandr_lib" = "x"; then
+                            xrandr_lib=[`ls $path/libXrandr.so.[0-9] 2>/dev/null | sort -r | sed 's/.*\/\(.*\)/\1/; q'`]
+                        fi
                     done
                     ;;
             esac
@@ -833,6 +841,7 @@
                 AC_DEFINE_UNQUOTED(SDL_VIDEO_DRIVER_X11_DYNAMIC, "$x11_lib")
                 AC_DEFINE_UNQUOTED(SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT, "$x11ext_lib")
             else
+                enable_x11_shared=no
                 EXTRA_LDFLAGS="$EXTRA_LDFLAGS $X_LIBS -lX11 -lXext"
             fi
             have_video=yes
@@ -884,6 +893,37 @@
                 AC_DEFINE(SDL_VIDEO_DRIVER_X11_XME)
                 SOURCES="$SOURCES $srcdir/src/video/Xext/XME/*.c"
             fi
+            AC_ARG_ENABLE(video-x11-xrandr,
+AC_HELP_STRING([--enable-video-x11-xrandr], [enable X11 Xrandr extension for fullscreen [default=yes]]),
+                            , enable_video_x11_xrandr=yes)
+            if test x$enable_video_x11_xrandr = xyes; then
+                definitely_enable_video_x11_xrandr=no
+                AC_CHECK_HEADER(X11/extensions/Xrandr.h,
+                                have_xrandr_h_hdr=yes,
+                                have_xrandr_h_hdr=no,
+                                [#include <X11/Xlib.h>
+                                ])
+                if test x$have_xrandr_h_hdr = xyes; then
+                    if test x$enable_x11_shared = xyes && test x$xrandr_lib != x ; then
+                        echo "-- dynamic libXrender -> $xrender_lib"
+                        echo "-- dynamic libXrandr -> $xrandr_lib"
+                        AC_DEFINE_UNQUOTED(SDL_VIDEO_DRIVER_X11_DYNAMIC_XRENDER, "$xrender_lib")
+                        AC_DEFINE_UNQUOTED(SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR, "$xrandr_lib")
+                        definitely_enable_video_x11_xrandr=yes
+                    else
+                        AC_CHECK_LIB(Xrender, XRenderQueryExtension, have_xrender_lib=yes)
+                        AC_CHECK_LIB(Xrandr, XRRQueryExtension, have_xrandr_lib=yes)
+                        if test x$have_xrender_lib = xyes && test x$have_xrandr_lib = xyes ; then
+                            EXTRA_LDFLAGS="$EXTRA_LDFLAGS -lXrandr -lXrender"
+                            definitely_enable_video_x11_xrandr=yes
+                        fi
+                    fi
+                fi
+            fi
+
+            if test x$definitely_enable_video_x11_xrandr = xyes; then
+                AC_DEFINE(SDL_VIDEO_DRIVER_X11_XRANDR)
+            fi
         fi
     fi
 }
--- a/docs/html/sdlenvvars.html	Wed Mar 22 09:18:10 2006 +0000
+++ b/docs/html/sdlenvvars.html	Wed Mar 22 11:13:58 2006 +0000
@@ -576,6 +576,18 @@
 ><DT
 ><TT
 CLASS="LITERAL"
+>SDL_VIDEO_X11_NO_XRANDR</TT
+></DT
+><DD
+><P
+>If set, don't attempt to use the Xrandr extension for resolution mode
+switching.  Normally Xrandr takes precedence over the XF86VidMode
+extension, so setting this environment variable will cause the 
+XF86VidMode extension to be used if it is available.</P
+></DD
+><DT
+><TT
+CLASS="LITERAL"
 >SDL_VIDEO_YUV_DIRECT</TT
 ></DT
 ><DD
@@ -1209,4 +1221,4 @@
 ></DIV
 ></BODY
 ></HTML
->
\ No newline at end of file
+>
--- a/include/SDL_config.h.in	Wed Mar 22 09:18:10 2006 +0000
+++ b/include/SDL_config.h.in	Wed Mar 22 11:13:58 2006 +0000
@@ -278,9 +278,12 @@
 #undef SDL_VIDEO_DRIVER_X11_DGAMOUSE
 #undef SDL_VIDEO_DRIVER_X11_DYNAMIC
 #undef SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT
+#undef SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR
+#undef SDL_VIDEO_DRIVER_X11_DYNAMIC_XRENDER
 #undef SDL_VIDEO_DRIVER_X11_VIDMODE
 #undef SDL_VIDEO_DRIVER_X11_XINERAMA
 #undef SDL_VIDEO_DRIVER_X11_XME
+#undef SDL_VIDEO_DRIVER_X11_XRANDR
 #undef SDL_VIDEO_DRIVER_X11_XV
 #undef SDL_VIDEO_DRIVER_XBIOS
 
--- a/src/video/x11/SDL_x11dyn.c	Wed Mar 22 09:18:10 2006 +0000
+++ b/src/video/x11/SDL_x11dyn.c	Wed Mar 22 11:13:58 2006 +0000
@@ -21,13 +21,11 @@
 */
 #include "SDL_config.h"
 
-#if 0
-#define DEBUG_DYNAMIC_X11 1
-#endif
+#define DEBUG_DYNAMIC_X11 0
 
 #include "SDL_x11dyn.h"
 
-#ifdef DEBUG_DYNAMIC_X11
+#if DEBUG_DYNAMIC_X11
 #include <stdio.h>
 #endif
 
@@ -39,6 +37,10 @@
 static void *x11_handle = NULL;
 static const char *x11ext_library = SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT;
 static void *x11ext_handle = NULL;
+static const char *xrender_library = SDL_VIDEO_DRIVER_X11_DYNAMIC_XRENDER;
+static void *xrender_handle = NULL;
+static const char *xrandr_library = SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR;
+static void *xrandr_handle = NULL;
 
 typedef struct
 {
@@ -54,6 +56,8 @@
 	{
 		{ x11_handle, "libX11" },
 		{ x11ext_handle, "libX11ext" },
+		{ xrender_handle, "libXrender" },
+		{ xrandr_handle, "libXrandr" },
 	};
 
 	for (i = 0; i < (sizeof (libs) / sizeof (libs[0])); i++)
@@ -130,6 +134,14 @@
 				SDL_UnloadObject(x11ext_handle);
 				x11ext_handle = NULL;
 			}
+			if (xrender_handle != NULL) {
+				SDL_UnloadObject(xrender_handle);
+				xrender_handle = NULL;
+			}
+			if (xrandr_handle != NULL) {
+				SDL_UnloadObject(xrandr_handle);
+				xrandr_handle = NULL;
+			}
 		}
 	}
 	#endif
@@ -146,6 +158,8 @@
 		int *thismod = NULL;
 		x11_handle = SDL_LoadObject(x11_library);
 		x11ext_handle = SDL_LoadObject(x11ext_library);
+		xrender_handle = SDL_LoadObject(xrender_library);
+		xrandr_handle = SDL_LoadObject(xrandr_library);
 		#define SDL_X11_MODULE(modname) thismod = &SDL_X11_HAVE_##modname;
 		#define SDL_X11_SYM(a,fn,x,y,z) p##fn = X11_GetSym(#fn,thismod);
 		#include "SDL_x11sym.h"
--- a/src/video/x11/SDL_x11dyn.h	Wed Mar 22 09:18:10 2006 +0000
+++ b/src/video/x11/SDL_x11dyn.h	Wed Mar 22 11:13:58 2006 +0000
@@ -38,6 +38,10 @@
 #include <X11/extensions/XShm.h>
 #endif
 
+#if SDL_VIDEO_DRIVER_X11_XRANDR
+#include <X11/extensions/Xrandr.h>
+#endif
+
 /*
  * When using the "dynamic X11" functionality, we duplicate all the Xlib
  *  symbols that would be referenced by SDL inside of SDL itself.
--- a/src/video/x11/SDL_x11modes.c	Wed Mar 22 09:18:10 2006 +0000
+++ b/src/video/x11/SDL_x11modes.c	Wed Mar 22 11:13:58 2006 +0000
@@ -86,6 +86,18 @@
 }
 #endif
 
+#if SDL_VIDEO_DRIVER_X11_XRANDR
+static int cmpmodelist(const void *va, const void *vb)
+{
+    const SDL_Rect *a = *(const SDL_Rect **)va;
+    const SDL_Rect *b = *(const SDL_Rect **)vb;
+    if ( a->w == b->w )
+        return b->h - a->h;
+    else
+        return b->w - a->w;
+}
+#endif
+
 static void get_real_resolution(_THIS, int* w, int* h);
 
 static void set_best_resolution(_THIS, int width, int height)
@@ -180,6 +192,55 @@
     }
 #endif /* SDL_VIDEO_DRIVER_X11_XME */
 
+#if SDL_VIDEO_DRIVER_X11_XRANDR
+    if ( use_xrandr ) {
+#ifdef XRANDR_DEBUG
+        fprintf(stderr, "XRANDR: set_best_resolution(): w = %d, h = %d\n",
+                width, height);
+#endif
+        if ( SDL_modelist ) {
+            int i, nsizes;
+            XRRScreenSize *sizes;
+
+            /* find the smallest resolution that is at least as big as the user requested */
+            sizes = XRRConfigSizes(screen_config, &nsizes);
+            for ( i = (nsizes-1); i >= 0; i-- ) {
+                if ( (SDL_modelist[i]->w >= width) &&
+                     (SDL_modelist[i]->h >= height) ) {
+                    break;
+                }
+            }
+
+            if ( i >= 0 && SDL_modelist[i] ) { /* found one, lets try it */
+                int w, h;
+
+                /* check current mode so we can avoid uneccessary mode changes */
+                get_real_resolution(this, &w, &h);
+
+                if ( (SDL_modelist[i]->w != w) || (SDL_modelist[i]->h != h) ) {
+                    int size_id;
+
+#ifdef XRANDR_DEBUG
+                    fprintf(stderr, "XRANDR: set_best_resolution: "
+                            "XXRSetScreenConfig: %d %d\n",
+                            SDL_modelist[i]->w, SDL_modelist[i]->h);
+#endif
+
+                    /* find the matching size entry index */
+                    for ( size_id = 0; size_id < nsizes; ++size_id ) {
+                        if ( (sizes[size_id].width == SDL_modelist[i]->w) &&
+                             (sizes[size_id].height == SDL_modelist[i]->h) )
+                            break;
+                    }
+
+                    XRRSetScreenConfig(SDL_Display, screen_config, SDL_Root,
+                                       size_id, saved_rotation, CurrentTime);
+                }
+            }
+        }
+    }
+#endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
+
 }
 
 static void get_real_resolution(_THIS, int* w, int* h)
@@ -195,7 +256,7 @@
             return;
         }
     }
-#endif
+#endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */
 
 #if SDL_VIDEO_DRIVER_X11_XME
     if ( use_xme ) {
@@ -213,7 +274,30 @@
         XFree(modelist);
         return;
     }
-#endif /* XIG_XME */
+#endif /* SDL_VIDEO_DRIVER_X11_XME */
+
+#if SDL_VIDEO_DRIVER_X11_XRANDR
+    if ( use_xrandr ) {
+        int nsizes;
+        XRRScreenSize* sizes;
+
+        sizes = XRRConfigSizes(screen_config, &nsizes);
+        if ( nsizes > 0 ) {
+            int cur_size;
+            Rotation cur_rotation;
+
+            cur_size = XRRConfigCurrentConfiguration(screen_config, &cur_rotation);
+            if ( cur_size >= 0 && cur_size < nsizes ) {
+                *w = sizes[cur_size].width;
+                *h = sizes[cur_size].height;
+            }
+#ifdef XRANDR_DEBUG
+            fprintf(stderr, "XRANDR: get_real_resolution: w = %d h = %d\n", *w, *h);
+#endif
+            return;
+        }
+    }
+#endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
 
     *w = DisplayWidth(SDL_Display, SDL_Screen);
     *h = DisplayHeight(SDL_Display, SDL_Screen);
@@ -289,15 +373,69 @@
     int ractive, nummodes;
     XiGMiscResolutionInfo *modelist;
 #endif
+#if SDL_VIDEO_DRIVER_X11_XRANDR
+    int xrandr_major, xrandr_minor;
+    int nsizes;
+    XRRScreenSize *sizes;
+#endif
     int i, n;
     int screen_w;
     int screen_h;
 
     vm_error = -1;
     use_vidmode = 0;
+    use_xrandr = 0;
     screen_w = DisplayWidth(SDL_Display, SDL_Screen);
     screen_h = DisplayHeight(SDL_Display, SDL_Screen);
 
+    /* XRandR */
+#if SDL_VIDEO_DRIVER_X11_XRANDR
+    /* require at least XRandR v1.0 (arbitrary) */
+    if ( ( SDL_X11_HAVE_XRANDR ) &&
+         ( getenv("SDL_VIDEO_X11_NO_XRANDR") == NULL ) &&
+         ( XRRQueryVersion(SDL_Display, &xrandr_major, &xrandr_minor) ) &&
+         ( xrandr_major >= 1 ) ) {
+
+#ifdef XRANDR_DEBUG
+        fprintf(stderr, "XRANDR: XRRQueryVersion: V%d.%d\n",
+                xrandr_major, xrandr_minor);
+#endif
+
+        /* save the screen configuration since we must reference it
+           each time we toggle modes.
+        */
+        screen_config = XRRGetScreenInfo(SDL_Display, SDL_Root);
+
+        /* retrieve the list of resolution */
+        sizes = XRRConfigSizes(screen_config, &nsizes);
+        if (nsizes > 0) {
+            SDL_modelist = (SDL_Rect **)malloc((nsizes+1)*sizeof(SDL_Rect *));
+            if (SDL_modelist) {
+                for ( i=0; i < nsizes; i++ ) {
+                    if ((SDL_modelist[i] =
+                         (SDL_Rect *)malloc(sizeof(SDL_Rect))) == NULL)
+                        break;
+#ifdef XRANDR_DEBUG
+                    fprintf(stderr, "XRANDR: mode = %4d, w = %4d, h = %4d\n",
+                            i, sizes[i].width, sizes[i].height);
+#endif
+
+                    SDL_modelist[i]->x = 0;
+                    SDL_modelist[i]->y = 0;
+                    SDL_modelist[i]->w = sizes[i].width;
+                    SDL_modelist[i]->h = sizes[i].height;
+
+                }
+                /* sort the mode list descending as SDL expects */
+                qsort(SDL_modelist, nsizes, sizeof *SDL_modelist, cmpmodelist);
+                SDL_modelist[i] = NULL; /* terminator */
+            }
+            use_xrandr = xrandr_major * 100 + xrandr_minor;
+            saved_size_id = XRRConfigCurrentConfiguration(screen_config, &saved_rotation);
+        }
+    }
+#endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
+
 #if SDL_VIDEO_DRIVER_X11_VIDMODE
     /* Metro-X 4.3.0 and earlier has a broken implementation of
        XF86VidModeGetAllModeLines() - it hangs the client.
@@ -345,7 +483,7 @@
             buggy_X11 = 1;
         }
     }
-    if ( ! buggy_X11 &&
+    if ( ! buggy_X11 && ! use_xrandr &&
          SDL_NAME(XF86VidModeGetAllModeLines)(SDL_Display, SDL_Screen,&nmodes,&modes) ) {
 
 #ifdef XFREE86_DEBUG
@@ -426,7 +564,7 @@
              */
             fprintf(stderr, 
 "XME: If you are using Xi Graphics CDE and a Summit server, you need to\n"
-"XME: get the libXext update from our ftp site before fullscreen switching\n"
+"XME: get the libXext update from Xi's ftp site before fullscreen switching\n"
 "XME: will work.  Fullscreen switching is only supported on Summit Servers\n");
           }
     } else {
@@ -635,6 +773,14 @@
         SDL_free(SDL_modelist);
         SDL_modelist = NULL;
     }
+
+#if SDL_VIDEO_DRIVER_X11_XRANDR
+    /* Free the Xrandr screen configuration */
+    if ( screen_config ) {
+        XRRFreeScreenConfigInfo(screen_config);
+        screen_config = NULL;
+    }
+#endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
 }
 
 int X11_ResizeFullScreen(_THIS)
@@ -804,6 +950,13 @@
         }
 #endif
 
+#if SDL_VIDEO_DRIVER_X11_XRANDR
+        if ( use_xrandr ) {
+            XRRSetScreenConfig(SDL_Display, screen_config, SDL_Root,
+                               saved_size_id, saved_rotation, CurrentTime);
+        }
+#endif
+
         XUnmapWindow(SDL_Display, FSwindow);
         X11_WaitUnmapped(this, FSwindow);
         XSync(SDL_Display, True);   /* Flush spurious mode change events */
--- a/src/video/x11/SDL_x11sym.h	Wed Mar 22 09:18:10 2006 +0000
+++ b/src/video/x11/SDL_x11sym.h	Wed Mar 22 11:13:58 2006 +0000
@@ -176,5 +176,16 @@
 SDL_X11_SYM(int,ipUnallocateAndSendData,(ChannelPtr a, IPCard b),(a,b),return)
 #endif
 
+/* Xrandr support. */
+#if SDL_VIDEO_DRIVER_X11_XRANDR
+SDL_X11_MODULE(XRANDR)
+SDL_X11_SYM(Status,XRRQueryVersion,(Display *dpy,int *major_versionp,int *minor_versionp),(dpy,major_versionp,minor_versionp),return)
+SDL_X11_SYM(XRRScreenConfiguration *,XRRGetScreenInfo,(Display *dpy,Drawable draw),(dpy,draw),return)
+SDL_X11_SYM(SizeID,XRRConfigCurrentConfiguration,(XRRScreenConfiguration *config,Rotation *rotation),(config,rotation),return)
+SDL_X11_SYM(XRRScreenSize *,XRRConfigSizes,(XRRScreenConfiguration *config, int *nsizes),(config,nsizes),return)
+SDL_X11_SYM(Status,XRRSetScreenConfig,(Display *dpy, XRRScreenConfiguration *config, Drawable draw, int size_index, Rotation rotation, Time timestamp),(dpy,config,draw,size_index,rotation,timestamp),return)
+SDL_X11_SYM(void,XRRFreeScreenConfigInfo,(XRRScreenConfiguration *config),(config),)
+#endif
+
 /* end of SDL_x11sym.h ... */
 
--- a/src/video/x11/SDL_x11video.h	Wed Mar 22 09:18:10 2006 +0000
+++ b/src/video/x11/SDL_x11video.h	Wed Mar 22 11:13:58 2006 +0000
@@ -40,6 +40,9 @@
 #if SDL_VIDEO_DRIVER_X11_XME
 #include "../Xext/extensions/xme.h"
 #endif
+#if SDL_VIDEO_DRIVER_X11_XRANDR
+#include <X11/extensions/Xrandr.h>
+#endif
 
 #include "SDL_x11dyn.h"
 
@@ -116,10 +119,16 @@
     int use_xme;
     XiGMiscResolutionInfo saved_res;
 #endif
+#if SDL_VIDEO_DRIVER_X11_XRANDR
+    XRRScreenConfiguration* screen_config;
+    int saved_size_id;
+    Rotation saved_rotation;
+#endif
 
     int xinerama_x;
     int xinerama_y;
     int use_vidmode;
+    int use_xrandr;
     int currently_fullscreen;
 
     /* Automatic mode switching support (entering/leaving fullscreen) */
@@ -169,6 +178,10 @@
 #define saved_view		(this->hidden->saved_view)
 #define use_xme			(this->hidden->use_xme)
 #define saved_res		(this->hidden->saved_res)
+#define use_xrandr		(this->hidden->use_xrandr)
+#define screen_config		(this->hidden->screen_config)
+#define saved_size_id		(this->hidden->saved_size_id)
+#define saved_rotation		(this->hidden->saved_rotation)
 #define xinerama_x		(this->hidden->xinerama_x)
 #define xinerama_y		(this->hidden->xinerama_y)
 #define use_vidmode		(this->hidden->use_vidmode)