1 /* xscreensaver, Copyright (c) 1991-2000 Jamie Zawinski <jwz@jwz.org>
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
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.
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.)
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.
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.)
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
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.
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.
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".
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.
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.)
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.
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.
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.
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.
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
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."
121 * ======================================================================== */
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>
135 #include <netdb.h> /* for gethostbyname() */
138 # include <X11/Xmu/Error.h>
140 # include <Xmu/Error.h>
142 #else /* !HAVE_XMU */
144 #endif /* !HAVE_XMU */
146 #ifdef HAVE_XIDLE_EXTENSION
147 # include <X11/extensions/xidle.h>
148 #endif /* HAVE_XIDLE_EXTENSION */
150 #include "xscreensaver.h"
152 #include "yarandom.h"
153 #include "resources.h"
157 saver_info *global_si_kludge = 0; /* I hate C so much... */
164 static Atom XA_SCREENSAVER_RESPONSE;
165 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
166 static Atom XA_RESTART, XA_SELECT;
167 static Atom XA_THROTTLE, XA_UNTHROTTLE;
168 Atom XA_DEMO, XA_PREFS, XA_EXIT, XA_LOCK, XA_BLANK;
171 static XrmOptionDescRec options [] = {
172 { "-timeout", ".timeout", XrmoptionSepArg, 0 },
173 { "-cycle", ".cycle", XrmoptionSepArg, 0 },
174 { "-lock-mode", ".lock", XrmoptionNoArg, "on" },
175 { "-no-lock-mode", ".lock", XrmoptionNoArg, "off" },
176 { "-no-lock", ".lock", XrmoptionNoArg, "off" },
177 { "-lock-timeout", ".lockTimeout", XrmoptionSepArg, 0 },
178 { "-lock-vts", ".lockVTs", XrmoptionNoArg, "on" },
179 { "-no-lock-vts", ".lockVTs", XrmoptionNoArg, "off" },
180 { "-visual", ".visualID", XrmoptionSepArg, 0 },
181 { "-install", ".installColormap", XrmoptionNoArg, "on" },
182 { "-no-install", ".installColormap", XrmoptionNoArg, "off" },
183 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
184 { "-silent", ".verbose", XrmoptionNoArg, "off" },
185 { "-timestamp", ".timestamp", XrmoptionNoArg, "on" },
186 { "-capture-stderr", ".captureStderr", XrmoptionNoArg, "on" },
187 { "-no-capture-stderr", ".captureStderr", XrmoptionNoArg, "off" },
188 { "-xidle-extension", ".xidleExtension", XrmoptionNoArg, "on" },
189 { "-no-xidle-extension", ".xidleExtension", XrmoptionNoArg, "off" },
190 { "-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "on" },
191 { "-no-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "off" },
192 { "-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "on" },
193 { "-no-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "off" },
194 { "-proc-interrupts", ".procInterrupts", XrmoptionNoArg, "on" },
195 { "-no-proc-interrupts", ".procInterrupts", XrmoptionNoArg, "off" },
196 { "-splash", ".splash", XrmoptionNoArg, "on" },
197 { "-no-splash", ".splash", XrmoptionNoArg, "off" },
198 { "-nosplash", ".splash", XrmoptionNoArg, "off" },
199 { "-idelay", ".initialDelay", XrmoptionSepArg, 0 },
200 { "-nice", ".nice", XrmoptionSepArg, 0 },
202 /* Actually these are built in to Xt, but just to be sure... */
203 { "-synchronous", ".synchronous", XrmoptionNoArg, "on" },
204 { "-xrm", NULL, XrmoptionResArg, NULL }
207 static char *defaults[] = {
208 #include "XScreenSaver_ad.h"
213 ERROR! You must not include vroot.h in this file.
217 do_help (saver_info *si)
222 xscreensaver %s, copyright (c) 1991-2000 by Jamie Zawinski <jwz@jwz.org>\n\
223 The standard Xt command-line options are accepted; other options include:\n\
225 -timeout <minutes> When the screensaver should activate.\n\
226 -cycle <minutes> How long to let each hack run before switching.\n\
227 -lock-mode Require a password before deactivating.\n\
228 -lock-timeout <minutes> Grace period before locking; default 0.\n\
229 -visual <id-or-class> Which X visual to run on.\n\
230 -install Install a private colormap.\n\
232 -no-splash Don't display a splash-screen at startup.\n\
233 -help This message.\n\
235 See the manual for other options and X resources.\n\
237 The `xscreensaver' program should be left running in the background.\n\
238 Use the `xscreensaver-demo' and `xscreensaver-command' programs to\n\
239 manipulate a running xscreensaver.\n\
241 The `*programs' resource controls which graphics demos will be launched by\n\
242 the screensaver. See `man xscreensaver' or the web page for more details.\n\
244 Just getting started? Try this:\n\
249 For updates, check http://www.jwz.org/xscreensaver/\n\
261 time_t now = time ((time_t *) 0);
262 char *str = (char *) ctime (&now);
263 char *nl = (char *) strchr (str, '\n');
264 if (nl) *nl = 0; /* take off that dang newline */
268 static Bool blurb_timestamp_p = False; /* kludge */
273 if (!blurb_timestamp_p)
277 static char buf[255];
278 char *ct = timestring();
279 int n = strlen(progname);
281 strncpy(buf, progname, n);
284 strncpy(buf+n, ct+11, 8);
285 strcpy(buf+n+9, ": ");
292 saver_ehandler (Display *dpy, XErrorEvent *error)
294 saver_info *si = global_si_kludge; /* I hate C so much... */
296 if (!real_stderr) real_stderr = stderr;
298 fprintf (real_stderr, "\n"
299 "#######################################"
300 "#######################################\n\n"
301 "%s: X Error! PLEASE REPORT THIS BUG.\n\n"
302 "#######################################"
303 "#######################################\n\n",
305 if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
307 fprintf (real_stderr, "\n");
308 if (si->prefs.xsync_p)
310 saver_exit (si, -1, "because of synchronous X Error");
314 fprintf (real_stderr,
315 "#######################################"
316 "#######################################\n\n");
317 fprintf (real_stderr,
318 " If at all possible, please re-run xscreensaver with the command
\ e\n"
319 " line arguments `-sync -verbose -no-capture', and reproduce this\n"
320 " bug. That will cause xscreensaver to dump a `core' file to the\n"
321 " current directory. Please include the stack trace from that core\n"
322 " file in your bug report. *DO NOT* mail the core file itself!\n"
323 " That won't work.\n"
325 " http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
326 " the most useful bug reports, and how to examine core files.\n"
328 " The more information you can provide, the better. But please\n"
329 " report this bug, regardless!\n"
331 fprintf (real_stderr,
332 "#######################################"
333 "#######################################\n\n");
335 saver_exit (si, -1, 0);
339 fprintf (real_stderr, " (nonfatal.)\n");
344 /* This error handler is used only while the X connection is being set up;
345 after we've got a connection, we don't use this handler again. The only
346 reason for having this is so that we can present a more idiot-proof error
347 message than "cannot open display."
350 startup_ehandler (String name, String type, String class,
351 String defalt, /* one can't even spel properly
352 in this joke of a language */
353 String *av, Cardinal *ac)
357 saver_info *si = global_si_kludge; /* I hate C so much... */
358 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
360 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
361 fmt, sizeof(fmt)-1, *db);
363 fprintf (stderr, "%s: ", blurb());
365 memset (p, 0, sizeof(p));
366 if (*ac > countof (p)) *ac = countof (p);
367 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
368 fprintf (stderr, fmt, /* Did I mention that I hate C? */
369 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
370 fprintf (stderr, "\n");
372 describe_uids (si, stderr);
373 fprintf (stderr, "\n"
374 "%s: Errors at startup are usually authorization problems.\n"
375 " Did you read the manual and the FAQ? Specifically,\n"
376 " the parts of the manual that talk about XAUTH, XDM,\n"
377 " and root logins?\n"
379 " http://www.jwz.org/xscreensaver/faq.html\n"
380 " http://www.jwz.org/xscreensaver/man.html\n"
390 /* The zillions of initializations.
393 /* Set progname, version, etc. This is done very early.
396 set_version_string (saver_info *si, int *argc, char **argv)
398 progclass = "XScreenSaver";
400 /* progname is reset later, after we connect to X. */
401 progname = strrchr(argv[0], '/');
402 if (progname) progname++;
403 else progname = argv[0];
405 if (strlen(progname) > 100) /* keep it short. */
408 /* The X resource database blows up if argv[0] has a "." in it. */
411 while ((s = strchr (s, '.')))
415 si->version = (char *) malloc (5);
416 memcpy (si->version, screensaver_id + 17, 4);
421 /* Initializations that potentially take place as a priveleged user:
422 If the xscreensaver executable is setuid root, then these initializations
423 are run as root, before discarding privileges.
426 privileged_initialization (saver_info *si, int *argc, char **argv)
429 /* before hack_uid() for proper permissions */
430 lock_priv_init (*argc, argv, si->prefs.verbose_p);
431 #endif /* NO_LOCKING */
437 /* Figure out what locking mechanisms are supported.
440 lock_initialization (saver_info *si, int *argc, char **argv)
443 si->locking_disabled_p = True;
444 si->nolock_reason = "not compiled with locking support";
445 #else /* !NO_LOCKING */
447 /* Finish initializing locking, now that we're out of privileged code. */
448 if (! lock_init (*argc, argv, si->prefs.verbose_p))
450 si->locking_disabled_p = True;
451 si->nolock_reason = "error getting password";
453 #endif /* NO_LOCKING */
459 /* Open the connection to the X server, and intern our Atoms.
462 connect_to_server (saver_info *si, int *argc, char **argv)
464 Widget toplevel_shell;
466 XSetErrorHandler (saver_ehandler);
468 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
469 toplevel_shell = XtAppInitialize (&si->app, progclass,
470 options, XtNumber (options),
471 argc, argv, defaults, 0, 0);
472 XtAppSetErrorMsgHandler (si->app, 0);
474 si->dpy = XtDisplay (toplevel_shell);
475 si->prefs.db = XtDatabase (si->dpy);
476 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
478 if(strlen(progname) > 100) /* keep it short. */
481 db = si->prefs.db; /* resources.c needs this */
483 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
484 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
485 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
486 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
487 XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
488 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
490 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
491 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
492 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
493 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
494 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
495 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
496 XA_PREV = XInternAtom (si->dpy, "PREV", False);
497 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
498 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
499 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
500 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
501 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
502 XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
503 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
504 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
506 return toplevel_shell;
510 /* Handle the command-line arguments that were not handled for us by Xt.
511 Issue an error message and exit if there are unknown options.
514 process_command_line (saver_info *si, int *argc, char **argv)
517 for (i = 1; i < *argc; i++)
519 if (!strcmp (argv[i], "-debug"))
520 /* no resource for this one, out of paranoia. */
521 si->prefs.debug_p = True;
523 else if (!strcmp (argv[i], "-h") ||
524 !strcmp (argv[i], "-help") ||
525 !strcmp (argv[i], "--help"))
530 const char *s = argv[i];
531 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
534 if (s[0] == '-' && s[1] == '-') s++;
535 if (!strcmp (s, "-activate") ||
536 !strcmp (s, "-deactivate") ||
537 !strcmp (s, "-cycle") ||
538 !strcmp (s, "-next") ||
539 !strcmp (s, "-prev") ||
540 !strcmp (s, "-exit") ||
541 !strcmp (s, "-restart") ||
542 !strcmp (s, "-demo") ||
543 !strcmp (s, "-prefs") ||
544 !strcmp (s, "-preferences") ||
545 !strcmp (s, "-lock") ||
546 !strcmp (s, "-version") ||
547 !strcmp (s, "-time"))
550 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
551 fprintf (stderr, "\n\
552 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
554 fprintf (stderr, "\n\
555 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
558 The `xscreensaver' program is a daemon that runs in the background.\n\
559 You control a running xscreensaver process by sending it messages\n\
560 with `xscreensaver-demo' or `xscreensaver-command'.\n\
561 . See the man pages for details, or check the web page:\n\
562 http://www.jwz.org/xscreensaver/\n\n");
564 /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
565 suggest that explicitly. */
566 if (!strcmp (s, "-lock"))
568 Or perhaps you meant either the \"-lock-mode\" or the\n\
569 \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
578 /* Print out the xscreensaver banner to the tty if applicable;
579 Issue any other warnings that are called for at this point.
582 print_banner (saver_info *si)
584 saver_preferences *p = &si->prefs;
586 /* This resource gets set some time before the others, so that we know
587 whether to print the banner (and so that the banner gets printed before
588 any resource-database-related error messages.)
590 p->verbose_p = (p->debug_p || get_boolean_resource ("verbose", "Boolean"));
592 /* Ditto, for the locking_disabled_p message. */
593 p->lock_p = get_boolean_resource ("lock", "Boolean");
597 "%s %s, copyright (c) 1991-2000 "
598 "by Jamie Zawinski <jwz@jwz.org>.\n",
599 progname, si->version);
602 fprintf (stderr, "\n"
603 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
605 "\tNote that in debug mode, the xscreensaver window will only\n"
606 "\tcover the left half of the screen. (The idea is that you\n"
607 "\tcan still see debugging output in a shell, if you position\n"
608 "\tit on the right side of the screen.)\n"
610 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
611 "\tuntrusted environments.\n"
617 if (!si->uid_message || !*si->uid_message)
618 describe_uids (si, stderr);
621 if (si->orig_uid && *si->orig_uid)
622 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
623 blurb(), si->orig_uid);
624 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
627 fprintf (stderr, "%s: in process %lu.\n", blurb(),
628 (unsigned long) getpid());
631 /* If locking was not able to be initalized for some reason, explain why.
632 (This has to be done after we've read the lock_p resource.)
634 if (p->lock_p && si->locking_disabled_p)
637 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
639 if (strstr (si->nolock_reason, "passw"))
640 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
641 "consult the manual.\n", blurb());
642 else if (strstr (si->nolock_reason, "running as "))
644 "%s: locking only works when xscreensaver is launched\n"
645 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
646 "\t See the manual for details.\n",
652 /* Examine all of the display's screens, and populate the `saver_screen_info'
653 structures. Make sure this is called after hack_environment() sets $PATH.
656 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
658 Bool found_any_writable_cells = False;
661 si->nscreens = ScreenCount(si->dpy);
662 si->screens = (saver_screen_info *)
663 calloc(sizeof(saver_screen_info), si->nscreens);
665 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
667 for (i = 0; i < si->nscreens; i++)
669 saver_screen_info *ssi = &si->screens[i];
671 ssi->screen = ScreenOfDisplay (si->dpy, i);
673 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
674 ssi->default_visual =
675 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
677 ssi->current_visual = ssi->default_visual;
678 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
680 /* Execute a subprocess to find the GL visual. */
681 ssi->best_gl_visual = get_best_gl_visual (ssi);
683 if (ssi == si->default_screen)
684 /* Since this is the default screen, use the one already created. */
685 ssi->toplevel_shell = toplevel_shell;
687 /* Otherwise, each screen must have its own unmapped root widget. */
688 ssi->toplevel_shell =
689 XtVaAppCreateShell (progname, progclass, applicationShellWidgetClass,
691 XtNscreen, ssi->screen,
692 XtNvisual, ssi->current_visual,
693 XtNdepth, visual_depth (ssi->screen,
694 ssi->current_visual),
697 if (! found_any_writable_cells)
699 /* Check to see whether fading is ever possible -- if any of the
700 screens on the display has a PseudoColor visual, then fading can
701 work (on at least some screens.) If no screen has a PseudoColor
702 visual, then don't bother ever trying to fade, because it will
703 just cause a delay without causing any visible effect.
705 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
706 get_visual (ssi->screen, "PseudoColor", True, False) ||
707 get_visual (ssi->screen, "GrayScale", True, False))
708 found_any_writable_cells = True;
712 si->fading_possible_p = found_any_writable_cells;
716 /* If any server extensions have been requested, try and initialize them.
717 Issue warnings if requests can't be honored.
720 initialize_server_extensions (saver_info *si)
722 saver_preferences *p = &si->prefs;
724 Bool server_has_xidle_extension_p = False;
725 Bool server_has_sgi_saver_extension_p = False;
726 Bool server_has_mit_saver_extension_p = False;
727 Bool system_has_proc_interrupts_p = False;
728 const char *piwhy = 0;
730 si->using_xidle_extension = p->use_xidle_extension;
731 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
732 si->using_mit_saver_extension = p->use_mit_saver_extension;
733 si->using_proc_interrupts = p->use_proc_interrupts;
735 #ifdef HAVE_XIDLE_EXTENSION
736 server_has_xidle_extension_p = query_xidle_extension (si);
738 #ifdef HAVE_SGI_SAVER_EXTENSION
739 server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
741 #ifdef HAVE_MIT_SAVER_EXTENSION
742 server_has_mit_saver_extension_p = query_mit_saver_extension (si);
744 #ifdef HAVE_PROC_INTERRUPTS
745 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
748 if (!server_has_xidle_extension_p)
749 si->using_xidle_extension = False;
750 else if (p->verbose_p)
752 if (si->using_xidle_extension)
753 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
755 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
758 if (!server_has_sgi_saver_extension_p)
759 si->using_sgi_saver_extension = False;
760 else if (p->verbose_p)
762 if (si->using_sgi_saver_extension)
763 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
766 "%s: not using server's SGI SCREEN_SAVER extension.\n",
770 if (!server_has_mit_saver_extension_p)
771 si->using_mit_saver_extension = False;
772 else if (p->verbose_p)
774 if (si->using_mit_saver_extension)
775 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
779 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
783 if (!system_has_proc_interrupts_p)
785 si->using_proc_interrupts = False;
786 if (p->verbose_p && piwhy)
787 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
790 else if (p->verbose_p)
792 if (si->using_proc_interrupts)
794 "%s: consulting /proc/interrupts for keyboard activity.\n",
798 "%s: not consulting /proc/interrupts for keyboard activity.\n",
804 /* For the case where we aren't using an server extensions, select user events
805 on all the existing windows, and launch timers to select events on
806 newly-created windows as well.
808 If a server extension is being used, this does nothing.
811 select_events (saver_info *si)
813 saver_preferences *p = &si->prefs;
816 if (si->using_xidle_extension ||
817 si->using_mit_saver_extension ||
818 si->using_sgi_saver_extension)
821 if (p->initial_delay)
825 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
826 (int) p->initial_delay/1000,
827 (p->initial_delay == 1000 ? "" : "s"));
831 usleep (p->initial_delay);
833 fprintf (stderr, " done.\n");
838 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
843 /* Select events on the root windows of every screen. This also selects
844 for window creation events, so that new subwindows will be noticed.
846 for (i = 0; i < si->nscreens; i++)
847 start_notice_events_timer (si, RootWindowOfScreen (si->screens[i].screen),
851 fprintf (stderr, " done.\n");
856 maybe_reload_init_file (saver_info *si)
858 saver_preferences *p = &si->prefs;
859 if (init_file_changed_p (p))
862 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
863 blurb(), init_file_name());
867 /* If a server extension is in use, and p->timeout has changed,
868 we need to inform the server of the new timeout. */
869 disable_builtin_screensaver (si, False);
876 - wait until the user is idle;
878 - wait until the user is active;
879 - unblank the screen;
884 main_loop (saver_info *si)
886 saver_preferences *p = &si->prefs;
891 Bool was_locked = False;
892 sleep_until_idle (si, True);
897 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
898 si->selection_mode, timestring());
901 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
905 maybe_reload_init_file (si);
907 if (! blank_screen (si))
909 /* We were unable to grab either the keyboard or mouse.
910 This means we did not (and must not) blank the screen.
911 If we were to blank the screen while some other program
912 is holding both the mouse and keyboard grabbed, then
913 we would never be able to un-blank it! We would never
914 see any events, and the display would be wedged.
916 So, just go around the loop again and wait for the
917 next bout of idleness.
921 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
926 kill_screenhack (si);
928 if (!si->throttled_p)
929 spawn_screenhack (si, True);
930 else if (p->verbose_p)
931 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
933 /* Don't start the cycle timer in demo mode. */
934 if (!si->demoing_p && p->cycle)
935 si->cycle_id = XtAppAddTimeOut (si->app,
937 /* see comment in cycle_timer() */
946 Time lock_timeout = p->lock_timeout;
948 if (si->emergency_lock_p && p->lock_p && lock_timeout)
950 int secs = p->lock_timeout / 1000;
953 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
955 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
959 si->emergency_lock_p = False;
961 if (!si->demoing_p && /* if not going into demo mode */
962 p->lock_p && /* and locking is enabled */
963 !si->locking_disabled_p && /* and locking is possible */
964 lock_timeout == 0) /* and locking is not timer-deferred */
965 set_locked_p (si, True); /* then lock right now. */
967 /* locked_p might be true already because of the above, or because of
968 the LOCK ClientMessage. But if not, and if we're supposed to lock
969 after some time, set up a timer to do so.
974 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
978 #endif /* !NO_LOCKING */
981 ok_to_unblank = True;
984 sleep_until_idle (si, False); /* until not idle */
985 maybe_reload_init_file (si);
990 saver_screen_info *ssi = si->default_screen;
991 if (si->locking_disabled_p) abort ();
994 si->dbox_up_p = True;
995 suspend_screenhack (si, True);
996 XUndefineCursor (si->dpy, ssi->screensaver_window);
998 ok_to_unblank = unlock_p (si);
1000 si->dbox_up_p = False;
1001 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1002 suspend_screenhack (si, False); /* resume */
1004 if (!ok_to_unblank &&
1005 !screenhack_running_p (si))
1007 /* If the lock dialog has been dismissed and we're not about to
1008 unlock the screen, and there is currently no hack running,
1009 then launch one. (There might be no hack running if DPMS
1010 had kicked in. But DPMS is off now, so bring back the hack)
1013 XtRemoveTimeOut (si->cycle_id);
1015 cycle_timer ((XtPointer) si, 0);
1018 #endif /* !NO_LOCKING */
1020 } while (!ok_to_unblank);
1024 fprintf (stderr, "%s: unblanking screen at %s.\n",
1025 blurb(), timestring ());
1027 /* Kill before unblanking, to stop drawing as soon as possible. */
1028 kill_screenhack (si);
1029 unblank_screen (si);
1031 set_locked_p (si, False);
1032 si->emergency_lock_p = False;
1034 si->selection_mode = 0;
1036 /* If we're throttled, and the user has explicitly unlocked the screen,
1037 then unthrottle. If we weren't locked, then don't unthrottle
1038 automatically, because someone might have just bumped the desk... */
1041 if (si->throttled_p && p->verbose_p)
1042 fprintf (stderr, "%s: unthrottled.\n", blurb());
1043 si->throttled_p = False;
1048 XtRemoveTimeOut (si->cycle_id);
1054 XtRemoveTimeOut (si->lock_id);
1059 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1063 static void analyze_display (saver_info *si);
1066 main (int argc, char **argv)
1070 saver_info *si = &the_si;
1071 saver_preferences *p = &si->prefs;
1074 memset(si, 0, sizeof(*si));
1075 global_si_kludge = si; /* I hate C so much... */
1077 # undef ya_rand_init
1080 save_argv (argc, argv);
1081 set_version_string (si, &argc, argv);
1082 privileged_initialization (si, &argc, argv);
1083 hack_environment (si);
1085 shell = connect_to_server (si, &argc, argv);
1086 process_command_line (si, &argc, argv);
1089 load_init_file (p); /* must be before initialize_per_screen_info() */
1090 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1092 /* We can only issue this warnings now. */
1093 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1095 "%s: there are no PseudoColor or GrayScale visuals.\n"
1096 "%s: ignoring the request for fading/unfading.\n",
1099 for (i = 0; i < si->nscreens; i++)
1100 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1103 lock_initialization (si, &argc, argv);
1105 if (p->xsync_p) XSynchronize (si->dpy, True);
1106 blurb_timestamp_p = p->timestamp_p; /* kludge */
1108 if (p->verbose_p) analyze_display (si);
1109 initialize_server_extensions (si);
1111 si->blank_time = time ((time_t) 0); /* must be before ..._window */
1112 initialize_screensaver_window (si);
1116 disable_builtin_screensaver (si, True);
1117 initialize_stderr (si);
1119 make_splash_dialog (si);
1121 main_loop (si); /* doesn't return */
1126 /* Processing ClientMessage events.
1130 clientmessage_response (saver_info *si, Window w, Bool error,
1131 const char *stderr_msg,
1132 const char *protocol_msg)
1136 saver_preferences *p = &si->prefs;
1137 if (error || p->verbose_p)
1138 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1140 L = strlen(protocol_msg);
1141 proto = (char *) malloc (L + 2);
1142 proto[0] = (error ? '-' : '+');
1143 strcpy (proto+1, protocol_msg);
1146 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1147 PropModeReplace, (unsigned char *) proto, L);
1148 XSync (si->dpy, False);
1153 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1155 saver_preferences *p = &si->prefs;
1157 Window window = event->xclient.window;
1159 /* Preferences might affect our handling of client messages. */
1160 maybe_reload_init_file (si);
1162 if (event->xclient.message_type != XA_SCREENSAVER)
1165 str = XGetAtomName (si->dpy, event->xclient.message_type);
1166 fprintf (stderr, "%s: unrecognised ClientMessage type %s received\n",
1167 blurb(), (str ? str : "(null)"));
1168 if (str) XFree (str);
1171 if (event->xclient.format != 32)
1173 fprintf (stderr, "%s: ClientMessage of format %d received, not 32\n",
1174 blurb(), event->xclient.format);
1178 type = event->xclient.data.l[0];
1179 if (type == XA_ACTIVATE)
1183 clientmessage_response(si, window, False,
1184 "ACTIVATE ClientMessage received.",
1186 si->selection_mode = 0;
1187 si->demoing_p = False;
1189 if (si->throttled_p && p->verbose_p)
1190 fprintf (stderr, "%s: unthrottled.\n", blurb());
1191 si->throttled_p = False;
1193 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1195 XForceScreenSaver (si->dpy, ScreenSaverActive);
1203 clientmessage_response(si, window, True,
1204 "ClientMessage ACTIVATE received while already active.",
1207 else if (type == XA_DEACTIVATE)
1211 if (si->throttled_p && p->verbose_p)
1212 fprintf (stderr, "%s: unthrottled.\n", blurb());
1213 si->throttled_p = False;
1215 clientmessage_response(si, window, False,
1216 "DEACTIVATE ClientMessage received.",
1218 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1220 XForceScreenSaver (si->dpy, ScreenSaverReset);
1228 clientmessage_response(si, window, True,
1229 "ClientMessage DEACTIVATE received while inactive.",
1232 else if (type == XA_CYCLE)
1236 clientmessage_response(si, window, False,
1237 "CYCLE ClientMessage received.",
1239 si->selection_mode = 0; /* 0 means randomize when its time. */
1240 si->demoing_p = False;
1242 if (si->throttled_p && p->verbose_p)
1243 fprintf (stderr, "%s: unthrottled.\n", blurb());
1244 si->throttled_p = False;
1247 XtRemoveTimeOut (si->cycle_id);
1249 cycle_timer ((XtPointer) si, 0);
1252 clientmessage_response(si, window, True,
1253 "ClientMessage CYCLE received while inactive.",
1256 else if (type == XA_NEXT || type == XA_PREV)
1258 clientmessage_response(si, window, False,
1260 ? "NEXT ClientMessage received."
1261 : "PREV ClientMessage received."),
1263 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1264 si->demoing_p = False;
1266 if (si->throttled_p && p->verbose_p)
1267 fprintf (stderr, "%s: unthrottled.\n", blurb());
1268 si->throttled_p = False;
1273 XtRemoveTimeOut (si->cycle_id);
1275 cycle_timer ((XtPointer) si, 0);
1280 else if (type == XA_SELECT)
1284 long which = event->xclient.data.l[1];
1286 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1287 sprintf (buf2, "activating (%ld).", which);
1288 clientmessage_response (si, window, False, buf, buf2);
1290 if (which < 0) which = 0; /* 0 == "random" */
1291 si->selection_mode = which;
1292 si->demoing_p = False;
1294 if (si->throttled_p && p->verbose_p)
1295 fprintf (stderr, "%s: unthrottled.\n", blurb());
1296 si->throttled_p = False;
1301 XtRemoveTimeOut (si->cycle_id);
1303 cycle_timer ((XtPointer) si, 0);
1308 else if (type == XA_EXIT)
1310 /* Ignore EXIT message if the screen is locked. */
1311 if (until_idle_p || !si->locked_p)
1313 clientmessage_response (si, window, False,
1314 "EXIT ClientMessage received.",
1318 unblank_screen (si);
1319 kill_screenhack (si);
1320 XSync (si->dpy, False);
1322 saver_exit (si, 0, 0);
1325 clientmessage_response (si, window, True,
1326 "EXIT ClientMessage received while locked.",
1327 "screen is locked.");
1329 else if (type == XA_RESTART)
1331 /* The RESTART message works whether the screensaver is active or not,
1332 unless the screen is locked, in which case it doesn't work.
1334 if (until_idle_p || !si->locked_p)
1336 clientmessage_response (si, window, False,
1337 "RESTART ClientMessage received.",
1341 unblank_screen (si);
1342 kill_screenhack (si);
1343 XSync (si->dpy, False);
1348 if (real_stdout) fflush (real_stdout);
1349 if (real_stderr) fflush (real_stderr);
1350 /* make sure error message shows up before exit. */
1351 if (real_stderr && stderr != real_stderr)
1352 dup2 (fileno(real_stderr), fileno(stderr));
1354 restart_process (si);
1355 exit (1); /* shouldn't get here; but if restarting didn't work,
1356 make this command be the same as EXIT. */
1359 clientmessage_response (si, window, True,
1360 "RESTART ClientMessage received while locked.",
1361 "screen is locked.");
1363 else if (type == XA_DEMO)
1365 long arg = event->xclient.data.l[1];
1366 Bool demo_one_hack_p = (arg == 300);
1368 if (demo_one_hack_p)
1372 long which = event->xclient.data.l[2];
1375 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1376 sprintf (buf2, "demoing (%ld).", which);
1377 clientmessage_response (si, window, False, buf, buf2);
1379 if (which < 0) which = 0; /* 0 == "random" */
1380 si->selection_mode = which;
1381 si->demoing_p = True;
1383 if (si->throttled_p && p->verbose_p)
1384 fprintf (stderr, "%s: unthrottled.\n", blurb());
1385 si->throttled_p = False;
1390 clientmessage_response (si, window, True,
1391 "DEMO ClientMessage received while active.",
1396 clientmessage_response (si, window, True,
1397 "obsolete form of DEMO ClientMessage.",
1398 "obsolete form of DEMO ClientMessage.");
1401 else if (type == XA_PREFS)
1403 clientmessage_response (si, window, True,
1404 "the PREFS client-message is obsolete.",
1405 "the PREFS client-message is obsolete.");
1407 else if (type == XA_LOCK)
1410 clientmessage_response (si, window, True,
1411 "not compiled with support for locking.",
1412 "locking not enabled.");
1413 #else /* !NO_LOCKING */
1414 if (si->locking_disabled_p)
1415 clientmessage_response (si, window, True,
1416 "LOCK ClientMessage received, but locking is disabled.",
1417 "locking not enabled.");
1418 else if (si->locked_p)
1419 clientmessage_response (si, window, True,
1420 "LOCK ClientMessage received while already locked.",
1425 char *response = (until_idle_p
1426 ? "activating and locking."
1428 sprintf (buf, "LOCK ClientMessage received; %s", response);
1429 clientmessage_response (si, window, False, buf, response);
1430 set_locked_p (si, True);
1431 si->selection_mode = 0;
1432 si->demoing_p = False;
1434 if (si->lock_id) /* we're doing it now, so lose the timeout */
1436 XtRemoveTimeOut (si->lock_id);
1442 if (si->using_mit_saver_extension ||
1443 si->using_sgi_saver_extension)
1445 XForceScreenSaver (si->dpy, ScreenSaverActive);
1454 #endif /* !NO_LOCKING */
1456 else if (type == XA_THROTTLE)
1458 if (si->throttled_p)
1459 clientmessage_response (si, window, True,
1460 "THROTTLE ClientMessage received, but "
1461 "already throttled.",
1462 "already throttled.");
1466 char *response = "throttled.";
1467 si->throttled_p = True;
1468 si->selection_mode = 0;
1469 si->demoing_p = False;
1470 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
1471 clientmessage_response (si, window, False, buf, response);
1476 XtRemoveTimeOut (si->cycle_id);
1478 cycle_timer ((XtPointer) si, 0);
1482 else if (type == XA_UNTHROTTLE)
1484 if (! si->throttled_p)
1485 clientmessage_response (si, window, True,
1486 "UNTHROTTLE ClientMessage received, but "
1492 char *response = "unthrottled.";
1493 si->throttled_p = False;
1494 si->selection_mode = 0;
1495 si->demoing_p = False;
1496 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
1497 clientmessage_response (si, window, False, buf, response);
1502 XtRemoveTimeOut (si->cycle_id);
1504 cycle_timer ((XtPointer) si, 0);
1512 str = (type ? XGetAtomName(si->dpy, type) : 0);
1516 if (strlen (str) > 80)
1517 strcpy (str+70, "...");
1518 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
1525 "unrecognised screensaver ClientMessage 0x%x received.",
1526 (unsigned int) event->xclient.data.l[0]);
1529 clientmessage_response (si, window, True, buf, buf);
1535 /* Some random diagnostics printed in -verbose mode.
1539 analyze_display (saver_info *si)
1542 static const char *exts[][2] = {
1543 { "SCREEN_SAVER", "SGI Screen-Saver" },
1544 { "SCREEN-SAVER", "SGI Screen-Saver" },
1545 { "MIT-SCREEN-SAVER", "MIT Screen-Saver" },
1546 { "XIDLE", "XIdle" },
1547 { "SGI-VIDEO-CONTROL", "SGI Video-Control" },
1548 { "READDISPLAY", "SGI Read-Display" },
1549 { "MIT-SHM", "Shared Memory" },
1550 { "DOUBLE-BUFFER", "Double-Buffering" },
1551 { "DPMS", "Power Management" },
1553 { "XFree86-VidModeExtension", "XF86 Video-Mode" },
1554 { "XINERAMA", "Xinerama" }
1557 fprintf (stderr, "%s: running on display \"%s\"\n", blurb(),
1558 DisplayString(si->dpy));
1559 fprintf (stderr, "%s: vendor is %s, %d\n", blurb(),
1560 ServerVendor(si->dpy), VendorRelease(si->dpy));
1562 fprintf (stderr, "%s: useful extensions:\n", blurb());
1563 for (i = 0; i < countof(exts); i++)
1565 int op = 0, event = 0, error = 0;
1566 if (XQueryExtension (si->dpy, exts[i][0], &op, &event, &error))
1567 fprintf (stderr, "%s: %s\n", blurb(), exts[i][1]);
1570 for (i = 0; i < si->nscreens; i++)
1572 unsigned long colormapped_depths = 0;
1573 unsigned long non_mapped_depths = 0;
1574 XVisualInfo vi_in, *vi_out;
1577 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
1578 if (!vi_out) continue;
1579 for (j = 0; j < out_count; j++)
1580 if (vi_out[j].class == PseudoColor)
1581 colormapped_depths |= (1 << vi_out[j].depth);
1583 non_mapped_depths |= (1 << vi_out[j].depth);
1584 XFree ((char *) vi_out);
1586 if (colormapped_depths)
1588 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(), i);
1589 for (j = 0; j < 32; j++)
1590 if (colormapped_depths & (1 << j))
1591 fprintf (stderr, " %d", j);
1592 fprintf (stderr, "\n");
1594 if (non_mapped_depths)
1596 fprintf (stderr, "%s: screen %d non-mapped depths:", blurb(), i);
1597 for (j = 0; j < 32; j++)
1598 if (non_mapped_depths & (1 << j))
1599 fprintf (stderr, " %d", j);
1600 fprintf (stderr, "\n");
1606 display_is_on_console_p (saver_info *si)
1608 Bool not_on_console = True;
1609 char *dpystr = DisplayString (si->dpy);
1610 char *tail = (char *) strchr (dpystr, ':');
1611 if (! tail || strncmp (tail, ":0", 2))
1612 not_on_console = True;
1615 char dpyname[255], localname[255];
1616 strncpy (dpyname, dpystr, tail-dpystr);
1617 dpyname [tail-dpystr] = 0;
1619 !strcmp(dpyname, "unix") ||
1620 !strcmp(dpyname, "localhost"))
1621 not_on_console = False;
1622 else if (gethostname (localname, sizeof (localname)))
1623 not_on_console = True; /* can't find hostname? */
1626 /* We have to call gethostbyname() on the result of gethostname()
1627 because the two aren't guarenteed to be the same name for the
1628 same host: on some losing systems, one is a FQDN and the other
1629 is not. Here in the wide wonderful world of Unix it's rocket
1630 science to obtain the local hostname in a portable fashion.
1632 And don't forget, gethostbyname() reuses the structure it
1633 returns, so we have to copy the fucker before calling it again.
1634 Thank you master, may I have another.
1636 struct hostent *h = gethostbyname (dpyname);
1638 not_on_console = True;
1643 strcpy (hn, h->h_name);
1644 l = gethostbyname (localname);
1645 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
1649 return !not_on_console;