"Fossies" - the Fresh Open Source Software Archive

Member "xscreensaver-6.01/driver/screens.c" (1 Jun 2021, 24646 Bytes) of package /linux/misc/xscreensaver-6.01.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "screens.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 6.00_vs_6.01.

    1 /* screens.c --- dealing with RANDR, Xinerama, and VidMode Viewports.
    2  * xscreensaver, Copyright © 1991-2021 Jamie Zawinski <jwz@jwz.org>
    3  *
    4  * Permission to use, copy, modify, distribute, and sell this software and its
    5  * documentation for any purpose is hereby granted without fee, provided that
    6  * the above copyright notice appear in all copies and that both that
    7  * copyright notice and this permission notice appear in supporting
    8  * documentation.  No representations are made about the suitability of this
    9  * software for any purpose.  It is provided "as is" without express or 
   10  * implied warranty.
   11  */
   12 
   13 /*   There are a bunch of different mechanisms for multiple monitors
   14  *   available in X.  XScreenSaver needs to care about this for two reasons:
   15  *   first, to ensure that all visible areas go black; and second, so that
   16  *   the windows of screen savers exactly fill the glass of each monitor
   17  *   (instead of one saver spanning multiple monitors, or a monitor
   18  *   displaying only a sub-rectangle of the screen saver.)
   19  *
   20  *   1) Multi-screen:
   21  *
   22  *      This is the original way.  Each monitor gets its own display number.
   23  *      ":0.0" is the first one, ":0.1" is the next, and so on.  The value
   24  *      of $DISPLAY determines which screen windows open on by default.  A
   25  *      single app can open windows on multiple screens with the same
   26  *      display connection, but windows cannot be moved from one screen to
   27  *      another.  The mouse can be moved from one screen to another, though.
   28  *      Screens may be different depths (e.g., one can be TrueColor and one
   29  *      can be PseudoColor.)  Screens cannot be resized or moved without
   30  *      restarting X.  Sometimes this mode is referred to as "ZaphodHeads".
   31  *
   32  *      Everyone hates this way of doing things because of the inability to
   33  *      move a window from one screen to another without restarting the
   34  *      application.
   35  *
   36  *   2) Xinerama:
   37  *
   38  *      There is a single giant root window that spans all the monitors.
   39  *      All monitors are the same depth, and windows can be moved around.
   40  *      Applications can learn which rectangles are actually visible on
   41  *      monitors by querying the Xinerama server extension.  (If you don't
   42  *      do that, you end up with dialog boxes that try to appear in the
   43  *      middle of the screen actually spanning the gap between two
   44  *      monitors.)
   45  *
   46  *      Xinerama didn't work with DRI, which means that Xinerama precluded
   47  *      hardware acceleration in OpenGL programs.  Also, screens couldn't
   48  *      be resized or moved without restarting X.
   49  *
   50  *   3) Vidmode Viewports:
   51  *
   52  *      No longer supported as of XScreenSaver 6.
   53  *
   54  *      With this extension, the root window could be bigger than the
   55  *      monitor.  Moving the mouse near the edges of the screen would
   56  *      scroll around, like a pan-and-scan movie.  There was also a
   57  *      hot-key for changing the monitor's resolution (zooming in/out).
   58  *
   59  *      Trying to combine this with Xinerama crashed the server, so you
   60  *      could ONLY use this if you had only a single screen, or were in
   61  *      old multi-screen mode.
   62  *
   63  *      Also, half the time it didn't work at all: it tended to lie about
   64  *      the size of the rectangle in use.
   65  *
   66  *   4) RANDR 1.0:
   67  *
   68  *      The first version of the "Resize and Rotate" extension let you
   69  *      change the resolution of a screen on the fly.  The root window
   70  *      would actually resize.  However, it was also incompatible with
   71  *      Xinerama (did it crash, or just do nothing? I can't remember) so
   72  *      you needed to be in single-screen or old multi-screen mode.  I
   73  *      believe RANDR could co-exist with Vidmode Viewports, but I'm not
   74  *      sure.
   75  *
   76  *   5) RANDR 1.2:
   77  *
   78  *      Finally, RANDR added the functionality of Xinerama, plus some.
   79  *      Each X screen (in the sense of #1, "multi-screen") can have a
   80  *      number of sub-rectangles that are displayed on monitors, and each
   81  *      of those sub-rectangles can be displayed on more than one monitor.
   82  *      So it's possible (I think) to have a hybrid of multi-screen and
   83  *      Xinerama (e.g., to have two monitors running in one depth, and
   84  *      three monitors running in another?)  Typically though, there will
   85  *      be a single X screen with one giant root window underlying the
   86  *      rectangles of multiple monitors.  Also everything is dynamic:
   87  *      monitors can be added, removed, and resized at runtime, with
   88  *      notification events.
   89  *
   90  *      RANDR rectangles can overlap, meaning one monitor can mirror
   91  *      another, or show a sub-rectangle of another, or just overlap in
   92  *      strange ways.  The proper way to respond to weird layouts is... not
   93  *      always obvious.
   94  *
   95  *      Also sometimes RANDR says stupid shit like, "You have one screen,
   96  *      and it has no available sizes or orientations."
   97  *
   98  *      Sometimes RANDR and Xinerama report the same info, and sometimes
   99  *      not, so we look at both and see which looks most plausible.
  100  *
  101  *      Also, Nvidia fucked it up: their drivers that were popular in 2008,
  102  *      when running in "TwinView" mode, reported correct sizes via
  103  *      Xinerama, but reported one giant screen via RANDR.  Nvidia's
  104  *      response was, "We don't support RANDR, use Xinerama instead", which
  105  *      is another reason that XScreenSaver historically had to query both
  106  *      extensions and make a guess.  Maybe this is no longer necessary.
  107  */
  108 
  109 #ifdef HAVE_CONFIG_H
  110 # include "config.h"
  111 #endif
  112 
  113 #include <stdio.h>
  114 #include <string.h>
  115 #include <stdlib.h>
  116 #include <X11/Xlib.h>
  117 
  118 #ifndef HAVE_RANDR_12
  119 # undef HAVE_RANDR  /* RANDR 1.1 is no longer supported */
  120 #endif
  121 
  122 #ifdef HAVE_RANDR
  123 # include <X11/extensions/Xrandr.h>
  124 #endif /* HAVE_RANDR */
  125 
  126 #ifdef HAVE_XINERAMA
  127 # include <X11/extensions/Xinerama.h>
  128 #endif /* HAVE_XINERAMA */
  129 
  130 #ifdef HAVE_XF86VMODE
  131 # include <X11/extensions/xf86vmode.h>
  132 #endif /* HAVE_XF86VMODE */
  133 
  134 #include "blurb.h"
  135 #include "screens.h"
  136 
  137 #undef countof
  138 #define countof(x) (sizeof((x))/sizeof((*x)))
  139 
  140 
  141 void
  142 free_monitors (monitor **monitors)
  143 {
  144   monitor **m2 = monitors;
  145   if (! monitors) return;
  146   while (*m2) 
  147     {
  148       if ((*m2)->desc) free ((*m2)->desc);
  149       if ((*m2)->err) free ((*m2)->err);
  150       free (*m2);
  151       m2++;
  152     }
  153   free (monitors);
  154 }
  155 
  156 
  157 static char *
  158 append (char *s1, const char *s2)
  159 {
  160   char *s = (char *) malloc ((s1 ? strlen(s1) : 0) +
  161                              (s2 ? strlen(s2) : 0) + 3);
  162   *s = 0;
  163   if (s1) strcat (s, s1);
  164   if (s1 && s2) strcat (s, "\n");
  165   if (s2) strcat (s, s2);
  166   if (s1) free (s1);
  167   return s;
  168 }
  169 
  170 
  171 #ifdef HAVE_XINERAMA
  172 
  173 static monitor **
  174 xinerama_scan_monitors (Display *dpy, char **errP)
  175 {
  176   Screen *screen = DefaultScreenOfDisplay (dpy);
  177   int event, error, nscreens, i;
  178   XineramaScreenInfo *xsi;
  179   monitor **monitors;
  180 
  181   if (! XineramaQueryExtension (dpy, &event, &error))
  182     return 0;
  183 
  184   if (! XineramaIsActive (dpy)) 
  185     return 0;
  186 
  187   xsi = XineramaQueryScreens (dpy, &nscreens);
  188   if (!xsi) return 0;
  189 
  190   monitors = (monitor **) calloc (nscreens + 1, sizeof(*monitors));
  191   if (!monitors) return 0;
  192 
  193   for (i = 0; i < nscreens; i++)
  194     {
  195       monitor *m = (monitor *) calloc (1, sizeof (monitor));
  196       monitors[i] = m;
  197       m->id       = i;
  198       m->screen   = screen;
  199       m->x        = xsi[i].x_org;
  200       m->y        = xsi[i].y_org;
  201       m->width    = xsi[i].width;
  202       m->height   = xsi[i].height;
  203     }
  204   return monitors;
  205 }
  206 
  207 #endif /* HAVE_XINERAMA */
  208 
  209 
  210 #ifdef HAVE_RANDR
  211 
  212 static monitor **
  213 randr_scan_monitors (Display *dpy, char **errP)
  214 {
  215   int event, error, major, minor, nscreens, i, j;
  216   monitor **monitors;
  217 
  218   if (! XRRQueryExtension (dpy, &event, &error))
  219     return 0;
  220 
  221   if (! XRRQueryVersion (dpy, &major, &minor))
  222     return 0;
  223 
  224   if (! (major > 1 || (major == 1 && minor >= 2)))
  225     return 0;  /* 1.2 ir newer is required */
  226 
  227   /* Add up the virtual screens on each X screen. */
  228   nscreens = 0;
  229   for (i = 0; i < ScreenCount (dpy); i++)
  230     {
  231       XRRScreenResources *res =
  232         XRRGetScreenResources (dpy, RootWindow (dpy, i));
  233       nscreens += res->noutput;
  234       XRRFreeScreenResources (res);
  235     }
  236 
  237   if (nscreens <= 0)
  238     {
  239       *errP = append (*errP,
  240                       "WARNING: RANDR reported no screens!  Ignoring it.");
  241       return 0;
  242     }
  243 
  244   monitors = (monitor **) calloc (nscreens + 1, sizeof(*monitors));
  245   if (!monitors) return 0;
  246 
  247   for (i = 0, j = 0; i < ScreenCount (dpy); i++)
  248     {
  249       Screen *screen = ScreenOfDisplay (dpy, i);
  250       int k;
  251       XRRScreenResources *res = 
  252         XRRGetScreenResources (dpy, RootWindowOfScreen (screen));
  253       for (k = 0; k < res->noutput; k++, j++)
  254         {
  255           monitor *m = (monitor *) calloc (1, sizeof (monitor));
  256           XRROutputInfo *rroi = XRRGetOutputInfo (dpy, res, 
  257                                                   res->outputs[k]);
  258           RRCrtc crtc = (rroi->crtc  ? rroi->crtc :
  259                          rroi->ncrtc ? rroi->crtcs[0] : 0);
  260           XRRCrtcInfo *crtci = (crtc ? XRRGetCrtcInfo(dpy, res, crtc) : 0);
  261 
  262           monitors[j] = m;
  263           m->screen   = screen;
  264           m->id       = (i * 1000) + j;
  265           m->desc     = (rroi->name ? strdup (rroi->name) : 0);
  266 
  267           if (crtci)
  268             {
  269               /* Note: if the screen is rotated, XRRConfigSizes contains
  270                  the unrotated WxH, but XRRCrtcInfo contains rotated HxW.
  271                */
  272               m->x      = crtci->x;
  273               m->y      = crtci->y;
  274               m->width  = crtci->width;
  275               m->height = crtci->height;
  276             }
  277 
  278           if (rroi->connection == RR_Disconnected)
  279             m->sanity = S_DISABLED;
  280           /* #### do the same for RR_UnknownConnection? */
  281 
  282           if (crtci) 
  283             XRRFreeCrtcInfo (crtci);
  284           XRRFreeOutputInfo (rroi);
  285         }
  286       XRRFreeScreenResources (res);
  287     }
  288 
  289   /* Work around more fucking brain damage. */
  290   {
  291     int ok = 0;
  292     int i = 0;
  293     while (monitors[i]) 
  294       {
  295         if (monitors[i]->width != 0 && monitors[i]->height != 0)
  296           ok++;
  297         i++;
  298       }
  299     if (! ok)
  300       {
  301         *errP = append (*errP,
  302               "WARNING: RANDR says all screens are 0x0!  Ignoring it.");
  303         free_monitors (monitors);
  304         monitors = 0;
  305       }
  306   }
  307 
  308   return monitors;
  309 }
  310 
  311 #endif /* HAVE_RANDR */
  312 
  313 
  314 static monitor **
  315 basic_scan_monitors (Display *dpy, char **errP)
  316 {
  317   int nscreens = ScreenCount (dpy);
  318   int i;
  319   monitor **monitors = (monitor **) calloc (nscreens + 1, sizeof(*monitors));
  320   if (!monitors) return 0;
  321 
  322   for (i = 0; i < nscreens; i++)
  323     {
  324       Screen *screen = ScreenOfDisplay (dpy, i);
  325       monitor *m = (monitor *) calloc (1, sizeof (monitor));
  326       monitors[i] = m;
  327       m->id       = i;
  328       m->screen   = screen;
  329       m->x        = 0;
  330       m->y        = 0;
  331       m->width    = WidthOfScreen (screen);
  332       m->height   = HeightOfScreen (screen);
  333     }
  334   return monitors;
  335 }
  336 
  337 
  338 #if defined(HAVE_RANDR) && defined(HAVE_XINERAMA)
  339 
  340 /*   From: Aaron Plattner <aplattner@nvidia.com>
  341      Date: August 7, 2008 10:21:25 AM PDT
  342      To: linux-bugs@nvidia.com
  343 
  344      The NVIDIA X driver does not yet support RandR 1.2.  The X server has
  345      a compatibility layer in it that allows RandR 1.2 clients to talk to
  346      RandR 1.1 drivers through an RandR 1.2 pseudo-output called "default".
  347      This reports the total combined resolution of the TwinView display,
  348      since it doesn't have any visibility into TwinView metamodes.  There
  349      is no way for the driver to prevent the server from turning on this
  350      compatibility layer.
  351 
  352      The intention is for X client applications to continue to use the
  353      Xinerama extension to query the screen geometry.  RandR 1.2 reports
  354      its own Xinerama info for this purpose.  I would recommend against
  355      modifying xscreensaver to try to get this information from RandR.
  356  */
  357 static monitor **
  358 randr_versus_xinerama_fight (Display *dpy, monitor **randr_monitors, 
  359                              char **errP)
  360 {
  361   monitor **xinerama_monitors;
  362 
  363   if (!randr_monitors) 
  364     return 0;
  365 
  366   xinerama_monitors = xinerama_scan_monitors (dpy, errP);
  367   if (!xinerama_monitors)
  368     return randr_monitors;
  369 
  370   if (! monitor_layouts_differ_p (randr_monitors, xinerama_monitors))
  371     {
  372       free_monitors (xinerama_monitors);
  373       return randr_monitors;
  374     }
  375   else if (   randr_monitors[0] &&   !randr_monitors[1] &&  /* 1 monitor */
  376            xinerama_monitors[0] && xinerama_monitors[1])    /* >1 monitor */
  377     {
  378       *errP = append (*errP,
  379                       "WARNING: RANDR reports 1 screen but Xinerama\n"
  380                       "         reports multiple.  Believing Xinerama.");
  381       free_monitors (randr_monitors);
  382       return xinerama_monitors;
  383     }
  384   else
  385     {
  386       *errP = append (*errP,  /* This is "normal" now, I guess. */
  387                       "RANDR and Xinerama report different screen layouts");
  388       free_monitors (xinerama_monitors);
  389       return randr_monitors;
  390     }
  391 }
  392 
  393 #endif /* HAVE_RANDR && HAVE_XINERAMA */
  394 
  395 
  396 #ifdef DEBUG_MULTISCREEN
  397 
  398 /* If DEBUG_MULTISCREEN is defined, then in "-debug" mode, xscreensaver
  399    will pretend that it is changing the number of connected monitors
  400    every few seconds, using the geometries in the following list,
  401    for stress-testing purposes.
  402  */
  403 static monitor **
  404 debug_scan_monitors (Display *dpy, char **errP)
  405 {
  406   static const char * const geoms[] = {
  407     "1600x1028+0+22",
  408     "1024x768+0+22",
  409     "800x600+0+22",
  410     "800x600+0+22,800x600+800+22",
  411     "800x600+0+22,800x600+800+22,800x600+300+622",
  412     "800x600+0+22,800x600+800+22,800x600+0+622,800x600+800+622",
  413     "640x480+0+22,640x480+640+22,640x480+0+502,640x480+640+502",
  414     "640x480+240+22,640x480+0+502,640x480+640+502",
  415     "640x480+0+200,640x480+640+200",
  416     "800x600+400+22",
  417     "320x200+0+22,320x200+320+22,320x200+640+22,320x200+960+22,320x200+0+222,320x200+320+222,320x200+640+222,320x200+960+222,320x200+0+422,320x200+320+422,320x200+640+422,320x200+960+422,320x200+0+622,320x200+320+622,320x200+640+622,320x200+960+622,320x200+0+822,320x200+320+822,320x200+640+822,320x200+960+822"
  418   };
  419   static int index = 0;
  420   monitor **monitors = (monitor **) calloc (100, sizeof(*monitors));
  421   int nscreens = 0;
  422   Screen *screen = DefaultScreenOfDisplay (dpy);
  423 
  424   char *s = strdup (geoms[index]);
  425   char *token = strtok (s, ",");
  426   while (token)
  427     {
  428       monitor *m = calloc (1, sizeof (monitor));
  429       char c;
  430       m->id = nscreens;
  431       m->screen = screen;
  432       if (4 != sscanf (token, "%dx%d+%d+%d%c", 
  433                        &m->width, &m->height, &m->x, &m->y, &c))
  434         abort();
  435       m->width -= 2;
  436       m->height -= 2;
  437       monitors[nscreens++] = m;
  438       token = strtok (0, ",");
  439     }
  440   free (s);
  441   
  442   index = (index+1) % countof(geoms);
  443   return monitors;
  444 }
  445 #endif /* DEBUG_MULTISCREEN */
  446 
  447 
  448 #ifdef QUAD_MODE
  449 static monitor **
  450 quadruple (monitor **monitors, Bool debug_p, char **errP)
  451 {
  452   int i, j, count = 0;
  453   monitor **monitors2;
  454   while (monitors[count])
  455     count++;
  456   monitors2 = (monitor **) calloc (count * 4 + 1, sizeof(*monitors));
  457   if (!monitors2) abort();
  458 
  459   for (i = 0, j = 0; i < count; i++)
  460     {
  461       int k;
  462       for (k = 0; k < 4; k++)
  463         {
  464           monitors2[j+k] = (monitor *) calloc (1, sizeof (monitor));
  465           *monitors2[j+k] = *monitors[i];
  466           monitors2[j+k]->width  /= (debug_p ? 4 : 2);
  467           monitors2[j+k]->height /= 2;
  468           monitors2[j+k]->id = (monitors[i]->id * 4) + k;
  469           monitors2[j+k]->name = (monitors[i]->name
  470                                   ? strdup (monitors[i]->name) : 0);
  471         }
  472       monitors2[j+1]->x += monitors2[j]->width;
  473       monitors2[j+2]->y += monitors2[j]->height;
  474       monitors2[j+3]->x += monitors2[j]->width;
  475       monitors2[j+3]->y += monitors2[j]->height;
  476       j += 4;
  477     }
  478 
  479   free_monitors (monitors);
  480   return monitors2;
  481 }
  482 #endif /* QUAD_MODE */
  483 
  484 
  485 monitor **
  486 scan_monitors (Display *dpy)
  487 {
  488   monitor **monitors = 0;
  489   char *err = 0;
  490 
  491 # ifdef DEBUG_MULTISCREEN
  492     if (! monitors) monitors = debug_scan_monitors (dpy, &err);
  493 # endif
  494 
  495 # ifdef HAVE_RANDR
  496     if (! monitors) monitors = randr_scan_monitors (dpy, &err);
  497 
  498 #  ifdef HAVE_XINERAMA
  499    monitors = randr_versus_xinerama_fight (dpy, monitors, &err);
  500 #  endif
  501 # endif /* HAVE_RANDR */
  502 
  503 # ifdef HAVE_XINERAMA
  504   if (! monitors) monitors = xinerama_scan_monitors (dpy, &err);
  505 # endif
  506 
  507   if (! monitors) monitors = basic_scan_monitors (dpy, &err);
  508 
  509 # ifdef QUAD_MODE
  510   if (p->quad_p)
  511     monitors = quadruple (monitors, p->debug_p, &err);
  512 # endif
  513 
  514   if (monitors && *monitors && err) monitors[0]->err = err;
  515 
  516   return monitors;
  517 }
  518 
  519 
  520 static Bool
  521 monitors_overlap_p (monitor *a, monitor *b)
  522 {
  523   /* Two rectangles overlap if the max of the tops is less than the
  524      min of the bottoms and the max of the lefts is less than the min
  525      of the rights.
  526    */
  527 # undef MAX
  528 # undef MIN
  529 # define MAX(A,B) ((A)>(B)?(A):(B))
  530 # define MIN(A,B) ((A)<(B)?(A):(B))
  531 
  532   int maxleft  = MAX(a->x, b->x);
  533   int maxtop   = MAX(a->y, b->y);
  534   int minright = MIN(a->x + a->width  - 1, b->x + b->width);
  535   int minbot   = MIN(a->y + a->height - 1, b->y + b->height);
  536   return (maxtop < minbot && maxleft < minright);
  537 }
  538 
  539 
  540 static Bool
  541 plausible_aspect_ratio_p (monitor **monitors)
  542 {
  543   /* Modern wide-screen monitors come in the following aspect ratios:
  544 
  545             One monitor:        If you tack a 640x480 monitor
  546                                 onto the right, the ratio is:
  547          16 x 9    --> 1.78
  548         852 x 480  --> 1.77        852+640 x 480  --> 3.11      "SD 480p"
  549        1280 x 720  --> 1.78       1280+640 x 720  --> 2.67      "HD 720p"
  550        1280 x 920  --> 1.39       1280+640 x 920  --> 2.09
  551        1366 x 768  --> 1.78       1366+640 x 768  --> 2.61      "HD 768p"
  552        1440 x 900  --> 1.60       1440+640 x 900  --> 2.31
  553        1680 x 1050 --> 1.60       1680+640 x 1050 --> 2.21
  554        1690 x 1050 --> 1.61       1690+640 x 1050 --> 2.22
  555        1920 x 1080 --> 1.78       1920+640 x 1080 --> 2.37      "HD 1080p"
  556        1920 x 1200 --> 1.60       1920+640 x 1200 --> 2.13
  557        2560 x 1600 --> 1.60       2560+640 x 1600 --> 2.00
  558 
  559      So that implies that if we ever see an aspect ratio >= 2.0,
  560      we can be pretty sure that the X server is lying to us, and
  561      that's actually two monitors, not one.
  562    */
  563   if (monitors[0] && !monitors[1] &&    /* exactly 1 monitor */
  564       monitors[0]->height &&
  565       monitors[0]->width / (double) monitors[0]->height >= 1.9)
  566     return False;
  567   else
  568     return True;
  569 }
  570 
  571 
  572 /* Mark the ones that overlap, etc.
  573  */
  574 void
  575 check_monitor_sanity (monitor **monitors)
  576 {
  577   int i, j, count = 0;
  578 
  579   while (monitors[count])
  580     count++;
  581 
  582 #  define X1 monitors[i]->x
  583 #  define X2 monitors[j]->x
  584 #  define Y1 monitors[i]->y
  585 #  define Y2 monitors[j]->y
  586 #  define W1 monitors[i]->width
  587 #  define W2 monitors[j]->width
  588 #  define H1 monitors[i]->height
  589 #  define H2 monitors[j]->height
  590 
  591   /* If a monitor is enclosed by any other monitor, that's insane.
  592    */
  593   for (i = 0; i < count; i++)
  594     for (j = 0; j < count; j++)
  595       if (i != j &&
  596           monitors[i]->sanity == S_SANE &&
  597           monitors[j]->sanity == S_SANE &&
  598           monitors[i]->screen == monitors[j]->screen &&
  599           X2 >= X1 &&
  600           Y2 >= Y1 &&
  601           (X2+W2) <= (X1+W1) &&
  602           (Y2+H2) <= (Y1+H1))
  603         {
  604           if (X1 == X2 &&
  605               Y1 == Y2 &&
  606               W1 == W2 &&
  607               H1 == H2)
  608             monitors[j]->sanity = S_DUPLICATE;
  609           else 
  610             monitors[j]->sanity = S_ENCLOSED;
  611           monitors[j]->enemy = i;
  612         }
  613 
  614   /* After checking for enclosure, check for other lossage against earlier
  615      monitors.  We do enclosure first so that we make sure to pick the
  616      larger one.
  617    */
  618   for (i = 0; i < count; i++)
  619     for (j = 0; j < i; j++)
  620       {
  621         if (monitors[i]->sanity != S_SANE) continue; /* already marked */
  622         if (monitors[j]->sanity != S_SANE) continue;
  623         if (monitors[i]->screen != monitors[j]->screen) continue;
  624 
  625         if (monitors_overlap_p (monitors[i], monitors[j]))
  626           {
  627             monitors[i]->sanity = S_OVERLAP;
  628             monitors[i]->enemy = j;
  629           }
  630       }
  631 
  632   /* Finally, make sure all monitors have sane positions and sizes.
  633      Xinerama sometimes reports 1024x768 VPs at -1936862040, -1953705044.
  634    */
  635   for (i = 0; i < count; i++)
  636     {
  637       if (monitors[i]->sanity != S_SANE) continue; /* already marked */
  638       if (X1    <  0      || Y1    <  0 || 
  639           W1    <= 0      || H1    <= 0 || 
  640           X1+W1 >= 0x7FFF || Y1+H1 >= 0x7FFF)
  641         {
  642           monitors[i]->sanity = S_OFFSCREEN;
  643           monitors[i]->enemy = 0;
  644         }
  645     }
  646 
  647 #  undef X1
  648 #  undef X2
  649 #  undef Y1
  650 #  undef Y2
  651 #  undef W1
  652 #  undef W2
  653 #  undef H1
  654 #  undef H2
  655 }
  656 
  657 
  658 Bool
  659 monitor_layouts_differ_p (monitor **a, monitor **b)
  660 {
  661   if (!a || !b) return True;
  662   while (1)
  663     {
  664       if (!*a) break;
  665       if (!*b) break;
  666       if ((*a)->screen != (*b)->screen ||
  667           (*a)->x      != (*b)->x      ||
  668           (*a)->y      != (*b)->y      ||
  669           (*a)->width  != (*b)->width  ||
  670           (*a)->height != (*b)->height)
  671         return True;
  672       a++;
  673       b++;
  674     }
  675   if (*a) return True;
  676   if (*b) return True;
  677 
  678   return False;
  679 }
  680 
  681 
  682 static int
  683 screen_number (Screen *screen)
  684 {
  685   Display *dpy = DisplayOfScreen (screen);
  686   int i;
  687   for (i = 0; i < ScreenCount (dpy); i++)
  688     if (ScreenOfDisplay (dpy, i) == screen)
  689       return i;
  690   return 0;
  691 }
  692 
  693 
  694 void
  695 describe_monitor_layout (monitor **monitors)
  696 {
  697   int count = 0;
  698   int good_count = 0;
  699   int bad_count = 0;
  700   int implausible_p = !plausible_aspect_ratio_p (monitors);
  701 
  702   while (monitors[count])
  703     {
  704       if (monitors[count]->sanity == S_SANE)
  705         good_count++;
  706       else
  707         bad_count++;
  708       count++;
  709     }
  710 
  711   if (monitors && *monitors && monitors[0]->err)   /* deferred error msg */
  712     {
  713       char *token = strtok (monitors[0]->err, "\n");
  714       while (token)
  715         {
  716           fprintf (stderr, "%s: %s\n", blurb(), token);
  717           token = strtok (0, "\n");
  718         }
  719       free (monitors[0]->err);
  720       monitors[0]->err = 0;
  721     }
  722 
  723   if (count == 0)
  724     fprintf (stderr, "%s: no screens!\n", blurb());
  725   else
  726     {
  727       int i;
  728       fprintf (stderr, "%s: screens in use: %d\n", blurb(), good_count);
  729       for (i = 0; i < count; i++)
  730         {
  731           monitor *m = monitors[i];
  732           if (m->sanity != S_SANE) continue;
  733           fprintf (stderr, "%s:  %3d/%d: %dx%d+%d+%d",
  734                    blurb(), m->id, screen_number (m->screen),
  735                    m->width, m->height, m->x, m->y);
  736           if (m->desc && *m->desc) fprintf (stderr, " (%s)", m->desc);
  737           fprintf (stderr, "\n");
  738         }
  739       if (bad_count > 0)
  740         {
  741           fprintf (stderr, "%s: rejected screens: %d\n", blurb(), bad_count);
  742           for (i = 0; i < count; i++)
  743             {
  744               monitor *m = monitors[i];
  745               monitor *e = monitors[m->enemy];
  746               if (m->sanity == S_SANE) continue;
  747               fprintf (stderr, "%s:  %3d/%d: %dx%d+%d+%d",
  748                        blurb(), m->id, screen_number (m->screen),
  749                        m->width, m->height, m->x, m->y);
  750               if (m->desc && *m->desc) fprintf (stderr, " (%s)", m->desc);
  751               fprintf (stderr, " -- ");
  752               switch (m->sanity)
  753                 {
  754                 case S_SANE: abort(); break;
  755                 case S_ENCLOSED:
  756                   fprintf (stderr, "enclosed by %d (%dx%d+%d+%d)\n",
  757                            e->id, e->width, e->height, e->x, e->y);
  758                   break;
  759                 case S_DUPLICATE:
  760                   fprintf (stderr, "duplicate of %d\n", e->id);
  761                   break;
  762                 case S_OVERLAP:
  763                   fprintf (stderr, "overlaps %d (%dx%d+%d+%d)\n",
  764                            e->id, e->width, e->height, e->x, e->y);
  765                   break;
  766                 case S_OFFSCREEN:
  767                   fprintf (stderr, "off screen (%dx%d)\n",
  768                            WidthOfScreen (e->screen), 
  769                            HeightOfScreen (e->screen));
  770                   break;
  771                 case S_DISABLED:
  772                   fprintf (stderr, "output disabled\n");
  773                   break;
  774                 }
  775             }
  776         }
  777 
  778       if (implausible_p)
  779         fprintf (stderr,
  780                  "%s: WARNING: single screen aspect ratio is %dx%d = %.2f\n"
  781                  "%s:          probable X server bug in Xinerama/RANDR!\n",
  782                  blurb(), monitors[0]->width, monitors[0]->height,
  783                  monitors[0]->width / (double) monitors[0]->height,
  784                  blurb());
  785     }
  786 }