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