http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.06.tar.gz
[xscreensaver] / driver / screens.c
1 /* screens.c --- dealing with RANDR, Xinerama, and VidMode Viewports.
2  * xscreensaver, Copyright (c) 1991-2008 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
15  *   reasons: first, to ensure that all visible areas go black; and
16  *   second, so that the windows of screen savers exactly fill the
17  *   glass of each monitor (instead of one saver spanning multiple
18  *   monitors, or a monitor displaying only a sub-rectangle of the
19  *   screen saver.)
20  *
21  *   1) Multi-screen:
22  *
23  *      This is the original way.  Each monitor gets its own display
24  *      number.  :0.0 is the first one, :0.1 is the next, etc.  The
25  *      value of $DISPLAY determines which screen windows open on by
26  *      default.  A single app can open windows on multiple screens
27  *      with the same display connection, but windows cannot be moved
28  *      from one screen to another.  The mouse can be moved from one
29  *      screen to another, though.  Screens may be different depths
30  *      (e.g., one can be TrueColor and one can be PseudoColor.)
31  *      Screens cannot be resized or moved without restarting X.
32  *
33  *      Everyone hates this way of doing things because of the
34  *      inability to move a window from one screen to another without
35  *      restarting the application.
36  *
37  *   2) Xinerama:
38  *
39  *      There is a single giant root window that spans all the
40  *      monitors.  All monitors are the same depth, and windows can be
41  *      moved around.  Applications can learn which rectangles are
42  *      actually visible on monitors by querying the Xinerama server
43  *      extension.  (If you don't do that, you end up with dialog
44  *      boxes that try to appear in the middle of the screen actually
45  *      spanning the gap between two monitors.)
46  *
47  *      Xinerama doesn't work with DRI, which means that if you use
48  *      it, you lose hardware acceleration on OpenGL programs.  Also,
49  *      screens can't be resized or moved without restarting X.
50  *
51  *   3) Vidmode Viewports:
52  *
53  *      With this extension, the root window can be bigger than the
54  *      monitor.  Moving the mouse near the edges of the screen
55  *      scrolls around, like a pan-and-scan movie.  There can also be
56  *      a hot key for changing the monitor's resolution (zooming
57  *      in/out).
58  *
59  *      Trying to combine this with Xinerama crashes the server, so
60  *      you can only use this if you have only a single screen, or are
61  *      in old-multi-screen mode.
62  *
63  *      Also, half the time it doesn't work at all: it tends to lie
64  *      about 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)
72  *      so you needed to be in single-screen or old multi-screen mode.
73  *      I believe RANDR could co-exist with Vidmode Viewports, but I'm
74  *      not 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
81  *      each of those sub-rectangles can be displayed on more than one
82  *      monitor.  So it's possible (I think) to have a hybrid of
83  *      multi-screen and Xinerama (e.g., to have two monitors running
84  *      in one depth, and three monitors running in another?)
85  *      Typically though, there will be a single X screen, with
86  *      Xinerama-like division of that large root window onto multiple
87  *      monitors.  Also everything's dynamic: monitors can be added,
88  *      removed, and resized at runtime.
89  *
90  *      I believe that as of RANDR 1.2, the Xinerama extension still
91  *      exists but only as a compatiblity layer: it's actually
92  *      returning data from the RANDR extension.
93  *
94  *      Though RANDR 1.2 allows the same image to be cloned onto more
95  *      than one monitor, and also allows one monitor to show a
96  *      subsection of something on another monitor (e.g., the
97  *      rectangles can be enclosed or overlap).  Since there's no way
98  *      to put seperate savers on those duplicated-or-overlapping
99  *      monitors, xscreensaver just ignores them (which allows them to
100  *      display duplicates or overlaps).
101  */
102
103 #ifdef HAVE_CONFIG_H
104 # include "config.h"
105 #endif
106
107 #include <X11/Xlib.h>
108
109 #ifdef HAVE_RANDR
110 # include <X11/extensions/Xrandr.h>
111 #endif /* HAVE_RANDR */
112
113 #ifdef HAVE_XINERAMA
114 # include <X11/extensions/Xinerama.h>
115 #endif /* HAVE_XINERAMA */
116
117 #ifdef HAVE_XF86VMODE
118 # include <X11/extensions/xf86vmode.h>
119 #endif /* HAVE_XF86VMODE */
120
121 /* This file doesn't need the Xt headers, so stub these types out... */
122 #undef XtPointer
123 #define XtAppContext void*
124 #define XrmDatabase  void*
125 #define XtIntervalId void*
126 #define XtPointer    void*
127 #define Widget       void*
128
129 #include "xscreensaver.h"
130 #include "visual.h"
131
132
133 typedef enum { S_SANE, S_ENCLOSED, S_DUPLICATE, S_OVERLAP, 
134                S_OFFSCREEN, S_DISABLED } monitor_sanity;
135
136 /* 'typedef monitor' is in types.h */
137 struct _monitor {
138   int id;
139   char *desc;
140   Screen *screen;
141   int x, y, width, height;
142   monitor_sanity sanity;        /* I'm not crazy you're the one who's crazy */
143   int enemy;                    /* which monitor it overlaps or duplicates */
144 };
145
146 static void
147 free_monitors (monitor **monitors)
148 {
149   monitor **m2 = monitors;
150   if (! monitors) return;
151   while (*m2) 
152     {
153       if ((*m2)->desc) free ((*m2)->desc);
154       free (*m2);
155       m2++;
156     }
157   free (monitors);
158 }
159
160
161 #ifdef HAVE_XINERAMA
162
163 static monitor **
164 xinerama_scan_monitors (Display *dpy)
165 {
166   Screen *screen = DefaultScreenOfDisplay (dpy);
167   int event, error, nscreens, i;
168   XineramaScreenInfo *xsi;
169   monitor **monitors;
170
171   if (! XineramaQueryExtension (dpy, &event, &error))
172     return 0;
173
174   if (! XineramaIsActive (dpy)) 
175     return 0;
176
177   xsi = XineramaQueryScreens (dpy, &nscreens);
178   if (!xsi) return 0;
179
180   monitors = (monitor **) calloc (nscreens + 1, sizeof(*monitors));
181   if (!monitors) return 0;
182
183   for (i = 0; i < nscreens; i++)
184     {
185       monitor *m = (monitor *) calloc (1, sizeof (monitor));
186       monitors[i] = m;
187       m->id       = i;
188       m->screen   = screen;
189       m->x        = xsi[i].x_org;
190       m->y        = xsi[i].y_org;
191       m->width    = xsi[i].width;
192       m->height   = xsi[i].height;
193     }
194   return monitors;
195 }
196
197 #endif /* HAVE_XINERAMA */
198
199
200 #ifdef HAVE_XF86VMODE
201
202 static monitor **
203 vidmode_scan_monitors (Display *dpy)
204 {
205   int event, error, nscreens, i;
206   monitor **monitors;
207
208   /* Note that XF86VidModeGetViewPort() tends to be full of lies on laptops
209      that have a docking station or external monitor that runs in a different
210      resolution than the laptop's screen:
211
212          http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=81593
213          http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=208417
214          http://bugs.xfree86.org/show_bug.cgi?id=421
215
216      Presumably this is fixed by using RANDR instead of VidMode.
217    */
218
219 # ifdef HAVE_XINERAMA
220   /* Attempts to use the VidMode extension when the Xinerama extension is
221      active can result in a server crash! Yay! */
222   if (XQueryExtension (dpy, "XINERAMA", &error, &event, &error))
223     return 0;
224 #  endif /* !HAVE_XINERAMA */
225
226   if (! XF86VidModeQueryExtension (dpy, &event, &error))
227     return 0;
228
229   nscreens = ScreenCount (dpy);
230   monitors = (monitor **) calloc (nscreens + 1, sizeof(*monitors));
231   if (!monitors) return 0;
232
233   for (i = 0; i < nscreens; i++)
234     {
235       monitor *m = (monitor *) calloc (1, sizeof (monitor));
236       XF86VidModeModeLine ml;
237       int dot;
238       Screen *screen = ScreenOfDisplay (dpy, i);
239
240       monitors[i] = m;
241       m->id       = i;
242       m->screen   = screen;
243
244       if (! safe_XF86VidModeGetViewPort (dpy, i, &m->x, &m->y))
245         m->x = m->y = -1;
246
247       if (XF86VidModeGetModeLine (dpy, i, &dot, &ml))
248         {
249           m->width  = ml.hdisplay;
250           m->height = ml.vdisplay;
251         }
252
253       /* Apparently, though the server stores the X position in increments of
254          1 pixel, it will only make changes to the *display* in some other
255          increment.  With XF86_SVGA on a Thinkpad, the display only updates
256          in multiples of 8 pixels when in 8-bit mode, and in multiples of 4
257          pixels in 16-bit mode.  I don't know what it does in 24- and 32-bit
258          mode, because I don't have enough video memory to find out.
259
260          I consider it a bug that XF86VidModeGetViewPort() is telling me the
261          server's *target* scroll position rather than the server's *actual*
262          scroll position.  David Dawes agrees, and says they may fix this in
263          XFree86 4.0, but it's notrivial.
264
265          He also confirms that this behavior is server-dependent, so the
266          actual scroll position cannot be reliably determined by the client.
267          So... that means the only solution is to provide a ``sandbox''
268          around the blackout window -- we make the window be up to N pixels
269          larger than the viewport on both the left and right sides.  That
270          means some part of the outer edges of each hack might not be
271          visible, but screw it.
272
273          I'm going to guess that 16 pixels is enough, and that the Y dimension
274          doesn't have this problem.
275
276          The drawback of doing this, of course, is that some of the screenhacks
277          will still look pretty stupid -- for example, "slidescreen" will cut
278          off the left and right edges of the grid, etc.
279       */
280 #  define FUDGE 16
281       if (m->x > 0 && m->x < m->width - ml.hdisplay)
282         {
283           /* Not at left edge or right edge:
284              Round X position down to next lower multiple of FUDGE.
285              Increase width by 2*FUDGE in case some server rounds up.
286            */
287           m->x = ((m->x - 1) / FUDGE) * FUDGE;
288           m->width += (FUDGE * 2);
289         }
290 #  undef FUDGE
291     }
292
293   return monitors;
294 }
295
296 #endif /* HAVE_XF86VMODE */
297
298
299 #ifdef HAVE_RANDR
300
301 static monitor **
302 randr_scan_monitors (Display *dpy)
303 {
304   int event, error, major, minor, nscreens, i, j;
305   monitor **monitors;
306   Bool new_randr_p = False;
307
308   if (! XRRQueryExtension (dpy, &event, &error))
309     return 0;
310
311   if (! XRRQueryVersion (dpy, &major, &minor))
312     return 0;
313
314   if (major <= 0)    /* Protocol was still in flux back then -- fuck it. */
315     return 0;
316
317 # ifdef HAVE_RANDR_12
318   new_randr_p = (major > 1 || (major == 1 && minor >= 2));
319 # endif
320
321   if (! new_randr_p)
322     /* RANDR 1.0 -- no Xinerama-like virtual screens. */
323     nscreens = ScreenCount (dpy);
324   else  /* RANDR 1.2 or newer -- built-in Xinerama */
325     {
326 # ifdef HAVE_RANDR_12
327       int xsc = ScreenCount (dpy);
328       nscreens = 0;
329       /* Add up the virtual screens on each X screen. */
330       for (i = 0; i < xsc; i++)
331         {
332           XRRScreenResources *res = 
333             XRRGetScreenResources (dpy, RootWindow (dpy, i));
334           nscreens += res->noutput;
335           XRRFreeScreenResources (res);
336         }
337 # endif /* HAVE_RANDR_12 */
338     }
339
340   monitors = (monitor **) calloc (nscreens + 1, sizeof(*monitors));
341   if (!monitors) return 0;
342
343   for (i = 0, j = 0; i < ScreenCount (dpy); i++)
344     {
345       Screen *screen = ScreenOfDisplay (dpy, j);
346
347       if (! new_randr_p)  /* RANDR 1.0 */
348         {
349           XRRScreenConfiguration *rrc;
350           monitor *m = (monitor *) calloc (1, sizeof (monitor));
351           monitors[i] = m;
352           m->screen   = screen;
353           m->id       = i;
354
355           rrc = XRRGetScreenInfo (dpy, RootWindowOfScreen (screen));
356           if (rrc)
357             {
358               SizeID size = -1;
359               Rotation rot = ~0;
360               XRRScreenSize *rrsizes;
361               int nsizes;
362
363               size = XRRConfigCurrentConfiguration (rrc, &rot);
364               rrsizes = XRRConfigSizes (rrc, &nsizes);
365
366               if (rot & (RR_Rotate_90|RR_Rotate_270))
367                 {
368                   m->width  = rrsizes[size].height;
369                   m->height = rrsizes[size].width;
370                 }
371               else
372                 {
373                   m->width  = rrsizes[size].width;
374                   m->height = rrsizes[size].height;
375                 }
376
377               /* don't free 'rrsizes' */
378               XRRFreeScreenConfigInfo (rrc);
379             }
380         }
381       else   /* RANDR 1.2 or newer */
382         {
383 # ifdef HAVE_RANDR_12
384           int k;
385           XRRScreenResources *res = 
386             XRRGetScreenResources (dpy, RootWindowOfScreen (screen));
387           for (k = 0; k < res->noutput; k++)
388             {
389               monitor *m = (monitor *) calloc (1, sizeof (monitor));
390               XRROutputInfo *rroi = XRRGetOutputInfo (dpy, res, 
391                                                       res->outputs[k]);
392               RRCrtc crtc = (rroi->crtc ? rroi->crtc : rroi->crtcs[0]);
393               XRRCrtcInfo *crtci = XRRGetCrtcInfo (dpy, res, crtc);
394
395               monitors[j] = m;
396               m->screen   = screen;
397               m->id       = (i * 1000) + j;
398               m->desc     = (rroi->name ? strdup (rroi->name) : 0);
399               m->x        = crtci->x;
400               m->y        = crtci->y;
401
402               if (crtci->rotation & (RR_Rotate_90|RR_Rotate_270))
403                 {
404                   m->width  = crtci->height;
405                   m->height = crtci->width;
406                 }
407               else
408                 {
409                   m->width  = crtci->width;
410                   m->height = crtci->height;
411                 }
412
413               j++;
414
415               if (rroi->connection == RR_Disconnected)
416                 m->sanity = S_DISABLED;
417               /* #### do the same for RR_UnknownConnection? */
418
419               XRRFreeCrtcInfo (crtci);
420               XRRFreeOutputInfo (rroi);
421             }
422           XRRFreeScreenResources (res);
423 # endif /* HAVE_RANDR_12 */
424         }
425     }
426
427   return monitors;
428 }
429
430 #endif /* HAVE_RANDR */
431
432
433 static monitor **
434 basic_scan_monitors (Display *dpy)
435 {
436   int nscreens = ScreenCount (dpy);
437   int i;
438   monitor **monitors = (monitor **) calloc (nscreens + 1, sizeof(*monitors));
439   if (!monitors) return 0;
440
441   for (i = 0; i < nscreens; i++)
442     {
443       Screen *screen = ScreenOfDisplay (dpy, i);
444       monitor *m = (monitor *) calloc (1, sizeof (monitor));
445       monitors[i] = m;
446       m->id       = i;
447       m->screen   = screen;
448       m->x        = 0;
449       m->y        = 0;
450       m->width    = WidthOfScreen (screen);
451       m->height   = HeightOfScreen (screen);
452     }
453   return monitors;
454 }
455
456
457 #ifdef DEBUG_MULTISCREEN
458
459 /* If DEBUG_MULTISCREEN is defined, then in "-debug" mode, xscreensaver
460    will pretend that it is changing the number of connected monitors
461    every few seconds, using the geometries in the following list,
462    for stress-testing purposes.
463  */
464 static monitor **
465 debug_scan_monitors (Display *dpy)
466 {
467   static const char * const geoms[] = {
468     "1600x1028+0+22",
469     "1024x768+0+22",
470     "800x600+0+22",
471     "800x600+0+22,800x600+800+22",
472     "800x600+0+22,800x600+800+22,800x600+300+622",
473     "800x600+0+22,800x600+800+22,800x600+0+622,800x600+800+622",
474     "640x480+0+22,640x480+640+22,640x480+0+502,640x480+640+502",
475     "640x480+240+22,640x480+0+502,640x480+640+502",
476     "640x480+0+200,640x480+640+200",
477     "800x600+400+22",
478     "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"
479   };
480   static int index = 0;
481   monitor **monitors = (monitor **) calloc (100, sizeof(*monitors));
482   int nscreens = 0;
483   Screen *screen = DefaultScreenOfDisplay (dpy);
484
485   char *s = strdup (geoms[index]);
486   char *token = strtok (s, ",");
487   while (token)
488     {
489       monitor *m = calloc (1, sizeof (monitor));
490       char c;
491       m->id = nscreens;
492       m->screen = screen;
493       if (4 != sscanf (token, "%dx%d+%d+%d%c", 
494                        &m->width, &m->height, &m->x, &m->y, &c))
495         abort();
496       m->width -= 2;
497       m->height -= 2;
498       monitors[nscreens++] = m;
499       token = strtok (0, ",");
500     }
501   free (s);
502   
503   index = (index+1) % countof(geoms);
504   return monitors;
505 }
506
507 #endif /* DEBUG_MULTISCREEN */
508
509
510 #ifdef QUAD_MODE
511 static monitor **
512 quadruple (monitor **monitors, Bool debug_p)
513 {
514   int i, j, count = 0;
515   monitor **monitors2;
516   while (monitors[count])
517     count++;
518   monitors2 = (monitor **) calloc (count * 4 + 1, sizeof(*monitors));
519   if (!monitors2) abort();
520
521   for (i = 0, j = 0; i < count; i++)
522     {
523       int k;
524       for (k = 0; k < 4; k++)
525         {
526           monitors2[j+k] = (monitor *) calloc (1, sizeof (monitor));
527           *monitors2[j+k] = *monitors[i];
528           monitors2[j+k]->width  /= (debug_p ? 4 : 2);
529           monitors2[j+k]->height /= 2;
530           monitors2[j+k]->id = (monitors[i]->id * 4) + k;
531           monitors2[j+k]->name = (monitors[i]->name
532                                   ? strdup (monitors[i]->name) : 0);
533         }
534       monitors2[j+1]->x += monitors2[j]->width;
535       monitors2[j+2]->y += monitors2[j]->height;
536       monitors2[j+3]->x += monitors2[j]->width;
537       monitors2[j+3]->y += monitors2[j]->height;
538       j += 4;
539     }
540
541   free_monitors (monitors);
542   return monitors2;
543 }
544 #endif /* QUAD_MODE */
545
546
547 static monitor **
548 scan_monitors (saver_info *si)
549 {
550   saver_preferences *p = &si->prefs;
551   monitor **monitors = 0;
552
553 # ifdef DEBUG_MULTISCREEN
554     if (! monitors) monitors = debug_scan_monitors (si->dpy);
555 # endif
556
557 # ifdef HAVE_RANDR
558   if (! p->getviewport_full_of_lies_p)
559     if (! monitors) monitors = randr_scan_monitors (si->dpy);
560 # endif
561
562 # ifdef HAVE_XF86VMODE
563   if (! monitors) monitors = vidmode_scan_monitors (si->dpy);
564 # endif
565
566 # ifdef HAVE_XF86VMODE
567   if (! monitors) monitors = xinerama_scan_monitors (si->dpy);
568 # endif
569
570   if (! monitors) monitors = basic_scan_monitors (si->dpy);
571
572 # ifdef QUAD_MODE
573   if (p->quad_p)
574     monitors = quadruple (monitors, p->debug_p);
575 # endif
576
577   return monitors;
578 }
579
580
581 static Bool
582 monitors_overlap_p (monitor *a, monitor *b)
583 {
584   /* Two rectangles overlap if the max of the tops is less than the
585      min of the bottoms and the max of the lefts is less than the min
586      of the rights.
587    */
588 # undef MAX
589 # undef MIN
590 # define MAX(A,B) ((A)>(B)?(A):(B))
591 # define MIN(A,B) ((A)<(B)?(A):(B))
592
593   int maxleft  = MAX(a->x, b->x);
594   int maxtop   = MAX(a->y, b->y);
595   int minright = MIN(a->x + a->width  - 1, b->x + b->width);
596   int minbot   = MIN(a->y + a->height - 1, b->y + b->height);
597   return (maxtop < minbot && maxleft < minright);
598 }
599
600
601 /* Mark the ones that overlap, etc.
602  */
603 static void
604 check_monitor_sanity (monitor **monitors)
605 {
606   int i, j, count = 0;
607
608   while (monitors[count])
609     count++;
610
611 #  define X1 monitors[i]->x
612 #  define X2 monitors[j]->x
613 #  define Y1 monitors[i]->y
614 #  define Y2 monitors[j]->y
615 #  define W1 monitors[i]->width
616 #  define W2 monitors[j]->width
617 #  define H1 monitors[i]->height
618 #  define H2 monitors[j]->height
619
620   /* If a monitor is enclosed by any other monitor, that's insane.
621    */
622   for (i = 0; i < count; i++)
623     for (j = 0; j < count; j++)
624       if (i != j &&
625           monitors[i]->sanity == S_SANE &&
626           monitors[j]->sanity == S_SANE &&
627           X2 >= X1 &&
628           Y2 >= Y1 &&
629           (X2+W2) <= (X1+W1) &&
630           (Y2+H2) <= (Y1+H1))
631         {
632           if (X1 == X2 &&
633               Y1 == Y2 &&
634               W1 == W2 &&
635               H1 == H2)
636             monitors[j]->sanity = S_DUPLICATE;
637           else 
638             monitors[j]->sanity = S_ENCLOSED;
639           monitors[j]->enemy = i;
640         }
641
642   /* After checking for enclosure, check for other lossage against earlier
643      monitors.  We do enclosure first so that we make sure to pick the
644      larger one.
645    */
646   for (i = 0; i < count; i++)
647     for (j = 0; j < i; j++)
648       {
649         if (monitors[i]->sanity != S_SANE) continue; /* already marked */
650         if (monitors[j]->sanity != S_SANE) continue;
651
652         if (monitors_overlap_p (monitors[i], monitors[j]))
653           {
654             monitors[i]->sanity = S_OVERLAP;
655             monitors[i]->enemy = j;
656           }
657       }
658
659   /* Finally, make sure all monitors are enclosed by their X screen.
660      Xinerama sometimes reports 1024x768 VPs at -1936862040, -1953705044.
661    */
662   for (i = 0; i < count; i++)
663     {
664       int sw = WidthOfScreen (monitors[i]->screen)  * 2;
665       int sh = HeightOfScreen (monitors[i]->screen) * 2;
666       if (monitors[i]->sanity != S_SANE) continue; /* already marked */
667       if (X1    <  0 || Y1    <  0 || 
668           W1    <= 0 || H1    <= 0 || 
669           X1+W1 > sw || Y1+H1 > sh)
670         {
671           monitors[i]->sanity = S_OFFSCREEN;
672           monitors[i]->enemy = 0;
673         }
674     }
675
676 #  undef X1
677 #  undef X2
678 #  undef Y1
679 #  undef Y2
680 #  undef W1
681 #  undef W2
682 #  undef H1
683 #  undef H2
684 }
685
686
687 static Bool
688 layouts_differ_p (monitor **a, monitor **b)
689 {
690   if (!a || !b) return True;
691   while (1)
692     {
693       if (!*a) break;
694       if (!*b) break;
695       if ((*a)->screen != (*b)->screen ||
696           (*a)->x      != (*b)->x      ||
697           (*a)->y      != (*b)->y      ||
698           (*a)->width  != (*b)->width  ||
699           (*a)->height != (*b)->height)
700         return True;
701       a++;
702       b++;
703     }
704   if (*a) return True;
705   if (*b) return True;
706
707   return False;
708 }
709
710
711 void
712 describe_monitor_layout (saver_info *si)
713 {
714   monitor **monitors = si->monitor_layout;
715   int count = 0;
716   int good_count = 0;
717   int bad_count = 0;
718   while (monitors[count])
719     {
720       if (monitors[count]->sanity == S_SANE)
721         good_count++;
722       else
723         bad_count++;
724       count++;
725     }
726
727   if (count == 0)
728     fprintf (stderr, "%s: no screens!\n", blurb());
729   else
730     {
731       int i;
732       fprintf (stderr, "%s: screens in use: %d\n", blurb(), good_count);
733       for (i = 0; i < count; i++)
734         {
735           monitor *m = monitors[i];
736           if (m->sanity != S_SANE) continue;
737           fprintf (stderr, "%s:  %3d/%d: %dx%d+%d+%d",
738                    blurb(), m->id, screen_number (m->screen),
739                    m->width, m->height, m->x, m->y);
740           if (m->desc && *m->desc) fprintf (stderr, " (%s)", m->desc);
741           fprintf (stderr, "\n");
742         }
743       if (bad_count > 0)
744         {
745           fprintf (stderr, "%s: rejected screens: %d\n", blurb(), bad_count);
746           for (i = 0; i < count; i++)
747             {
748               monitor *m = monitors[i];
749               monitor *e = monitors[m->enemy];
750               if (m->sanity == S_SANE) continue;
751               fprintf (stderr, "%s:  %3d/%d: %dx%d+%d+%d",
752                        blurb(), m->id, screen_number (m->screen),
753                        m->width, m->height, m->x, m->y);
754               if (m->desc && *m->desc) fprintf (stderr, " (%s)", m->desc);
755               fprintf (stderr, " -- ");
756               switch (m->sanity)
757                 {
758                 case S_SANE: abort(); break;
759                 case S_ENCLOSED:
760                   fprintf (stderr, "enclosed by %d (%dx%d+%d+%d)\n",
761                            e->id, e->width, e->height, e->x, e->y);
762                   break;
763                 case S_DUPLICATE:
764                   fprintf (stderr, "duplicate of %d\n", e->id);
765                   break;
766                 case S_OVERLAP:
767                   fprintf (stderr, "overlaps %d (%dx%d+%d+%d)\n",
768                            e->id, e->width, e->height, e->x, e->y);
769                   break;
770                 case S_OFFSCREEN:
771                   fprintf (stderr, "off screen (%dx%d)\n",
772                            WidthOfScreen (e->screen), 
773                            HeightOfScreen (e->screen));
774                   break;
775                 case S_DISABLED:
776                   fprintf (stderr, "output disabled\n");
777                   break;
778                 }
779             }
780         }
781     }
782 }
783
784
785 /* Synchronize the contents of si->ssi to the current state of the monitors.
786    Doesn't change anything if nothing has changed; otherwise, alters and
787    reuses existing saver_screen_info structs as much as possible.
788    Returns True if anything changed.
789  */
790 Bool
791 update_screen_layout (saver_info *si)
792 {
793   monitor **monitors = scan_monitors (si);
794   int count = 0;
795   int good_count = 0;
796   int i, j;
797   int seen_screens[100] = { 0, };
798
799   if (! layouts_differ_p (monitors, si->monitor_layout))
800     {
801       free_monitors (monitors);
802       return False;
803     }
804
805   free_monitors (si->monitor_layout);
806   si->monitor_layout = monitors;
807   check_monitor_sanity (si->monitor_layout);
808
809   while (monitors[count])
810     {
811       if (monitors[count]->sanity == S_SANE)
812         good_count++;
813       count++;
814     }
815
816   if (si->ssi_count == 0)
817     {
818       si->ssi_count = 10;
819       si->screens = (saver_screen_info *)
820         calloc (sizeof(*si->screens), si->ssi_count);
821     }
822
823   if (si->ssi_count <= good_count)
824     {
825       si->ssi_count = good_count + 10;
826       si->screens = (saver_screen_info *)
827         realloc (si->screens, sizeof(*si->screens) * si->ssi_count);
828       memset (si->screens + si->nscreens, 0, 
829               sizeof(*si->screens) * (si->ssi_count - si->nscreens));
830     }
831
832   if (! si->screens) abort();
833
834   si->nscreens = good_count;
835
836   /* Regenerate the list of GL visuals as needed. */
837   if (si->best_gl_visuals)
838     free (si->best_gl_visuals);
839   si->best_gl_visuals = 0;
840
841   for (i = 0, j = 0; i < count; i++)
842     {
843       monitor *m = monitors[i];
844       saver_screen_info *ssi = &si->screens[j];
845       Screen *old_screen = ssi->screen;
846       int sn;
847       if (monitors[i]->sanity != S_SANE) continue;
848
849       ssi->global = si;
850       ssi->number = j;
851
852       sn = screen_number (m->screen);
853       ssi->screen = m->screen;
854       ssi->real_screen_number = sn;
855       ssi->real_screen_p = (seen_screens[sn] == 0);
856       seen_screens[sn]++;
857
858       ssi->default_visual =
859         get_visual_resource (ssi->screen, "visualID", "VisualID", False);
860       ssi->current_visual = ssi->default_visual;
861       ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
862
863       /* If the screen changed (or if this is the first time) we need
864          a new toplevel shell for this screen's depth.
865        */
866       if (ssi->screen != old_screen)
867         initialize_screen_root_widget (ssi);
868
869       ssi->poll_mouse_last_root_x = -1;
870       ssi->poll_mouse_last_root_y = -1;
871
872       ssi->x      = m->x;
873       ssi->y      = m->y;
874       ssi->width  = m->width;
875       ssi->height = m->height;
876
877 # ifndef DEBUG_MULTISCREEN
878       {
879         saver_preferences *p = &si->prefs;
880         if (p->debug_p
881 #  ifdef QUAD_MODE
882             && !p->quad_p
883 #  endif
884             )
885           ssi->width /= 2;
886       }
887 # endif
888
889       j++;
890     }
891
892   si->default_screen = &si->screens[0];
893   return True;
894 }