1 /* xscreensaver, Copyright (c) 1991-2001 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-2001 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... */
297 if (!real_stderr) real_stderr = stderr;
299 fprintf (real_stderr, "\n"
300 "#######################################"
301 "#######################################\n\n"
302 "%s: X Error! PLEASE REPORT THIS BUG.\n",
305 for (i = 0; i < si->nscreens; i++)
306 fprintf (real_stderr, "%s: screen %d: 0x%x, 0x%x, 0x%x\n",
308 RootWindowOfScreen (si->screens[i].screen),
309 si->screens[i].real_vroot,
310 si->screens[i].screensaver_window);
312 fprintf (real_stderr, "\n"
313 "#######################################"
314 "#######################################\n\n");
316 if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
318 fprintf (real_stderr, "\n");
319 if (si->prefs.xsync_p)
321 saver_exit (si, -1, "because of synchronous X Error");
325 fprintf (real_stderr,
326 "#######################################"
327 "#######################################\n\n");
328 fprintf (real_stderr,
329 " If at all possible, please re-run xscreensaver with the command\n"
330 " line arguments `-sync -verbose -no-capture', and reproduce this\n"
331 " bug. That will cause xscreensaver to dump a `core' file to the\n"
332 " current directory. Please include the stack trace from that core\n"
333 " file in your bug report. *DO NOT* mail the core file itself!\n"
334 " That won't work.\n"
336 " http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
337 " the most useful bug reports, and how to examine core files.\n"
339 " The more information you can provide, the better. But please\n"
340 " report this bug, regardless!\n"
342 fprintf (real_stderr,
343 "#######################################"
344 "#######################################\n\n");
346 saver_exit (si, -1, 0);
350 fprintf (real_stderr, " (nonfatal.)\n");
355 /* This error handler is used only while the X connection is being set up;
356 after we've got a connection, we don't use this handler again. The only
357 reason for having this is so that we can present a more idiot-proof error
358 message than "cannot open display."
361 startup_ehandler (String name, String type, String class,
362 String defalt, /* one can't even spel properly
363 in this joke of a language */
364 String *av, Cardinal *ac)
368 saver_info *si = global_si_kludge; /* I hate C so much... */
369 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
371 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
372 fmt, sizeof(fmt)-1, *db);
374 fprintf (stderr, "%s: ", blurb());
376 memset (p, 0, sizeof(p));
377 if (*ac > countof (p)) *ac = countof (p);
378 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
379 fprintf (stderr, fmt, /* Did I mention that I hate C? */
380 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
381 fprintf (stderr, "\n");
383 describe_uids (si, stderr);
385 if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
387 fprintf (stderr, "\n"
388 "%s: This is probably because you're logging in as root. You\n"
389 " shouldn't log in as root: you should log in as a normal user,\n"
390 " and then `su' as needed. If you insist on logging in as\n"
391 " root, you will have to turn off X's security features before\n"
392 " xscreensaver will work.\n"
394 " Please read the manual and FAQ for more information:\n",
399 fprintf (stderr, "\n"
400 "%s: Errors at startup are usually authorization problems.\n"
401 " But you're not logging in as root (good!) so something\n"
402 " else must be wrong. Did you read the manual and the FAQ?\n",
406 fprintf (stderr, "\n"
407 " http://www.jwz.org/xscreensaver/faq.html\n"
408 " http://www.jwz.org/xscreensaver/man.html\n"
417 /* The zillions of initializations.
420 /* Set progname, version, etc. This is done very early.
423 set_version_string (saver_info *si, int *argc, char **argv)
425 progclass = "XScreenSaver";
427 /* progname is reset later, after we connect to X. */
428 progname = strrchr(argv[0], '/');
429 if (progname) progname++;
430 else progname = argv[0];
432 if (strlen(progname) > 100) /* keep it short. */
435 /* The X resource database blows up if argv[0] has a "." in it. */
438 while ((s = strchr (s, '.')))
442 si->version = (char *) malloc (5);
443 memcpy (si->version, screensaver_id + 17, 4);
448 /* Initializations that potentially take place as a priveleged user:
449 If the xscreensaver executable is setuid root, then these initializations
450 are run as root, before discarding privileges.
453 privileged_initialization (saver_info *si, int *argc, char **argv)
456 /* before hack_uid() for proper permissions */
457 lock_priv_init (*argc, argv, si->prefs.verbose_p);
458 #endif /* NO_LOCKING */
464 /* Figure out what locking mechanisms are supported.
467 lock_initialization (saver_info *si, int *argc, char **argv)
470 si->locking_disabled_p = True;
471 si->nolock_reason = "not compiled with locking support";
472 #else /* !NO_LOCKING */
474 /* Finish initializing locking, now that we're out of privileged code. */
475 if (! lock_init (*argc, argv, si->prefs.verbose_p))
477 si->locking_disabled_p = True;
478 si->nolock_reason = "error getting password";
480 #endif /* NO_LOCKING */
486 /* Open the connection to the X server, and intern our Atoms.
489 connect_to_server (saver_info *si, int *argc, char **argv)
491 Widget toplevel_shell;
494 char *d = getenv ("DISPLAY");
497 char ndpy[] = "DISPLAY=:0.0";
498 /* if (si->prefs.verbose_p) */ /* sigh, too early to test this... */
500 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
505 #endif /* HAVE_PUTENV */
507 XSetErrorHandler (saver_ehandler);
509 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
510 toplevel_shell = XtAppInitialize (&si->app, progclass,
511 options, XtNumber (options),
512 argc, argv, defaults, 0, 0);
513 XtAppSetErrorMsgHandler (si->app, 0);
515 si->dpy = XtDisplay (toplevel_shell);
516 si->prefs.db = XtDatabase (si->dpy);
517 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
519 if(strlen(progname) > 100) /* keep it short. */
522 db = si->prefs.db; /* resources.c needs this */
524 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
525 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
526 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
527 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
528 XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
529 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
531 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
532 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
533 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
534 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
535 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
536 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
537 XA_PREV = XInternAtom (si->dpy, "PREV", False);
538 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
539 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
540 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
541 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
542 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
543 XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
544 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
545 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
547 return toplevel_shell;
551 /* Handle the command-line arguments that were not handled for us by Xt.
552 Issue an error message and exit if there are unknown options.
555 process_command_line (saver_info *si, int *argc, char **argv)
558 for (i = 1; i < *argc; i++)
560 if (!strcmp (argv[i], "-debug"))
561 /* no resource for this one, out of paranoia. */
562 si->prefs.debug_p = True;
564 else if (!strcmp (argv[i], "-h") ||
565 !strcmp (argv[i], "-help") ||
566 !strcmp (argv[i], "--help"))
571 const char *s = argv[i];
572 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
575 if (s[0] == '-' && s[1] == '-') s++;
576 if (!strcmp (s, "-activate") ||
577 !strcmp (s, "-deactivate") ||
578 !strcmp (s, "-cycle") ||
579 !strcmp (s, "-next") ||
580 !strcmp (s, "-prev") ||
581 !strcmp (s, "-exit") ||
582 !strcmp (s, "-restart") ||
583 !strcmp (s, "-demo") ||
584 !strcmp (s, "-prefs") ||
585 !strcmp (s, "-preferences") ||
586 !strcmp (s, "-lock") ||
587 !strcmp (s, "-version") ||
588 !strcmp (s, "-time"))
591 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
592 fprintf (stderr, "\n\
593 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
595 fprintf (stderr, "\n\
596 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
599 The `xscreensaver' program is a daemon that runs in the background.\n\
600 You control a running xscreensaver process by sending it messages\n\
601 with `xscreensaver-demo' or `xscreensaver-command'.\n\
602 . See the man pages for details, or check the web page:\n\
603 http://www.jwz.org/xscreensaver/\n\n");
605 /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
606 suggest that explicitly. */
607 if (!strcmp (s, "-lock"))
609 Or perhaps you meant either the \"-lock-mode\" or the\n\
610 \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
619 /* Print out the xscreensaver banner to the tty if applicable;
620 Issue any other warnings that are called for at this point.
623 print_banner (saver_info *si)
625 saver_preferences *p = &si->prefs;
627 /* This resource gets set some time before the others, so that we know
628 whether to print the banner (and so that the banner gets printed before
629 any resource-database-related error messages.)
631 p->verbose_p = (p->debug_p || get_boolean_resource ("verbose", "Boolean"));
633 /* Ditto, for the locking_disabled_p message. */
634 p->lock_p = get_boolean_resource ("lock", "Boolean");
638 "%s %s, copyright (c) 1991-2001 "
639 "by Jamie Zawinski <jwz@jwz.org>.\n",
640 progname, si->version);
643 fprintf (stderr, "\n"
644 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
646 "\tNote that in debug mode, the xscreensaver window will only\n"
647 "\tcover the left half of the screen. (The idea is that you\n"
648 "\tcan still see debugging output in a shell, if you position\n"
649 "\tit on the right side of the screen.)\n"
651 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
652 "\tuntrusted environments.\n"
658 if (!si->uid_message || !*si->uid_message)
659 describe_uids (si, stderr);
662 if (si->orig_uid && *si->orig_uid)
663 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
664 blurb(), si->orig_uid);
665 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
668 fprintf (stderr, "%s: in process %lu.\n", blurb(),
669 (unsigned long) getpid());
672 /* If locking was not able to be initalized for some reason, explain why.
673 (This has to be done after we've read the lock_p resource.)
675 if (p->lock_p && si->locking_disabled_p)
678 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
680 if (strstr (si->nolock_reason, "passw"))
681 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
682 "consult the manual.\n", blurb());
683 else if (strstr (si->nolock_reason, "running as "))
685 "%s: locking only works when xscreensaver is launched\n"
686 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
687 "\t See the manual for details.\n",
693 /* Examine all of the display's screens, and populate the `saver_screen_info'
694 structures. Make sure this is called after hack_environment() sets $PATH.
697 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
699 Bool found_any_writable_cells = False;
702 si->nscreens = ScreenCount(si->dpy);
703 si->screens = (saver_screen_info *)
704 calloc(sizeof(saver_screen_info), si->nscreens);
706 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
708 for (i = 0; i < si->nscreens; i++)
710 saver_screen_info *ssi = &si->screens[i];
712 ssi->screen = ScreenOfDisplay (si->dpy, i);
714 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
715 ssi->default_visual =
716 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
718 ssi->current_visual = ssi->default_visual;
719 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
721 /* Execute a subprocess to find the GL visual. */
722 ssi->best_gl_visual = get_best_gl_visual (ssi);
724 if (ssi == si->default_screen)
725 /* Since this is the default screen, use the one already created. */
726 ssi->toplevel_shell = toplevel_shell;
728 /* Otherwise, each screen must have its own unmapped root widget. */
729 ssi->toplevel_shell =
730 XtVaAppCreateShell (progname, progclass, applicationShellWidgetClass,
732 XtNscreen, ssi->screen,
733 XtNvisual, ssi->current_visual,
734 XtNdepth, visual_depth (ssi->screen,
735 ssi->current_visual),
738 if (! found_any_writable_cells)
740 /* Check to see whether fading is ever possible -- if any of the
741 screens on the display has a PseudoColor visual, then fading can
742 work (on at least some screens.) If no screen has a PseudoColor
743 visual, then don't bother ever trying to fade, because it will
744 just cause a delay without causing any visible effect.
746 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
747 get_visual (ssi->screen, "PseudoColor", True, False) ||
748 get_visual (ssi->screen, "GrayScale", True, False))
749 found_any_writable_cells = True;
753 si->fading_possible_p = found_any_writable_cells;
755 #ifdef HAVE_XF86VMODE_GAMMA
756 si->fading_possible_p = True; /* if we can gamma fade, go for it */
761 /* If any server extensions have been requested, try and initialize them.
762 Issue warnings if requests can't be honored.
765 initialize_server_extensions (saver_info *si)
767 saver_preferences *p = &si->prefs;
769 Bool server_has_xidle_extension_p = False;
770 Bool server_has_sgi_saver_extension_p = False;
771 Bool server_has_mit_saver_extension_p = False;
772 Bool system_has_proc_interrupts_p = False;
773 const char *piwhy = 0;
775 si->using_xidle_extension = p->use_xidle_extension;
776 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
777 si->using_mit_saver_extension = p->use_mit_saver_extension;
778 si->using_proc_interrupts = p->use_proc_interrupts;
780 #ifdef HAVE_XIDLE_EXTENSION
781 server_has_xidle_extension_p = query_xidle_extension (si);
783 #ifdef HAVE_SGI_SAVER_EXTENSION
784 server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
786 #ifdef HAVE_MIT_SAVER_EXTENSION
787 server_has_mit_saver_extension_p = query_mit_saver_extension (si);
789 #ifdef HAVE_PROC_INTERRUPTS
790 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
793 if (!server_has_xidle_extension_p)
794 si->using_xidle_extension = False;
795 else if (p->verbose_p)
797 if (si->using_xidle_extension)
798 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
800 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
803 if (!server_has_sgi_saver_extension_p)
804 si->using_sgi_saver_extension = False;
805 else if (p->verbose_p)
807 if (si->using_sgi_saver_extension)
808 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
811 "%s: not using server's SGI SCREEN_SAVER extension.\n",
815 if (!server_has_mit_saver_extension_p)
816 si->using_mit_saver_extension = False;
817 else if (p->verbose_p)
819 if (si->using_mit_saver_extension)
820 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
824 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
828 if (!system_has_proc_interrupts_p)
830 si->using_proc_interrupts = False;
831 if (p->verbose_p && piwhy)
832 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
835 else if (p->verbose_p)
837 if (si->using_proc_interrupts)
839 "%s: consulting /proc/interrupts for keyboard activity.\n",
843 "%s: not consulting /proc/interrupts for keyboard activity.\n",
849 /* For the case where we aren't using an server extensions, select user events
850 on all the existing windows, and launch timers to select events on
851 newly-created windows as well.
853 If a server extension is being used, this does nothing.
856 select_events (saver_info *si)
858 saver_preferences *p = &si->prefs;
861 if (si->using_xidle_extension ||
862 si->using_mit_saver_extension ||
863 si->using_sgi_saver_extension)
866 if (p->initial_delay)
870 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
871 (int) p->initial_delay/1000,
872 (p->initial_delay == 1000 ? "" : "s"));
876 usleep (p->initial_delay);
878 fprintf (stderr, " done.\n");
883 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
888 /* Select events on the root windows of every screen. This also selects
889 for window creation events, so that new subwindows will be noticed.
891 for (i = 0; i < si->nscreens; i++)
892 start_notice_events_timer (si, RootWindowOfScreen (si->screens[i].screen),
896 fprintf (stderr, " done.\n");
901 maybe_reload_init_file (saver_info *si)
903 saver_preferences *p = &si->prefs;
904 if (init_file_changed_p (p))
907 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
908 blurb(), init_file_name());
912 /* If a server extension is in use, and p->timeout has changed,
913 we need to inform the server of the new timeout. */
914 disable_builtin_screensaver (si, False);
916 /* If the DPMS settings in the init file have changed,
917 change the settings on the server to match. */
918 sync_server_dpms_settings (si->dpy, p->dpms_enabled_p,
919 p->dpms_standby / 1000,
920 p->dpms_suspend / 1000,
929 - wait until the user is idle;
931 - wait until the user is active;
932 - unblank the screen;
937 main_loop (saver_info *si)
939 saver_preferences *p = &si->prefs;
944 Bool was_locked = False;
945 sleep_until_idle (si, True);
950 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
951 si->selection_mode, timestring());
954 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
958 maybe_reload_init_file (si);
960 if (! blank_screen (si))
962 /* We were unable to grab either the keyboard or mouse.
963 This means we did not (and must not) blank the screen.
964 If we were to blank the screen while some other program
965 is holding both the mouse and keyboard grabbed, then
966 we would never be able to un-blank it! We would never
967 see any events, and the display would be wedged.
969 So, just go around the loop again and wait for the
970 next bout of idleness.
974 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
979 kill_screenhack (si);
981 if (!si->throttled_p)
982 spawn_screenhack (si, True);
983 else if (p->verbose_p)
984 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
986 /* Don't start the cycle timer in demo mode. */
987 if (!si->demoing_p && p->cycle)
988 si->cycle_id = XtAppAddTimeOut (si->app,
990 /* see comment in cycle_timer() */
999 Time lock_timeout = p->lock_timeout;
1001 if (si->emergency_lock_p && p->lock_p && lock_timeout)
1003 int secs = p->lock_timeout / 1000;
1006 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1008 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1012 si->emergency_lock_p = False;
1014 if (!si->demoing_p && /* if not going into demo mode */
1015 p->lock_p && /* and locking is enabled */
1016 !si->locking_disabled_p && /* and locking is possible */
1017 lock_timeout == 0) /* and locking is not timer-deferred */
1018 set_locked_p (si, True); /* then lock right now. */
1020 /* locked_p might be true already because of the above, or because of
1021 the LOCK ClientMessage. But if not, and if we're supposed to lock
1022 after some time, set up a timer to do so.
1027 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1028 activate_lock_timer,
1031 #endif /* !NO_LOCKING */
1034 ok_to_unblank = True;
1037 sleep_until_idle (si, False); /* until not idle */
1038 maybe_reload_init_file (si);
1043 saver_screen_info *ssi = si->default_screen;
1044 if (si->locking_disabled_p) abort ();
1047 si->dbox_up_p = True;
1048 suspend_screenhack (si, True);
1049 XUndefineCursor (si->dpy, ssi->screensaver_window);
1051 ok_to_unblank = unlock_p (si);
1053 si->dbox_up_p = False;
1054 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1055 suspend_screenhack (si, False); /* resume */
1057 if (!ok_to_unblank &&
1058 !screenhack_running_p (si))
1060 /* If the lock dialog has been dismissed and we're not about to
1061 unlock the screen, and there is currently no hack running,
1062 then launch one. (There might be no hack running if DPMS
1063 had kicked in. But DPMS is off now, so bring back the hack)
1066 XtRemoveTimeOut (si->cycle_id);
1068 cycle_timer ((XtPointer) si, 0);
1071 #endif /* !NO_LOCKING */
1073 } while (!ok_to_unblank);
1077 fprintf (stderr, "%s: unblanking screen at %s.\n",
1078 blurb(), timestring ());
1080 /* Kill before unblanking, to stop drawing as soon as possible. */
1081 kill_screenhack (si);
1082 unblank_screen (si);
1084 set_locked_p (si, False);
1085 si->emergency_lock_p = False;
1087 si->selection_mode = 0;
1089 /* If we're throttled, and the user has explicitly unlocked the screen,
1090 then unthrottle. If we weren't locked, then don't unthrottle
1091 automatically, because someone might have just bumped the desk... */
1094 if (si->throttled_p && p->verbose_p)
1095 fprintf (stderr, "%s: unthrottled.\n", blurb());
1096 si->throttled_p = False;
1101 XtRemoveTimeOut (si->cycle_id);
1107 XtRemoveTimeOut (si->lock_id);
1112 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1116 static void analyze_display (saver_info *si);
1119 main (int argc, char **argv)
1123 saver_info *si = &the_si;
1124 saver_preferences *p = &si->prefs;
1127 memset(si, 0, sizeof(*si));
1128 global_si_kludge = si; /* I hate C so much... */
1130 # undef ya_rand_init
1133 save_argv (argc, argv);
1134 set_version_string (si, &argc, argv);
1135 privileged_initialization (si, &argc, argv);
1136 hack_environment (si);
1138 shell = connect_to_server (si, &argc, argv);
1139 process_command_line (si, &argc, argv);
1142 load_init_file (p); /* must be before initialize_per_screen_info() */
1143 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1145 /* We can only issue this warnings now. */
1146 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1148 "%s: there are no PseudoColor or GrayScale visuals.\n"
1149 "%s: ignoring the request for fading/unfading.\n",
1152 for (i = 0; i < si->nscreens; i++)
1153 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1156 lock_initialization (si, &argc, argv);
1158 if (p->xsync_p) XSynchronize (si->dpy, True);
1159 blurb_timestamp_p = p->timestamp_p; /* kludge */
1161 if (p->verbose_p) analyze_display (si);
1162 initialize_server_extensions (si);
1164 si->blank_time = time ((time_t) 0); /* must be before ..._window */
1165 initialize_screensaver_window (si);
1170 disable_builtin_screensaver (si, True);
1171 sync_server_dpms_settings (si->dpy, p->dpms_enabled_p,
1172 p->dpms_standby / 1000,
1173 p->dpms_suspend / 1000,
1177 initialize_stderr (si);
1179 make_splash_dialog (si);
1181 main_loop (si); /* doesn't return */
1186 /* Processing ClientMessage events.
1190 static Bool error_handler_hit_p = False;
1193 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1195 error_handler_hit_p = True;
1199 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1200 them. We only look up the atom names for printing warning messages,
1201 so don't bomb out when it happens...
1204 XGetAtomName_safe (Display *dpy, Atom atom)
1207 XErrorHandler old_handler;
1208 if (!atom) return 0;
1211 error_handler_hit_p = False;
1212 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1213 result = XGetAtomName (dpy, atom);
1215 XSetErrorHandler (old_handler);
1217 if (error_handler_hit_p) result = 0;
1224 sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned long) atom);
1225 return strdup (buf);
1231 clientmessage_response (saver_info *si, Window w, Bool error,
1232 const char *stderr_msg,
1233 const char *protocol_msg)
1237 saver_preferences *p = &si->prefs;
1238 XErrorHandler old_handler;
1240 if (error || p->verbose_p)
1241 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1243 L = strlen(protocol_msg);
1244 proto = (char *) malloc (L + 2);
1245 proto[0] = (error ? '-' : '+');
1246 strcpy (proto+1, protocol_msg);
1249 /* Ignore all X errors while sending a response to a ClientMessage.
1250 Pretty much the only way we could get an error here is if the
1251 window we're trying to send the reply on has been deleted, in
1252 which case, the sender of the ClientMessage won't see our response
1255 XSync (si->dpy, False);
1256 error_handler_hit_p = False;
1257 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1259 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1260 PropModeReplace, (unsigned char *) proto, L);
1262 XSync (si->dpy, False);
1263 XSetErrorHandler (old_handler);
1264 XSync (si->dpy, False);
1270 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1272 saver_preferences *p = &si->prefs;
1274 Window window = event->xclient.window;
1276 /* Preferences might affect our handling of client messages. */
1277 maybe_reload_init_file (si);
1279 if (event->xclient.message_type != XA_SCREENSAVER)
1282 str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1283 fprintf (stderr, "%s: unrecognised ClientMessage type %s received\n",
1284 blurb(), (str ? str : "(null)"));
1285 if (str) XFree (str);
1288 if (event->xclient.format != 32)
1290 fprintf (stderr, "%s: ClientMessage of format %d received, not 32\n",
1291 blurb(), event->xclient.format);
1295 type = event->xclient.data.l[0];
1296 if (type == XA_ACTIVATE)
1300 clientmessage_response(si, window, False,
1301 "ACTIVATE ClientMessage received.",
1303 si->selection_mode = 0;
1304 si->demoing_p = False;
1306 if (si->throttled_p && p->verbose_p)
1307 fprintf (stderr, "%s: unthrottled.\n", blurb());
1308 si->throttled_p = False;
1310 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1312 XForceScreenSaver (si->dpy, ScreenSaverActive);
1320 clientmessage_response(si, window, True,
1321 "ClientMessage ACTIVATE received while already active.",
1324 else if (type == XA_DEACTIVATE)
1328 if (si->throttled_p && p->verbose_p)
1329 fprintf (stderr, "%s: unthrottled.\n", blurb());
1330 si->throttled_p = False;
1332 clientmessage_response(si, window, False,
1333 "DEACTIVATE ClientMessage received.",
1335 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1337 XForceScreenSaver (si->dpy, ScreenSaverReset);
1345 clientmessage_response(si, window, True,
1346 "ClientMessage DEACTIVATE received while inactive.",
1349 else if (type == XA_CYCLE)
1353 clientmessage_response(si, window, False,
1354 "CYCLE ClientMessage received.",
1356 si->selection_mode = 0; /* 0 means randomize when its time. */
1357 si->demoing_p = False;
1359 if (si->throttled_p && p->verbose_p)
1360 fprintf (stderr, "%s: unthrottled.\n", blurb());
1361 si->throttled_p = False;
1364 XtRemoveTimeOut (si->cycle_id);
1366 cycle_timer ((XtPointer) si, 0);
1369 clientmessage_response(si, window, True,
1370 "ClientMessage CYCLE received while inactive.",
1373 else if (type == XA_NEXT || type == XA_PREV)
1375 clientmessage_response(si, window, False,
1377 ? "NEXT ClientMessage received."
1378 : "PREV ClientMessage received."),
1380 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1381 si->demoing_p = False;
1383 if (si->throttled_p && p->verbose_p)
1384 fprintf (stderr, "%s: unthrottled.\n", blurb());
1385 si->throttled_p = False;
1390 XtRemoveTimeOut (si->cycle_id);
1392 cycle_timer ((XtPointer) si, 0);
1397 else if (type == XA_SELECT)
1401 long which = event->xclient.data.l[1];
1403 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1404 sprintf (buf2, "activating (%ld).", which);
1405 clientmessage_response (si, window, False, buf, buf2);
1407 if (which < 0) which = 0; /* 0 == "random" */
1408 si->selection_mode = which;
1409 si->demoing_p = False;
1411 if (si->throttled_p && p->verbose_p)
1412 fprintf (stderr, "%s: unthrottled.\n", blurb());
1413 si->throttled_p = False;
1418 XtRemoveTimeOut (si->cycle_id);
1420 cycle_timer ((XtPointer) si, 0);
1425 else if (type == XA_EXIT)
1427 /* Ignore EXIT message if the screen is locked. */
1428 if (until_idle_p || !si->locked_p)
1430 clientmessage_response (si, window, False,
1431 "EXIT ClientMessage received.",
1435 unblank_screen (si);
1436 kill_screenhack (si);
1437 XSync (si->dpy, False);
1439 saver_exit (si, 0, 0);
1442 clientmessage_response (si, window, True,
1443 "EXIT ClientMessage received while locked.",
1444 "screen is locked.");
1446 else if (type == XA_RESTART)
1448 /* The RESTART message works whether the screensaver is active or not,
1449 unless the screen is locked, in which case it doesn't work.
1451 if (until_idle_p || !si->locked_p)
1453 clientmessage_response (si, window, False,
1454 "RESTART ClientMessage received.",
1458 unblank_screen (si);
1459 kill_screenhack (si);
1460 XSync (si->dpy, False);
1465 if (real_stdout) fflush (real_stdout);
1466 if (real_stderr) fflush (real_stderr);
1467 /* make sure error message shows up before exit. */
1468 if (real_stderr && stderr != real_stderr)
1469 dup2 (fileno(real_stderr), fileno(stderr));
1471 restart_process (si);
1472 exit (1); /* shouldn't get here; but if restarting didn't work,
1473 make this command be the same as EXIT. */
1476 clientmessage_response (si, window, True,
1477 "RESTART ClientMessage received while locked.",
1478 "screen is locked.");
1480 else if (type == XA_DEMO)
1482 long arg = event->xclient.data.l[1];
1483 Bool demo_one_hack_p = (arg == 300);
1485 if (demo_one_hack_p)
1489 long which = event->xclient.data.l[2];
1492 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1493 sprintf (buf2, "demoing (%ld).", which);
1494 clientmessage_response (si, window, False, buf, buf2);
1496 if (which < 0) which = 0; /* 0 == "random" */
1497 si->selection_mode = which;
1498 si->demoing_p = True;
1500 if (si->throttled_p && p->verbose_p)
1501 fprintf (stderr, "%s: unthrottled.\n", blurb());
1502 si->throttled_p = False;
1507 clientmessage_response (si, window, True,
1508 "DEMO ClientMessage received while active.",
1513 clientmessage_response (si, window, True,
1514 "obsolete form of DEMO ClientMessage.",
1515 "obsolete form of DEMO ClientMessage.");
1518 else if (type == XA_PREFS)
1520 clientmessage_response (si, window, True,
1521 "the PREFS client-message is obsolete.",
1522 "the PREFS client-message is obsolete.");
1524 else if (type == XA_LOCK)
1527 clientmessage_response (si, window, True,
1528 "not compiled with support for locking.",
1529 "locking not enabled.");
1530 #else /* !NO_LOCKING */
1531 if (si->locking_disabled_p)
1532 clientmessage_response (si, window, True,
1533 "LOCK ClientMessage received, but locking is disabled.",
1534 "locking not enabled.");
1535 else if (si->locked_p)
1536 clientmessage_response (si, window, True,
1537 "LOCK ClientMessage received while already locked.",
1542 char *response = (until_idle_p
1543 ? "activating and locking."
1545 sprintf (buf, "LOCK ClientMessage received; %s", response);
1546 clientmessage_response (si, window, False, buf, response);
1547 set_locked_p (si, True);
1548 si->selection_mode = 0;
1549 si->demoing_p = False;
1551 if (si->lock_id) /* we're doing it now, so lose the timeout */
1553 XtRemoveTimeOut (si->lock_id);
1559 if (si->using_mit_saver_extension ||
1560 si->using_sgi_saver_extension)
1562 XForceScreenSaver (si->dpy, ScreenSaverActive);
1571 #endif /* !NO_LOCKING */
1573 else if (type == XA_THROTTLE)
1575 if (si->throttled_p)
1576 clientmessage_response (si, window, True,
1577 "THROTTLE ClientMessage received, but "
1578 "already throttled.",
1579 "already throttled.");
1583 char *response = "throttled.";
1584 si->throttled_p = True;
1585 si->selection_mode = 0;
1586 si->demoing_p = False;
1587 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
1588 clientmessage_response (si, window, False, buf, response);
1593 XtRemoveTimeOut (si->cycle_id);
1595 cycle_timer ((XtPointer) si, 0);
1599 else if (type == XA_UNTHROTTLE)
1601 if (! si->throttled_p)
1602 clientmessage_response (si, window, True,
1603 "UNTHROTTLE ClientMessage received, but "
1609 char *response = "unthrottled.";
1610 si->throttled_p = False;
1611 si->selection_mode = 0;
1612 si->demoing_p = False;
1613 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
1614 clientmessage_response (si, window, False, buf, response);
1619 XtRemoveTimeOut (si->cycle_id);
1621 cycle_timer ((XtPointer) si, 0);
1629 str = XGetAtomName_safe (si->dpy, type);
1633 if (strlen (str) > 80)
1634 strcpy (str+70, "...");
1635 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
1642 "unrecognised screensaver ClientMessage 0x%x received.",
1643 (unsigned int) event->xclient.data.l[0]);
1646 clientmessage_response (si, window, True, buf, buf);
1652 /* Some random diagnostics printed in -verbose mode.
1656 analyze_display (saver_info *si)
1660 const char *name; const char *desc; Bool useful_p;
1663 { "SCREEN_SAVER", "SGI Screen-Saver",
1664 # ifdef HAVE_SGI_SAVER_EXTENSION
1669 }, { "SCREEN-SAVER", "SGI Screen-Saver",
1670 # ifdef HAVE_SGI_SAVER_EXTENSION
1675 }, { "MIT-SCREEN-SAVER", "MIT Screen-Saver",
1676 # ifdef HAVE_MIT_SAVER_EXTENSION
1681 }, { "XIDLE", "XIdle",
1682 # ifdef HAVE_XIDLE_EXTENSION
1687 }, { "SGI-VIDEO-CONTROL", "SGI Video-Control",
1688 # ifdef HAVE_SGI_VC_EXTENSION
1693 }, { "READDISPLAY", "SGI Read-Display",
1694 # ifdef HAVE_READ_DISPLAY_EXTENSION
1699 }, { "MIT-SHM", "Shared Memory",
1700 # ifdef HAVE_XSHM_EXTENSION
1705 }, { "DOUBLE-BUFFER", "Double-Buffering",
1706 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
1711 }, { "DPMS", "Power Management",
1712 # ifdef HAVE_DPMS_EXTENSION
1723 }, { "XFree86-VidModeExtension", "XF86 Video-Mode",
1724 # ifdef HAVE_XF86VMODE
1729 }, { "XINERAMA", "Xinerama",
1734 fprintf (stderr, "%s: running on display \"%s\"\n", blurb(),
1735 DisplayString(si->dpy));
1736 fprintf (stderr, "%s: vendor is %s, %d\n", blurb(),
1737 ServerVendor(si->dpy), VendorRelease(si->dpy));
1739 fprintf (stderr, "%s: useful extensions:\n", blurb());
1740 for (i = 0; i < countof(exts); i++)
1742 int op = 0, event = 0, error = 0;
1743 if (XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
1744 fprintf (stderr, "%s: %s%s\n", blurb(),
1746 (exts[i].useful_p ? "" :
1747 " \t<== unsupported at compile-time!"));
1750 for (i = 0; i < si->nscreens; i++)
1752 unsigned long colormapped_depths = 0;
1753 unsigned long non_mapped_depths = 0;
1754 XVisualInfo vi_in, *vi_out;
1757 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
1758 if (!vi_out) continue;
1759 for (j = 0; j < out_count; j++)
1760 if (vi_out[j].class == PseudoColor)
1761 colormapped_depths |= (1 << vi_out[j].depth);
1763 non_mapped_depths |= (1 << vi_out[j].depth);
1764 XFree ((char *) vi_out);
1766 if (colormapped_depths)
1768 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(), i);
1769 for (j = 0; j < 32; j++)
1770 if (colormapped_depths & (1 << j))
1771 fprintf (stderr, " %d", j);
1772 fprintf (stderr, "\n");
1774 if (non_mapped_depths)
1776 fprintf (stderr, "%s: screen %d non-mapped depths:", blurb(), i);
1777 for (j = 0; j < 32; j++)
1778 if (non_mapped_depths & (1 << j))
1779 fprintf (stderr, " %d", j);
1780 fprintf (stderr, "\n");
1786 display_is_on_console_p (saver_info *si)
1788 Bool not_on_console = True;
1789 char *dpystr = DisplayString (si->dpy);
1790 char *tail = (char *) strchr (dpystr, ':');
1791 if (! tail || strncmp (tail, ":0", 2))
1792 not_on_console = True;
1795 char dpyname[255], localname[255];
1796 strncpy (dpyname, dpystr, tail-dpystr);
1797 dpyname [tail-dpystr] = 0;
1799 !strcmp(dpyname, "unix") ||
1800 !strcmp(dpyname, "localhost"))
1801 not_on_console = False;
1802 else if (gethostname (localname, sizeof (localname)))
1803 not_on_console = True; /* can't find hostname? */
1806 /* We have to call gethostbyname() on the result of gethostname()
1807 because the two aren't guarenteed to be the same name for the
1808 same host: on some losing systems, one is a FQDN and the other
1809 is not. Here in the wide wonderful world of Unix it's rocket
1810 science to obtain the local hostname in a portable fashion.
1812 And don't forget, gethostbyname() reuses the structure it
1813 returns, so we have to copy the fucker before calling it again.
1814 Thank you master, may I have another.
1816 struct hostent *h = gethostbyname (dpyname);
1818 not_on_console = True;
1823 strcpy (hn, h->h_name);
1824 l = gethostbyname (localname);
1825 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
1829 return !not_on_console;