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