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