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