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