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