http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.06.tar.gz
[xscreensaver] / driver / xscreensaver.c
1 /* xscreensaver, Copyright (c) 1991-2008 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /*   ========================================================================
13  *   First we wait until the keyboard and mouse become idle for the specified
14  *   amount of time.  We do this in one of three different ways: periodically
15  *   checking with the XIdle server extension; selecting key and mouse events
16  *   on (nearly) all windows; or by waiting for the MIT-SCREEN-SAVER extension
17  *   to send us a "you are idle" event.
18  *
19  *   Then, we map a full screen black window.
20  *
21  *   We place a __SWM_VROOT property on this window, so that newly-started
22  *   clients will think that this window is a "virtual root" window (as per
23  *   the logic in the historical "vroot.h" header.)
24  *
25  *   If there is an existing "virtual root" window (one that already had
26  *   an __SWM_VROOT property) then we remove that property from that window.
27  *   Otherwise, clients would see that window (the real virtual root) instead
28  *   of ours (the impostor.)
29  *
30  *   Then we pick a random program to run, and start it.  Two assumptions 
31  *   are made about this program: that it has been specified with whatever
32  *   command-line options are necessary to make it run on the root window;
33  *   and that it has been compiled with vroot.h, so that it is able to find
34  *   the root window when a virtual-root window manager (or this program) is
35  *   running.
36  *
37  *   Then, we wait for keyboard or mouse events to be generated on the window.
38  *   When they are, we kill the inferior process, unmap the window, and restore
39  *   the __SWM_VROOT property to the real virtual root window if there was one.
40  *
41  *   On multi-screen systems, we do the above on each screen, and start
42  *   multiple programs, each with a different value of $DISPLAY.
43  *
44  *   On Xinerama systems, we do a similar thing, but instead create multiple
45  *   windows on the (only) display, and tell the subprocess which one to use
46  *   via the $XSCREENSAVER_WINDOW environment variable -- this trick requires
47  *   a recent (Aug 2003) revision of vroot.h.
48  *
49  *   (See comments in screens.c for more details about Xinerama/RANDR stuff.)
50  *
51  *   While we are waiting for user activity, we also set up timers so that,
52  *   after a certain amount of time has passed, we can start a different
53  *   screenhack.  We do this by killing the running child process with
54  *   SIGTERM, and then starting a new one in the same way.
55  *
56  *   If there was a real virtual root, meaning that we removed the __SWM_VROOT
57  *   property from it, meaning we must (absolutely must) restore it before we
58  *   exit, then we set up signal handlers for most signals (SIGINT, SIGTERM,
59  *   etc.) that do this.  Most Xlib and Xt routines are not reentrant, so it
60  *   is not generally safe to call them from signal handlers; however, this
61  *   program spends most of its time waiting, so the window of opportunity 
62  *   when code could be called reentrantly is fairly small; and also, the worst
63  *   that could happen is that the call would fail.  If we've gotten one of
64  *   these signals, then we're on our way out anyway.  If we didn't restore the
65  *   __SWM_VROOT property, that would be very bad, so it's worth a shot.  Note
66  *   that this means that, if you're using a virtual-root window manager, you
67  *   can really fuck up the world by killing this process with "kill -9".
68  *
69  *   This program accepts ClientMessages of type SCREENSAVER; these messages
70  *   may contain the atoms ACTIVATE, DEACTIVATE, etc, meaning to turn the 
71  *   screensaver on or off now, regardless of the idleness of the user,
72  *   and a few other things.  The included "xscreensaver-command" program
73  *   sends these messsages.
74  *
75  *   If we don't have the XIdle, MIT-SCREEN-SAVER, or SGI SCREEN_SAVER
76  *   extensions, then we do the XAutoLock trick: notice every window that
77  *   gets created, and wait 30 seconds or so until its creating process has
78  *   settled down, and then select KeyPress events on those windows which
79  *   already select for KeyPress events.  It's important that we not select
80  *   KeyPress on windows which don't select them, because that would
81  *   interfere with event propagation.  This will break if any program
82  *   changes its event mask to contain KeyRelease or PointerMotion more than
83  *   30 seconds after creating the window, but such programs do not seem to
84  *   occur in nature (I've never seen it happen in all these years.)
85  *
86  *   The reason that we can't select KeyPresses on windows that don't have
87  *   them already is that, when dispatching a KeyPress event, X finds the
88  *   lowest (leafmost) window in the hierarchy on which *any* client selects
89  *   for KeyPress, and sends the event to that window.  This means that if a
90  *   client had a window with subwindows, and expected to receive KeyPress
91  *   events on the parent window instead of the subwindows, then that client
92  *   would malfunction if some other client selected KeyPress events on the
93  *   subwindows.  It is an incredible misdesign that one client can make
94  *   another client malfunction in this way.
95  *
96  *   To detect mouse motion, we periodically wake up and poll the mouse
97  *   position and button/modifier state, and notice when something has
98  *   changed.  We make this check every five seconds by default, and since the
99  *   screensaver timeout has a granularity of one minute, this makes the
100  *   chance of a false positive very small.  We could detect mouse motion in
101  *   the same way as keyboard activity, but that would suffer from the same
102  *   "client changing event mask" problem that the KeyPress events hack does.
103  *   I think polling is more reliable.
104  *
105  *   On systems with /proc/interrupts (Linux) we poll that file and note when
106  *   the interrupt counter numbers on the "keyboard" and "PS/2" lines change.
107  *   (There is no reliable way, using /proc/interrupts, to detect non-PS/2
108  *   mice, so it doesn't help for serial or USB mice.)
109  *
110  *   None of this crap happens if we're using one of the extensions.  Sadly,
111  *   the XIdle extension hasn't been available for many years; the SGI
112  *   extension only exists on SGIs; and the MIT extension, while widely
113  *   deployed, is garbage in several ways.
114  *
115  *   A third idle-detection option could be implemented (but is not): when
116  *   running on the console display ($DISPLAY is `localhost`:0) and we're on a
117  *   machine where /dev/tty and /dev/mouse have reasonable last-modification
118  *   times, we could just stat() those.  But the incremental benefit of
119  *   implementing this is really small, so forget I said anything.
120  *
121  *   Debugging hints:
122  *     - Have a second terminal handy.
123  *     - Be careful where you set your breakpoints, you don't want this to
124  *       stop under the debugger with the keyboard grabbed or the blackout
125  *       window exposed.
126  *     - If you run your debugger under XEmacs, try M-ESC (x-grab-keyboard)
127  *       to keep your emacs window alive even when xscreensaver has grabbed.
128  *     - Go read the code related to `debug_p'.
129  *     - You probably can't set breakpoints in functions that are called on
130  *       the other side of a call to fork() -- if your subprocesses are
131  *       dying with signal 5, Trace/BPT Trap, you're losing in this way.
132  *     - If you aren't using a server extension, don't leave this stopped
133  *       under the debugger for very long, or the X input buffer will get
134  *       huge because of the keypress events it's selecting for.  This can
135  *       make your X server wedge with "no more input buffers."
136  *
137  * ======================================================================== */
138
139 #ifdef HAVE_CONFIG_H
140 # include "config.h"
141 #endif
142
143 #include <stdio.h>
144 #include <ctype.h>
145 #include <X11/Xlib.h>
146
147 #include <X11/Xlibint.h>
148
149 #include <X11/Xatom.h>
150 #include <X11/Intrinsic.h>
151 #include <X11/StringDefs.h>
152 #include <X11/Shell.h>
153 #include <X11/Xos.h>
154 #include <time.h>
155 #include <sys/time.h>
156 #include <netdb.h>      /* for gethostbyname() */
157 #include <sys/types.h>
158 #include <pwd.h>
159 #ifdef HAVE_XMU
160 # ifndef VMS
161 #  include <X11/Xmu/Error.h>
162 # else  /* !VMS */
163 #  include <Xmu/Error.h>
164 # endif /* !VMS */
165 #else  /* !HAVE_XMU */
166 # include "xmu.h"
167 #endif /* !HAVE_XMU */
168
169 #ifdef HAVE_XIDLE_EXTENSION
170 # include <X11/extensions/xidle.h>
171 #endif /* HAVE_XIDLE_EXTENSION */
172
173 #ifdef HAVE_XINERAMA
174 # include <X11/extensions/Xinerama.h>
175 #endif /* HAVE_XINERAMA */
176
177 #include "xscreensaver.h"
178 #include "version.h"
179 #include "yarandom.h"
180 #include "resources.h"
181 #include "visual.h"
182 #include "usleep.h"
183 #include "auth.h"
184
185 saver_info *global_si_kludge = 0;       /* I hate C so much... */
186
187 char *progname = 0;
188 char *progclass = 0;
189 XrmDatabase db = 0;
190
191
192 static Atom XA_SCREENSAVER_RESPONSE;
193 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
194 static Atom XA_RESTART, XA_SELECT;
195 static Atom XA_THROTTLE, XA_UNTHROTTLE;
196 Atom XA_DEMO, XA_PREFS, XA_EXIT, XA_LOCK, XA_BLANK;
197
198 \f
199 static XrmOptionDescRec options [] = {
200
201   { "-verbose",            ".verbose",          XrmoptionNoArg, "on" },
202   { "-silent",             ".verbose",          XrmoptionNoArg, "off" },
203
204   /* xscreensaver-demo uses this one */
205   { "-nosplash",           ".splash",           XrmoptionNoArg, "off" },
206   { "-no-splash",          ".splash",           XrmoptionNoArg, "off" },
207
208   /* useful for debugging */
209   { "-no-capture-stderr",  ".captureStderr",    XrmoptionNoArg, "off" },
210   { "-log",                ".logFile",          XrmoptionSepArg, 0 },
211 };
212
213 #ifdef __GNUC__
214  __extension__     /* shut up about "string length is greater than the length
215                       ISO C89 compilers are required to support" when including
216                       the .ad file... */
217 #endif
218
219 static char *defaults[] = {
220 #include "XScreenSaver_ad.h"
221  0
222 };
223
224 #ifdef _VROOT_H_
225 ERROR!  You must not include vroot.h in this file.
226 #endif
227
228 static void
229 do_help (saver_info *si)
230 {
231   fflush (stdout);
232   fflush (stderr);
233   fprintf (stdout, "\
234 xscreensaver %s, copyright (c) 1991-2008 by Jamie Zawinski <jwz@jwz.org>\n\
235 \n\
236   All xscreensaver configuration is via the `~/.xscreensaver' file.\n\
237   Rather than editing that file by hand, just run `xscreensaver-demo':\n\
238   that program lets you configure the screen saver graphically,\n\
239   including timeouts, locking, and display modes.\n\
240 \n",
241           si->version);
242   fprintf (stdout, "\
243   Just getting started?  Try this:\n\
244 \n\
245         xscreensaver &\n\
246         xscreensaver-demo\n\
247 \n\
248   For updates, online manual, and FAQ, please see the web page:\n\
249 \n\
250        http://www.jwz.org/xscreensaver/\n\
251 \n");
252
253   fflush (stdout);
254   fflush (stderr);
255   exit (1);
256 }
257
258
259 char *
260 timestring (void)
261 {
262   time_t now = time ((time_t *) 0);
263   char *str = (char *) ctime (&now);
264   char *nl = (char *) strchr (str, '\n');
265   if (nl) *nl = 0; /* take off that dang newline */
266   return str;
267 }
268
269 static Bool blurb_timestamp_p = True;   /* kludge */
270
271 const char *
272 blurb (void)
273 {
274   if (!blurb_timestamp_p)
275     return progname;
276   else
277     {
278       static char buf[255];
279       char *ct = timestring();
280       int n = strlen(progname);
281       if (n > 100) n = 99;
282       strncpy(buf, progname, n);
283       buf[n++] = ':';
284       buf[n++] = ' ';
285       strncpy(buf+n, ct+11, 8);
286       strcpy(buf+n+9, ": ");
287       return buf;
288     }
289 }
290
291
292 int
293 saver_ehandler (Display *dpy, XErrorEvent *error)
294 {
295   saver_info *si = global_si_kludge;    /* I hate C so much... */
296   int i;
297   Bool fatal_p;
298
299   if (!real_stderr) real_stderr = stderr;
300
301   fprintf (real_stderr, "\n"
302            "#######################################"
303            "#######################################\n\n"
304            "%s: X Error!  PLEASE REPORT THIS BUG.\n",
305            blurb());
306
307   for (i = 0; i < si->nscreens; i++)
308     {
309       saver_screen_info *ssi = &si->screens[i];
310       fprintf (real_stderr, "%s: screen %d/%d: 0x%x, 0x%x, 0x%x\n",
311                blurb(), ssi->real_screen_number, ssi->number,
312                (unsigned int) RootWindowOfScreen (si->screens[i].screen),
313                (unsigned int) si->screens[i].real_vroot,
314                (unsigned int) si->screens[i].screensaver_window);
315     }
316
317   fprintf (real_stderr, "\n"
318            "#######################################"
319            "#######################################\n\n");
320
321   fatal_p = XmuPrintDefaultErrorMessage (dpy, error, real_stderr);
322
323   fatal_p = True;  /* The only time I've ever seen a supposedly nonfatal error,
324                       it has been BadImplementation / Xlib sequence lost, which
325                       are in truth pretty damned fatal.
326                     */
327
328   fprintf (real_stderr, "\n");
329
330   if (! fatal_p)
331     fprintf (real_stderr, "%s: nonfatal error.\n\n", blurb());
332   else
333     {
334       if (si->prefs.xsync_p)
335         {
336           saver_exit (si, -1, "because of synchronous X Error");
337         }
338       else
339         {
340 #ifdef __GNUC__
341   __extension__   /* don't warn about "string length is greater than the
342                      length ISO C89 compilers are required to support". */
343 #endif
344           fprintf (real_stderr,
345    "#######################################################################\n"
346    "\n"
347    "    If at all possible, please re-run xscreensaver with the command\n"
348    "    line arguments `-sync -verbose -log log.txt', and reproduce this\n"
349    "    bug.  That will cause xscreensaver to dump a `core' file to the\n"
350    "    current directory.  Please include the stack trace from that core\n"
351    "    file in your bug report.  *DO NOT* mail the core file itself!  That\n"
352    "    won't work.  A \"log.txt\" file will also be written.  Please *do*\n"
353    "    include the complete \"log.txt\" file with your bug report.\n"
354    "\n"
355    "    http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
356    "    the most useful bug reports, and how to examine core files.\n"
357    "\n"
358    "    The more information you can provide, the better.  But please\n"
359    "    report this bug, regardless!\n"
360    "\n"
361    "#######################################################################\n"
362    "\n"
363    "\n");
364
365           saver_exit (si, -1, 0);
366         }
367     }
368
369   return 0;
370 }
371
372
373 /* This error handler is used only while the X connection is being set up;
374    after we've got a connection, we don't use this handler again.  The only
375    reason for having this is so that we can present a more idiot-proof error
376    message than "cannot open display."
377  */
378 static void 
379 startup_ehandler (String name, String type, String class,
380                   String defalt,  /* one can't even spel properly
381                                      in this joke of a language */
382                   String *av, Cardinal *ac)
383 {
384   char fmt[512];
385   String p[10];
386   saver_info *si = global_si_kludge;    /* I hate C so much... */
387   XrmDatabase *db = XtAppGetErrorDatabase(si->app);
388   *fmt = 0;
389   XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
390                             fmt, sizeof(fmt)-1, *db);
391
392   fprintf (stderr, "%s: ", blurb());
393
394   memset (p, 0, sizeof(p));
395   if (*ac > countof (p)) *ac = countof (p);
396   memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
397   fprintf (stderr, fmt,         /* Did I mention that I hate C? */
398            p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
399   fprintf (stderr, "\n");
400
401   describe_uids (si, stderr);
402
403   if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
404     {
405       fprintf (stderr, "\n"
406           "%s: This is probably because you're logging in as root.  You\n"
407 "              shouldn't log in as root: you should log in as a normal user,\n"
408 "              and then `su' as needed.  If you insist on logging in as\n"
409 "              root, you will have to turn off X's security features before\n"
410 "              xscreensaver will work.\n"
411                "\n"
412 "              Please read the manual and FAQ for more information:\n",
413                blurb());
414     }
415   else
416     {
417       fprintf (stderr, "\n"
418           "%s: Errors at startup are usually authorization problems.\n"
419 "              But you're not logging in as root (good!) so something\n"
420 "              else must be wrong.  Did you read the manual and the FAQ?\n",
421            blurb());
422     }
423
424   fprintf (stderr, "\n"
425           "              http://www.jwz.org/xscreensaver/faq.html\n"
426           "              http://www.jwz.org/xscreensaver/man.html\n"
427           "\n");
428
429   fflush (stderr);
430   fflush (stdout);
431   exit (1);
432 }
433
434 \f
435 /* The zillions of initializations.
436  */
437
438 /* Set progname, version, etc.  This is done very early.
439  */
440 static void
441 set_version_string (saver_info *si, int *argc, char **argv)
442 {
443   progclass = "XScreenSaver";
444
445   /* progname is reset later, after we connect to X. */
446   progname = strrchr(argv[0], '/');
447   if (progname) progname++;
448   else progname = argv[0];
449
450   if (strlen(progname) > 100)   /* keep it short. */
451     progname[99] = 0;
452
453   /* The X resource database blows up if argv[0] has a "." in it. */
454   {
455     char *s = argv[0];
456     while ((s = strchr (s, '.')))
457       *s = '_';
458   }
459
460   si->version = (char *) malloc (5);
461   memcpy (si->version, screensaver_id + 17, 4);
462   si->version [4] = 0;
463 }
464
465
466 /* Initializations that potentially take place as a priveleged user:
467    If the xscreensaver executable is setuid root, then these initializations
468    are run as root, before discarding privileges.
469  */
470 static void
471 privileged_initialization (saver_info *si, int *argc, char **argv)
472 {
473 #ifndef NO_LOCKING
474   /* before hack_uid() for proper permissions */
475   lock_priv_init (*argc, argv, si->prefs.verbose_p);
476 #endif /* NO_LOCKING */
477
478   hack_uid (si);
479 }
480
481
482 /* Figure out what locking mechanisms are supported.
483  */
484 static void
485 lock_initialization (saver_info *si, int *argc, char **argv)
486 {
487 #ifdef NO_LOCKING
488   si->locking_disabled_p = True;
489   si->nolock_reason = "not compiled with locking support";
490 #else /* !NO_LOCKING */
491
492   /* Finish initializing locking, now that we're out of privileged code. */
493   if (! lock_init (*argc, argv, si->prefs.verbose_p))
494     {
495       si->locking_disabled_p = True;
496       si->nolock_reason = "error getting password";
497     }
498
499   /* If locking is currently enabled, but the environment indicates that
500      we have been launched as GDM's "Background" program, then disable
501      locking just in case.
502    */
503   if (!si->locking_disabled_p && getenv ("RUNNING_UNDER_GDM"))
504     {
505       si->locking_disabled_p = True;
506       si->nolock_reason = "running under GDM";
507     }
508
509   /* If the server is XDarwin (MacOS X) then disable locking.
510      (X grabs only affect X programs, so you can use Command-Tab
511      to bring any other Mac program to the front, e.g., Terminal.)
512    */
513   if (!si->locking_disabled_p)
514     {
515       int op = 0, event = 0, error = 0;
516       Bool macos_p = False;
517
518 #ifdef __APPLE__
519       /* Disable locking if *running* on Apple hardware, since we have no
520          reliable way to determine whether the server is running on MacOS.
521          Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
522          but I'm not really sure about that.
523        */
524       macos_p = True;
525 #endif
526
527       if (!macos_p)
528         /* This extension exists on the Apple X11 server, but not
529            on earlier versions of the XDarwin server. */
530         macos_p = XQueryExtension (si->dpy, "Apple-DRI", &op, &event, &error);
531
532       if (macos_p)
533         {
534           si->locking_disabled_p = True;
535           si->nolock_reason = "Cannot lock securely on MacOS X";
536         }
537     }
538
539 #endif /* NO_LOCKING */
540 }
541
542
543 /* Open the connection to the X server, and intern our Atoms.
544  */
545 static Widget
546 connect_to_server (saver_info *si, int *argc, char **argv)
547 {
548   Widget toplevel_shell;
549
550 #ifdef HAVE_PUTENV
551   char *d = getenv ("DISPLAY");
552   if (!d || !*d)
553     {
554       char *ndpy = strdup("DISPLAY=:0.0");
555       /* if (si->prefs.verbose_p) */      /* sigh, too early to test this... */
556         fprintf (stderr,
557                  "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
558                  blurb(), ndpy+8);
559       if (putenv (ndpy))
560         abort ();
561       /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
562          glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
563          do not.  So we must leak it (and/or the previous setting). Yay.
564        */
565     }
566 #endif /* HAVE_PUTENV */
567
568   XSetErrorHandler (saver_ehandler);
569
570   XtAppSetErrorMsgHandler (si->app, startup_ehandler);
571   toplevel_shell = XtAppInitialize (&si->app, progclass,
572                                     options, XtNumber (options),
573                                     argc, argv, defaults, 0, 0);
574   XtAppSetErrorMsgHandler (si->app, 0);
575
576   si->dpy = XtDisplay (toplevel_shell);
577   si->prefs.db = XtDatabase (si->dpy);
578   XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
579
580   if(strlen(progname) > 100)    /* keep it short. */
581     progname [99] = 0;
582
583   db = si->prefs.db;    /* resources.c needs this */
584
585   XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
586   XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
587   XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
588   XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
589   XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
590   XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
591                                          False);
592   XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
593   XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False);
594   XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False);
595   XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
596   XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
597   XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
598   XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
599   XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
600   XA_PREV = XInternAtom (si->dpy, "PREV", False);
601   XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
602   XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
603   XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
604   XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
605   XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
606   XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
607   XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
608   XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
609
610   return toplevel_shell;
611 }
612
613
614 /* Handle the command-line arguments that were not handled for us by Xt.
615    Issue an error message and exit if there are unknown options.
616  */
617 static void
618 process_command_line (saver_info *si, int *argc, char **argv)
619 {
620   int i;
621   for (i = 1; i < *argc; i++)
622     {
623       if (!strcmp (argv[i], "-debug"))
624         /* no resource for this one, out of paranoia. */
625         si->prefs.debug_p = True;
626
627       else if (!strcmp (argv[i], "-h") ||
628                !strcmp (argv[i], "-help") ||
629                !strcmp (argv[i], "--help"))
630         do_help (si);
631
632       else
633         {
634           const char *s = argv[i];
635           fprintf (stderr, "%s: unknown option \"%s\".  Try \"-help\".\n",
636                    blurb(), s);
637
638           if (s[0] == '-' && s[1] == '-') s++;
639           if (!strcmp (s, "-activate") ||
640               !strcmp (s, "-deactivate") ||
641               !strcmp (s, "-cycle") ||
642               !strcmp (s, "-next") ||
643               !strcmp (s, "-prev") ||
644               !strcmp (s, "-exit") ||
645               !strcmp (s, "-restart") ||
646               !strcmp (s, "-demo") ||
647               !strcmp (s, "-prefs") ||
648               !strcmp (s, "-preferences") ||
649               !strcmp (s, "-lock") ||
650               !strcmp (s, "-version") ||
651               !strcmp (s, "-time"))
652             {
653
654               if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
655                 fprintf (stderr, "\n\
656     Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
657               else
658                 fprintf (stderr, "\n\
659     However, `%s' is an option to the `xscreensaver-command' program.\n", s);
660
661               fprintf (stderr, "\
662     The `xscreensaver' program is a daemon that runs in the background.\n\
663     You control a running xscreensaver process by sending it messages\n\
664     with `xscreensaver-demo' or `xscreensaver-command'.\n\
665 .   See the man pages for details, or check the web page:\n\
666     http://www.jwz.org/xscreensaver/\n\n");
667             }
668
669           exit (1);
670         }
671     }
672 }
673
674
675 /* Print out the xscreensaver banner to the tty if applicable;
676    Issue any other warnings that are called for at this point.
677  */
678 static void
679 print_banner (saver_info *si)
680 {
681   saver_preferences *p = &si->prefs;
682
683   /* This resource gets set some time before the others, so that we know
684      whether to print the banner (and so that the banner gets printed before
685      any resource-database-related error messages.)
686    */
687   p->verbose_p = (p->debug_p || 
688                   get_boolean_resource (si->dpy, "verbose", "Boolean"));
689
690   /* Ditto, for the locking_disabled_p message. */
691   p->lock_p = get_boolean_resource (si->dpy, "lock", "Boolean");
692
693   if (p->verbose_p)
694     fprintf (stderr,
695              "%s %s, copyright (c) 1991-2008 "
696              "by Jamie Zawinski <jwz@jwz.org>.\n",
697              progname, si->version);
698
699   if (p->debug_p)
700     fprintf (stderr, "\n"
701              "%s: Warning: running in DEBUG MODE.  Be afraid.\n"
702              "\n"
703              "\tNote that in debug mode, the xscreensaver window will only\n"
704              "\tcover the left half of the screen.  (The idea is that you\n"
705              "\tcan still see debugging output in a shell, if you position\n"
706              "\tit on the right side of the screen.)\n"
707              "\n"
708              "\tDebug mode is NOT SECURE.  Do not run with -debug in\n"
709              "\tuntrusted environments.\n"
710              "\n",
711              blurb());
712
713   if (p->verbose_p)
714     {
715       if (!si->uid_message || !*si->uid_message)
716         describe_uids (si, stderr);
717       else
718         {
719           if (si->orig_uid && *si->orig_uid)
720             fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
721                      blurb(), si->orig_uid);
722           fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
723         }
724
725       fprintf (stderr, "%s: in process %lu.\n", blurb(),
726                (unsigned long) getpid());
727     }
728 }
729
730 static void
731 print_lock_failure_banner (saver_info *si)
732 {
733   saver_preferences *p = &si->prefs;
734
735   /* If locking was not able to be initalized for some reason, explain why.
736      (This has to be done after we've read the lock_p resource.)
737    */
738   if (si->locking_disabled_p)
739     {
740       p->lock_p = False;
741       fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
742                si->nolock_reason);
743       if (strstr (si->nolock_reason, "passw"))
744         fprintf (stderr, "%s: does xscreensaver need to be setuid?  "
745                  "consult the manual.\n", blurb());
746       else if (strstr (si->nolock_reason, "running as "))
747         fprintf (stderr, 
748                  "%s: locking only works when xscreensaver is launched\n"
749                  "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
750                  "\t See the manual for details.\n",
751                  blurb());
752     }
753
754 }
755
756
757 /* called from screens.c so that all the Xt crud is here. */
758 void
759 initialize_screen_root_widget (saver_screen_info *ssi)
760 {
761   saver_info *si = ssi->global;
762   if (ssi->toplevel_shell)
763     XtDestroyWidget (ssi->toplevel_shell);
764   ssi->toplevel_shell =
765     XtVaAppCreateShell (progname, progclass, 
766                         applicationShellWidgetClass,
767                         si->dpy,
768                         XtNscreen, ssi->screen,
769                         XtNvisual, ssi->current_visual,
770                         XtNdepth,  visual_depth (ssi->screen,
771                                                  ssi->current_visual),
772                         NULL);
773 }
774
775
776 /* Examine all of the display's screens, and populate the `saver_screen_info'
777    structures.  Make sure this is called after hack_environment() sets $PATH.
778  */
779 static void
780 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
781 {
782   int i;
783
784   update_screen_layout (si);
785
786   /* Check to see whether fading is ever possible -- if any of the
787      screens on the display has a PseudoColor visual, then fading can
788      work (on at least some screens.)  If no screen has a PseudoColor
789      visual, then don't bother ever trying to fade, because it will
790      just cause a delay without causing any visible effect.
791    */
792   for (i = 0; i < si->nscreens; i++)
793     {
794       saver_screen_info *ssi = &si->screens[i];
795       if (has_writable_cells (ssi->screen, ssi->current_visual) ||
796           get_visual (ssi->screen, "PseudoColor", True, False) ||
797           get_visual (ssi->screen, "GrayScale", True, False))
798         {
799           si->fading_possible_p = True;
800           break;
801         }
802     }
803
804 #ifdef HAVE_XF86VMODE_GAMMA
805   si->fading_possible_p = True;  /* if we can gamma fade, go for it */
806 #endif
807 }
808
809
810 /* If any server extensions have been requested, try and initialize them.
811    Issue warnings if requests can't be honored.
812  */
813 static void
814 initialize_server_extensions (saver_info *si)
815 {
816   saver_preferences *p = &si->prefs;
817
818   Bool server_has_xidle_extension_p = False;
819   Bool server_has_sgi_saver_extension_p = False;
820   Bool server_has_mit_saver_extension_p = False;
821   Bool system_has_proc_interrupts_p = False;
822   const char *piwhy = 0;
823
824   si->using_xidle_extension = p->use_xidle_extension;
825   si->using_sgi_saver_extension = p->use_sgi_saver_extension;
826   si->using_mit_saver_extension = p->use_mit_saver_extension;
827   si->using_proc_interrupts = p->use_proc_interrupts;
828
829 #ifdef HAVE_XIDLE_EXTENSION
830   server_has_xidle_extension_p = query_xidle_extension (si);
831 #endif
832 #ifdef HAVE_SGI_SAVER_EXTENSION
833   server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
834 #endif
835 #ifdef HAVE_MIT_SAVER_EXTENSION
836   server_has_mit_saver_extension_p = query_mit_saver_extension (si);
837 #endif
838 #ifdef HAVE_PROC_INTERRUPTS
839   system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
840 #endif
841
842   if (!server_has_xidle_extension_p)
843     si->using_xidle_extension = False;
844   else if (p->verbose_p)
845     {
846       if (si->using_xidle_extension)
847         fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
848       else
849         fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
850     }
851
852   if (!server_has_sgi_saver_extension_p)
853     si->using_sgi_saver_extension = False;
854   else if (p->verbose_p)
855     {
856       if (si->using_sgi_saver_extension)
857         fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
858       else
859         fprintf (stderr,
860                  "%s: not using server's SGI SCREEN_SAVER extension.\n",
861                  blurb());
862     }
863
864   if (!server_has_mit_saver_extension_p)
865     si->using_mit_saver_extension = False;
866   else if (p->verbose_p)
867     {
868       if (si->using_mit_saver_extension)
869         fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
870                  blurb());
871       else
872         fprintf (stderr,
873                  "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
874                  blurb());
875     }
876
877 #ifdef HAVE_RANDR
878   query_randr_extension (si);
879 #endif
880
881   if (!system_has_proc_interrupts_p)
882     {
883       si->using_proc_interrupts = False;
884       if (p->verbose_p && piwhy)
885         fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
886                  piwhy);
887     }
888   else if (p->verbose_p)
889     {
890       if (si->using_proc_interrupts)
891         fprintf (stderr,
892                  "%s: consulting /proc/interrupts for keyboard activity.\n",
893                  blurb());
894       else
895         fprintf (stderr,
896                 "%s: not consulting /proc/interrupts for keyboard activity.\n",
897                  blurb());
898     }
899 }
900
901
902 #ifdef DEBUG_MULTISCREEN
903 static void
904 debug_multiscreen_timer (XtPointer closure, XtIntervalId *id)
905 {
906   saver_info *si = (saver_info *) closure;
907   saver_preferences *p = &si->prefs;
908   if (update_screen_layout (si))
909     {
910       if (p->verbose_p)
911         {
912           fprintf (stderr, "%s: new layout:\n", blurb());
913           describe_monitor_layout (si);
914         }
915       resize_screensaver_window (si);
916     }
917   XtAppAddTimeOut (si->app, 1000*4, debug_multiscreen_timer, (XtPointer) si);
918 }
919 #endif /* DEBUG_MULTISCREEN */
920
921
922 /* For the case where we aren't using an server extensions, select user events
923    on all the existing windows, and launch timers to select events on
924    newly-created windows as well.
925
926    If a server extension is being used, this does nothing.
927  */
928 static void
929 select_events (saver_info *si)
930 {
931   saver_preferences *p = &si->prefs;
932   int i;
933
934   if (si->using_xidle_extension ||
935       si->using_mit_saver_extension ||
936       si->using_sgi_saver_extension)
937     return;
938
939   if (p->initial_delay)
940     {
941       if (p->verbose_p)
942         {
943           fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
944                    (int) p->initial_delay/1000,
945                    (p->initial_delay == 1000 ? "" : "s"));
946           fflush (stderr);
947           fflush (stdout);
948         }
949       usleep (p->initial_delay);
950       if (p->verbose_p)
951         fprintf (stderr, " done.\n");
952     }
953
954   if (p->verbose_p)
955     {
956       fprintf (stderr, "%s: selecting events on extant windows...", blurb());
957       fflush (stderr);
958       fflush (stdout);
959     }
960
961   /* Select events on the root windows of every screen.  This also selects
962      for window creation events, so that new subwindows will be noticed.
963    */
964   for (i = 0; i < si->nscreens; i++)
965     {
966       saver_screen_info *ssi = &si->screens[i];
967       if (ssi->real_screen_p)
968         start_notice_events_timer (si,
969            RootWindowOfScreen (si->screens[i].screen), False);
970     }
971
972   if (p->verbose_p)
973     fprintf (stderr, " done.\n");
974
975 # ifdef DEBUG_MULTISCREEN
976   if (p->debug_p) debug_multiscreen_timer ((XtPointer) si, 0);
977 # endif
978 }
979
980
981 void
982 maybe_reload_init_file (saver_info *si)
983 {
984   saver_preferences *p = &si->prefs;
985   if (init_file_changed_p (p))
986     {
987       if (p->verbose_p)
988         fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
989                  blurb(), init_file_name());
990
991       load_init_file (si->dpy, p);
992
993       /* If a server extension is in use, and p->timeout has changed,
994          we need to inform the server of the new timeout. */
995       disable_builtin_screensaver (si, False);
996
997       /* If the DPMS settings in the init file have changed,
998          change the settings on the server to match. */
999       sync_server_dpms_settings (si->dpy,
1000                                  (p->dpms_enabled_p  &&
1001                                   p->mode != DONT_BLANK),
1002                                  p->dpms_standby / 1000,
1003                                  p->dpms_suspend / 1000,
1004                                  p->dpms_off / 1000,
1005                                  False);
1006     }
1007 }
1008
1009
1010 /* Loop forever:
1011
1012        - wait until the user is idle;
1013        - blank the screen;
1014        - wait until the user is active;
1015        - unblank the screen;
1016        - repeat.
1017
1018  */
1019 static void
1020 main_loop (saver_info *si)
1021 {
1022   saver_preferences *p = &si->prefs;
1023   Bool ok_to_unblank;
1024   int i;
1025
1026   while (1)
1027     {
1028       Bool was_locked = False;
1029
1030       if (p->verbose_p)
1031         fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1032
1033       check_for_leaks ("unblanked A");
1034       sleep_until_idle (si, True);
1035       check_for_leaks ("unblanked B");
1036
1037       if (p->verbose_p)
1038         {
1039           if (si->demoing_p)
1040             fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
1041                      si->selection_mode, timestring());
1042           else
1043             fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
1044                      timestring());
1045         }
1046
1047       maybe_reload_init_file (si);
1048
1049       if (p->mode == DONT_BLANK)
1050         {
1051           if (p->verbose_p)
1052             fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
1053                      blurb(), timestring());
1054
1055           /* Go around the loop and wait for the next bout of idleness,
1056              or for the init file to change, or for a remote command to
1057              come in, or something.
1058
1059              But, if locked_p is true, go ahead.  This can only happen
1060              if we're in "disabled" mode but a "lock" clientmessage came
1061              in: in that case, we should go ahead and blank/lock the screen.
1062            */
1063           if (!si->locked_p)
1064             continue;
1065         }
1066
1067       /* Since we're about to blank the screen, kill the de-race timer,
1068          if any.  It might still be running if we have unblanked and then
1069          re-blanked in a short period (e.g., when using the "next" button
1070          in xscreensaver-demo.)
1071        */
1072       if (si->de_race_id)
1073         {
1074           if (p->verbose_p)
1075             fprintf (stderr, "%s: stopping de-race timer (%d remaining.)\n",
1076                      blurb(), si->de_race_ticks);
1077           XtRemoveTimeOut (si->de_race_id);
1078           si->de_race_id = 0;
1079         }
1080
1081
1082       /* Now, try to blank.
1083        */
1084
1085       if (! blank_screen (si))
1086         {
1087           /* We were unable to grab either the keyboard or mouse.
1088              This means we did not (and must not) blank the screen.
1089              If we were to blank the screen while some other program
1090              is holding both the mouse and keyboard grabbed, then
1091              we would never be able to un-blank it!  We would never
1092              see any events, and the display would be wedged.
1093
1094              So, just go around the loop again and wait for the
1095              next bout of idleness.  (If the user remains idle, we
1096              will next try to blank the screen again in no more than
1097              60 seconds.)
1098           */
1099           Time retry = 60 * 1000;
1100           if (p->timeout < retry)
1101             retry = p->timeout;
1102
1103           if (p->debug_p)
1104             {
1105               fprintf (stderr,
1106                   "%s: DEBUG MODE: unable to grab -- BLANKING ANYWAY.\n",
1107                        blurb());
1108             }
1109           else
1110             {
1111               fprintf (stderr,
1112                   "%s: unable to grab keyboard or mouse!  Blanking aborted.\n",
1113                        blurb());
1114
1115               schedule_wakeup_event (si, retry, p->debug_p);
1116               continue;
1117             }
1118         }
1119
1120       for (i = 0; i < si->nscreens; i++)
1121         kill_screenhack (&si->screens[i]);
1122
1123       raise_window (si, True, True, False);
1124       if (si->throttled_p)
1125         fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
1126       else
1127         for (i = 0; i < si->nscreens; i++)
1128           spawn_screenhack (&si->screens[i]);
1129
1130       /* Don't start the cycle timer in demo mode. */
1131       if (!si->demoing_p && p->cycle)
1132         si->cycle_id = XtAppAddTimeOut (si->app,
1133                                         (si->selection_mode
1134                                          /* see comment in cycle_timer() */
1135                                          ? 1000 * 60 * 60
1136                                          : p->cycle),
1137                                         cycle_timer,
1138                                         (XtPointer) si);
1139
1140
1141 #ifndef NO_LOCKING
1142       /* Maybe start locking the screen.
1143        */
1144       {
1145         Time lock_timeout = p->lock_timeout;
1146
1147         if (si->emergency_lock_p && p->lock_p && lock_timeout)
1148           {
1149             int secs = p->lock_timeout / 1000;
1150             if (p->verbose_p)
1151               fprintf (stderr,
1152                      "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1153                        blurb(),
1154                        (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1155             lock_timeout = 0;
1156           }
1157
1158         si->emergency_lock_p = False;
1159
1160         if (!si->demoing_p &&           /* if not going into demo mode */
1161             p->lock_p &&                /* and locking is enabled */
1162             !si->locking_disabled_p &&  /* and locking is possible */
1163             lock_timeout == 0)          /* and locking is not timer-deferred */
1164           set_locked_p (si, True);      /* then lock right now. */
1165
1166         /* locked_p might be true already because of the above, or because of
1167            the LOCK ClientMessage.  But if not, and if we're supposed to lock
1168            after some time, set up a timer to do so.
1169         */
1170         if (p->lock_p &&
1171             !si->locked_p &&
1172             lock_timeout > 0)
1173           si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1174                                          activate_lock_timer,
1175                                          (XtPointer) si);
1176       }
1177 #endif /* !NO_LOCKING */
1178
1179
1180       ok_to_unblank = True;
1181       do {
1182
1183         check_for_leaks ("blanked A");
1184         sleep_until_idle (si, False);           /* until not idle */
1185         check_for_leaks ("blanked B");
1186
1187         maybe_reload_init_file (si);
1188
1189 #ifndef NO_LOCKING
1190         /* Maybe unlock the screen.
1191          */
1192         if (si->locked_p)
1193           {
1194             saver_screen_info *ssi = si->default_screen;
1195             if (si->locking_disabled_p) abort ();
1196
1197             was_locked = True;
1198             si->dbox_up_p = True;
1199             for (i = 0; i < si->nscreens; i++)
1200               suspend_screenhack (&si->screens[i], True);         /* suspend */
1201             XUndefineCursor (si->dpy, ssi->screensaver_window);
1202
1203             ok_to_unblank = unlock_p (si);
1204
1205             si->dbox_up_p = False;
1206             XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1207             for (i = 0; i < si->nscreens; i++)
1208               suspend_screenhack (&si->screens[i], False);         /* resume */
1209
1210             if (!ok_to_unblank &&
1211                 !screenhack_running_p (si))
1212               {
1213                 /* If the lock dialog has been dismissed and we're not about to
1214                    unlock the screen, and there is currently no hack running,
1215                    then launch one.  (There might be no hack running if DPMS
1216                    had kicked in.  But DPMS is off now, so bring back the hack)
1217                  */
1218                 if (si->cycle_id)
1219                   XtRemoveTimeOut (si->cycle_id);
1220                 si->cycle_id = 0;
1221                 cycle_timer ((XtPointer) si, 0);
1222               }
1223           }
1224 #endif /* !NO_LOCKING */
1225
1226         } while (!ok_to_unblank);
1227
1228
1229       if (p->verbose_p)
1230         fprintf (stderr, "%s: unblanking screen at %s.\n",
1231                  blurb(), timestring ());
1232
1233       /* Kill before unblanking, to stop drawing as soon as possible. */
1234       for (i = 0; i < si->nscreens; i++)
1235         kill_screenhack (&si->screens[i]);
1236       unblank_screen (si);
1237
1238       set_locked_p (si, False);
1239       si->emergency_lock_p = False;
1240       si->demoing_p = 0;
1241       si->selection_mode = 0;
1242
1243       /* If we're throttled, and the user has explicitly unlocked the screen,
1244          then unthrottle.  If we weren't locked, then don't unthrottle
1245          automatically, because someone might have just bumped the desk... */
1246       if (was_locked)
1247         {
1248           if (si->throttled_p && p->verbose_p)
1249             fprintf (stderr, "%s: unthrottled.\n", blurb());
1250           si->throttled_p = False;
1251         }
1252
1253       if (si->cycle_id)
1254         {
1255           XtRemoveTimeOut (si->cycle_id);
1256           si->cycle_id = 0;
1257         }
1258
1259       if (si->lock_id)
1260         {
1261           XtRemoveTimeOut (si->lock_id);
1262           si->lock_id = 0;
1263         }
1264
1265       /* Since we're unblanked now, break race conditions and make
1266          sure we stay that way (see comment in timers.c.) */
1267       if (! si->de_race_id)
1268         de_race_timer ((XtPointer) si, 0);
1269     }
1270 }
1271
1272 static void analyze_display (saver_info *si);
1273 static void fix_fds (void);
1274
1275 int
1276 main (int argc, char **argv)
1277 {
1278   Widget shell;
1279   saver_info the_si;
1280   saver_info *si = &the_si;
1281   saver_preferences *p = &si->prefs;
1282   struct passwd *spasswd;
1283   int i;
1284
1285   memset(si, 0, sizeof(*si));
1286   global_si_kludge = si;        /* I hate C so much... */
1287
1288   fix_fds();
1289
1290 # undef ya_rand_init
1291   ya_rand_init (0);
1292
1293   save_argv (argc, argv);
1294   set_version_string (si, &argc, argv);
1295   privileged_initialization (si, &argc, argv);
1296   hack_environment (si);
1297
1298   spasswd = getpwuid(getuid());
1299   if (!spasswd)
1300     {
1301       fprintf(stderr, "Could not figure out who the current user is!\n");
1302       return 1;
1303     }
1304
1305   si->user = strdup(spasswd->pw_name ? spasswd->pw_name : "(unknown)");
1306
1307 # ifndef NO_LOCKING
1308   si->unlock_cb = gui_auth_conv;
1309   si->auth_finished_cb = auth_finished_cb;
1310 # endif /* !NO_LOCKING */
1311
1312   shell = connect_to_server (si, &argc, argv);
1313   process_command_line (si, &argc, argv);
1314   stderr_log_file (si);
1315   print_banner (si);
1316
1317   load_init_file(si->dpy, p); /* must be before initialize_per_screen_info() */
1318   blurb_timestamp_p = p->timestamp_p;  /* kludge */
1319   initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1320
1321   /* We can only issue this warning now. */
1322   if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1323     fprintf (stderr,
1324              "%s: there are no PseudoColor or GrayScale visuals.\n"
1325              "%s: ignoring the request for fading/unfading.\n",
1326              blurb(), blurb());
1327
1328   for (i = 0; i < si->nscreens; i++)
1329     {
1330       saver_screen_info *ssi = &si->screens[i];
1331       if (ssi->real_screen_p)
1332         if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1333           exit (1);
1334     }
1335
1336   lock_initialization (si, &argc, argv);
1337   print_lock_failure_banner (si);
1338
1339   if (p->xsync_p) XSynchronize (si->dpy, True);
1340
1341   if (p->verbose_p) analyze_display (si);
1342   initialize_server_extensions (si);
1343
1344   si->blank_time = time ((time_t) 0); /* must be before ..._window */
1345   initialize_screensaver_window (si);
1346
1347   select_events (si);
1348   init_sigchld ();
1349
1350   disable_builtin_screensaver (si, True);
1351   sync_server_dpms_settings (si->dpy,
1352                              (p->dpms_enabled_p  &&
1353                               p->mode != DONT_BLANK),
1354                              p->dpms_standby / 1000,
1355                              p->dpms_suspend / 1000,
1356                              p->dpms_off / 1000,
1357                              False);
1358
1359   initialize_stderr (si);
1360   handle_signals (si);
1361
1362   make_splash_dialog (si);
1363
1364   main_loop (si);               /* doesn't return */
1365   return 0;
1366 }
1367
1368 static void
1369 fix_fds (void)
1370 {
1371   /* Bad Things Happen if stdin, stdout, and stderr have been closed
1372      (as by the `sh incantation "xscreensaver >&- 2>&-").  When you do
1373      that, the X connection gets allocated to one of these fds, and
1374      then some random library writes to stderr, and random bits get
1375      stuffed down the X pipe, causing "Xlib: sequence lost" errors.
1376      So, we cause the first three file descriptors to be open to
1377      /dev/null if they aren't open to something else already.  This
1378      must be done before any other files are opened (or the closing
1379      of that other file will again free up one of the "magic" first
1380      three FDs.)
1381
1382      We do this by opening /dev/null three times, and then closing
1383      those fds, *unless* any of them got allocated as #0, #1, or #2,
1384      in which case we leave them open.  Gag.
1385
1386      Really, this crap is technically required of *every* X program,
1387      if you want it to be robust in the face of "2>&-".
1388    */
1389   int fd0 = open ("/dev/null", O_RDWR);
1390   int fd1 = open ("/dev/null", O_RDWR);
1391   int fd2 = open ("/dev/null", O_RDWR);
1392   if (fd0 > 2) close (fd0);
1393   if (fd1 > 2) close (fd1);
1394   if (fd2 > 2) close (fd2);
1395 }
1396
1397
1398 \f
1399 /* Processing ClientMessage events.
1400  */
1401
1402
1403 static Bool error_handler_hit_p = False;
1404
1405 static int
1406 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1407 {
1408   error_handler_hit_p = True;
1409   return 0;
1410 }
1411
1412 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1413    them.  We only look up the atom names for printing warning messages,
1414    so don't bomb out when it happens...
1415  */
1416 static char *
1417 XGetAtomName_safe (Display *dpy, Atom atom)
1418 {
1419   char *result;
1420   XErrorHandler old_handler;
1421   if (!atom) return 0;
1422
1423   XSync (dpy, False);
1424   error_handler_hit_p = False;
1425   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1426   result = XGetAtomName (dpy, atom);
1427   XSync (dpy, False);
1428   XSetErrorHandler (old_handler);
1429   XSync (dpy, False);
1430   if (error_handler_hit_p) result = 0;
1431
1432   if (result)
1433     return result;
1434   else
1435     {
1436       char buf[100];
1437       sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
1438       return strdup (buf);
1439     }
1440 }
1441
1442
1443 static void
1444 clientmessage_response (saver_info *si, Window w, Bool error,
1445                         const char *stderr_msg,
1446                         const char *protocol_msg)
1447 {
1448   char *proto;
1449   int L;
1450   saver_preferences *p = &si->prefs;
1451   XErrorHandler old_handler;
1452
1453   if (error || p->verbose_p)
1454     fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1455
1456   L = strlen(protocol_msg);
1457   proto = (char *) malloc (L + 2);
1458   proto[0] = (error ? '-' : '+');
1459   strcpy (proto+1, protocol_msg);
1460   L++;
1461
1462   /* Ignore all X errors while sending a response to a ClientMessage.
1463      Pretty much the only way we could get an error here is if the
1464      window we're trying to send the reply on has been deleted, in
1465      which case, the sender of the ClientMessage won't see our response
1466      anyway.
1467    */
1468   XSync (si->dpy, False);
1469   error_handler_hit_p = False;
1470   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1471
1472   XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1473                    PropModeReplace, (unsigned char *) proto, L);
1474
1475   XSync (si->dpy, False);
1476   XSetErrorHandler (old_handler);
1477   XSync (si->dpy, False);
1478
1479   free (proto);
1480 }
1481
1482
1483 static void
1484 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1485 {
1486 #if 0  /* Oh, fuck it.  GNOME likes to spew random ClientMessages at us
1487           all the time.  This is presumably indicative of an error in
1488           the sender of that ClientMessage: if we're getting it and 
1489           ignoring it, then it's not reaching the intended recipient.
1490           But people complain to me about this all the time ("waaah!
1491           xscreensaver is printing to it's stderr and that gets my
1492           panties all in a bunch!")  And I'm sick of hearing about it.
1493           So we'll just ignore these messages and let GNOME go right
1494           ahead and continue to stumble along in its malfunction.
1495         */
1496
1497   saver_preferences *p = &si->prefs;
1498   char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1499   Window w = event->xclient.window;
1500   char wdesc[255];
1501   int screen = 0;
1502   Bool root_p = False;
1503
1504   *wdesc = 0;
1505   for (screen = 0; screen < si->nscreens; screen++)
1506     if (w == si->screens[screen].screensaver_window)
1507       {
1508         strcpy (wdesc, "xscreensaver");
1509         break;
1510       }
1511     else if (w == RootWindow (si->dpy, screen))
1512       {
1513         strcpy (wdesc, "root");
1514         root_p = True;
1515         break;
1516       }
1517
1518   /* If this ClientMessage was sent to the real root window instead of to the
1519      xscreensaver window, then it might be intended for someone else who is
1520      listening on the root window (e.g., the window manager).  So only print
1521      the warning if: we are in debug mode; or if the bogus message was
1522      actually sent to one of the xscreensaver-created windows.
1523    */
1524   if (root_p && !p->debug_p)
1525     return;
1526
1527   if (!*wdesc)
1528     {
1529       XErrorHandler old_handler;
1530       XClassHint hint;
1531       XWindowAttributes xgwa;
1532       memset (&hint, 0, sizeof(hint));
1533       memset (&xgwa, 0, sizeof(xgwa));
1534
1535       XSync (si->dpy, False);
1536       old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1537       XGetClassHint (si->dpy, w, &hint);
1538       XGetWindowAttributes (si->dpy, w, &xgwa);
1539       XSync (si->dpy, False);
1540       XSetErrorHandler (old_handler);
1541       XSync (si->dpy, False);
1542
1543       screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1544
1545       sprintf (wdesc, "%.20s / %.20s",
1546                (hint.res_name  ? hint.res_name  : "(null)"),
1547                (hint.res_class ? hint.res_class : "(null)"));
1548       if (hint.res_name)  XFree (hint.res_name);
1549       if (hint.res_class) XFree (hint.res_class);
1550     }
1551
1552   fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1553            blurb(), screen, (str ? str : "(null)"));
1554   fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1555            blurb(), screen, (unsigned long) w, wdesc);
1556   if (str) XFree (str);
1557
1558 #endif /* 0 */
1559 }
1560
1561
1562 Bool
1563 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1564 {
1565   saver_preferences *p = &si->prefs;
1566   Atom type = 0;
1567   Window window = event->xclient.window;
1568
1569   /* Preferences might affect our handling of client messages. */
1570   maybe_reload_init_file (si);
1571
1572   if (event->xclient.message_type != XA_SCREENSAVER ||
1573       event->xclient.format != 32)
1574     {
1575       bogus_clientmessage_warning (si, event);
1576       return False;
1577     }
1578
1579   type = event->xclient.data.l[0];
1580   if (type == XA_ACTIVATE)
1581     {
1582       if (until_idle_p)
1583         {
1584           if (p->mode == DONT_BLANK)
1585             {
1586               clientmessage_response(si, window, True,
1587                          "ACTIVATE ClientMessage received in DONT_BLANK mode.",
1588                                      "screen blanking is currently disabled.");
1589               return False;
1590             }
1591
1592           clientmessage_response(si, window, False,
1593                                  "ACTIVATE ClientMessage received.",
1594                                  "activating.");
1595           si->selection_mode = 0;
1596           si->demoing_p = False;
1597
1598           if (si->throttled_p && p->verbose_p)
1599             fprintf (stderr, "%s: unthrottled.\n", blurb());
1600           si->throttled_p = False;
1601
1602           if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1603             {
1604               XForceScreenSaver (si->dpy, ScreenSaverActive);
1605               return False;
1606             }
1607           else
1608             {
1609               return True;
1610             }
1611         }
1612       clientmessage_response(si, window, True,
1613                        "ClientMessage ACTIVATE received while already active.",
1614                              "already active.");
1615     }
1616   else if (type == XA_DEACTIVATE)
1617     {
1618       if (! until_idle_p)
1619         {
1620           if (si->throttled_p && p->verbose_p)
1621             fprintf (stderr, "%s: unthrottled.\n", blurb());
1622           si->throttled_p = False;
1623
1624           clientmessage_response(si, window, False,
1625                                  "DEACTIVATE ClientMessage received.",
1626                                  "deactivating.");
1627           if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1628             {
1629               XForceScreenSaver (si->dpy, ScreenSaverReset);
1630               return False;
1631             }
1632           else
1633             {
1634               return True;
1635             }
1636         }
1637       clientmessage_response(si, window, False,
1638      "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
1639                              "not active: idle timer reset.");
1640       reset_timers (si);
1641     }
1642   else if (type == XA_CYCLE)
1643     {
1644       if (! until_idle_p)
1645         {
1646           clientmessage_response(si, window, False,
1647                                  "CYCLE ClientMessage received.",
1648                                  "cycling.");
1649           si->selection_mode = 0;       /* 0 means randomize when its time. */
1650           si->demoing_p = False;
1651
1652           if (si->throttled_p && p->verbose_p)
1653             fprintf (stderr, "%s: unthrottled.\n", blurb());
1654           si->throttled_p = False;
1655
1656           if (si->cycle_id)
1657             XtRemoveTimeOut (si->cycle_id);
1658           si->cycle_id = 0;
1659           cycle_timer ((XtPointer) si, 0);
1660           return False;
1661         }
1662       clientmessage_response(si, window, True,
1663                              "ClientMessage CYCLE received while inactive.",
1664                              "not active.");
1665     }
1666   else if (type == XA_NEXT || type == XA_PREV)
1667     {
1668       clientmessage_response(si, window, False,
1669                              (type == XA_NEXT
1670                               ? "NEXT ClientMessage received."
1671                               : "PREV ClientMessage received."),
1672                              "cycling.");
1673       si->selection_mode = (type == XA_NEXT ? -1 : -2);
1674       si->demoing_p = False;
1675
1676       if (si->throttled_p && p->verbose_p)
1677         fprintf (stderr, "%s: unthrottled.\n", blurb());
1678       si->throttled_p = False;
1679
1680       if (! until_idle_p)
1681         {
1682           if (si->cycle_id)
1683             XtRemoveTimeOut (si->cycle_id);
1684           si->cycle_id = 0;
1685           cycle_timer ((XtPointer) si, 0);
1686         }
1687       else
1688         return True;
1689     }
1690   else if (type == XA_SELECT)
1691     {
1692       char buf [255];
1693       char buf2 [255];
1694       long which = event->xclient.data.l[1];
1695
1696       if (p->mode == DONT_BLANK)
1697         {
1698           clientmessage_response(si, window, True,
1699                            "SELECT ClientMessage received in DONT_BLANK mode.",
1700                                  "screen blanking is currently disabled.");
1701           return False;
1702         }
1703
1704       sprintf (buf, "SELECT %ld ClientMessage received.", which);
1705       sprintf (buf2, "activating (%ld).", which);
1706       clientmessage_response (si, window, False, buf, buf2);
1707
1708       if (which < 0) which = 0;         /* 0 == "random" */
1709       si->selection_mode = which;
1710       si->demoing_p = False;
1711
1712       if (si->throttled_p && p->verbose_p)
1713         fprintf (stderr, "%s: unthrottled.\n", blurb());
1714       si->throttled_p = False;
1715
1716       if (! until_idle_p)
1717         {
1718           if (si->cycle_id)
1719             XtRemoveTimeOut (si->cycle_id);
1720           si->cycle_id = 0;
1721           cycle_timer ((XtPointer) si, 0);
1722         }
1723       else
1724         return True;
1725     }
1726   else if (type == XA_EXIT)
1727     {
1728       /* Ignore EXIT message if the screen is locked. */
1729       if (until_idle_p || !si->locked_p)
1730         {
1731           clientmessage_response (si, window, False,
1732                                   "EXIT ClientMessage received.",
1733                                   "exiting.");
1734           if (! until_idle_p)
1735             {
1736               int i;
1737               for (i = 0; i < si->nscreens; i++)
1738                 kill_screenhack (&si->screens[i]);
1739               unblank_screen (si);
1740               XSync (si->dpy, False);
1741             }
1742           saver_exit (si, 0, 0);
1743         }
1744       else
1745         clientmessage_response (si, window, True,
1746                                 "EXIT ClientMessage received while locked.",
1747                                 "screen is locked.");
1748     }
1749   else if (type == XA_RESTART)
1750     {
1751       /* The RESTART message works whether the screensaver is active or not,
1752          unless the screen is locked, in which case it doesn't work.
1753        */
1754       if (until_idle_p || !si->locked_p)
1755         {
1756           clientmessage_response (si, window, False,
1757                                   "RESTART ClientMessage received.",
1758                                   "restarting.");
1759           if (! until_idle_p)
1760             {
1761               int i;
1762               for (i = 0; i < si->nscreens; i++)
1763                 kill_screenhack (&si->screens[i]);
1764               unblank_screen (si);
1765               XSync (si->dpy, False);
1766             }
1767
1768           restart_process (si);  /* does not return */
1769           abort();
1770         }
1771       else
1772         clientmessage_response (si, window, True,
1773                                 "RESTART ClientMessage received while locked.",
1774                                 "screen is locked.");
1775     }
1776   else if (type == XA_DEMO)
1777     {
1778       long arg = event->xclient.data.l[1];
1779       Bool demo_one_hack_p = (arg == 5000);
1780
1781       if (demo_one_hack_p)
1782         {
1783           if (until_idle_p)
1784             {
1785               long which = event->xclient.data.l[2];
1786               char buf [255];
1787               char buf2 [255];
1788               sprintf (buf, "DEMO %ld ClientMessage received.", which);
1789               sprintf (buf2, "demoing (%ld).", which);
1790               clientmessage_response (si, window, False, buf, buf2);
1791
1792               if (which < 0) which = 0;         /* 0 == "random" */
1793               si->selection_mode = which;
1794               si->demoing_p = True;
1795
1796               if (si->throttled_p && p->verbose_p)
1797                 fprintf (stderr, "%s: unthrottled.\n", blurb());
1798               si->throttled_p = False;
1799
1800               return True;
1801             }
1802
1803           clientmessage_response (si, window, True,
1804                                   "DEMO ClientMessage received while active.",
1805                                   "already active.");
1806         }
1807       else
1808         {
1809           clientmessage_response (si, window, True,
1810                                   "obsolete form of DEMO ClientMessage.",
1811                                   "obsolete form of DEMO ClientMessage.");
1812         }
1813     }
1814   else if (type == XA_PREFS)
1815     {
1816       clientmessage_response (si, window, True,
1817                               "the PREFS client-message is obsolete.",
1818                               "the PREFS client-message is obsolete.");
1819     }
1820   else if (type == XA_LOCK)
1821     {
1822 #ifdef NO_LOCKING
1823       clientmessage_response (si, window, True,
1824                               "not compiled with support for locking.",
1825                               "locking not enabled.");
1826 #else /* !NO_LOCKING */
1827       if (si->locking_disabled_p)
1828         clientmessage_response (si, window, True,
1829                       "LOCK ClientMessage received, but locking is disabled.",
1830                               "locking not enabled.");
1831       else if (si->locked_p)
1832         clientmessage_response (si, window, True,
1833                            "LOCK ClientMessage received while already locked.",
1834                                 "already locked.");
1835       else
1836         {
1837           char buf [255];
1838           char *response = (until_idle_p
1839                             ? "activating and locking."
1840                             : "locking.");
1841           sprintf (buf, "LOCK ClientMessage received; %s", response);
1842           clientmessage_response (si, window, False, buf, response);
1843           set_locked_p (si, True);
1844           si->selection_mode = 0;
1845           si->demoing_p = False;
1846
1847           if (si->lock_id)      /* we're doing it now, so lose the timeout */
1848             {
1849               XtRemoveTimeOut (si->lock_id);
1850               si->lock_id = 0;
1851             }
1852
1853           if (until_idle_p)
1854             {
1855               if (si->using_mit_saver_extension ||
1856                   si->using_sgi_saver_extension)
1857                 {
1858                   XForceScreenSaver (si->dpy, ScreenSaverActive);
1859                   return False;
1860                 }
1861               else
1862                 {
1863                   return True;
1864                 }
1865             }
1866         }
1867 #endif /* !NO_LOCKING */
1868     }
1869   else if (type == XA_THROTTLE)
1870     {
1871       /* The THROTTLE command is deprecated -- it predates the XDPMS
1872          extension.  Instead of using -throttle, users should instead
1873          just power off the monitor (e.g., "xset dpms force off".)
1874          In a few minutes, xscreensaver will notice that the monitor
1875          is off, and cease running hacks.
1876        */
1877       if (si->throttled_p)
1878         clientmessage_response (si, window, True,
1879                                 "THROTTLE ClientMessage received, but "
1880                                 "already throttled.",
1881                                 "already throttled.");
1882       else
1883         {
1884           char buf [255];
1885           char *response = "throttled.";
1886           si->throttled_p = True;
1887           si->selection_mode = 0;
1888           si->demoing_p = False;
1889           sprintf (buf, "THROTTLE ClientMessage received; %s", response);
1890           clientmessage_response (si, window, False, buf, response);
1891
1892           if (! until_idle_p)
1893             {
1894               if (si->cycle_id)
1895                 XtRemoveTimeOut (si->cycle_id);
1896               si->cycle_id = 0;
1897               cycle_timer ((XtPointer) si, 0);
1898             }
1899         }
1900     }
1901   else if (type == XA_UNTHROTTLE)
1902     {
1903       if (! si->throttled_p)
1904         clientmessage_response (si, window, True,
1905                                 "UNTHROTTLE ClientMessage received, but "
1906                                 "not throttled.",
1907                                 "not throttled.");
1908       else
1909         {
1910           char buf [255];
1911           char *response = "unthrottled.";
1912           si->throttled_p = False;
1913           si->selection_mode = 0;
1914           si->demoing_p = False;
1915           sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
1916           clientmessage_response (si, window, False, buf, response);
1917
1918           if (! until_idle_p)
1919             {
1920               if (si->cycle_id)
1921                 XtRemoveTimeOut (si->cycle_id);
1922               si->cycle_id = 0;
1923               cycle_timer ((XtPointer) si, 0);
1924             }
1925         }
1926     }
1927   else
1928     {
1929       char buf [1024];
1930       char *str;
1931       str = XGetAtomName_safe (si->dpy, type);
1932
1933       if (str)
1934         {
1935           if (strlen (str) > 80)
1936             strcpy (str+70, "...");
1937           sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
1938                    str);
1939           free (str);
1940         }
1941       else
1942         {
1943           sprintf (buf,
1944                    "unrecognised screensaver ClientMessage 0x%x received.",
1945                    (unsigned int) event->xclient.data.l[0]);
1946         }
1947
1948       clientmessage_response (si, window, True, buf, buf);
1949     }
1950   return False;
1951 }
1952
1953 \f
1954 /* Some random diagnostics printed in -verbose mode.
1955  */
1956
1957 static void
1958 analyze_display (saver_info *si)
1959 {
1960   int i, j;
1961   static struct {
1962     const char *name; const char *desc; Bool useful_p;
1963   } exts[] = {
1964
1965    { "SCREEN_SAVER", /* underscore */           "SGI Screen-Saver",
1966 #     ifdef HAVE_SGI_SAVER_EXTENSION
1967         True
1968 #     else
1969         False
1970 #     endif
1971    }, { "SCREEN-SAVER", /* dash */              "SGI Screen-Saver",
1972 #     ifdef HAVE_SGI_SAVER_EXTENSION
1973         True
1974 #     else
1975         False
1976 #     endif
1977    }, { "MIT-SCREEN-SAVER",                     "MIT Screen-Saver",
1978 #     ifdef HAVE_MIT_SAVER_EXTENSION
1979         True
1980 #     else
1981         False
1982 #     endif
1983    }, { "XIDLE",                                "XIdle",           
1984 #     ifdef HAVE_XIDLE_EXTENSION
1985         True
1986 #     else
1987         False
1988 #     endif
1989    }, { "SGI-VIDEO-CONTROL",                    "SGI Video-Control",
1990 #     ifdef HAVE_SGI_VC_EXTENSION
1991         True
1992 #     else
1993         False
1994 #     endif
1995    }, { "READDISPLAY",                          "SGI Read-Display",
1996 #     ifdef HAVE_READ_DISPLAY_EXTENSION
1997         True
1998 #     else
1999         False
2000 #     endif
2001    }, { "MIT-SHM",                              "Shared Memory",   
2002 #     ifdef HAVE_XSHM_EXTENSION
2003         True
2004 #     else
2005         False
2006 #     endif
2007    }, { "DOUBLE-BUFFER",                        "Double-Buffering",
2008 #     ifdef HAVE_DOUBLE_BUFFER_EXTENSION
2009         True
2010 #     else
2011         False
2012 #     endif
2013    }, { "DPMS",                                 "Power Management",
2014 #     ifdef HAVE_DPMS_EXTENSION
2015         True
2016 #     else
2017         False
2018 #     endif
2019    }, { "GLX",                                  "GLX",             
2020 #     ifdef HAVE_GL
2021         True
2022 #     else
2023         False
2024 #     endif
2025    }, { "XFree86-VidModeExtension",             "XF86 Video-Mode", 
2026 #     ifdef HAVE_XF86VMODE
2027         True
2028 #     else
2029         False
2030 #     endif
2031    }, { "XINERAMA",                             "Xinerama",
2032 #     ifdef HAVE_XINERAMA
2033         True
2034 #     else
2035         False
2036 #     endif
2037    }, { "RANDR",                                "Resize-and-Rotate",
2038 #     ifdef HAVE_RANDR
2039         True
2040 #     else
2041         False
2042 #     endif
2043    }, { "DRI",                                  "DRI",
2044         True
2045    }, { "Apple-DRI",                            "Apple-DRI (XDarwin)",
2046         True
2047    },
2048   };
2049
2050   fprintf (stderr, "%s: running on display \"%s\"\n", blurb(), 
2051            DisplayString(si->dpy));
2052   fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
2053            ServerVendor(si->dpy), VendorRelease(si->dpy));
2054
2055   fprintf (stderr, "%s: useful extensions:\n", blurb());
2056   for (i = 0; i < countof(exts); i++)
2057     {
2058       int op = 0, event = 0, error = 0;
2059       char buf [255];
2060       int j;
2061       if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
2062         continue;
2063       sprintf (buf, "%s:   ", blurb());
2064       j = strlen (buf);
2065       strcat (buf, exts[i].desc);
2066       if (!exts[i].useful_p)
2067         strcat (buf, " (disabled at compile time)");
2068       fprintf (stderr, "%s\n", buf);
2069     }
2070
2071   for (i = 0; i < si->nscreens; i++)
2072     {
2073       saver_screen_info *ssi = &si->screens[i];
2074       unsigned long colormapped_depths = 0;
2075       unsigned long non_mapped_depths = 0;
2076       XVisualInfo vi_in, *vi_out;
2077       int out_count;
2078
2079       if (!ssi->real_screen_p) continue;
2080
2081       vi_in.screen = ssi->real_screen_number;
2082       vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
2083       if (!vi_out) continue;
2084       for (j = 0; j < out_count; j++)
2085         if (vi_out[j].class == PseudoColor)
2086           colormapped_depths |= (1 << vi_out[j].depth);
2087         else
2088           non_mapped_depths  |= (1 << vi_out[j].depth);
2089       XFree ((char *) vi_out);
2090
2091       if (colormapped_depths)
2092         {
2093           fprintf (stderr, "%s: screen %d colormapped depths:", blurb(),
2094                    ssi->real_screen_number);
2095           for (j = 0; j < 32; j++)
2096             if (colormapped_depths & (1 << j))
2097               fprintf (stderr, " %d", j);
2098           fprintf (stderr, ".\n");
2099         }
2100       if (non_mapped_depths)
2101         {
2102           fprintf (stderr, "%s: screen %d non-colormapped depths:",
2103                    blurb(), ssi->real_screen_number);
2104           for (j = 0; j < 32; j++)
2105             if (non_mapped_depths & (1 << j))
2106               fprintf (stderr, " %d", j);
2107           fprintf (stderr, ".\n");
2108         }
2109     }
2110
2111   describe_monitor_layout (si);
2112 }
2113
2114
2115 Bool
2116 display_is_on_console_p (saver_info *si)
2117 {
2118   Bool not_on_console = True;
2119   char *dpystr = DisplayString (si->dpy);
2120   char *tail = (char *) strchr (dpystr, ':');
2121   if (! tail || strncmp (tail, ":0", 2))
2122     not_on_console = True;
2123   else
2124     {
2125       char dpyname[255], localname[255];
2126       strncpy (dpyname, dpystr, tail-dpystr);
2127       dpyname [tail-dpystr] = 0;
2128       if (!*dpyname ||
2129           !strcmp(dpyname, "unix") ||
2130           !strcmp(dpyname, "localhost"))
2131         not_on_console = False;
2132       else if (gethostname (localname, sizeof (localname)))
2133         not_on_console = True;  /* can't find hostname? */
2134       else if (!strncmp (dpyname, "/tmp/launch-", 12))  /* MacOS X launchd */
2135         not_on_console = False;
2136       else
2137         {
2138           /* We have to call gethostbyname() on the result of gethostname()
2139              because the two aren't guarenteed to be the same name for the
2140              same host: on some losing systems, one is a FQDN and the other
2141              is not.  Here in the wide wonderful world of Unix it's rocket
2142              science to obtain the local hostname in a portable fashion.
2143              
2144              And don't forget, gethostbyname() reuses the structure it
2145              returns, so we have to copy the fucker before calling it again.
2146              Thank you master, may I have another.
2147            */
2148           struct hostent *h = gethostbyname (dpyname);
2149           if (!h)
2150             not_on_console = True;
2151           else
2152             {
2153               char hn [255];
2154               struct hostent *l;
2155               strcpy (hn, h->h_name);
2156               l = gethostbyname (localname);
2157               not_on_console = (!l || !!(strcmp (l->h_name, hn)));
2158             }
2159         }
2160     }
2161   return !not_on_console;
2162 }
2163
2164
2165 /* Do a little bit of heap introspection...
2166  */
2167 void
2168 check_for_leaks (const char *where)
2169 {
2170 #if defined(HAVE_SBRK) && defined(LEAK_PARANOIA)
2171   static unsigned long last_brk = 0;
2172   int b = (unsigned long) sbrk(0);
2173   if (last_brk && last_brk < b)
2174     fprintf (stderr, "%s: %s: brk grew by %luK.\n",
2175              blurb(), where,
2176              (((b - last_brk) + 1023) / 1024));
2177   last_brk = b;
2178 #endif /* HAVE_SBRK */
2179 }