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