Fixed XRandR code to change resolution on the correct monitor in a multi-monitor setup.
authorSam Lantinga
Wed, 03 Oct 2012 11:33:09 -0700
changeset 653785cf169db434
parent 6536 d761a7490c4a
child 6538 05cf19d1eb53
Fixed XRandR code to change resolution on the correct monitor in a multi-monitor setup.
src/video/x11/SDL_x11modes.c
src/video/x11/SDL_x11sym.h
     1.1 --- a/src/video/x11/SDL_x11modes.c	Tue Oct 02 23:23:44 2012 -0700
     1.2 +++ b/src/video/x11/SDL_x11modes.c	Wed Oct 03 11:33:09 2012 -0700
     1.3 @@ -25,7 +25,7 @@
     1.4  #include "SDL_hints.h"
     1.5  #include "SDL_x11video.h"
     1.6  
     1.7 -/*#define X11MODES_DEBUG*/
     1.8 +#define X11MODES_DEBUG
     1.9  
    1.10  static int
    1.11  get_visualinfo(Display * display, int screen, XVisualInfo * vinfo)
    1.12 @@ -339,6 +339,21 @@
    1.13  #endif
    1.14      return SDL_TRUE;
    1.15  }
    1.16 +
    1.17 +#define XRANDR_ROTATION_LEFT    (1 << 1)
    1.18 +#define XRANDR_ROTATION_RIGHT   (1 << 3)
    1.19 +
    1.20 +static void
    1.21 +get_xrandr_mode_size(XRRModeInfo *mode, Rotation rotation, int *w, int *h)
    1.22 +{
    1.23 +    if (rotation & (XRANDR_ROTATION_LEFT|XRANDR_ROTATION_RIGHT)) {
    1.24 +        *w = mode->height;
    1.25 +        *h = mode->width;
    1.26 +    } else {
    1.27 +        *w = mode->width;
    1.28 +        *h = mode->height;
    1.29 +    }
    1.30 +}
    1.31  #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
    1.32  
    1.33  #if SDL_VIDEO_DRIVER_X11_XVIDMODE
    1.34 @@ -517,6 +532,7 @@
    1.35          if (nsizes > 0) {
    1.36              int i, j;
    1.37              for (i = 0; i < nsizes; i++) {
    1.38 +                get_xrandr_mode_size(XRRModeInfo *mode, Rotation rotation, int *w, int *h)
    1.39                  mode.w = sizes[i].width;
    1.40                  mode.h = sizes[i].height;
    1.41  
    1.42 @@ -685,48 +701,150 @@
    1.43          XRRScreenSize *sizes;
    1.44          short *rates;
    1.45  
    1.46 -        /* find the smallest resolution that is at least as big as the user requested */
    1.47 -        best = -1;
    1.48 -        sizes = XRRConfigSizes(data->screen_config, &nsizes);
    1.49 -        for (i = 0; i < nsizes; ++i) {
    1.50 -            if (sizes[i].width < w || sizes[i].height < h) {
    1.51 -                continue;
    1.52 +#if SDL_VIDEO_DRIVER_X11_XINERAMA
    1.53 +        if (data->use_xrandr >= 102 && data->use_xinerama) {
    1.54 +            /* See http://cgit.freedesktop.org/xorg/app/xrandr/tree/xrandr.c for the rationale behind this */
    1.55 +            /* Note by Gabriel: the refresh rate is ignored in this code, and seeing that both xrandr
    1.56 +             * and nvidia-settings don't provide a way to set it when Xinerama is enabled, it may not be possible to set it */
    1.57 +            int screencount;
    1.58 +            XineramaScreenInfo * xinerama = NULL;
    1.59 +            XRRScreenResources  *res;
    1.60 +
    1.61 +            /* Update the current screen layout information */
    1.62 +            xinerama = XineramaQueryScreens(display, &screencount);
    1.63 +            if (xinerama && data->xinerama_screen < screencount) {
    1.64 +                data->xinerama_info = xinerama[data->xinerama_screen];
    1.65              }
    1.66 -            if (sizes[i].width == w && sizes[i].height == h) {
    1.67 -                best = i;
    1.68 -                break;
    1.69 -            }
    1.70 -            if (best == -1 ||
    1.71 -                (sizes[i].width < sizes[best].width) ||
    1.72 -                (sizes[i].width == sizes[best].width
    1.73 -                 && sizes[i].height < sizes[best].height)) {
    1.74 -                best = i;
    1.75 +            if (xinerama) XFree(xinerama);
    1.76 +
    1.77 +            res = XRRGetScreenResources (display, RootWindow(display, data->screen));
    1.78 +            if (res) {
    1.79 +                XRROutputInfo *output_info = NULL;
    1.80 +                XRRCrtcInfo *crtc = NULL;
    1.81 +                XRRModeInfo *mode_info, *best_mode_info = NULL;
    1.82 +                int mode_index = 0, output_mode_index = 0, output;
    1.83 +                int mode_w, mode_h, best_w = 0, best_h = 0;
    1.84 +
    1.85 +                for ( output = 0; output < res->noutput; output++) {
    1.86 +                    output_info = XRRGetOutputInfo (display, res, res->outputs[output]);
    1.87 +                    if (!output_info || !output_info->crtc) {
    1.88 +                        XRRFreeOutputInfo(output_info);
    1.89 +                        continue;
    1.90 +                    }
    1.91 +
    1.92 +                    crtc = XRRGetCrtcInfo (display, res, output_info->crtc);
    1.93 +                    if (!crtc || data->xinerama_info.x_org != crtc->x || data->xinerama_info.y_org != crtc->y) {
    1.94 +                        XRRFreeCrtcInfo(crtc);
    1.95 +                        continue;
    1.96 +                    }
    1.97 +
    1.98 +                    /* The CRT offset matches the Xinerama mode, let's hope it's the right one! */
    1.99 +#ifdef X11MODES_DEBUG
   1.100 +                    fprintf(stderr, "XRANDR: set_best_resolution, matched Xinerama screen %d to CRT %d at %d,%d\n",
   1.101 +                        data->xinerama_info.screen_number, output_info->crtc, crtc->x, crtc->y);
   1.102 +#endif
   1.103 +                    /* Find out the best mode we can use */
   1.104 +                    for (mode_index = 0; mode_index < res->nmode; mode_index++) {
   1.105 +                        mode_info = &res->modes[mode_index];
   1.106 +                        get_xrandr_mode_size(mode_info, crtc->rotation, &mode_w, &mode_h);
   1.107 +                        if (mode_w >= w && mode_h >= h) {
   1.108 +                            /* This may be a useful mode, check out if it belongs to the correct output */
   1.109 +#ifdef X11MODES_DEBUG
   1.110 +                            fprintf(stderr, "Evaluating valid mode %d, w: %d, h: %d\n", mode_index, mode_info->width, mode_info->height);
   1.111 +#endif
   1.112 +                            for ( output_mode_index = 0; output_mode_index < output_info->nmode; output_mode_index++) {
   1.113 +                                if (output_info->modes[output_mode_index] == mode_info->id) {
   1.114 +                                    break;
   1.115 +                                }
   1.116 +                            }
   1.117 +                            if (output_mode_index < output_info->nmode) {
   1.118 +#ifdef X11MODES_DEBUG
   1.119 +                                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);
   1.120 +#endif
   1.121 +                                /* 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 */
   1.122 +                                if ( !best_mode_info || mode_w < best_w || (mode_w == best_w && mode_h < best_h) ) {
   1.123 +                                    best_mode_info = mode_info;
   1.124 +                                    best_w = mode_w;
   1.125 +                                    best_h = mode_h;
   1.126 +                                }
   1.127 +                            }
   1.128 +#ifdef X11MODES_DEBUG
   1.129 +                            else {
   1.130 +                                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);
   1.131 +                            }
   1.132 +#endif
   1.133 +                        }
   1.134 +                    }
   1.135 +
   1.136 +                    if (best_mode_info) {
   1.137 +#ifdef X11MODES_DEBUG
   1.138 +                            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",
   1.139 +                                    best_mode_info->width, best_mode_info->height, output, crtc->x, crtc->y, data->xinerama_info.screen_number);
   1.140 +#endif
   1.141 +                        XGrabServer (display);
   1.142 +                        XRRSetCrtcConfig (display, res, output_info->crtc, CurrentTime,
   1.143 +                          crtc->x, crtc->y, best_mode_info->id, crtc->rotation,
   1.144 +                          &res->outputs[output], 1);
   1.145 +                        /* TODO: Handle screen rotations, should we call XRRSetCrtcTransform if Xrandr >=1.3 ? */
   1.146 +                        XUngrabServer (display);
   1.147 +                    } else {
   1.148 +                        /* If we reach here, we have found the right screen but no valid best mode */
   1.149 +                        SDL_SetError("The selected screen can't support a resolution of the size desired");
   1.150 +                    }
   1.151 +                    XRRFreeCrtcInfo(crtc);
   1.152 +                    XRRFreeOutputInfo(output_info);
   1.153 +                    break;
   1.154 +                }
   1.155 +                XRRFreeScreenResources(res);
   1.156              }
   1.157          }
   1.158 -
   1.159 -        if (best >= 0) {
   1.160 -            best_rate = 0;
   1.161 -            rates = XRRConfigRates(data->screen_config, best, &nrates);
   1.162 -            for (i = 0; i < nrates; ++i) {
   1.163 -                if (rates[i] == rate) {
   1.164 -                    best_rate = rate;
   1.165 +        else
   1.166 +#endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   1.167 +        {
   1.168 +            /* Use older xrandr functions that don't play along very nicely with multi monitors setups */
   1.169 +            /* find the smallest resolution that is at least as big as the user requested */
   1.170 +            best = -1;
   1.171 +            sizes = XRRConfigSizes(data->screen_config, &nsizes);
   1.172 +            for (i = 0; i < nsizes; ++i) {
   1.173 +                if (sizes[i].width < w || sizes[i].height < h) {
   1.174 +                    continue;
   1.175 +                }
   1.176 +                if (sizes[i].width == w && sizes[i].height == h) {
   1.177 +                    best = i;
   1.178                      break;
   1.179                  }
   1.180 -                if (!rate) {
   1.181 -                    /* Higher is better, right? */
   1.182 -                    if (rates[i] > best_rate) {
   1.183 -                        best_rate = rates[i];
   1.184 +                if (best == -1 ||
   1.185 +                    (sizes[i].width < sizes[best].width) ||
   1.186 +                    (sizes[i].width == sizes[best].width
   1.187 +                     && sizes[i].height < sizes[best].height)) {
   1.188 +                    best = i;
   1.189 +                }
   1.190 +            }
   1.191 +
   1.192 +            if (best >= 0) {
   1.193 +                best_rate = 0;
   1.194 +                rates = XRRConfigRates(data->screen_config, best, &nrates);
   1.195 +                for (i = 0; i < nrates; ++i) {
   1.196 +                    if (rates[i] == rate) {
   1.197 +                        best_rate = rate;
   1.198 +                        break;
   1.199                      }
   1.200 -                } else {
   1.201 -                    if (SDL_abs(rates[i] - rate) < SDL_abs(best_rate - rate)) {
   1.202 -                        best_rate = rates[i];
   1.203 +                    if (!rate) {
   1.204 +                        /* Higher is better, right? */
   1.205 +                        if (rates[i] > best_rate) {
   1.206 +                            best_rate = rates[i];
   1.207 +                        }
   1.208 +                    } else {
   1.209 +                        if (SDL_abs(rates[i] - rate) < SDL_abs(best_rate - rate)) {
   1.210 +                            best_rate = rates[i];
   1.211 +                        }
   1.212                      }
   1.213                  }
   1.214 +                XRRSetScreenConfigAndRate(display, data->screen_config,
   1.215 +                                          RootWindow(display, data->screen), best,
   1.216 +                                          data->saved_rotation, best_rate,
   1.217 +                                          CurrentTime);
   1.218              }
   1.219 -            XRRSetScreenConfigAndRate(display, data->screen_config,
   1.220 -                                      RootWindow(display, data->screen), best,
   1.221 -                                      data->saved_rotation, best_rate,
   1.222 -                                      CurrentTime);
   1.223          }
   1.224          return;
   1.225      }
     2.1 --- a/src/video/x11/SDL_x11sym.h	Tue Oct 02 23:23:44 2012 -0700
     2.2 +++ b/src/video/x11/SDL_x11sym.h	Wed Oct 03 11:33:09 2012 -0700
     2.3 @@ -237,6 +237,15 @@
     2.4  SDL_X11_SYM(XRRScreenSize *,XRRConfigSizes,(XRRScreenConfiguration *config,int *nsizes),(config,nsizes),return)
     2.5  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)
     2.6  SDL_X11_SYM(void,XRRFreeScreenConfigInfo,(XRRScreenConfiguration *config),(config),)
     2.7 +SDL_X11_SYM(void,XRRSetScreenSize,(Display *dpy, Window window,int width, int height,int mmWidth, int mmHeight),(dpy,window,width,height,mmWidth,mmHeight),)
     2.8 +SDL_X11_SYM(Status,XRRGetScreenSizeRange,(Display *dpy, Window window,int *minWidth, int *minHeight, int *maxWidth, int *maxHeight),(dpy,window,minWidth,minHeight,maxWidth,maxHeight),return)
     2.9 +SDL_X11_SYM(XRRScreenResources *,XRRGetScreenResources,(Display *dpy, Window window),(dpy, window),return)
    2.10 +SDL_X11_SYM(void,XRRFreeScreenResources,(XRRScreenResources *resources),(resources),)
    2.11 +SDL_X11_SYM(XRROutputInfo *,XRRGetOutputInfo,(Display *dpy, XRRScreenResources *resources, RROutput output),(dpy,resources,output),return)
    2.12 +SDL_X11_SYM(void,XRRFreeOutputInfo,(XRROutputInfo *outputInfo),(outputInfo),)
    2.13 +SDL_X11_SYM(XRRCrtcInfo *,XRRGetCrtcInfo,(Display *dpy, XRRScreenResources *resources, RRCrtc crtc),(dpy,resources,crtc),return)
    2.14 +SDL_X11_SYM(void,XRRFreeCrtcInfo,(XRRCrtcInfo *crtcInfo),(crtcInfo),)
    2.15 +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)
    2.16  #endif
    2.17  
    2.18  /* MIT-SCREEN-SAVER support */