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