# HG changeset patch # User Sam Lantinga # Date 1229449263 0 # Node ID b33e38aaa0271194ccbbed5371e527d96902846d # Parent 762f0663c36ae0225a5ec1e58ba58164872d41c3 Progress on fullscreen mode switching on X11 diff -r 762f0663c36a -r b33e38aaa027 src/video/x11/SDL_x11modes.c --- a/src/video/x11/SDL_x11modes.c Tue Dec 16 17:40:30 2008 +0000 +++ b/src/video/x11/SDL_x11modes.c Tue Dec 16 17:41:03 2008 +0000 @@ -23,6 +23,10 @@ #include "SDL_x11video.h" +//#define X11MODES_DEBUG +#undef SDL_VIDEO_DRIVER_X11_XINERAMA +#undef SDL_VIDEO_DRIVER_X11_XRANDR +#undef SDL_VIDEO_DRIVER_X11_VIDMODE static int get_visualinfo(Display * display, int screen, XVisualInfo * vinfo) @@ -153,20 +157,487 @@ } } +/* Global for the error handler */ +int vm_event, vm_error = -1; + +#if SDL_VIDEO_DRIVER_X11_XINERAMA +static SDL_bool +CheckXinerama(Display *display, int *major, int *minor) +{ + const char *env; + + /* Default the extension not available */ + *major = *minor = 0; + + /* Allow environment override */ + env = getenv("SDL_VIDEO_X11_XINERAMA"); + if (env && !SDL_atoi(env)) { + return SDL_FALSE; + } + + /* Query the extension version */ + if (!SDL_NAME(XineramaQueryExtension)(display, major, minor) || + !SDL_NAME(XineramaIsActive)(display)) { + return SDL_FALSE; + } + return SDL_TRUE; +} +#endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */ + +#if SDL_VIDEO_DRIVER_X11_XRANDR +static SDL_bool +CheckXRandR(Display *display, int *major, int *minor) +{ + const char *env; + + /* Default the extension not available */ + *major = *minor = 0; + + /* Allow environment override */ + env = getenv("SDL_VIDEO_X11_XRANDR"); + if (env && !SDL_atoi(env)) { + return SDL_FALSE; + } + + if (!SDL_X11_HAVE_XRANDR) { + return SDL_FALSE; + } + + /* Query the extension version */ + if (!XRRQueryVersion(display, major, minor)) { + return SDL_FALSE; + } + return SDL_TRUE; +} +#endif /* SDL_VIDEO_DRIVER_X11_XRANDR */ + +#if SDL_VIDEO_DRIVER_X11_VIDMODE +static SDL_bool +CheckVidMode(Display *display, int *major, int *minor) +{ + const char *env; + + /* Default the extension not available */ + *major = *minor = 0; + + /* Allow environment override */ + env = getenv("SDL_VIDEO_X11_VIDMODE"); + if (env && !SDL_atoi(env)) { + return SDL_FALSE; + } + + /* Query the extension version */ + vm_error = -1; + if (!SDL_NAME(XF86VidModeQueryExtension)(display, &vm_event, &vm_error) || + !SDL_NAME(XF86VidModeQueryVersion)(display, major, minor)) { + return SDL_FALSE; + } + return SDL_TRUE; +} + +Bool SDL_NAME(XF86VidModeGetModeInfo)(Display *dpy, int scr, SDL_NAME(XF86VidModeModeInfo) *info) +{ + Bool retval; + int dotclock; + SDL_NAME(XF86VidModeModeLine) l; + SDL_zerop(info); + SDL_zero(l); + retval = SDL_NAME(XF86VidModeGetModeLine)(dpy, scr, &dotclock, &l); + info->dotclock = dotclock; + info->hdisplay = l.hdisplay; + info->hsyncstart = l.hsyncstart; + info->hsyncend = l.hsyncend; + info->htotal = l.htotal; + info->hskew = l.hskew; + info->vdisplay = l.vdisplay; + info->vsyncstart = l.vsyncstart; + info->vsyncend = l.vsyncend; + info->vtotal = l.vtotal; + info->flags = l.flags; + info->privsize = l.privsize; + info->private = l.private; + return retval; +} + +static int +calculate_rate(SDL_NAME(XF86VidModeModeInfo) *info) +{ + return (info->htotal && info->vtotal) ? (1000 * info->dotclock / (info->htotal * info->vtotal)) : 0; +} + +static void +save_mode(Display *display, SDL_DisplayData *data) +{ + SDL_NAME(XF86VidModeGetModeInfo)(display, data->screen, &data->saved_mode); + SDL_NAME(XF86VidModeGetViewPort)(display, data->screen, &data->saved_view.x,&data->saved_view.y); +} + +static void +restore_mode(Display *display, SDL_DisplayData *data) +{ + SDL_NAME(XF86VidModeModeInfo) mode; + + if (SDL_NAME(XF86VidModeGetModeInfo)(display, data->screen, &mode)) { + if (SDL_memcmp(&mode, &data->saved_mode, sizeof(mode)) != 0) { + SDL_NAME(XF86VidModeSwitchToMode)(display, data->screen, &data->saved_mode); + } + } + if ((data->saved_view.x != 0) || (data->saved_view.y != 0)) { + SDL_NAME(XF86VidModeSetViewPort)(display, data->screen, data->saved_view.x, data->saved_view.y); + } +} +#endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */ + void X11_GetDisplayModes(_THIS) { Display *display = ((SDL_VideoData *) _this->driverdata)->display; SDL_DisplayData *data = (SDL_DisplayData *) SDL_CurrentDisplay.driverdata; +#if SDL_VIDEO_DRIVER_X11_XINERAMA + int xinerama_major, xinerama_minor; + int screens; + SDL_NAME(XineramaScreenInfo) *xinerama; +#endif +#if SDL_VIDEO_DRIVER_X11_XRANDR + int xrandr_major, xrandr_minor; + int nsizes, nrates; + XRRScreenSize *sizes; + short *rates; +#endif +#if SDL_VIDEO_DRIVER_X11_VIDMODE + int vm_major, vm_minor; + int nmodes; + SDL_NAME(XF86VidModeModeInfo) **modes; +#endif + int screen_w; + int screen_h; SDL_DisplayMode mode; + /* Unfortunately X11 requires the window to be created with the correct + * visual and depth ahead of time, but the SDL API allows you to create + * a window before setting the fullscreen display mode. This means that + * we have to use the same format for all windows and all display modes. + * (or support recreating the window with a new visual behind the scenes) + */ + mode.format = SDL_CurrentDisplay.current_mode.format; + mode.driverdata = NULL; + + data->use_xinerama = 0; + data->use_xrandr = 0; + data->use_vidmode = 0; + screen_w = DisplayWidth(display, data->screen); + screen_h = DisplayHeight(display, data->screen); + +#if SDL_VIDEO_DRIVER_X11_XINERAMA + /* Query Xinerama extention */ + if (CheckXinerama(display, &xinerama_major, &xinerama_minor)) { +#ifdef X11MODES_DEBUG + printf("X11 detected Xinerama:\n"); +#endif + xinerama = SDL_NAME(XineramaQueryScreens)(display, &screens); + if (xinerama) { + int i; + for (i = 0; i < screens; i++) { +#ifdef X11MODES_DEBUG + printf("xinerama %d: %dx%d+%d+%d\n", + xinerama[i].screen_number, + xinerama[i].width, xinerama[i].height, + xinerama[i].x_org, xinerama[i].y_org); +#endif + if (xinerama[i].screen_number == data->screen) { + data->use_xinerama = xinerama_major * 100 + xinerama_minor; + data->xinerama_info = xinerama[i]; + } + } + XFree(xinerama); + } + + if (data->use_xinerama) { + /* Add the full xinerama mode */ + if (screen_w > data->xinerama_info.width || + screen_h > data->xinerama_info.height) { + mode.w = screen_w; + mode.h = screen_h; + mode.refresh_rate = 0; + SDL_AddDisplayMode(_this->current_display, &mode); + } + + /* Add the head xinerama mode */ + mode.w = data->xinerama_info.width; + mode.h = data->xinerama_info.height; + mode.refresh_rate = 0; + SDL_AddDisplayMode(_this->current_display, &mode); + } + } +#endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */ + +#if SDL_VIDEO_DRIVER_X11_XRANDR + /* XRandR */ + /* require at least XRandR v1.0 (arbitrary) */ + if (CheckXRandR(display, &xrandr_major, &xrandr_minor) && xrandr_major >= 1) { +#ifdef X11MODES_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. + */ + data->screen_config = XRRGetScreenInfo(display, RootWindow(display, data->screen)); + + /* retrieve the list of resolution */ + sizes = XRRConfigSizes(data->screen_config, &nsizes); + if (nsizes > 0) { + int i, j; + for ( i=0; i < nsizes; i++) { + mode.w = sizes[i].width; + mode.h = sizes[i].height; + + rates = XRRConfigRates(data->screen_config, i, &nrates); + for (j = 0; j < nrates; ++j) { + mode.refresh_rate = rates[j]; +#ifdef X11MODES_DEBUG + fprintf(stderr, "XRANDR: mode = %4d[%d], w = %4d, h = %4d, rate = %4d\n", + i, j, mode.w, mode.h, mode.refresh_rate); +#endif + SDL_AddDisplayMode(_this->current_display, &mode); + } + } + + data->use_xrandr = xrandr_major * 100 + xrandr_minor; + data->saved_size = XRRConfigCurrentConfiguration(data->screen_config, &data->saved_rotation); + data->saved_rate = XRRConfigCurrentRate(data->screen_config); + } + } +#endif /* SDL_VIDEO_DRIVER_X11_XRANDR */ + +#if SDL_VIDEO_DRIVER_X11_VIDMODE + /* XVidMode */ + if (!data->use_xrandr && +#if SDL_VIDEO_DRIVER_X11_XINERAMA + (!data->use_xinerama || data->xinerama_info.screen_number == 0) && +#endif + CheckVidMode(display, &vm_major, &vm_minor) && + SDL_NAME(XF86VidModeGetAllModeLines)(display, data->screen, &nmodes, &modes) ) + { + int i; + +#ifdef X11MODES_DEBUG + printf("VidMode modes: (unsorted)\n"); + for (i = 0; i < nmodes; ++i) { + printf("Mode %d: %d x %d @ %d\n", i, + modes[i]->hdisplay, modes[i]->vdisplay, + calculate_rate(modes[i])); + } +#endif + for (i = 0; i < nmodes; ++i) { + mode.w = modes[i]->hdisplay; + mode.h = modes[i]->vdisplay; + mode.refresh_rate = calculate_rate(modes[i]); + SDL_AddDisplayMode(_this->current_display, &mode); + } + XFree(modes); + + data->use_vidmode = vm_major * 100 + vm_minor; + save_mode(display, data); + } +#endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */ + + if (!data->use_xrandr && !data->use_vidmode) { + mode.w = screen_w; + mode.h = screen_h; + mode.refresh_rate = 0; + SDL_AddDisplayMode(_this->current_display, &mode); + } + +#ifdef X11MODES_DEBUG + if (data->use_xinerama) { + printf("Xinerama is enabled\n"); + } + + if (data->use_xrandr) { + printf("XRandR is enabled\n"); + } + + if (data->use_vidmode) { + printf("VidMode is enabled\n"); + } +#endif /* X11MODES_DEBUG */ +} + +static void +get_real_resolution(Display *display, SDL_DisplayData *data, int *w, int *h, int *rate) +{ +#if SDL_VIDEO_DRIVER_X11_XRANDR + if (data->use_xrandr) { + int nsizes; + XRRScreenSize *sizes; + + sizes = XRRConfigSizes(data->screen_config, &nsizes); + if (nsizes > 0) { + int cur_size; + Rotation cur_rotation; + + cur_size = XRRConfigCurrentConfiguration(data->screen_config, &cur_rotation); + *w = sizes[cur_size].width; + *h = sizes[cur_size].height; + *rate = XRRConfigCurrentRate(data->screen_config); +#ifdef X11MODES_DEBUG + fprintf(stderr, "XRANDR: get_real_resolution: w = %d, h = %d, rate = %d\n", *w, *h, *rate); +#endif + return; + } + } +#endif /* SDL_VIDEO_DRIVER_X11_XRANDR */ + +#if SDL_VIDEO_DRIVER_X11_VIDMODE + if (data->use_vidmode) { + SDL_NAME(XF86VidModeModeInfo) mode; + + if (SDL_NAME(XF86VidModeGetModeInfo)(display, data->screen, &mode)) { + *w = mode.hdisplay; + *h = mode.vdisplay; + *rate = calculate_rate(&mode); + return; + } + } +#endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */ + +#if SDL_VIDEO_DRIVER_X11_XINERAMA + if (data->use_xinerama) { + *w = data->xinerama_info.width; + *h = data->xinerama_info.height; + *rate = 0; + return; + } +#endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */ + + *w = DisplayWidth(display, data->screen); + *h = DisplayHeight(display, data->screen); + *rate = 0; +} + +static void +set_best_resolution(Display *display, SDL_DisplayData *data, int w, int h, int rate) +{ + int real_w, real_h, real_rate; + + /* check current mode so we can avoid uneccessary mode changes */ + get_real_resolution(display, data, &real_w, &real_h, &real_rate); + if (w == real_w && h == real_h && (!rate || rate == real_rate)) { + return; + } + +#if SDL_VIDEO_DRIVER_X11_XRANDR + if (data->use_xrandr) { +#ifdef X11MODES_DEBUG + fprintf(stderr, "XRANDR: set_best_resolution(): w = %d, h = %d\n", + w, h); +#endif + int i, nsizes, nrates; + int best; + int best_rate; + XRRScreenSize *sizes; + short *rates; + + /* find the smallest resolution that is at least as big as the user requested */ + best = -1; + sizes = XRRConfigSizes(data->screen_config, &nsizes); + for (i = 0; i < nsizes; ++i) { + if (sizes[i].width < w || sizes[i].height < h) { + continue; + } + if (sizes[i].width == w && sizes[i].height == h) { + best = i; + break; + } + if (best == -1 || + (sizes[i].width < sizes[best].width) || + (sizes[i].width == sizes[best].width && sizes[i].height < sizes[best].height)) { + best = i; + } + } + + if (best >= 0) { + best_rate = 0; + rates = XRRConfigRates(data->screen_config, best, &nrates); + for (i = 0; i < nrates; ++i) { + if (rates[i] == rate) { + best_rate = rate; + break; + } + if (!rate) { + /* Higher is better, right? */ + if (rates[i] > best_rate) { + best_rate = rates[i]; + } + } else { + if (SDL_abs(rates[i]-rate) < SDL_abs(best_rate-rate)) { + best_rate = rates[i]; + } + } + } + XRRSetScreenConfigAndRate(display, data->screen_config, RootWindow(display, data->screen), best, data->saved_rotation, best_rate, CurrentTime); + } + return; + } +#endif /* SDL_VIDEO_DRIVER_X11_XRANDR */ + +#if SDL_VIDEO_DRIVER_X11_VIDMODE + if (data->use_vidmode) { + SDL_NAME(XF86VidModeModeInfo) **modes; + int i, nmodes; + int best; + + if (SDL_NAME(XF86VidModeGetAllModeLines)(display,data->screen,&nmodes,&modes)) { + best = -1; + for (i = 0; i < nmodes; ++i) { + if (modes[i]->hdisplay < w || modes[i]->vdisplay < h) { + continue; + } + if (best == -1 || + (modes[i]->hdisplay < modes[best]->hdisplay) || + (modes[i]->hdisplay == modes[best]->hdisplay && modes[i]->vdisplay < modes[best]->vdisplay)) { + best = i; + continue; + } + if ((modes[i]->hdisplay == modes[best]->hdisplay) && + (modes[i]->vdisplay == modes[best]->vdisplay)) { + if (!rate) { + /* Higher is better, right? */ + if (calculate_rate(modes[i]) > calculate_rate(modes[best])) { + best = i; + } + } else { + if (SDL_abs(calculate_rate(modes[i])-rate) < SDL_abs(calculate_rate(modes[best])-rate)) { + best = i; + } + } + } + } + if (best >= 0) { +#ifdef X11MODES_DEBUG + printf("Best Mode %d: %d x %d @ %d\n", best, + modes[best]->hdisplay, modes[best]->vdisplay, + calculate_rate(modes[best])); +#endif + SDL_NAME(XF86VidModeSwitchToMode)(display, data->screen, modes[best]); + } + XFree(modes); + } + return; + } +#endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */ } int X11_SetDisplayMode(_THIS, SDL_DisplayMode * mode) { - //SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata; - return -1; + Display *display = ((SDL_VideoData *) _this->driverdata)->display; + SDL_DisplayData *data = (SDL_DisplayData *) SDL_CurrentDisplay.driverdata; + + set_best_resolution(display, data, mode->w, mode->h, mode->refresh_rate); + return 0; } void diff -r 762f0663c36a -r b33e38aaa027 src/video/x11/SDL_x11modes.h --- a/src/video/x11/SDL_x11modes.h Tue Dec 16 17:40:30 2008 +0000 +++ b/src/video/x11/SDL_x11modes.h Tue Dec 16 17:41:03 2008 +0000 @@ -29,6 +29,27 @@ int screen; Visual *visual; int depth; + + int use_xinerama; + int use_xrandr; + int use_vidmode; + +#if SDL_VIDEO_DRIVER_X11_XINERAMA + SDL_NAME(XineramaScreenInfo) xinerama_info; +#endif +#if SDL_VIDEO_DRIVER_X11_XRANDR + XRRScreenConfiguration *screen_config; + int saved_size; + Rotation saved_rotation; + short saved_rate; +#endif +#if SDL_VIDEO_DRIVER_X11_VIDMODE + SDL_NAME(XF86VidModeModeInfo) saved_mode; + struct { + int x, y; + } saved_view; +#endif + } SDL_DisplayData; extern void X11_InitModes(_THIS); diff -r 762f0663c36a -r b33e38aaa027 src/video/x11/SDL_x11sym.h --- a/src/video/x11/SDL_x11sym.h Tue Dec 16 17:40:30 2008 +0000 +++ b/src/video/x11/SDL_x11sym.h Tue Dec 16 17:41:03 2008 +0000 @@ -202,8 +202,10 @@ 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(short,XRRConfigCurrentRate,(XRRScreenConfiguration *config),(config),return) +SDL_X11_SYM(short *,XRRConfigRates,(XRRScreenConfiguration *config,int sizeID,int *nrates),(config,sizeID,nrates),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(Status,XRRSetScreenConfigAndRate,(Display *dpy,XRRScreenConfiguration *config,Drawable draw,int size_index,Rotation rotation,short rate,Time timestamp),(dpy,config,draw,size_index,rotation,rate,timestamp),return) SDL_X11_SYM(void,XRRFreeScreenConfigInfo,(XRRScreenConfiguration *config),(config),) #endif