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