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