http://www.jwz.org/xscreensaver/xscreensaver-5.14.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, optionally power down monitor right now.
1231          To do this, we might need to temporarily re-enable DPMS first.
1232        */
1233       if (p->mode == BLANK_ONLY &&
1234           p->dpms_enabled_p && 
1235           p->dpms_quickoff_p)
1236         {
1237           sync_server_dpms_settings (si->dpy, True,
1238                                      p->dpms_standby / 1000,
1239                                      p->dpms_suspend / 1000,
1240                                      (p->dpms_off
1241                                       ? (p->dpms_off / 1000)
1242                                       : 0xFFFF),
1243                                      False);
1244           monitor_power_on (si, False);
1245         }
1246
1247       /* Don't start the cycle timer in demo mode. */
1248       if (!si->demoing_p && p->cycle)
1249         si->cycle_id = XtAppAddTimeOut (si->app,
1250                                         (si->selection_mode
1251                                          /* see comment in cycle_timer() */
1252                                          ? 1000 * 60 * 60
1253                                          : p->cycle),
1254                                         cycle_timer,
1255                                         (XtPointer) si);
1256
1257
1258 #ifndef NO_LOCKING
1259       /* Maybe start locking the screen.
1260        */
1261       {
1262         Time lock_timeout = p->lock_timeout;
1263
1264         if (si->emergency_lock_p && p->lock_p && lock_timeout)
1265           {
1266             int secs = p->lock_timeout / 1000;
1267             if (p->verbose_p)
1268               fprintf (stderr,
1269                      "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1270                        blurb(),
1271                        (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1272             lock_timeout = 0;
1273           }
1274
1275         si->emergency_lock_p = False;
1276
1277         if (!si->demoing_p &&           /* if not going into demo mode */
1278             p->lock_p &&                /* and locking is enabled */
1279             !si->locking_disabled_p &&  /* and locking is possible */
1280             lock_timeout == 0)          /* and locking is not timer-deferred */
1281           set_locked_p (si, True);      /* then lock right now. */
1282
1283         /* locked_p might be true already because of the above, or because of
1284            the LOCK ClientMessage.  But if not, and if we're supposed to lock
1285            after some time, set up a timer to do so.
1286         */
1287         if (p->lock_p &&
1288             !si->locked_p &&
1289             lock_timeout > 0)
1290           si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1291                                          activate_lock_timer,
1292                                          (XtPointer) si);
1293       }
1294 #endif /* !NO_LOCKING */
1295
1296
1297       ok_to_unblank = True;
1298       do {
1299
1300         check_for_leaks ("blanked A");
1301         sleep_until_idle (si, False);           /* until not idle */
1302         check_for_leaks ("blanked B");
1303
1304         maybe_reload_init_file (si);
1305
1306 #ifndef NO_LOCKING
1307         /* Maybe unlock the screen.
1308          */
1309         if (si->locked_p)
1310           {
1311             saver_screen_info *ssi = si->default_screen;
1312             if (si->locking_disabled_p) abort ();
1313
1314             was_locked = True;
1315             si->dbox_up_p = True;
1316             for (i = 0; i < si->nscreens; i++)
1317               suspend_screenhack (&si->screens[i], True);         /* suspend */
1318             XUndefineCursor (si->dpy, ssi->screensaver_window);
1319
1320             ok_to_unblank = unlock_p (si);
1321
1322             si->dbox_up_p = False;
1323             XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1324             for (i = 0; i < si->nscreens; i++)
1325               suspend_screenhack (&si->screens[i], False);         /* resume */
1326
1327             if (!ok_to_unblank &&
1328                 !screenhack_running_p (si))
1329               {
1330                 /* If the lock dialog has been dismissed and we're not about to
1331                    unlock the screen, and there is currently no hack running,
1332                    then launch one.  (There might be no hack running if DPMS
1333                    had kicked in.  But DPMS is off now, so bring back the hack)
1334                  */
1335                 if (si->cycle_id)
1336                   XtRemoveTimeOut (si->cycle_id);
1337                 si->cycle_id = 0;
1338                 cycle_timer ((XtPointer) si, 0);
1339               }
1340           }
1341 #endif /* !NO_LOCKING */
1342
1343         } while (!ok_to_unblank);
1344
1345
1346       if (p->verbose_p)
1347         fprintf (stderr, "%s: unblanking screen at %s.\n",
1348                  blurb(), timestring ());
1349
1350       /* Kill before unblanking, to stop drawing as soon as possible. */
1351       for (i = 0; i < si->nscreens; i++)
1352         kill_screenhack (&si->screens[i]);
1353       unblank_screen (si);
1354
1355       set_locked_p (si, False);
1356       si->emergency_lock_p = False;
1357       si->demoing_p = 0;
1358       si->selection_mode = 0;
1359
1360       /* If we're throttled, and the user has explicitly unlocked the screen,
1361          then unthrottle.  If we weren't locked, then don't unthrottle
1362          automatically, because someone might have just bumped the desk... */
1363       if (was_locked)
1364         {
1365           if (si->throttled_p && p->verbose_p)
1366             fprintf (stderr, "%s: unthrottled.\n", blurb());
1367           si->throttled_p = False;
1368         }
1369
1370       if (si->cycle_id)
1371         {
1372           XtRemoveTimeOut (si->cycle_id);
1373           si->cycle_id = 0;
1374         }
1375
1376       if (si->lock_id)
1377         {
1378           XtRemoveTimeOut (si->lock_id);
1379           si->lock_id = 0;
1380         }
1381
1382       /* Since we're unblanked now, break race conditions and make
1383          sure we stay that way (see comment in timers.c.) */
1384       if (! si->de_race_id)
1385         de_race_timer ((XtPointer) si, 0);
1386     }
1387 }
1388
1389 static void analyze_display (saver_info *si);
1390 static void fix_fds (void);
1391
1392 int
1393 main (int argc, char **argv)
1394 {
1395   Widget shell;
1396   saver_info the_si;
1397   saver_info *si = &the_si;
1398   saver_preferences *p = &si->prefs;
1399   struct passwd *spasswd;
1400   int i;
1401
1402   /* It turns out that if we do NLS stuff here, people running in Japanese
1403      locales get font craziness on the password dialog, presumably because
1404      it is displaying Japanese characters in a non-Japanese font.  I don't
1405      understand how to automatically make all this crap work properly by
1406      default, so until someone sends me a better patch, just leave it off
1407      and run the daemon in English.  -- jwz, 29-Sep-2010
1408    */
1409 #undef ENABLE_NLS
1410
1411 #ifdef ENABLE_NLS
1412   if (!setlocale (LC_ALL, ""))
1413     fprintf (stderr, "locale not supported by C library\n");
1414
1415   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1416   textdomain (GETTEXT_PACKAGE);
1417 #endif /* ENABLE_NLS */
1418
1419   memset(si, 0, sizeof(*si));
1420   global_si_kludge = si;        /* I hate C so much... */
1421
1422   fix_fds();
1423
1424 # undef ya_rand_init
1425   ya_rand_init (0);
1426
1427   save_argv (argc, argv);
1428   set_version_string (si, &argc, argv);
1429   privileged_initialization (si, &argc, argv);
1430   hack_environment (si);
1431
1432   spasswd = getpwuid(getuid());
1433   if (!spasswd)
1434     {
1435       fprintf(stderr, "Could not figure out who the current user is!\n");
1436       return 1;
1437     }
1438
1439   si->user = strdup(spasswd->pw_name ? spasswd->pw_name : "(unknown)");
1440
1441 # ifndef NO_LOCKING
1442   si->unlock_cb = gui_auth_conv;
1443   si->auth_finished_cb = auth_finished_cb;
1444 # endif /* !NO_LOCKING */
1445
1446   shell = connect_to_server (si, &argc, argv);
1447   process_command_line (si, &argc, argv);
1448   stderr_log_file (si);
1449   print_banner (si);
1450
1451   load_init_file(si->dpy, p); /* must be before initialize_per_screen_info() */
1452   blurb_timestamp_p = p->timestamp_p;  /* kludge */
1453   initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1454
1455   /* We can only issue this warning now. */
1456   if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1457     fprintf (stderr,
1458              "%s: there are no PseudoColor or GrayScale visuals.\n"
1459              "%s: ignoring the request for fading/unfading.\n",
1460              blurb(), blurb());
1461
1462   for (i = 0; i < si->nscreens; i++)
1463     {
1464       saver_screen_info *ssi = &si->screens[i];
1465       if (ssi->real_screen_p)
1466         if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1467           exit (1);
1468     }
1469
1470   lock_initialization (si, &argc, argv);
1471   print_lock_failure_banner (si);
1472
1473   if (p->xsync_p) XSynchronize (si->dpy, True);
1474
1475   if (p->verbose_p) analyze_display (si);
1476   initialize_server_extensions (si);
1477
1478   si->blank_time = time ((time_t) 0); /* must be before ..._window */
1479   initialize_screensaver_window (si);
1480
1481   select_events (si);
1482   init_sigchld ();
1483
1484   disable_builtin_screensaver (si, True);
1485   sync_server_dpms_settings (si->dpy,
1486                              (p->dpms_enabled_p  &&
1487                               p->mode != DONT_BLANK),
1488                              p->dpms_standby / 1000,
1489                              p->dpms_suspend / 1000,
1490                              p->dpms_off / 1000,
1491                              False);
1492
1493   initialize_stderr (si);
1494   handle_signals (si);
1495
1496   make_splash_dialog (si);
1497
1498   main_loop (si);               /* doesn't return */
1499   return 0;
1500 }
1501
1502 static void
1503 fix_fds (void)
1504 {
1505   /* Bad Things Happen if stdin, stdout, and stderr have been closed
1506      (as by the `sh incantation "xscreensaver >&- 2>&-").  When you do
1507      that, the X connection gets allocated to one of these fds, and
1508      then some random library writes to stderr, and random bits get
1509      stuffed down the X pipe, causing "Xlib: sequence lost" errors.
1510      So, we cause the first three file descriptors to be open to
1511      /dev/null if they aren't open to something else already.  This
1512      must be done before any other files are opened (or the closing
1513      of that other file will again free up one of the "magic" first
1514      three FDs.)
1515
1516      We do this by opening /dev/null three times, and then closing
1517      those fds, *unless* any of them got allocated as #0, #1, or #2,
1518      in which case we leave them open.  Gag.
1519
1520      Really, this crap is technically required of *every* X program,
1521      if you want it to be robust in the face of "2>&-".
1522    */
1523   int fd0 = open ("/dev/null", O_RDWR);
1524   int fd1 = open ("/dev/null", O_RDWR);
1525   int fd2 = open ("/dev/null", O_RDWR);
1526   if (fd0 > 2) close (fd0);
1527   if (fd1 > 2) close (fd1);
1528   if (fd2 > 2) close (fd2);
1529 }
1530
1531
1532 \f
1533 /* Processing ClientMessage events.
1534  */
1535
1536
1537 static Bool error_handler_hit_p = False;
1538
1539 static int
1540 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1541 {
1542   error_handler_hit_p = True;
1543   return 0;
1544 }
1545
1546 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1547    them.  We only look up the atom names for printing warning messages,
1548    so don't bomb out when it happens...
1549  */
1550 static char *
1551 XGetAtomName_safe (Display *dpy, Atom atom)
1552 {
1553   char *result;
1554   XErrorHandler old_handler;
1555   if (!atom) return 0;
1556
1557   XSync (dpy, False);
1558   error_handler_hit_p = False;
1559   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1560   result = XGetAtomName (dpy, atom);
1561   XSync (dpy, False);
1562   XSetErrorHandler (old_handler);
1563   XSync (dpy, False);
1564   if (error_handler_hit_p) result = 0;
1565
1566   if (result)
1567     return result;
1568   else
1569     {
1570       char buf[100];
1571       sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
1572       return strdup (buf);
1573     }
1574 }
1575
1576
1577 static void
1578 clientmessage_response (saver_info *si, Window w, Bool error,
1579                         const char *stderr_msg,
1580                         const char *protocol_msg)
1581 {
1582   char *proto;
1583   int L;
1584   saver_preferences *p = &si->prefs;
1585   XErrorHandler old_handler;
1586
1587   if (error || p->verbose_p)
1588     fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1589
1590   L = strlen(protocol_msg);
1591   proto = (char *) malloc (L + 2);
1592   proto[0] = (error ? '-' : '+');
1593   strcpy (proto+1, protocol_msg);
1594   L++;
1595
1596   /* Ignore all X errors while sending a response to a ClientMessage.
1597      Pretty much the only way we could get an error here is if the
1598      window we're trying to send the reply on has been deleted, in
1599      which case, the sender of the ClientMessage won't see our response
1600      anyway.
1601    */
1602   XSync (si->dpy, False);
1603   error_handler_hit_p = False;
1604   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1605
1606   XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1607                    PropModeReplace, (unsigned char *) proto, L);
1608
1609   XSync (si->dpy, False);
1610   XSetErrorHandler (old_handler);
1611   XSync (si->dpy, False);
1612
1613   free (proto);
1614 }
1615
1616
1617 static void
1618 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1619 {
1620 #if 0  /* Oh, fuck it.  GNOME likes to spew random ClientMessages at us
1621           all the time.  This is presumably indicative of an error in
1622           the sender of that ClientMessage: if we're getting it and 
1623           ignoring it, then it's not reaching the intended recipient.
1624           But people complain to me about this all the time ("waaah!
1625           xscreensaver is printing to it's stderr and that gets my
1626           panties all in a bunch!")  And I'm sick of hearing about it.
1627           So we'll just ignore these messages and let GNOME go right
1628           ahead and continue to stumble along in its malfunction.
1629         */
1630
1631   saver_preferences *p = &si->prefs;
1632   char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1633   Window w = event->xclient.window;
1634   char wdesc[255];
1635   int screen = 0;
1636   Bool root_p = False;
1637
1638   *wdesc = 0;
1639   for (screen = 0; screen < si->nscreens; screen++)
1640     if (w == si->screens[screen].screensaver_window)
1641       {
1642         strcpy (wdesc, "xscreensaver");
1643         break;
1644       }
1645     else if (w == RootWindow (si->dpy, screen))
1646       {
1647         strcpy (wdesc, "root");
1648         root_p = True;
1649         break;
1650       }
1651
1652   /* If this ClientMessage was sent to the real root window instead of to the
1653      xscreensaver window, then it might be intended for someone else who is
1654      listening on the root window (e.g., the window manager).  So only print
1655      the warning if: we are in debug mode; or if the bogus message was
1656      actually sent to one of the xscreensaver-created windows.
1657    */
1658   if (root_p && !p->debug_p)
1659     return;
1660
1661   if (!*wdesc)
1662     {
1663       XErrorHandler old_handler;
1664       XClassHint hint;
1665       XWindowAttributes xgwa;
1666       memset (&hint, 0, sizeof(hint));
1667       memset (&xgwa, 0, sizeof(xgwa));
1668
1669       XSync (si->dpy, False);
1670       old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1671       XGetClassHint (si->dpy, w, &hint);
1672       XGetWindowAttributes (si->dpy, w, &xgwa);
1673       XSync (si->dpy, False);
1674       XSetErrorHandler (old_handler);
1675       XSync (si->dpy, False);
1676
1677       screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1678
1679       sprintf (wdesc, "%.20s / %.20s",
1680                (hint.res_name  ? hint.res_name  : "(null)"),
1681                (hint.res_class ? hint.res_class : "(null)"));
1682       if (hint.res_name)  XFree (hint.res_name);
1683       if (hint.res_class) XFree (hint.res_class);
1684     }
1685
1686   fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1687            blurb(), screen, (str ? str : "(null)"));
1688   fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1689            blurb(), screen, (unsigned long) w, wdesc);
1690   if (str) XFree (str);
1691
1692 #endif /* 0 */
1693 }
1694
1695
1696 Bool
1697 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1698 {
1699   saver_preferences *p = &si->prefs;
1700   Atom type = 0;
1701   Window window = event->xclient.window;
1702
1703   /* Preferences might affect our handling of client messages. */
1704   maybe_reload_init_file (si);
1705
1706   if (event->xclient.message_type != XA_SCREENSAVER ||
1707       event->xclient.format != 32)
1708     {
1709       bogus_clientmessage_warning (si, event);
1710       return False;
1711     }
1712
1713   type = event->xclient.data.l[0];
1714   if (type == XA_ACTIVATE)
1715     {
1716       if (until_idle_p)
1717         {
1718           if (p->mode == DONT_BLANK)
1719             {
1720               clientmessage_response(si, window, True,
1721                          "ACTIVATE ClientMessage received in DONT_BLANK mode.",
1722                                      "screen blanking is currently disabled.");
1723               return False;
1724             }
1725
1726           clientmessage_response(si, window, False,
1727                                  "ACTIVATE ClientMessage received.",
1728                                  "activating.");
1729           si->selection_mode = 0;
1730           si->demoing_p = False;
1731
1732           if (si->throttled_p && p->verbose_p)
1733             fprintf (stderr, "%s: unthrottled.\n", blurb());
1734           si->throttled_p = False;
1735
1736           if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1737             {
1738               XForceScreenSaver (si->dpy, ScreenSaverActive);
1739               return False;
1740             }
1741           else
1742             {
1743               return True;
1744             }
1745         }
1746       clientmessage_response(si, window, True,
1747                        "ClientMessage ACTIVATE received while already active.",
1748                              "already active.");
1749     }
1750   else if (type == XA_DEACTIVATE)
1751     {
1752       if (! until_idle_p)
1753         {
1754           if (si->throttled_p && p->verbose_p)
1755             fprintf (stderr, "%s: unthrottled.\n", blurb());
1756           si->throttled_p = False;
1757
1758           clientmessage_response(si, window, False,
1759                                  "DEACTIVATE ClientMessage received.",
1760                                  "deactivating.");
1761           if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1762             {
1763               XForceScreenSaver (si->dpy, ScreenSaverReset);
1764               return False;
1765             }
1766           else
1767             {
1768               return True;
1769             }
1770         }
1771       clientmessage_response(si, window, False,
1772      "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
1773                              "not active: idle timer reset.");
1774       reset_timers (si);
1775     }
1776   else if (type == XA_CYCLE)
1777     {
1778       if (! until_idle_p)
1779         {
1780           clientmessage_response(si, window, False,
1781                                  "CYCLE ClientMessage received.",
1782                                  "cycling.");
1783           si->selection_mode = 0;       /* 0 means randomize when its time. */
1784           si->demoing_p = False;
1785
1786           if (si->throttled_p && p->verbose_p)
1787             fprintf (stderr, "%s: unthrottled.\n", blurb());
1788           si->throttled_p = False;
1789
1790           if (si->cycle_id)
1791             XtRemoveTimeOut (si->cycle_id);
1792           si->cycle_id = 0;
1793           cycle_timer ((XtPointer) si, 0);
1794           return False;
1795         }
1796       clientmessage_response(si, window, True,
1797                              "ClientMessage CYCLE received while inactive.",
1798                              "not active.");
1799     }
1800   else if (type == XA_NEXT || type == XA_PREV)
1801     {
1802       clientmessage_response(si, window, False,
1803                              (type == XA_NEXT
1804                               ? "NEXT ClientMessage received."
1805                               : "PREV ClientMessage received."),
1806                              "cycling.");
1807       si->selection_mode = (type == XA_NEXT ? -1 : -2);
1808       si->demoing_p = False;
1809
1810       if (si->throttled_p && p->verbose_p)
1811         fprintf (stderr, "%s: unthrottled.\n", blurb());
1812       si->throttled_p = False;
1813
1814       if (! until_idle_p)
1815         {
1816           if (si->cycle_id)
1817             XtRemoveTimeOut (si->cycle_id);
1818           si->cycle_id = 0;
1819           cycle_timer ((XtPointer) si, 0);
1820         }
1821       else
1822         return True;
1823     }
1824   else if (type == XA_SELECT)
1825     {
1826       char buf [255];
1827       char buf2 [255];
1828       long which = event->xclient.data.l[1];
1829
1830       if (p->mode == DONT_BLANK)
1831         {
1832           clientmessage_response(si, window, True,
1833                            "SELECT ClientMessage received in DONT_BLANK mode.",
1834                                  "screen blanking is currently disabled.");
1835           return False;
1836         }
1837
1838       sprintf (buf, "SELECT %ld ClientMessage received.", which);
1839       sprintf (buf2, "activating (%ld).", which);
1840       clientmessage_response (si, window, False, buf, buf2);
1841
1842       if (which < 0) which = 0;         /* 0 == "random" */
1843       si->selection_mode = which;
1844       si->demoing_p = False;
1845
1846       if (si->throttled_p && p->verbose_p)
1847         fprintf (stderr, "%s: unthrottled.\n", blurb());
1848       si->throttled_p = False;
1849
1850       if (! until_idle_p)
1851         {
1852           if (si->cycle_id)
1853             XtRemoveTimeOut (si->cycle_id);
1854           si->cycle_id = 0;
1855           cycle_timer ((XtPointer) si, 0);
1856         }
1857       else
1858         return True;
1859     }
1860   else if (type == XA_EXIT)
1861     {
1862       /* Ignore EXIT message if the screen is locked. */
1863       if (until_idle_p || !si->locked_p)
1864         {
1865           clientmessage_response (si, window, False,
1866                                   "EXIT ClientMessage received.",
1867                                   "exiting.");
1868           if (! until_idle_p)
1869             {
1870               int i;
1871               for (i = 0; i < si->nscreens; i++)
1872                 kill_screenhack (&si->screens[i]);
1873               unblank_screen (si);
1874               XSync (si->dpy, False);
1875             }
1876           saver_exit (si, 0, 0);
1877         }
1878       else
1879         clientmessage_response (si, window, True,
1880                                 "EXIT ClientMessage received while locked.",
1881                                 "screen is locked.");
1882     }
1883   else if (type == XA_RESTART)
1884     {
1885       /* The RESTART message works whether the screensaver is active or not,
1886          unless the screen is locked, in which case it doesn't work.
1887        */
1888       if (until_idle_p || !si->locked_p)
1889         {
1890           clientmessage_response (si, window, False,
1891                                   "RESTART ClientMessage received.",
1892                                   "restarting.");
1893           if (! until_idle_p)
1894             {
1895               int i;
1896               for (i = 0; i < si->nscreens; i++)
1897                 kill_screenhack (&si->screens[i]);
1898               unblank_screen (si);
1899               XSync (si->dpy, False);
1900             }
1901
1902           restart_process (si);  /* does not return */
1903           abort();
1904         }
1905       else
1906         clientmessage_response (si, window, True,
1907                                 "RESTART ClientMessage received while locked.",
1908                                 "screen is locked.");
1909     }
1910   else if (type == XA_DEMO)
1911     {
1912       long arg = event->xclient.data.l[1];
1913       Bool demo_one_hack_p = (arg == 5000);
1914
1915       if (demo_one_hack_p)
1916         {
1917           if (until_idle_p)
1918             {
1919               long which = event->xclient.data.l[2];
1920               char buf [255];
1921               char buf2 [255];
1922               sprintf (buf, "DEMO %ld ClientMessage received.", which);
1923               sprintf (buf2, "demoing (%ld).", which);
1924               clientmessage_response (si, window, False, buf, buf2);
1925
1926               if (which < 0) which = 0;         /* 0 == "random" */
1927               si->selection_mode = which;
1928               si->demoing_p = True;
1929
1930               if (si->throttled_p && p->verbose_p)
1931                 fprintf (stderr, "%s: unthrottled.\n", blurb());
1932               si->throttled_p = False;
1933
1934               return True;
1935             }
1936
1937           clientmessage_response (si, window, True,
1938                                   "DEMO ClientMessage received while active.",
1939                                   "already active.");
1940         }
1941       else
1942         {
1943           clientmessage_response (si, window, True,
1944                                   "obsolete form of DEMO ClientMessage.",
1945                                   "obsolete form of DEMO ClientMessage.");
1946         }
1947     }
1948   else if (type == XA_PREFS)
1949     {
1950       clientmessage_response (si, window, True,
1951                               "the PREFS client-message is obsolete.",
1952                               "the PREFS client-message is obsolete.");
1953     }
1954   else if (type == XA_LOCK)
1955     {
1956 #ifdef NO_LOCKING
1957       clientmessage_response (si, window, True,
1958                               "not compiled with support for locking.",
1959                               "locking not enabled.");
1960 #else /* !NO_LOCKING */
1961       if (si->locking_disabled_p)
1962         clientmessage_response (si, window, True,
1963                       "LOCK ClientMessage received, but locking is disabled.",
1964                               "locking not enabled.");
1965       else if (si->locked_p)
1966         clientmessage_response (si, window, True,
1967                            "LOCK ClientMessage received while already locked.",
1968                                 "already locked.");
1969       else
1970         {
1971           char buf [255];
1972           char *response = (until_idle_p
1973                             ? "activating and locking."
1974                             : "locking.");
1975           sprintf (buf, "LOCK ClientMessage received; %s", response);
1976           clientmessage_response (si, window, False, buf, response);
1977           set_locked_p (si, True);
1978           si->selection_mode = 0;
1979           si->demoing_p = False;
1980
1981           if (si->lock_id)      /* we're doing it now, so lose the timeout */
1982             {
1983               XtRemoveTimeOut (si->lock_id);
1984               si->lock_id = 0;
1985             }
1986
1987           if (until_idle_p)
1988             {
1989               if (si->using_mit_saver_extension ||
1990                   si->using_sgi_saver_extension)
1991                 {
1992                   XForceScreenSaver (si->dpy, ScreenSaverActive);
1993                   return False;
1994                 }
1995               else
1996                 {
1997                   return True;
1998                 }
1999             }
2000         }
2001 #endif /* !NO_LOCKING */
2002     }
2003   else if (type == XA_THROTTLE)
2004     {
2005       /* The THROTTLE command is deprecated -- it predates the XDPMS
2006          extension.  Instead of using -throttle, users should instead
2007          just power off the monitor (e.g., "xset dpms force off".)
2008          In a few minutes, xscreensaver will notice that the monitor
2009          is off, and cease running hacks.
2010        */
2011       if (si->throttled_p)
2012         clientmessage_response (si, window, True,
2013                                 "THROTTLE ClientMessage received, but "
2014                                 "already throttled.",
2015                                 "already throttled.");
2016       else
2017         {
2018           char buf [255];
2019           char *response = "throttled.";
2020           si->throttled_p = True;
2021           si->selection_mode = 0;
2022           si->demoing_p = False;
2023           sprintf (buf, "THROTTLE ClientMessage received; %s", response);
2024           clientmessage_response (si, window, False, buf, response);
2025
2026           if (! until_idle_p)
2027             {
2028               if (si->cycle_id)
2029                 XtRemoveTimeOut (si->cycle_id);
2030               si->cycle_id = 0;
2031               cycle_timer ((XtPointer) si, 0);
2032             }
2033         }
2034     }
2035   else if (type == XA_UNTHROTTLE)
2036     {
2037       if (! si->throttled_p)
2038         clientmessage_response (si, window, True,
2039                                 "UNTHROTTLE ClientMessage received, but "
2040                                 "not throttled.",
2041                                 "not throttled.");
2042       else
2043         {
2044           char buf [255];
2045           char *response = "unthrottled.";
2046           si->throttled_p = False;
2047           si->selection_mode = 0;
2048           si->demoing_p = False;
2049           sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
2050           clientmessage_response (si, window, False, buf, response);
2051
2052           if (! until_idle_p)
2053             {
2054               if (si->cycle_id)
2055                 XtRemoveTimeOut (si->cycle_id);
2056               si->cycle_id = 0;
2057               cycle_timer ((XtPointer) si, 0);
2058             }
2059         }
2060     }
2061   else
2062     {
2063       char buf [1024];
2064       char *str;
2065       str = XGetAtomName_safe (si->dpy, type);
2066
2067       if (str)
2068         {
2069           if (strlen (str) > 80)
2070             strcpy (str+70, "...");
2071           sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
2072                    str);
2073           free (str);
2074         }
2075       else
2076         {
2077           sprintf (buf,
2078                    "unrecognised screensaver ClientMessage 0x%x received.",
2079                    (unsigned int) event->xclient.data.l[0]);
2080         }
2081
2082       clientmessage_response (si, window, True, buf, buf);
2083     }
2084   return False;
2085 }
2086
2087 \f
2088 /* Some random diagnostics printed in -verbose mode.
2089  */
2090
2091 static void
2092 analyze_display (saver_info *si)
2093 {
2094   int i, j;
2095   static struct {
2096     const char *name; const char *desc; 
2097     Bool useful_p;
2098     Status (*version_fn) (Display *, int *majP, int *minP);
2099   } exts[] = {
2100
2101    { "SCREEN_SAVER", /* underscore */           "SGI Screen-Saver",
2102 #     ifdef HAVE_SGI_SAVER_EXTENSION
2103         True,  0
2104 #     else
2105         False, 0
2106 #     endif
2107    }, { "SCREEN-SAVER", /* dash */              "SGI Screen-Saver",
2108 #     ifdef HAVE_SGI_SAVER_EXTENSION
2109         True,  0
2110 #     else
2111         False, 0
2112 #     endif
2113    }, { "MIT-SCREEN-SAVER",                     "MIT Screen-Saver",
2114 #     ifdef HAVE_MIT_SAVER_EXTENSION
2115         True,  XScreenSaverQueryVersion
2116 #     else
2117         False, 0
2118 #     endif
2119    }, { "XIDLE",                                "XIdle",           
2120 #     ifdef HAVE_XIDLE_EXTENSION
2121         True,  0
2122 #     else
2123         False, 0
2124 #     endif
2125    }, { "SGI-VIDEO-CONTROL",                    "SGI Video-Control",
2126 #     ifdef HAVE_SGI_VC_EXTENSION
2127         True,  XSGIvcQueryVersion
2128 #     else
2129         False, 0
2130 #     endif
2131    }, { "READDISPLAY",                          "SGI Read-Display",
2132 #     ifdef HAVE_READ_DISPLAY_EXTENSION
2133         True,  XReadDisplayQueryVersion
2134 #     else
2135         False, 0
2136 #     endif
2137    }, { "MIT-SHM",                              "Shared Memory",   
2138 #     ifdef HAVE_XSHM_EXTENSION
2139         True, (Status (*) (Display*,int*,int*)) XShmQueryVersion /* 4 args */
2140 #     else
2141         False, 0
2142 #     endif
2143    }, { "DOUBLE-BUFFER",                        "Double-Buffering",
2144 #     ifdef HAVE_DOUBLE_BUFFER_EXTENSION
2145         True, XdbeQueryExtension
2146 #     else
2147         False, 0
2148 #     endif
2149    }, { "DPMS",                                 "Power Management",
2150 #     ifdef HAVE_DPMS_EXTENSION
2151         True,  DPMSGetVersion
2152 #     else
2153         False, 0
2154 #     endif
2155    }, { "GLX",                                  "GLX",             
2156 #     ifdef HAVE_GL
2157         True,  0
2158 #     else
2159         False, 0
2160 #     endif
2161    }, { "XFree86-VidModeExtension",             "XF86 Video-Mode", 
2162 #     ifdef HAVE_XF86VMODE
2163         True,  XF86VidModeQueryVersion
2164 #     else
2165         False, 0
2166 #     endif
2167    }, { "XC-VidModeExtension",                  "XC Video-Mode", 
2168 #     ifdef HAVE_XF86VMODE
2169         True,  XF86VidModeQueryVersion
2170 #     else
2171         False, 0
2172 #     endif
2173    }, { "XFree86-MISC",                         "XF86 Misc", 
2174 #     ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2175         True,  XF86MiscQueryVersion
2176 #     else
2177         False, 0
2178 #     endif
2179    }, { "XC-MISC",                              "XC Misc", 
2180 #     ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2181         True,  XF86MiscQueryVersion
2182 #     else
2183         False, 0
2184 #     endif
2185    }, { "XINERAMA",                             "Xinerama",
2186 #     ifdef HAVE_XINERAMA
2187         True,  XineramaQueryVersion
2188 #     else
2189         False, 0
2190 #     endif
2191    }, { "RANDR",                                "Resize-and-Rotate",
2192 #     ifdef HAVE_RANDR
2193         True,  XRRQueryVersion
2194 #     else
2195         False, 0
2196 #     endif
2197    }, { "DRI",                                  "DRI",
2198         True,  0
2199    }, { "NV-CONTROL",                           "NVidia",
2200         True,  0
2201    }, { "NV-GLX",                               "NVidia GLX",
2202         True,  0
2203    }, { "Apple-DRI",                            "Apple-DRI (XDarwin)",
2204         True,  0
2205    }, { "XInputExtension",                      "XInput",
2206         True,  0
2207    },
2208   };
2209
2210   fprintf (stderr, "%s: running on display \"%s\"\n", blurb(), 
2211            DisplayString(si->dpy));
2212   fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
2213            ServerVendor(si->dpy), VendorRelease(si->dpy));
2214
2215   fprintf (stderr, "%s: useful extensions:\n", blurb());
2216   for (i = 0; i < countof(exts); i++)
2217     {
2218       int op = 0, event = 0, error = 0;
2219       char buf [255];
2220       int maj = 0, min = 0;
2221       int dummy1, dummy2, dummy3;
2222       int j;
2223
2224       /* Most of the extension version functions take 3 args,
2225          writing results into args 2 and 3, but some take more.
2226          We only ever care about the first two results, but we
2227          pass in three extra pointers just in case.
2228        */
2229       Status (*version_fn_2) (Display*,int*,int*,int*,int*,int*) =
2230         (Status (*) (Display*,int*,int*,int*,int*,int*)) exts[i].version_fn;
2231
2232       if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
2233         continue;
2234       sprintf (buf, "%s:   ", blurb());
2235       j = strlen (buf);
2236       strcat (buf, exts[i].desc);
2237
2238       if (!version_fn_2)
2239         ;
2240       else if (version_fn_2 (si->dpy, &maj, &min, &dummy1, &dummy2, &dummy3))
2241         sprintf (buf+strlen(buf), " (%d.%d)", maj, min);
2242       else
2243         strcat (buf, " (unavailable)");
2244
2245       if (!exts[i].useful_p)
2246         strcat (buf, " (disabled at compile time)");
2247       fprintf (stderr, "%s\n", buf);
2248     }
2249
2250   for (i = 0; i < si->nscreens; i++)
2251     {
2252       saver_screen_info *ssi = &si->screens[i];
2253       unsigned long colormapped_depths = 0;
2254       unsigned long non_mapped_depths = 0;
2255       XVisualInfo vi_in, *vi_out;
2256       int out_count;
2257
2258       if (!ssi->real_screen_p) continue;
2259
2260       vi_in.screen = ssi->real_screen_number;
2261       vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
2262       if (!vi_out) continue;
2263       for (j = 0; j < out_count; j++)
2264         if (vi_out[j].class == PseudoColor)
2265           colormapped_depths |= (1 << vi_out[j].depth);
2266         else
2267           non_mapped_depths  |= (1 << vi_out[j].depth);
2268       XFree ((char *) vi_out);
2269
2270       if (colormapped_depths)
2271         {
2272           fprintf (stderr, "%s: screen %d colormapped depths:", blurb(),
2273                    ssi->real_screen_number);
2274           for (j = 0; j < 32; j++)
2275             if (colormapped_depths & (1 << j))
2276               fprintf (stderr, " %d", j);
2277           fprintf (stderr, ".\n");
2278         }
2279       if (non_mapped_depths)
2280         {
2281           fprintf (stderr, "%s: screen %d non-colormapped depths:",
2282                    blurb(), ssi->real_screen_number);
2283           for (j = 0; j < 32; j++)
2284             if (non_mapped_depths & (1 << j))
2285               fprintf (stderr, " %d", j);
2286           fprintf (stderr, ".\n");
2287         }
2288     }
2289
2290   describe_monitor_layout (si);
2291 }
2292
2293
2294 Bool
2295 display_is_on_console_p (saver_info *si)
2296 {
2297   Bool not_on_console = True;
2298   char *dpystr = DisplayString (si->dpy);
2299   char *tail = (char *) strchr (dpystr, ':');
2300   if (! tail || strncmp (tail, ":0", 2))
2301     not_on_console = True;
2302   else
2303     {
2304       char dpyname[255], localname[255];
2305       strncpy (dpyname, dpystr, tail-dpystr);
2306       dpyname [tail-dpystr] = 0;
2307       if (!*dpyname ||
2308           !strcmp(dpyname, "unix") ||
2309           !strcmp(dpyname, "localhost"))
2310         not_on_console = False;
2311       else if (gethostname (localname, sizeof (localname)))
2312         not_on_console = True;  /* can't find hostname? */
2313       else if (!strncmp (dpyname, "/tmp/launch-", 12))  /* MacOS X launchd */
2314         not_on_console = False;
2315       else
2316         {
2317           /* We have to call gethostbyname() on the result of gethostname()
2318              because the two aren't guarenteed to be the same name for the
2319              same host: on some losing systems, one is a FQDN and the other
2320              is not.  Here in the wide wonderful world of Unix it's rocket
2321              science to obtain the local hostname in a portable fashion.
2322              
2323              And don't forget, gethostbyname() reuses the structure it
2324              returns, so we have to copy the fucker before calling it again.
2325              Thank you master, may I have another.
2326            */
2327           struct hostent *h = gethostbyname (dpyname);
2328           if (!h)
2329             not_on_console = True;
2330           else
2331             {
2332               char hn [255];
2333               struct hostent *l;
2334               strcpy (hn, h->h_name);
2335               l = gethostbyname (localname);
2336               not_on_console = (!l || !!(strcmp (l->h_name, hn)));
2337             }
2338         }
2339     }
2340   return !not_on_console;
2341 }
2342
2343
2344 /* Do a little bit of heap introspection...
2345  */
2346 void
2347 check_for_leaks (const char *where)
2348 {
2349 #if defined(HAVE_SBRK) && defined(LEAK_PARANOIA)
2350   static unsigned long last_brk = 0;
2351   int b = (unsigned long) sbrk(0);
2352   if (last_brk && last_brk < b)
2353     fprintf (stderr, "%s: %s: brk grew by %luK.\n",
2354              blurb(), where,
2355              (((b - last_brk) + 1023) / 1024));
2356   last_brk = b;
2357 #endif /* HAVE_SBRK */
2358 }