--- 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