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