From 00b4753440bdaf20dffad4138b8b999222efc2c6 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 3 Oct 2012 11:33:09 -0700 Subject: [PATCH] Fixed XRandR code to change resolution on the correct monitor in a multi-monitor setup. --- src/video/x11/SDL_x11modes.c | 186 ++++++++++++++++++++++++++++------- src/video/x11/SDL_x11sym.h | 9 ++ 2 files changed, 161 insertions(+), 34 deletions(-) diff --git a/src/video/x11/SDL_x11modes.c b/src/video/x11/SDL_x11modes.c index 795fcc4f7..d74ca090f 100644 --- a/src/video/x11/SDL_x11modes.c +++ b/src/video/x11/SDL_x11modes.c @@ -25,7 +25,7 @@ #include "SDL_hints.h" #include "SDL_x11video.h" -/*#define X11MODES_DEBUG*/ +#define X11MODES_DEBUG static int get_visualinfo(Display * display, int screen, XVisualInfo * vinfo) @@ -339,6 +339,21 @@ CheckXRandR(Display * display, int *major, int *minor) #endif return SDL_TRUE; } + +#define XRANDR_ROTATION_LEFT (1 << 1) +#define XRANDR_ROTATION_RIGHT (1 << 3) + +static void +get_xrandr_mode_size(XRRModeInfo *mode, Rotation rotation, int *w, int *h) +{ + if (rotation & (XRANDR_ROTATION_LEFT|XRANDR_ROTATION_RIGHT)) { + *w = mode->height; + *h = mode->width; + } else { + *w = mode->width; + *h = mode->height; + } +} #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */ #if SDL_VIDEO_DRIVER_X11_XVIDMODE @@ -517,6 +532,7 @@ X11_GetDisplayModes(_THIS, SDL_VideoDisplay * sdl_display) if (nsizes > 0) { int i, j; for (i = 0; i < nsizes; i++) { + get_xrandr_mode_size(XRRModeInfo *mode, Rotation rotation, int *w, int *h) mode.w = sizes[i].width; mode.h = sizes[i].height; @@ -685,48 +701,150 @@ set_best_resolution(Display * display, SDL_DisplayData * data, int w, int h, 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 SDL_VIDEO_DRIVER_X11_XINERAMA + if (data->use_xrandr >= 102 && data->use_xinerama) { + /* See http://cgit.freedesktop.org/xorg/app/xrandr/tree/xrandr.c for the rationale behind this */ + /* Note by Gabriel: the refresh rate is ignored in this code, and seeing that both xrandr + * and nvidia-settings don't provide a way to set it when Xinerama is enabled, it may not be possible to set it */ + int screencount; + XineramaScreenInfo * xinerama = NULL; + XRRScreenResources *res; + + /* Update the current screen layout information */ + xinerama = XineramaQueryScreens(display, &screencount); + if (xinerama && data->xinerama_screen < screencount) { + data->xinerama_info = xinerama[data->xinerama_screen]; } - if (best == -1 || - (sizes[i].width < sizes[best].width) || - (sizes[i].width == sizes[best].width - && sizes[i].height < sizes[best].height)) { - best = i; + if (xinerama) XFree(xinerama); + + res = XRRGetScreenResources (display, RootWindow(display, data->screen)); + if (res) { + XRROutputInfo *output_info = NULL; + XRRCrtcInfo *crtc = NULL; + XRRModeInfo *mode_info, *best_mode_info = NULL; + int mode_index = 0, output_mode_index = 0, output; + int mode_w, mode_h, best_w = 0, best_h = 0; + + for ( output = 0; output < res->noutput; output++) { + output_info = XRRGetOutputInfo (display, res, res->outputs[output]); + if (!output_info || !output_info->crtc) { + XRRFreeOutputInfo(output_info); + continue; + } + + crtc = XRRGetCrtcInfo (display, res, output_info->crtc); + if (!crtc || data->xinerama_info.x_org != crtc->x || data->xinerama_info.y_org != crtc->y) { + XRRFreeCrtcInfo(crtc); + continue; + } + + /* The CRT offset matches the Xinerama mode, let's hope it's the right one! */ +#ifdef X11MODES_DEBUG + fprintf(stderr, "XRANDR: set_best_resolution, matched Xinerama screen %d to CRT %d at %d,%d\n", + data->xinerama_info.screen_number, output_info->crtc, crtc->x, crtc->y); +#endif + /* Find out the best mode we can use */ + for (mode_index = 0; mode_index < res->nmode; mode_index++) { + mode_info = &res->modes[mode_index]; + get_xrandr_mode_size(mode_info, crtc->rotation, &mode_w, &mode_h); + if (mode_w >= w && mode_h >= h) { + /* This may be a useful mode, check out if it belongs to the correct output */ +#ifdef X11MODES_DEBUG + fprintf(stderr, "Evaluating valid mode %d, w: %d, h: %d\n", mode_index, mode_info->width, mode_info->height); +#endif + for ( output_mode_index = 0; output_mode_index < output_info->nmode; output_mode_index++) { + if (output_info->modes[output_mode_index] == mode_info->id) { + break; + } + } + if (output_mode_index < output_info->nmode) { +#ifdef X11MODES_DEBUG + fprintf(stderr, "Mode belongs to the desired output %d w: %d, h: %d, rotation: %x\n", mode_index, mode_info->width, mode_info->height, crtc->rotation); +#endif + /* We have a mode that belongs to the right output and that can contain the w,h required, see if it's smaller than the current one */ + if ( !best_mode_info || mode_w < best_w || (mode_w == best_w && mode_h < best_h) ) { + best_mode_info = mode_info; + best_w = mode_w; + best_h = mode_h; + } + } +#ifdef X11MODES_DEBUG + else { + fprintf(stderr, "Discarding mode because it does not belong to the desired output %d, w: %d, h: %d, rotation: %d\n", mode_index, mode_info->width, mode_info->height, crtc->rotation); + } +#endif + } + } + + if (best_mode_info) { +#ifdef X11MODES_DEBUG + fprintf(stderr, "XRANDR: set_best_resolution, setting mode w = %d, h = %d on output: %d, CRT with offsets: %d,%d for Xinerama screen: %d\n", + best_mode_info->width, best_mode_info->height, output, crtc->x, crtc->y, data->xinerama_info.screen_number); +#endif + XGrabServer (display); + XRRSetCrtcConfig (display, res, output_info->crtc, CurrentTime, + crtc->x, crtc->y, best_mode_info->id, crtc->rotation, + &res->outputs[output], 1); + /* TODO: Handle screen rotations, should we call XRRSetCrtcTransform if Xrandr >=1.3 ? */ + XUngrabServer (display); + } else { + /* If we reach here, we have found the right screen but no valid best mode */ + SDL_SetError("The selected screen can't support a resolution of the size desired"); + } + XRRFreeCrtcInfo(crtc); + XRRFreeOutputInfo(output_info); + break; + } + XRRFreeScreenResources(res); } } - - 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; + else +#endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */ + { + /* Use older xrandr functions that don't play along very nicely with multi monitors setups */ + /* 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 (!rate) { - /* Higher is better, right? */ - if (rates[i] > best_rate) { - best_rate = rates[i]; + 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; } - } else { - if (SDL_abs(rates[i] - rate) < SDL_abs(best_rate - rate)) { - best_rate = rates[i]; + 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); } - XRRSetScreenConfigAndRate(display, data->screen_config, - RootWindow(display, data->screen), best, - data->saved_rotation, best_rate, - CurrentTime); } return; } diff --git a/src/video/x11/SDL_x11sym.h b/src/video/x11/SDL_x11sym.h index 493a14e63..d194df44c 100644 --- a/src/video/x11/SDL_x11sym.h +++ b/src/video/x11/SDL_x11sym.h @@ -237,6 +237,15 @@ SDL_X11_SYM(short *,XRRConfigRates,(XRRScreenConfiguration *config,int sizeID,in SDL_X11_SYM(XRRScreenSize *,XRRConfigSizes,(XRRScreenConfiguration *config,int *nsizes),(config,nsizes),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),) +SDL_X11_SYM(void,XRRSetScreenSize,(Display *dpy, Window window,int width, int height,int mmWidth, int mmHeight),(dpy,window,width,height,mmWidth,mmHeight),) +SDL_X11_SYM(Status,XRRGetScreenSizeRange,(Display *dpy, Window window,int *minWidth, int *minHeight, int *maxWidth, int *maxHeight),(dpy,window,minWidth,minHeight,maxWidth,maxHeight),return) +SDL_X11_SYM(XRRScreenResources *,XRRGetScreenResources,(Display *dpy, Window window),(dpy, window),return) +SDL_X11_SYM(void,XRRFreeScreenResources,(XRRScreenResources *resources),(resources),) +SDL_X11_SYM(XRROutputInfo *,XRRGetOutputInfo,(Display *dpy, XRRScreenResources *resources, RROutput output),(dpy,resources,output),return) +SDL_X11_SYM(void,XRRFreeOutputInfo,(XRROutputInfo *outputInfo),(outputInfo),) +SDL_X11_SYM(XRRCrtcInfo *,XRRGetCrtcInfo,(Display *dpy, XRRScreenResources *resources, RRCrtc crtc),(dpy,resources,crtc),return) +SDL_X11_SYM(void,XRRFreeCrtcInfo,(XRRCrtcInfo *crtcInfo),(crtcInfo),) +SDL_X11_SYM(Status,XRRSetCrtcConfig,(Display *dpy, XRRScreenResources *resources, RRCrtc crtc, Time timestamp, int x, int y, RRMode mode, Rotation rotation, RROutput *outputs, int noutputs),(dpy,resources,crtc,timestamp,x,y,mode,rotation,outputs,noutputs),return) #endif /* MIT-SCREEN-SAVER support */