http://www.jwz.org/xscreensaver/xscreensaver-5.12.tar.gz
[xscreensaver] / driver / xscreensaver.c
1 /* xscreensaver, Copyright (c) 1991-2008 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /*   ========================================================================
13  *   First we wait until the keyboard and mouse become idle for the specified
14  *   amount of time.  We do this in one of three different ways: periodically
15  *   checking with the XIdle server extension; selecting key and mouse events
16  *   on (nearly) all windows; or by waiting for the MIT-SCREEN-SAVER extension
17  *   to send us a "you are idle" event.
18  *
19  *   Then, we map a full screen black window.
20  *
21  *   We place a __SWM_VROOT property on this window, so that newly-started
22  *   clients will think that this window is a "virtual root" window (as per
23  *   the logic in the historical "vroot.h" header.)
24  *
25  *   If there is an existing "virtual root" window (one that already had
26  *   an __SWM_VROOT property) then we remove that property from that window.
27  *   Otherwise, clients would see that window (the real virtual root) instead
28  *   of ours (the impostor.)
29  *
30  *   Then we pick a random program to run, and start it.  Two assumptions 
31  *   are made about this program: that it has been specified with whatever
32  *   command-line options are necessary to make it run on the root window;
33  *   and that it has been compiled with vroot.h, so that it is able to find
34  *   the root window when a virtual-root window manager (or this program) is
35  *   running.
36  *
37  *   Then, we wait for keyboard or mouse events to be generated on the window.
38  *   When they are, we kill the inferior process, unmap the window, and restore
39  *   the __SWM_VROOT property to the real virtual root window if there was one.
40  *
41  *   On multi-screen systems, we do the above on each screen, and start
42  *   multiple programs, each with a different value of $DISPLAY.
43  *
44  *   On Xinerama systems, we do a similar thing, but instead create multiple
45  *   windows on the (only) display, and tell the subprocess which one to use
46  *   via the $XSCREENSAVER_WINDOW environment variable -- this trick requires
47  *   a recent (Aug 2003) revision of vroot.h.
48  *
49  *   (See comments in screens.c for more details about Xinerama/RANDR stuff.)
50  *
51  *   While we are waiting for user activity, we also set up timers so that,
52  *   after a certain amount of time has passed, we can start a different
53  *   screenhack.  We do this by killing the running child process with
54  *   SIGTERM, and then starting a new one in the same way.
55  *
56  *   If there was a real virtual root, meaning that we removed the __SWM_VROOT
57  *   property from it, meaning we must (absolutely must) restore it before we
58  *   exit, then we set up signal handlers for most signals (SIGINT, SIGTERM,
59  *   etc.) that do this.  Most Xlib and Xt routines are not reentrant, so it
60  *   is not generally safe to call them from signal handlers; however, this
61  *   program spends most of its time waiting, so the window of opportunity 
62  *   when code could be called reentrantly is fairly small; and also, the worst
63  *   that could happen is that the call would fail.  If we've gotten one of
64  *   these signals, then we're on our way out anyway.  If we didn't restore the
65  *   __SWM_VROOT property, that would be very bad, so it's worth a shot.  Note
66  *   that this means that, if you're using a virtual-root window manager, you
67  *   can really fuck up the world by killing this process with "kill -9".
68  *
69  *   This program accepts ClientMessages of type SCREENSAVER; these messages
70  *   may contain the atoms ACTIVATE, DEACTIVATE, etc, meaning to turn the 
71  *   screensaver on or off now, regardless of the idleness of the user,
72  *   and a few other things.  The included "xscreensaver-command" program
73  *   sends these messsages.
74  *
75  *   If we don't have the XIdle, MIT-SCREEN-SAVER, or SGI SCREEN_SAVER
76  *   extensions, then we do the XAutoLock trick: notice every window that
77  *   gets created, and wait 30 seconds or so until its creating process has
78  *   settled down, and then select KeyPress events on those windows which
79  *   already select for KeyPress events.  It's important that we not select
80  *   KeyPress on windows which don't select them, because that would
81  *   interfere with event propagation.  This will break if any program
82  *   changes its event mask to contain KeyRelease or PointerMotion more than
83  *   30 seconds after creating the window, but such programs do not seem to
84  *   occur in nature (I've never seen it happen in all these years.)
85  *
86  *   The reason that we can't select KeyPresses on windows that don't have
87  *   them already is that, when dispatching a KeyPress event, X finds the
88  *   lowest (leafmost) window in the hierarchy on which *any* client selects
89  *   for KeyPress, and sends the event to that window.  This means that if a
90  *   client had a window with subwindows, and expected to receive KeyPress
91  *   events on the parent window instead of the subwindows, then that client
92  *   would malfunction if some other client selected KeyPress events on the
93  *   subwindows.  It is an incredible misdesign that one client can make
94  *   another client malfunction in this way.
95  *
96  *   To detect mouse motion, we periodically wake up and poll the mouse
97  *   position and button/modifier state, and notice when something has
98  *   changed.  We make this check every five seconds by default, and since the
99  *   screensaver timeout has a granularity of one minute, this makes the
100  *   chance of a false positive very small.  We could detect mouse motion in
101  *   the same way as keyboard activity, but that would suffer from the same
102  *   "client changing event mask" problem that the KeyPress events hack does.
103  *   I think polling is more reliable.
104  *
105  *   On systems with /proc/interrupts (Linux) we poll that file and note when
106  *   the interrupt counter numbers on the "keyboard" and "PS/2" lines change.
107  *   (There is no reliable way, using /proc/interrupts, to detect non-PS/2
108  *   mice, so it doesn't help for serial or USB mice.)
109  *
110  *   None of this crap happens if we're using one of the extensions.  Sadly,
111  *   the XIdle extension hasn't been available for many years; the SGI
112  *   extension only exists on SGIs; and the MIT extension, while widely
113  *   deployed, is garbage in several ways.
114  *
115  *   A third idle-detection option could be implemented (but is not): when
116  *   running on the console display ($DISPLAY is `localhost`:0) and we're on a
117  *   machine where /dev/tty and /dev/mouse have reasonable last-modification
118  *   times, we could just stat() those.  But the incremental benefit of
119  *   implementing this is really small, so forget I said anything.
120  *
121  *   Debugging hints:
122  *     - Have a second terminal handy.
123  *     - Be careful where you set your breakpoints, you don't want this to
124  *       stop under the debugger with the keyboard grabbed or the blackout
125  *       window exposed.
126  *     - If you run your debugger under XEmacs, try M-ESC (x-grab-keyboard)
127  *       to keep your emacs window alive even when xscreensaver has grabbed.
128  *     - Go read the code related to `debug_p'.
129  *     - You probably can't set breakpoints in functions that are called on
130  *       the other side of a call to fork() -- if your subprocesses are
131  *       dying with signal 5, Trace/BPT Trap, you're losing in this way.
132  *     - If you aren't using a server extension, don't leave this stopped
133  *       under the debugger for very long, or the X input buffer will get
134  *       huge because of the keypress events it's selecting for.  This can
135  *       make your X server wedge with "no more input buffers."
136  *
137  * ======================================================================== */
138
139 #ifdef HAVE_CONFIG_H
140 # include "config.h"
141 #endif
142
143 #include <stdio.h>
144 #include <ctype.h>
145 #include <X11/Xlib.h>
146
147 #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       /* Don't start the cycle timer in demo mode. */
1231       if (!si->demoing_p && p->cycle)
1232         si->cycle_id = XtAppAddTimeOut (si->app,
1233                                         (si->selection_mode
1234                                          /* see comment in cycle_timer() */
1235                                          ? 1000 * 60 * 60
1236                                          : p->cycle),
1237                                         cycle_timer,
1238                                         (XtPointer) si);
1239
1240
1241 #ifndef NO_LOCKING
1242       /* Maybe start locking the screen.
1243        */
1244       {
1245         Time lock_timeout = p->lock_timeout;
1246
1247         if (si->emergency_lock_p && p->lock_p && lock_timeout)
1248           {
1249             int secs = p->lock_timeout / 1000;
1250             if (p->verbose_p)
1251               fprintf (stderr,
1252                      "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1253                        blurb(),
1254                        (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1255             lock_timeout = 0;
1256           }
1257
1258         si->emergency_lock_p = False;
1259
1260         if (!si->demoing_p &&           /* if not going into demo mode */
1261             p->lock_p &&                /* and locking is enabled */
1262             !si->locking_disabled_p &&  /* and locking is possible */
1263             lock_timeout == 0)          /* and locking is not timer-deferred */
1264           set_locked_p (si, True);      /* then lock right now. */
1265
1266         /* locked_p might be true already because of the above, or because of
1267            the LOCK ClientMessage.  But if not, and if we're supposed to lock
1268            after some time, set up a timer to do so.
1269         */
1270         if (p->lock_p &&
1271             !si->locked_p &&
1272             lock_timeout > 0)
1273           si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1274                                          activate_lock_timer,
1275                                          (XtPointer) si);
1276       }
1277 #endif /* !NO_LOCKING */
1278
1279
1280       ok_to_unblank = True;
1281       do {
1282
1283         check_for_leaks ("blanked A");
1284         sleep_until_idle (si, False);           /* until not idle */
1285         check_for_leaks ("blanked B");
1286
1287         maybe_reload_init_file (si);
1288
1289 #ifndef NO_LOCKING
1290         /* Maybe unlock the screen.
1291          */
1292         if (si->locked_p)
1293           {
1294             saver_screen_info *ssi = si->default_screen;
1295             if (si->locking_disabled_p) abort ();
1296
1297             was_locked = True;
1298             si->dbox_up_p = True;
1299             for (i = 0; i < si->nscreens; i++)
1300               suspend_screenhack (&si->screens[i], True);         /* suspend */
1301             XUndefineCursor (si->dpy, ssi->screensaver_window);
1302
1303             ok_to_unblank = unlock_p (si);
1304
1305             si->dbox_up_p = False;
1306             XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1307             for (i = 0; i < si->nscreens; i++)
1308               suspend_screenhack (&si->screens[i], False);         /* resume */
1309
1310             if (!ok_to_unblank &&
1311                 !screenhack_running_p (si))
1312               {
1313                 /* If the lock dialog has been dismissed and we're not about to
1314                    unlock the screen, and there is currently no hack running,
1315                    then launch one.  (There might be no hack running if DPMS
1316                    had kicked in.  But DPMS is off now, so bring back the hack)
1317                  */
1318                 if (si->cycle_id)
1319                   XtRemoveTimeOut (si->cycle_id);
1320                 si->cycle_id = 0;
1321                 cycle_timer ((XtPointer) si, 0);
1322               }
1323           }
1324 #endif /* !NO_LOCKING */
1325
1326         } while (!ok_to_unblank);
1327
1328
1329       if (p->verbose_p)
1330         fprintf (stderr, "%s: unblanking screen at %s.\n",
1331                  blurb(), timestring ());
1332
1333       /* Kill before unblanking, to stop drawing as soon as possible. */
1334       for (i = 0; i < si->nscreens; i++)
1335         kill_screenhack (&si->screens[i]);
1336       unblank_screen (si);
1337
1338       set_locked_p (si, False);
1339       si->emergency_lock_p = False;
1340       si->demoing_p = 0;
1341       si->selection_mode = 0;
1342
1343       /* If we're throttled, and the user has explicitly unlocked the screen,
1344          then unthrottle.  If we weren't locked, then don't unthrottle
1345          automatically, because someone might have just bumped the desk... */
1346       if (was_locked)
1347         {
1348           if (si->throttled_p && p->verbose_p)
1349             fprintf (stderr, "%s: unthrottled.\n", blurb());
1350           si->throttled_p = False;
1351         }
1352
1353       if (si->cycle_id)
1354         {
1355           XtRemoveTimeOut (si->cycle_id);
1356           si->cycle_id = 0;
1357         }
1358
1359       if (si->lock_id)
1360         {
1361           XtRemoveTimeOut (si->lock_id);
1362           si->lock_id = 0;
1363         }
1364
1365       /* Since we're unblanked now, break race conditions and make
1366          sure we stay that way (see comment in timers.c.) */
1367       if (! si->de_race_id)
1368         de_race_timer ((XtPointer) si, 0);
1369     }
1370 }
1371
1372 static void analyze_display (saver_info *si);
1373 static void fix_fds (void);
1374
1375 int
1376 main (int argc, char **argv)
1377 {
1378   Widget shell;
1379   saver_info the_si;
1380   saver_info *si = &the_si;
1381   saver_preferences *p = &si->prefs;
1382   struct passwd *spasswd;
1383   int i;
1384
1385 #ifdef ENABLE_NLS
1386   if (!setlocale (LC_ALL, ""))
1387     fprintf (stderr, "locale not supported by C library\n");
1388
1389   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1390   textdomain (GETTEXT_PACKAGE);
1391 #endif /* ENABLE_NLS */
1392
1393   memset(si, 0, sizeof(*si));
1394   global_si_kludge = si;        /* I hate C so much... */
1395
1396   fix_fds();
1397
1398 # undef ya_rand_init
1399   ya_rand_init (0);
1400
1401   save_argv (argc, argv);
1402   set_version_string (si, &argc, argv);
1403   privileged_initialization (si, &argc, argv);
1404   hack_environment (si);
1405
1406   spasswd = getpwuid(getuid());
1407   if (!spasswd)
1408     {
1409       fprintf(stderr, "Could not figure out who the current user is!\n");
1410       return 1;
1411     }
1412
1413   si->user = strdup(spasswd->pw_name ? spasswd->pw_name : "(unknown)");
1414
1415 # ifndef NO_LOCKING
1416   si->unlock_cb = gui_auth_conv;
1417   si->auth_finished_cb = auth_finished_cb;
1418 # endif /* !NO_LOCKING */
1419
1420   shell = connect_to_server (si, &argc, argv);
1421   process_command_line (si, &argc, argv);
1422   stderr_log_file (si);
1423   print_banner (si);
1424
1425   load_init_file(si->dpy, p); /* must be before initialize_per_screen_info() */
1426   blurb_timestamp_p = p->timestamp_p;  /* kludge */
1427   initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1428
1429   /* We can only issue this warning now. */
1430   if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1431     fprintf (stderr,
1432              "%s: there are no PseudoColor or GrayScale visuals.\n"
1433              "%s: ignoring the request for fading/unfading.\n",
1434              blurb(), blurb());
1435
1436   for (i = 0; i < si->nscreens; i++)
1437     {
1438       saver_screen_info *ssi = &si->screens[i];
1439       if (ssi->real_screen_p)
1440         if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1441           exit (1);
1442     }
1443
1444   lock_initialization (si, &argc, argv);
1445   print_lock_failure_banner (si);
1446
1447   if (p->xsync_p) XSynchronize (si->dpy, True);
1448
1449   if (p->verbose_p) analyze_display (si);
1450   initialize_server_extensions (si);
1451
1452   si->blank_time = time ((time_t) 0); /* must be before ..._window */
1453   initialize_screensaver_window (si);
1454
1455   select_events (si);
1456   init_sigchld ();
1457
1458   disable_builtin_screensaver (si, True);
1459   sync_server_dpms_settings (si->dpy,
1460                              (p->dpms_enabled_p  &&
1461                               p->mode != DONT_BLANK),
1462                              p->dpms_standby / 1000,
1463                              p->dpms_suspend / 1000,
1464                              p->dpms_off / 1000,
1465                              False);
1466
1467   initialize_stderr (si);
1468   handle_signals (si);
1469
1470   make_splash_dialog (si);
1471
1472   main_loop (si);               /* doesn't return */
1473   return 0;
1474 }
1475
1476 static void
1477 fix_fds (void)
1478 {
1479   /* Bad Things Happen if stdin, stdout, and stderr have been closed
1480      (as by the `sh incantation "xscreensaver >&- 2>&-").  When you do
1481      that, the X connection gets allocated to one of these fds, and
1482      then some random library writes to stderr, and random bits get
1483      stuffed down the X pipe, causing "Xlib: sequence lost" errors.
1484      So, we cause the first three file descriptors to be open to
1485      /dev/null if they aren't open to something else already.  This
1486      must be done before any other files are opened (or the closing
1487      of that other file will again free up one of the "magic" first
1488      three FDs.)
1489
1490      We do this by opening /dev/null three times, and then closing
1491      those fds, *unless* any of them got allocated as #0, #1, or #2,
1492      in which case we leave them open.  Gag.
1493
1494      Really, this crap is technically required of *every* X program,
1495      if you want it to be robust in the face of "2>&-".
1496    */
1497   int fd0 = open ("/dev/null", O_RDWR);
1498   int fd1 = open ("/dev/null", O_RDWR);
1499   int fd2 = open ("/dev/null", O_RDWR);
1500   if (fd0 > 2) close (fd0);
1501   if (fd1 > 2) close (fd1);
1502   if (fd2 > 2) close (fd2);
1503 }
1504
1505
1506 \f
1507 /* Processing ClientMessage events.
1508  */
1509
1510
1511 static Bool error_handler_hit_p = False;
1512
1513 static int
1514 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1515 {
1516   error_handler_hit_p = True;
1517   return 0;
1518 }
1519
1520 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1521    them.  We only look up the atom names for printing warning messages,
1522    so don't bomb out when it happens...
1523  */
1524 static char *
1525 XGetAtomName_safe (Display *dpy, Atom atom)
1526 {
1527   char *result;
1528   XErrorHandler old_handler;
1529   if (!atom) return 0;
1530
1531   XSync (dpy, False);
1532   error_handler_hit_p = False;
1533   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1534   result = XGetAtomName (dpy, atom);
1535   XSync (dpy, False);
1536   XSetErrorHandler (old_handler);
1537   XSync (dpy, False);
1538   if (error_handler_hit_p) result = 0;
1539
1540   if (result)
1541     return result;
1542   else
1543     {
1544       char buf[100];
1545       sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
1546       return strdup (buf);
1547     }
1548 }
1549
1550
1551 static void
1552 clientmessage_response (saver_info *si, Window w, Bool error,
1553                         const char *stderr_msg,
1554                         const char *protocol_msg)
1555 {
1556   char *proto;
1557   int L;
1558   saver_preferences *p = &si->prefs;
1559   XErrorHandler old_handler;
1560
1561   if (error || p->verbose_p)
1562     fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1563
1564   L = strlen(protocol_msg);
1565   proto = (char *) malloc (L + 2);
1566   proto[0] = (error ? '-' : '+');
1567   strcpy (proto+1, protocol_msg);
1568   L++;
1569
1570   /* Ignore all X errors while sending a response to a ClientMessage.
1571      Pretty much the only way we could get an error here is if the
1572      window we're trying to send the reply on has been deleted, in
1573      which case, the sender of the ClientMessage won't see our response
1574      anyway.
1575    */
1576   XSync (si->dpy, False);
1577   error_handler_hit_p = False;
1578   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1579
1580   XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1581                    PropModeReplace, (unsigned char *) proto, L);
1582
1583   XSync (si->dpy, False);
1584   XSetErrorHandler (old_handler);
1585   XSync (si->dpy, False);
1586
1587   free (proto);
1588 }
1589
1590
1591 static void
1592 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1593 {
1594 #if 0  /* Oh, fuck it.  GNOME likes to spew random ClientMessages at us
1595           all the time.  This is presumably indicative of an error in
1596           the sender of that ClientMessage: if we're getting it and 
1597           ignoring it, then it's not reaching the intended recipient.
1598           But people complain to me about this all the time ("waaah!
1599           xscreensaver is printing to it's stderr and that gets my
1600           panties all in a bunch!")  And I'm sick of hearing about it.
1601           So we'll just ignore these messages and let GNOME go right
1602           ahead and continue to stumble along in its malfunction.
1603         */
1604
1605   saver_preferences *p = &si->prefs;
1606   char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1607   Window w = event->xclient.window;
1608   char wdesc[255];
1609   int screen = 0;
1610   Bool root_p = False;
1611
1612   *wdesc = 0;
1613   for (screen = 0; screen < si->nscreens; screen++)
1614     if (w == si->screens[screen].screensaver_window)
1615       {
1616         strcpy (wdesc, "xscreensaver");
1617         break;
1618       }
1619     else if (w == RootWindow (si->dpy, screen))
1620       {
1621         strcpy (wdesc, "root");
1622         root_p = True;
1623         break;
1624       }
1625
1626   /* If this ClientMessage was sent to the real root window instead of to the
1627      xscreensaver window, then it might be intended for someone else who is
1628      listening on the root window (e.g., the window manager).  So only print
1629      the warning if: we are in debug mode; or if the bogus message was
1630      actually sent to one of the xscreensaver-created windows.
1631    */
1632   if (root_p && !p->debug_p)
1633     return;
1634
1635   if (!*wdesc)
1636     {
1637       XErrorHandler old_handler;
1638       XClassHint hint;
1639       XWindowAttributes xgwa;
1640       memset (&hint, 0, sizeof(hint));
1641       memset (&xgwa, 0, sizeof(xgwa));
1642
1643       XSync (si->dpy, False);
1644       old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1645       XGetClassHint (si->dpy, w, &hint);
1646       XGetWindowAttributes (si->dpy, w, &xgwa);
1647       XSync (si->dpy, False);
1648       XSetErrorHandler (old_handler);
1649       XSync (si->dpy, False);
1650
1651       screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1652
1653       sprintf (wdesc, "%.20s / %.20s",
1654                (hint.res_name  ? hint.res_name  : "(null)"),
1655                (hint.res_class ? hint.res_class : "(null)"));
1656       if (hint.res_name)  XFree (hint.res_name);
1657       if (hint.res_class) XFree (hint.res_class);
1658     }
1659
1660   fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1661            blurb(), screen, (str ? str : "(null)"));
1662   fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1663            blurb(), screen, (unsigned long) w, wdesc);
1664   if (str) XFree (str);
1665
1666 #endif /* 0 */
1667 }
1668
1669
1670 Bool
1671 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1672 {
1673   saver_preferences *p = &si->prefs;
1674   Atom type = 0;
1675   Window window = event->xclient.window;
1676
1677   /* Preferences might affect our handling of client messages. */
1678   maybe_reload_init_file (si);
1679
1680   if (event->xclient.message_type != XA_SCREENSAVER ||
1681       event->xclient.format != 32)
1682     {
1683       bogus_clientmessage_warning (si, event);
1684       return False;
1685     }
1686
1687   type = event->xclient.data.l[0];
1688   if (type == XA_ACTIVATE)
1689     {
1690       if (until_idle_p)
1691         {
1692           if (p->mode == DONT_BLANK)
1693             {
1694               clientmessage_response(si, window, True,
1695                          "ACTIVATE ClientMessage received in DONT_BLANK mode.",
1696                                      "screen blanking is currently disabled.");
1697               return False;
1698             }
1699
1700           clientmessage_response(si, window, False,
1701                                  "ACTIVATE ClientMessage received.",
1702                                  "activating.");
1703           si->selection_mode = 0;
1704           si->demoing_p = False;
1705
1706           if (si->throttled_p && p->verbose_p)
1707             fprintf (stderr, "%s: unthrottled.\n", blurb());
1708           si->throttled_p = False;
1709
1710           if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1711             {
1712               XForceScreenSaver (si->dpy, ScreenSaverActive);
1713               return False;
1714             }
1715           else
1716             {
1717               return True;
1718             }
1719         }
1720       clientmessage_response(si, window, True,
1721                        "ClientMessage ACTIVATE received while already active.",
1722                              "already active.");
1723     }
1724   else if (type == XA_DEACTIVATE)
1725     {
1726       if (! until_idle_p)
1727         {
1728           if (si->throttled_p && p->verbose_p)
1729             fprintf (stderr, "%s: unthrottled.\n", blurb());
1730           si->throttled_p = False;
1731
1732           clientmessage_response(si, window, False,
1733                                  "DEACTIVATE ClientMessage received.",
1734                                  "deactivating.");
1735           if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1736             {
1737               XForceScreenSaver (si->dpy, ScreenSaverReset);
1738               return False;
1739             }
1740           else
1741             {
1742               return True;
1743             }
1744         }
1745       clientmessage_response(si, window, False,
1746      "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
1747                              "not active: idle timer reset.");
1748       reset_timers (si);
1749     }
1750   else if (type == XA_CYCLE)
1751     {
1752       if (! until_idle_p)
1753         {
1754           clientmessage_response(si, window, False,
1755                                  "CYCLE ClientMessage received.",
1756                                  "cycling.");
1757           si->selection_mode = 0;       /* 0 means randomize when its time. */
1758           si->demoing_p = False;
1759
1760           if (si->throttled_p && p->verbose_p)
1761             fprintf (stderr, "%s: unthrottled.\n", blurb());
1762           si->throttled_p = False;
1763
1764           if (si->cycle_id)
1765             XtRemoveTimeOut (si->cycle_id);
1766           si->cycle_id = 0;
1767           cycle_timer ((XtPointer) si, 0);
1768           return False;
1769         }
1770       clientmessage_response(si, window, True,
1771                              "ClientMessage CYCLE received while inactive.",
1772                              "not active.");
1773     }
1774   else if (type == XA_NEXT || type == XA_PREV)
1775     {
1776       clientmessage_response(si, window, False,
1777                              (type == XA_NEXT
1778                               ? "NEXT ClientMessage received."
1779                               : "PREV ClientMessage received."),
1780                              "cycling.");
1781       si->selection_mode = (type == XA_NEXT ? -1 : -2);
1782       si->demoing_p = False;
1783
1784       if (si->throttled_p && p->verbose_p)
1785         fprintf (stderr, "%s: unthrottled.\n", blurb());
1786       si->throttled_p = False;
1787
1788       if (! until_idle_p)
1789         {
1790           if (si->cycle_id)
1791             XtRemoveTimeOut (si->cycle_id);
1792           si->cycle_id = 0;
1793           cycle_timer ((XtPointer) si, 0);
1794         }
1795       else
1796         return True;
1797     }
1798   else if (type == XA_SELECT)
1799     {
1800       char buf [255];
1801       char buf2 [255];
1802       long which = event->xclient.data.l[1];
1803
1804       if (p->mode == DONT_BLANK)
1805         {
1806           clientmessage_response(si, window, True,
1807                            "SELECT ClientMessage received in DONT_BLANK mode.",
1808                                  "screen blanking is currently disabled.");
1809           return False;
1810         }
1811
1812       sprintf (buf, "SELECT %ld ClientMessage received.", which);
1813       sprintf (buf2, "activating (%ld).", which);
1814       clientmessage_response (si, window, False, buf, buf2);
1815
1816       if (which < 0) which = 0;         /* 0 == "random" */
1817       si->selection_mode = which;
1818       si->demoing_p = False;
1819
1820       if (si->throttled_p && p->verbose_p)
1821         fprintf (stderr, "%s: unthrottled.\n", blurb());
1822       si->throttled_p = False;
1823
1824       if (! until_idle_p)
1825         {
1826           if (si->cycle_id)
1827             XtRemoveTimeOut (si->cycle_id);
1828           si->cycle_id = 0;
1829           cycle_timer ((XtPointer) si, 0);
1830         }
1831       else
1832         return True;
1833     }
1834   else if (type == XA_EXIT)
1835     {
1836       /* Ignore EXIT message if the screen is locked. */
1837       if (until_idle_p || !si->locked_p)
1838         {
1839           clientmessage_response (si, window, False,
1840                                   "EXIT ClientMessage received.",
1841                                   "exiting.");
1842           if (! until_idle_p)
1843             {
1844               int i;
1845               for (i = 0; i < si->nscreens; i++)
1846                 kill_screenhack (&si->screens[i]);
1847               unblank_screen (si);
1848               XSync (si->dpy, False);
1849             }
1850           saver_exit (si, 0, 0);
1851         }
1852       else
1853         clientmessage_response (si, window, True,
1854                                 "EXIT ClientMessage received while locked.",
1855                                 "screen is locked.");
1856     }
1857   else if (type == XA_RESTART)
1858     {
1859       /* The RESTART message works whether the screensaver is active or not,
1860          unless the screen is locked, in which case it doesn't work.
1861        */
1862       if (until_idle_p || !si->locked_p)
1863         {
1864           clientmessage_response (si, window, False,
1865                                   "RESTART ClientMessage received.",
1866                                   "restarting.");
1867           if (! until_idle_p)
1868             {
1869               int i;
1870               for (i = 0; i < si->nscreens; i++)
1871                 kill_screenhack (&si->screens[i]);
1872               unblank_screen (si);
1873               XSync (si->dpy, False);
1874             }
1875
1876           restart_process (si);  /* does not return */
1877           abort();
1878         }
1879       else
1880         clientmessage_response (si, window, True,
1881                                 "RESTART ClientMessage received while locked.",
1882                                 "screen is locked.");
1883     }
1884   else if (type == XA_DEMO)
1885     {
1886       long arg = event->xclient.data.l[1];
1887       Bool demo_one_hack_p = (arg == 5000);
1888
1889       if (demo_one_hack_p)
1890         {
1891           if (until_idle_p)
1892             {
1893               long which = event->xclient.data.l[2];
1894               char buf [255];
1895               char buf2 [255];
1896               sprintf (buf, "DEMO %ld ClientMessage received.", which);
1897               sprintf (buf2, "demoing (%ld).", which);
1898               clientmessage_response (si, window, False, buf, buf2);
1899
1900               if (which < 0) which = 0;         /* 0 == "random" */
1901               si->selection_mode = which;
1902               si->demoing_p = True;
1903
1904               if (si->throttled_p && p->verbose_p)
1905                 fprintf (stderr, "%s: unthrottled.\n", blurb());
1906               si->throttled_p = False;
1907
1908               return True;
1909             }
1910
1911           clientmessage_response (si, window, True,
1912                                   "DEMO ClientMessage received while active.",
1913                                   "already active.");
1914         }
1915       else
1916         {
1917           clientmessage_response (si, window, True,
1918                                   "obsolete form of DEMO ClientMessage.",
1919                                   "obsolete form of DEMO ClientMessage.");
1920         }
1921     }
1922   else if (type == XA_PREFS)
1923     {
1924       clientmessage_response (si, window, True,
1925                               "the PREFS client-message is obsolete.",
1926                               "the PREFS client-message is obsolete.");
1927     }
1928   else if (type == XA_LOCK)
1929     {
1930 #ifdef NO_LOCKING
1931       clientmessage_response (si, window, True,
1932                               "not compiled with support for locking.",
1933                               "locking not enabled.");
1934 #else /* !NO_LOCKING */
1935       if (si->locking_disabled_p)
1936         clientmessage_response (si, window, True,
1937                       "LOCK ClientMessage received, but locking is disabled.",
1938                               "locking not enabled.");
1939       else if (si->locked_p)
1940         clientmessage_response (si, window, True,
1941                            "LOCK ClientMessage received while already locked.",
1942                                 "already locked.");
1943       else
1944         {
1945           char buf [255];
1946           char *response = (until_idle_p
1947                             ? "activating and locking."
1948                             : "locking.");
1949           sprintf (buf, "LOCK ClientMessage received; %s", response);
1950           clientmessage_response (si, window, False, buf, response);
1951           set_locked_p (si, True);
1952           si->selection_mode = 0;
1953           si->demoing_p = False;
1954
1955           if (si->lock_id)      /* we're doing it now, so lose the timeout */
1956             {
1957               XtRemoveTimeOut (si->lock_id);
1958               si->lock_id = 0;
1959             }
1960
1961           if (until_idle_p)
1962             {
1963               if (si->using_mit_saver_extension ||
1964                   si->using_sgi_saver_extension)
1965                 {
1966                   XForceScreenSaver (si->dpy, ScreenSaverActive);
1967                   return False;
1968                 }
1969               else
1970                 {
1971                   return True;
1972                 }
1973             }
1974         }
1975 #endif /* !NO_LOCKING */
1976     }
1977   else if (type == XA_THROTTLE)
1978     {
1979       /* The THROTTLE command is deprecated -- it predates the XDPMS
1980          extension.  Instead of using -throttle, users should instead
1981          just power off the monitor (e.g., "xset dpms force off".)
1982          In a few minutes, xscreensaver will notice that the monitor
1983          is off, and cease running hacks.
1984        */
1985       if (si->throttled_p)
1986         clientmessage_response (si, window, True,
1987                                 "THROTTLE ClientMessage received, but "
1988                                 "already throttled.",
1989                                 "already throttled.");
1990       else
1991         {
1992           char buf [255];
1993           char *response = "throttled.";
1994           si->throttled_p = True;
1995           si->selection_mode = 0;
1996           si->demoing_p = False;
1997           sprintf (buf, "THROTTLE ClientMessage received; %s", response);
1998           clientmessage_response (si, window, False, buf, response);
1999
2000           if (! until_idle_p)
2001             {
2002               if (si->cycle_id)
2003                 XtRemoveTimeOut (si->cycle_id);
2004               si->cycle_id = 0;
2005               cycle_timer ((XtPointer) si, 0);
2006             }
2007         }
2008     }
2009   else if (type == XA_UNTHROTTLE)
2010     {
2011       if (! si->throttled_p)
2012         clientmessage_response (si, window, True,
2013                                 "UNTHROTTLE ClientMessage received, but "
2014                                 "not throttled.",
2015                                 "not throttled.");
2016       else
2017         {
2018           char buf [255];
2019           char *response = "unthrottled.";
2020           si->throttled_p = False;
2021           si->selection_mode = 0;
2022           si->demoing_p = False;
2023           sprintf (buf, "UNTHROTTLE 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
2036     {
2037       char buf [1024];
2038       char *str;
2039       str = XGetAtomName_safe (si->dpy, type);
2040
2041       if (str)
2042         {
2043           if (strlen (str) > 80)
2044             strcpy (str+70, "...");
2045           sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
2046                    str);
2047           free (str);
2048         }
2049       else
2050         {
2051           sprintf (buf,
2052                    "unrecognised screensaver ClientMessage 0x%x received.",
2053                    (unsigned int) event->xclient.data.l[0]);
2054         }
2055
2056       clientmessage_response (si, window, True, buf, buf);
2057     }
2058   return False;
2059 }
2060
2061 \f
2062 /* Some random diagnostics printed in -verbose mode.
2063  */
2064
2065 static void
2066 analyze_display (saver_info *si)
2067 {
2068   int i, j;
2069   static struct {
2070     const char *name; const char *desc; 
2071     Bool useful_p;
2072     Status (*version_fn) (Display *, int *majP, int *minP);
2073   } exts[] = {
2074
2075    { "SCREEN_SAVER", /* underscore */           "SGI Screen-Saver",
2076 #     ifdef HAVE_SGI_SAVER_EXTENSION
2077         True,  0
2078 #     else
2079         False, 0
2080 #     endif
2081    }, { "SCREEN-SAVER", /* dash */              "SGI Screen-Saver",
2082 #     ifdef HAVE_SGI_SAVER_EXTENSION
2083         True,  0
2084 #     else
2085         False, 0
2086 #     endif
2087    }, { "MIT-SCREEN-SAVER",                     "MIT Screen-Saver",
2088 #     ifdef HAVE_MIT_SAVER_EXTENSION
2089         True,  XScreenSaverQueryVersion
2090 #     else
2091         False, 0
2092 #     endif
2093    }, { "XIDLE",                                "XIdle",           
2094 #     ifdef HAVE_XIDLE_EXTENSION
2095         True,  0
2096 #     else
2097         False, 0
2098 #     endif
2099    }, { "SGI-VIDEO-CONTROL",                    "SGI Video-Control",
2100 #     ifdef HAVE_SGI_VC_EXTENSION
2101         True,  XSGIvcQueryVersion
2102 #     else
2103         False, 0
2104 #     endif
2105    }, { "READDISPLAY",                          "SGI Read-Display",
2106 #     ifdef HAVE_READ_DISPLAY_EXTENSION
2107         True,  XReadDisplayQueryVersion
2108 #     else
2109         False, 0
2110 #     endif
2111    }, { "MIT-SHM",                              "Shared Memory",   
2112 #     ifdef HAVE_XSHM_EXTENSION
2113         True, (Status (*) (Display*,int*,int*)) XShmQueryVersion /* 4 args */
2114 #     else
2115         False, 0
2116 #     endif
2117    }, { "DOUBLE-BUFFER",                        "Double-Buffering",
2118 #     ifdef HAVE_DOUBLE_BUFFER_EXTENSION
2119         True, XdbeQueryExtension
2120 #     else
2121         False, 0
2122 #     endif
2123    }, { "DPMS",                                 "Power Management",
2124 #     ifdef HAVE_DPMS_EXTENSION
2125         True,  DPMSGetVersion
2126 #     else
2127         False, 0
2128 #     endif
2129    }, { "GLX",                                  "GLX",             
2130 #     ifdef HAVE_GL
2131         True,  0
2132 #     else
2133         False, 0
2134 #     endif
2135    }, { "XFree86-VidModeExtension",             "XF86 Video-Mode", 
2136 #     ifdef HAVE_XF86VMODE
2137         True,  XF86VidModeQueryVersion
2138 #     else
2139         False, 0
2140 #     endif
2141    }, { "XC-VidModeExtension",                  "XC Video-Mode", 
2142 #     ifdef HAVE_XF86VMODE
2143         True,  XF86VidModeQueryVersion
2144 #     else
2145         False, 0
2146 #     endif
2147    }, { "XFree86-MISC",                         "XF86 Misc", 
2148 #     ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2149         True,  XF86MiscQueryVersion
2150 #     else
2151         False, 0
2152 #     endif
2153    }, { "XC-MISC",                              "XC Misc", 
2154 #     ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2155         True,  XF86MiscQueryVersion
2156 #     else
2157         False, 0
2158 #     endif
2159    }, { "XINERAMA",                             "Xinerama",
2160 #     ifdef HAVE_XINERAMA
2161         True,  XineramaQueryVersion
2162 #     else
2163         False, 0
2164 #     endif
2165    }, { "RANDR",                                "Resize-and-Rotate",
2166 #     ifdef HAVE_RANDR
2167         True,  XRRQueryVersion
2168 #     else
2169         False, 0
2170 #     endif
2171    }, { "DRI",                                  "DRI",
2172         True,  0
2173    }, { "NV-CONTROL",                           "NVidia",
2174         True,  0
2175    }, { "NV-GLX",                               "NVidia GLX",
2176         True,  0
2177    }, { "Apple-DRI",                            "Apple-DRI (XDarwin)",
2178         True,  0
2179    }, { "XInputExtension",                      "XInput",
2180         True,  0
2181    },
2182   };
2183
2184   fprintf (stderr, "%s: running on display \"%s\"\n", blurb(), 
2185            DisplayString(si->dpy));
2186   fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
2187            ServerVendor(si->dpy), VendorRelease(si->dpy));
2188
2189   fprintf (stderr, "%s: useful extensions:\n", blurb());
2190   for (i = 0; i < countof(exts); i++)
2191     {
2192       int op = 0, event = 0, error = 0;
2193       char buf [255];
2194       int maj = 0, min = 0;
2195       int dummy1, dummy2, dummy3;
2196       int j;
2197
2198       /* Most of the extension version functions take 3 args,
2199          writing results into args 2 and 3, but some take more.
2200          We only ever care about the first two results, but we
2201          pass in three extra pointers just in case.
2202        */
2203       Status (*version_fn_2) (Display*,int*,int*,int*,int*,int*) =
2204         (Status (*) (Display*,int*,int*,int*,int*,int*)) exts[i].version_fn;
2205
2206       if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
2207         continue;
2208       sprintf (buf, "%s:   ", blurb());
2209       j = strlen (buf);
2210       strcat (buf, exts[i].desc);
2211
2212       if (!version_fn_2)
2213         ;
2214       else if (version_fn_2 (si->dpy, &maj, &min, &dummy1, &dummy2, &dummy3))
2215         sprintf (buf+strlen(buf), " (%d.%d)", maj, min);
2216       else
2217         strcat (buf, " (unavailable)");
2218
2219       if (!exts[i].useful_p)
2220         strcat (buf, " (disabled at compile time)");
2221       fprintf (stderr, "%s\n", buf);
2222     }
2223
2224   for (i = 0; i < si->nscreens; i++)
2225     {
2226       saver_screen_info *ssi = &si->screens[i];
2227       unsigned long colormapped_depths = 0;
2228       unsigned long non_mapped_depths = 0;
2229       XVisualInfo vi_in, *vi_out;
2230       int out_count;
2231
2232       if (!ssi->real_screen_p) continue;
2233
2234       vi_in.screen = ssi->real_screen_number;
2235       vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
2236       if (!vi_out) continue;
2237       for (j = 0; j < out_count; j++)
2238         if (vi_out[j].class == PseudoColor)
2239           colormapped_depths |= (1 << vi_out[j].depth);
2240         else
2241           non_mapped_depths  |= (1 << vi_out[j].depth);
2242       XFree ((char *) vi_out);
2243
2244       if (colormapped_depths)
2245         {
2246           fprintf (stderr, "%s: screen %d colormapped depths:", blurb(),
2247                    ssi->real_screen_number);
2248           for (j = 0; j < 32; j++)
2249             if (colormapped_depths & (1 << j))
2250               fprintf (stderr, " %d", j);
2251           fprintf (stderr, ".\n");
2252         }
2253       if (non_mapped_depths)
2254         {
2255           fprintf (stderr, "%s: screen %d non-colormapped depths:",
2256                    blurb(), ssi->real_screen_number);
2257           for (j = 0; j < 32; j++)
2258             if (non_mapped_depths & (1 << j))
2259               fprintf (stderr, " %d", j);
2260           fprintf (stderr, ".\n");
2261         }
2262     }
2263
2264   describe_monitor_layout (si);
2265 }
2266
2267
2268 Bool
2269 display_is_on_console_p (saver_info *si)
2270 {
2271   Bool not_on_console = True;
2272   char *dpystr = DisplayString (si->dpy);
2273   char *tail = (char *) strchr (dpystr, ':');
2274   if (! tail || strncmp (tail, ":0", 2))
2275     not_on_console = True;
2276   else
2277     {
2278       char dpyname[255], localname[255];
2279       strncpy (dpyname, dpystr, tail-dpystr);
2280       dpyname [tail-dpystr] = 0;
2281       if (!*dpyname ||
2282           !strcmp(dpyname, "unix") ||
2283           !strcmp(dpyname, "localhost"))
2284         not_on_console = False;
2285       else if (gethostname (localname, sizeof (localname)))
2286         not_on_console = True;  /* can't find hostname? */
2287       else if (!strncmp (dpyname, "/tmp/launch-", 12))  /* MacOS X launchd */
2288         not_on_console = False;
2289       else
2290         {
2291           /* We have to call gethostbyname() on the result of gethostname()
2292              because the two aren't guarenteed to be the same name for the
2293              same host: on some losing systems, one is a FQDN and the other
2294              is not.  Here in the wide wonderful world of Unix it's rocket
2295              science to obtain the local hostname in a portable fashion.
2296              
2297              And don't forget, gethostbyname() reuses the structure it
2298              returns, so we have to copy the fucker before calling it again.
2299              Thank you master, may I have another.
2300            */
2301           struct hostent *h = gethostbyname (dpyname);
2302           if (!h)
2303             not_on_console = True;
2304           else
2305             {
2306               char hn [255];
2307               struct hostent *l;
2308               strcpy (hn, h->h_name);
2309               l = gethostbyname (localname);
2310               not_on_console = (!l || !!(strcmp (l->h_name, hn)));
2311             }
2312         }
2313     }
2314   return !not_on_console;
2315 }
2316
2317
2318 /* Do a little bit of heap introspection...
2319  */
2320 void
2321 check_for_leaks (const char *where)
2322 {
2323 #if defined(HAVE_SBRK) && defined(LEAK_PARANOIA)
2324   static unsigned long last_brk = 0;
2325   int b = (unsigned long) sbrk(0);
2326   if (last_brk && last_brk < b)
2327     fprintf (stderr, "%s: %s: brk grew by %luK.\n",
2328              blurb(), where,
2329              (((b - last_brk) + 1023) / 1024));
2330   last_brk = b;
2331 #endif /* HAVE_SBRK */
2332 }