1 /* xscreensaver, Copyright (c) 1991-2002 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 [] = {
173 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
174 { "-silent", ".verbose", XrmoptionNoArg, "off" },
176 /* xscreensaver-demo uses this one */
177 { "-nosplash", ".splash", XrmoptionNoArg, "off" },
178 { "-no-splash", ".splash", XrmoptionNoArg, "off" },
180 /* useful for debugging */
181 { "-no-capture-stderr", ".captureStderr", XrmoptionNoArg, "off" },
183 /* There's really no reason to have these command-line args; they just
184 lead to confusion when the .xscreensaver file has conflicting values.
187 { "-splash", ".splash", XrmoptionNoArg, "on" },
188 { "-capture-stderr", ".captureStderr", XrmoptionNoArg, "on" },
189 { "-timeout", ".timeout", XrmoptionSepArg, 0 },
190 { "-cycle", ".cycle", XrmoptionSepArg, 0 },
191 { "-lock-mode", ".lock", XrmoptionNoArg, "on" },
192 { "-no-lock-mode", ".lock", XrmoptionNoArg, "off" },
193 { "-no-lock", ".lock", XrmoptionNoArg, "off" },
194 { "-lock-timeout", ".lockTimeout", XrmoptionSepArg, 0 },
195 { "-lock-vts", ".lockVTs", XrmoptionNoArg, "on" },
196 { "-no-lock-vts", ".lockVTs", XrmoptionNoArg, "off" },
197 { "-visual", ".visualID", XrmoptionSepArg, 0 },
198 { "-install", ".installColormap", XrmoptionNoArg, "on" },
199 { "-no-install", ".installColormap", XrmoptionNoArg, "off" },
200 { "-timestamp", ".timestamp", XrmoptionNoArg, "on" },
201 { "-xidle-extension", ".xidleExtension", XrmoptionNoArg, "on" },
202 { "-no-xidle-extension", ".xidleExtension", XrmoptionNoArg, "off" },
203 { "-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "on" },
204 { "-no-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "off" },
205 { "-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "on" },
206 { "-no-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "off" },
207 { "-proc-interrupts", ".procInterrupts", XrmoptionNoArg, "on" },
208 { "-no-proc-interrupts", ".procInterrupts", XrmoptionNoArg, "off" },
209 { "-idelay", ".initialDelay", XrmoptionSepArg, 0 },
210 { "-nice", ".nice", XrmoptionSepArg, 0 },
214 static char *defaults[] = {
215 #include "XScreenSaver_ad.h"
220 ERROR! You must not include vroot.h in this file.
224 do_help (saver_info *si)
229 xscreensaver %s, copyright (c) 1991-2002 by Jamie Zawinski <jwz@jwz.org>\n\
231 All xscreensaver configuration is via the `~/.xscreensaver' file.\n\
232 Rather than editing that file by hand, just run `xscreensaver-demo':\n\
233 that program lets you configure the screen saver graphically,\n\
234 including timeouts, locking, and display modes.\n\
236 Just getting started? Try this:\n\
241 For updates, online manual, and FAQ, please see the web page:\n\
243 http://www.jwz.org/xscreensaver/\n\
255 time_t now = time ((time_t *) 0);
256 char *str = (char *) ctime (&now);
257 char *nl = (char *) strchr (str, '\n');
258 if (nl) *nl = 0; /* take off that dang newline */
262 static Bool blurb_timestamp_p = False; /* kludge */
267 if (!blurb_timestamp_p)
271 static char buf[255];
272 char *ct = timestring();
273 int n = strlen(progname);
275 strncpy(buf, progname, n);
278 strncpy(buf+n, ct+11, 8);
279 strcpy(buf+n+9, ": ");
286 saver_ehandler (Display *dpy, XErrorEvent *error)
288 saver_info *si = global_si_kludge; /* I hate C so much... */
291 if (!real_stderr) real_stderr = stderr;
293 fprintf (real_stderr, "\n"
294 "#######################################"
295 "#######################################\n\n"
296 "%s: X Error! PLEASE REPORT THIS BUG.\n",
299 for (i = 0; i < si->nscreens; i++)
300 fprintf (real_stderr, "%s: screen %d: 0x%x, 0x%x, 0x%x\n",
302 RootWindowOfScreen (si->screens[i].screen),
303 si->screens[i].real_vroot,
304 si->screens[i].screensaver_window);
306 fprintf (real_stderr, "\n"
307 "#######################################"
308 "#######################################\n\n");
310 if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
312 fprintf (real_stderr, "\n");
313 if (si->prefs.xsync_p)
315 saver_exit (si, -1, "because of synchronous X Error");
319 fprintf (real_stderr,
320 "#######################################"
321 "#######################################\n\n");
322 fprintf (real_stderr,
323 " If at all possible, please re-run xscreensaver with the command\n"
324 " line arguments `-sync -verbose -no-capture', and reproduce this\n"
325 " bug. That will cause xscreensaver to dump a `core' file to the\n"
326 " current directory. Please include the stack trace from that core\n"
327 " file in your bug report. *DO NOT* mail the core file itself!\n"
328 " That won't work.\n"
330 " http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
331 " the most useful bug reports, and how to examine core files.\n"
333 " The more information you can provide, the better. But please\n"
334 " report this bug, regardless!\n"
336 fprintf (real_stderr,
337 "#######################################"
338 "#######################################\n\n");
340 saver_exit (si, -1, 0);
344 fprintf (real_stderr, " (nonfatal.)\n");
349 /* This error handler is used only while the X connection is being set up;
350 after we've got a connection, we don't use this handler again. The only
351 reason for having this is so that we can present a more idiot-proof error
352 message than "cannot open display."
355 startup_ehandler (String name, String type, String class,
356 String defalt, /* one can't even spel properly
357 in this joke of a language */
358 String *av, Cardinal *ac)
362 saver_info *si = global_si_kludge; /* I hate C so much... */
363 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
365 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
366 fmt, sizeof(fmt)-1, *db);
368 fprintf (stderr, "%s: ", blurb());
370 memset (p, 0, sizeof(p));
371 if (*ac > countof (p)) *ac = countof (p);
372 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
373 fprintf (stderr, fmt, /* Did I mention that I hate C? */
374 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
375 fprintf (stderr, "\n");
377 describe_uids (si, stderr);
379 if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
381 fprintf (stderr, "\n"
382 "%s: This is probably because you're logging in as root. You\n"
383 " shouldn't log in as root: you should log in as a normal user,\n"
384 " and then `su' as needed. If you insist on logging in as\n"
385 " root, you will have to turn off X's security features before\n"
386 " xscreensaver will work.\n"
388 " Please read the manual and FAQ for more information:\n",
393 fprintf (stderr, "\n"
394 "%s: Errors at startup are usually authorization problems.\n"
395 " But you're not logging in as root (good!) so something\n"
396 " else must be wrong. Did you read the manual and the FAQ?\n",
400 fprintf (stderr, "\n"
401 " http://www.jwz.org/xscreensaver/faq.html\n"
402 " http://www.jwz.org/xscreensaver/man.html\n"
411 /* The zillions of initializations.
414 /* Set progname, version, etc. This is done very early.
417 set_version_string (saver_info *si, int *argc, char **argv)
419 progclass = "XScreenSaver";
421 /* progname is reset later, after we connect to X. */
422 progname = strrchr(argv[0], '/');
423 if (progname) progname++;
424 else progname = argv[0];
426 if (strlen(progname) > 100) /* keep it short. */
429 /* The X resource database blows up if argv[0] has a "." in it. */
432 while ((s = strchr (s, '.')))
436 si->version = (char *) malloc (5);
437 memcpy (si->version, screensaver_id + 17, 4);
442 /* Initializations that potentially take place as a priveleged user:
443 If the xscreensaver executable is setuid root, then these initializations
444 are run as root, before discarding privileges.
447 privileged_initialization (saver_info *si, int *argc, char **argv)
450 /* before hack_uid() for proper permissions */
451 lock_priv_init (*argc, argv, si->prefs.verbose_p);
452 #endif /* NO_LOCKING */
458 /* Figure out what locking mechanisms are supported.
461 lock_initialization (saver_info *si, int *argc, char **argv)
464 si->locking_disabled_p = True;
465 si->nolock_reason = "not compiled with locking support";
466 #else /* !NO_LOCKING */
468 /* Finish initializing locking, now that we're out of privileged code. */
469 if (! lock_init (*argc, argv, si->prefs.verbose_p))
471 si->locking_disabled_p = True;
472 si->nolock_reason = "error getting password";
474 #endif /* NO_LOCKING */
480 /* Open the connection to the X server, and intern our Atoms.
483 connect_to_server (saver_info *si, int *argc, char **argv)
485 Widget toplevel_shell;
488 char *d = getenv ("DISPLAY");
491 char ndpy[] = "DISPLAY=:0.0";
492 /* if (si->prefs.verbose_p) */ /* sigh, too early to test this... */
494 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
499 #endif /* HAVE_PUTENV */
501 XSetErrorHandler (saver_ehandler);
503 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
504 toplevel_shell = XtAppInitialize (&si->app, progclass,
505 options, XtNumber (options),
506 argc, argv, defaults, 0, 0);
507 XtAppSetErrorMsgHandler (si->app, 0);
509 si->dpy = XtDisplay (toplevel_shell);
510 si->prefs.db = XtDatabase (si->dpy);
511 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
513 if(strlen(progname) > 100) /* keep it short. */
516 db = si->prefs.db; /* resources.c needs this */
518 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
519 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
520 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
521 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
522 XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
523 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
525 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
526 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
527 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
528 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
529 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
530 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
531 XA_PREV = XInternAtom (si->dpy, "PREV", False);
532 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
533 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
534 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
535 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
536 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
537 XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
538 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
539 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
541 return toplevel_shell;
545 /* Handle the command-line arguments that were not handled for us by Xt.
546 Issue an error message and exit if there are unknown options.
549 process_command_line (saver_info *si, int *argc, char **argv)
552 for (i = 1; i < *argc; i++)
554 if (!strcmp (argv[i], "-debug"))
555 /* no resource for this one, out of paranoia. */
556 si->prefs.debug_p = True;
558 else if (!strcmp (argv[i], "-h") ||
559 !strcmp (argv[i], "-help") ||
560 !strcmp (argv[i], "--help"))
565 const char *s = argv[i];
566 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
569 if (s[0] == '-' && s[1] == '-') s++;
570 if (!strcmp (s, "-activate") ||
571 !strcmp (s, "-deactivate") ||
572 !strcmp (s, "-cycle") ||
573 !strcmp (s, "-next") ||
574 !strcmp (s, "-prev") ||
575 !strcmp (s, "-exit") ||
576 !strcmp (s, "-restart") ||
577 !strcmp (s, "-demo") ||
578 !strcmp (s, "-prefs") ||
579 !strcmp (s, "-preferences") ||
580 !strcmp (s, "-lock") ||
581 !strcmp (s, "-version") ||
582 !strcmp (s, "-time"))
585 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
586 fprintf (stderr, "\n\
587 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
589 fprintf (stderr, "\n\
590 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
593 The `xscreensaver' program is a daemon that runs in the background.\n\
594 You control a running xscreensaver process by sending it messages\n\
595 with `xscreensaver-demo' or `xscreensaver-command'.\n\
596 . See the man pages for details, or check the web page:\n\
597 http://www.jwz.org/xscreensaver/\n\n");
599 /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
600 suggest that explicitly. */
601 if (!strcmp (s, "-lock"))
603 Or perhaps you meant either the \"-lock-mode\" or the\n\
604 \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
613 /* Print out the xscreensaver banner to the tty if applicable;
614 Issue any other warnings that are called for at this point.
617 print_banner (saver_info *si)
619 saver_preferences *p = &si->prefs;
621 /* This resource gets set some time before the others, so that we know
622 whether to print the banner (and so that the banner gets printed before
623 any resource-database-related error messages.)
625 p->verbose_p = (p->debug_p || get_boolean_resource ("verbose", "Boolean"));
627 /* Ditto, for the locking_disabled_p message. */
628 p->lock_p = get_boolean_resource ("lock", "Boolean");
632 "%s %s, copyright (c) 1991-2002 "
633 "by Jamie Zawinski <jwz@jwz.org>.\n",
634 progname, si->version);
637 fprintf (stderr, "\n"
638 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
640 "\tNote that in debug mode, the xscreensaver window will only\n"
641 "\tcover the left half of the screen. (The idea is that you\n"
642 "\tcan still see debugging output in a shell, if you position\n"
643 "\tit on the right side of the screen.)\n"
645 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
646 "\tuntrusted environments.\n"
652 if (!si->uid_message || !*si->uid_message)
653 describe_uids (si, stderr);
656 if (si->orig_uid && *si->orig_uid)
657 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
658 blurb(), si->orig_uid);
659 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
662 fprintf (stderr, "%s: in process %lu.\n", blurb(),
663 (unsigned long) getpid());
666 /* If locking was not able to be initalized for some reason, explain why.
667 (This has to be done after we've read the lock_p resource.)
669 if (p->lock_p && si->locking_disabled_p)
672 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
674 if (strstr (si->nolock_reason, "passw"))
675 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
676 "consult the manual.\n", blurb());
677 else if (strstr (si->nolock_reason, "running as "))
679 "%s: locking only works when xscreensaver is launched\n"
680 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
681 "\t See the manual for details.\n",
687 /* Examine all of the display's screens, and populate the `saver_screen_info'
688 structures. Make sure this is called after hack_environment() sets $PATH.
691 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
693 Bool found_any_writable_cells = False;
696 si->nscreens = ScreenCount(si->dpy);
697 si->screens = (saver_screen_info *)
698 calloc(sizeof(saver_screen_info), si->nscreens);
700 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
702 for (i = 0; i < si->nscreens; i++)
704 saver_screen_info *ssi = &si->screens[i];
706 ssi->screen = ScreenOfDisplay (si->dpy, i);
709 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
710 ssi->default_visual =
711 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
713 ssi->current_visual = ssi->default_visual;
714 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
716 /* Execute a subprocess to find the GL visual. */
717 ssi->best_gl_visual = get_best_gl_visual (ssi);
719 if (ssi == si->default_screen)
720 /* Since this is the default screen, use the one already created. */
721 ssi->toplevel_shell = toplevel_shell;
723 /* Otherwise, each screen must have its own unmapped root widget. */
724 ssi->toplevel_shell =
725 XtVaAppCreateShell (progname, progclass, applicationShellWidgetClass,
727 XtNscreen, ssi->screen,
728 XtNvisual, ssi->current_visual,
729 XtNdepth, visual_depth (ssi->screen,
730 ssi->current_visual),
733 if (! found_any_writable_cells)
735 /* Check to see whether fading is ever possible -- if any of the
736 screens on the display has a PseudoColor visual, then fading can
737 work (on at least some screens.) If no screen has a PseudoColor
738 visual, then don't bother ever trying to fade, because it will
739 just cause a delay without causing any visible effect.
741 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
742 get_visual (ssi->screen, "PseudoColor", True, False) ||
743 get_visual (ssi->screen, "GrayScale", True, False))
744 found_any_writable_cells = True;
748 si->fading_possible_p = found_any_writable_cells;
750 #ifdef HAVE_XF86VMODE_GAMMA
751 si->fading_possible_p = True; /* if we can gamma fade, go for it */
756 /* If any server extensions have been requested, try and initialize them.
757 Issue warnings if requests can't be honored.
760 initialize_server_extensions (saver_info *si)
762 saver_preferences *p = &si->prefs;
764 Bool server_has_xidle_extension_p = False;
765 Bool server_has_sgi_saver_extension_p = False;
766 Bool server_has_mit_saver_extension_p = False;
767 Bool system_has_proc_interrupts_p = False;
768 const char *piwhy = 0;
770 si->using_xidle_extension = p->use_xidle_extension;
771 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
772 si->using_mit_saver_extension = p->use_mit_saver_extension;
773 si->using_proc_interrupts = p->use_proc_interrupts;
775 #ifdef HAVE_XIDLE_EXTENSION
776 server_has_xidle_extension_p = query_xidle_extension (si);
778 #ifdef HAVE_SGI_SAVER_EXTENSION
779 server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
781 #ifdef HAVE_MIT_SAVER_EXTENSION
782 server_has_mit_saver_extension_p = query_mit_saver_extension (si);
784 #ifdef HAVE_PROC_INTERRUPTS
785 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
788 if (!server_has_xidle_extension_p)
789 si->using_xidle_extension = False;
790 else if (p->verbose_p)
792 if (si->using_xidle_extension)
793 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
795 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
798 if (!server_has_sgi_saver_extension_p)
799 si->using_sgi_saver_extension = False;
800 else if (p->verbose_p)
802 if (si->using_sgi_saver_extension)
803 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
806 "%s: not using server's SGI SCREEN_SAVER extension.\n",
810 if (!server_has_mit_saver_extension_p)
811 si->using_mit_saver_extension = False;
812 else if (p->verbose_p)
814 if (si->using_mit_saver_extension)
815 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
819 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
823 if (!system_has_proc_interrupts_p)
825 si->using_proc_interrupts = False;
826 if (p->verbose_p && piwhy)
827 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
830 else if (p->verbose_p)
832 if (si->using_proc_interrupts)
834 "%s: consulting /proc/interrupts for keyboard activity.\n",
838 "%s: not consulting /proc/interrupts for keyboard activity.\n",
844 /* For the case where we aren't using an server extensions, select user events
845 on all the existing windows, and launch timers to select events on
846 newly-created windows as well.
848 If a server extension is being used, this does nothing.
851 select_events (saver_info *si)
853 saver_preferences *p = &si->prefs;
856 if (si->using_xidle_extension ||
857 si->using_mit_saver_extension ||
858 si->using_sgi_saver_extension)
861 if (p->initial_delay)
865 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
866 (int) p->initial_delay/1000,
867 (p->initial_delay == 1000 ? "" : "s"));
871 usleep (p->initial_delay);
873 fprintf (stderr, " done.\n");
878 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
883 /* Select events on the root windows of every screen. This also selects
884 for window creation events, so that new subwindows will be noticed.
886 for (i = 0; i < si->nscreens; i++)
887 start_notice_events_timer (si, RootWindowOfScreen (si->screens[i].screen),
891 fprintf (stderr, " done.\n");
896 maybe_reload_init_file (saver_info *si)
898 saver_preferences *p = &si->prefs;
899 if (init_file_changed_p (p))
902 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
903 blurb(), init_file_name());
907 /* If a server extension is in use, and p->timeout has changed,
908 we need to inform the server of the new timeout. */
909 disable_builtin_screensaver (si, False);
911 /* If the DPMS settings in the init file have changed,
912 change the settings on the server to match. */
913 sync_server_dpms_settings (si->dpy,
914 (p->dpms_enabled_p &&
915 p->mode != DONT_BLANK),
916 p->dpms_standby / 1000,
917 p->dpms_suspend / 1000,
926 - wait until the user is idle;
928 - wait until the user is active;
929 - unblank the screen;
934 main_loop (saver_info *si)
936 saver_preferences *p = &si->prefs;
941 Bool was_locked = False;
942 sleep_until_idle (si, True);
947 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
948 si->selection_mode, timestring());
950 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
954 maybe_reload_init_file (si);
956 if (p->mode == DONT_BLANK)
959 fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
960 blurb(), timestring());
962 /* Go around the loop and wait for the next bout of idleness,
963 or for the init file to change, or for a remote command to
964 come in, or something.
969 if (! blank_screen (si))
971 /* We were unable to grab either the keyboard or mouse.
972 This means we did not (and must not) blank the screen.
973 If we were to blank the screen while some other program
974 is holding both the mouse and keyboard grabbed, then
975 we would never be able to un-blank it! We would never
976 see any events, and the display would be wedged.
978 So, just go around the loop again and wait for the
979 next bout of idleness.
983 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
988 kill_screenhack (si);
990 if (!si->throttled_p)
991 spawn_screenhack (si, True);
992 else if (p->verbose_p)
993 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
995 /* Don't start the cycle timer in demo mode. */
996 if (!si->demoing_p && p->cycle)
997 si->cycle_id = XtAppAddTimeOut (si->app,
999 /* see comment in cycle_timer() */
1007 /* Maybe start locking the screen.
1010 Time lock_timeout = p->lock_timeout;
1012 if (si->emergency_lock_p && p->lock_p && lock_timeout)
1014 int secs = p->lock_timeout / 1000;
1017 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1019 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1023 si->emergency_lock_p = False;
1025 if (!si->demoing_p && /* if not going into demo mode */
1026 p->lock_p && /* and locking is enabled */
1027 !si->locking_disabled_p && /* and locking is possible */
1028 lock_timeout == 0) /* and locking is not timer-deferred */
1029 set_locked_p (si, True); /* then lock right now. */
1031 /* locked_p might be true already because of the above, or because of
1032 the LOCK ClientMessage. But if not, and if we're supposed to lock
1033 after some time, set up a timer to do so.
1038 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1039 activate_lock_timer,
1042 #endif /* !NO_LOCKING */
1045 ok_to_unblank = True;
1048 sleep_until_idle (si, False); /* until not idle */
1049 maybe_reload_init_file (si);
1052 /* Maybe unlock the screen.
1056 saver_screen_info *ssi = si->default_screen;
1057 if (si->locking_disabled_p) abort ();
1060 si->dbox_up_p = True;
1061 suspend_screenhack (si, True);
1062 XUndefineCursor (si->dpy, ssi->screensaver_window);
1064 ok_to_unblank = unlock_p (si);
1066 si->dbox_up_p = False;
1067 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1068 suspend_screenhack (si, False); /* resume */
1070 if (!ok_to_unblank &&
1071 !screenhack_running_p (si))
1073 /* If the lock dialog has been dismissed and we're not about to
1074 unlock the screen, and there is currently no hack running,
1075 then launch one. (There might be no hack running if DPMS
1076 had kicked in. But DPMS is off now, so bring back the hack)
1079 XtRemoveTimeOut (si->cycle_id);
1081 cycle_timer ((XtPointer) si, 0);
1084 #endif /* !NO_LOCKING */
1086 } while (!ok_to_unblank);
1090 fprintf (stderr, "%s: unblanking screen at %s.\n",
1091 blurb(), timestring ());
1093 /* Kill before unblanking, to stop drawing as soon as possible. */
1094 kill_screenhack (si);
1095 unblank_screen (si);
1097 set_locked_p (si, False);
1098 si->emergency_lock_p = False;
1100 si->selection_mode = 0;
1102 /* If we're throttled, and the user has explicitly unlocked the screen,
1103 then unthrottle. If we weren't locked, then don't unthrottle
1104 automatically, because someone might have just bumped the desk... */
1107 if (si->throttled_p && p->verbose_p)
1108 fprintf (stderr, "%s: unthrottled.\n", blurb());
1109 si->throttled_p = False;
1114 XtRemoveTimeOut (si->cycle_id);
1120 XtRemoveTimeOut (si->lock_id);
1125 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1129 static void analyze_display (saver_info *si);
1132 main (int argc, char **argv)
1136 saver_info *si = &the_si;
1137 saver_preferences *p = &si->prefs;
1140 memset(si, 0, sizeof(*si));
1141 global_si_kludge = si; /* I hate C so much... */
1143 # undef ya_rand_init
1146 save_argv (argc, argv);
1147 set_version_string (si, &argc, argv);
1148 privileged_initialization (si, &argc, argv);
1149 hack_environment (si);
1151 shell = connect_to_server (si, &argc, argv);
1152 process_command_line (si, &argc, argv);
1155 load_init_file (p); /* must be before initialize_per_screen_info() */
1156 blurb_timestamp_p = p->timestamp_p; /* kludge */
1157 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1159 /* We can only issue this warnings now. */
1160 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1162 "%s: there are no PseudoColor or GrayScale visuals.\n"
1163 "%s: ignoring the request for fading/unfading.\n",
1166 for (i = 0; i < si->nscreens; i++)
1167 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1170 lock_initialization (si, &argc, argv);
1172 if (p->xsync_p) XSynchronize (si->dpy, True);
1174 if (p->verbose_p) analyze_display (si);
1175 initialize_server_extensions (si);
1177 si->blank_time = time ((time_t) 0); /* must be before ..._window */
1178 initialize_screensaver_window (si);
1183 disable_builtin_screensaver (si, True);
1184 sync_server_dpms_settings (si->dpy,
1185 (p->dpms_enabled_p &&
1186 p->mode != DONT_BLANK),
1187 p->dpms_standby / 1000,
1188 p->dpms_suspend / 1000,
1192 initialize_stderr (si);
1194 make_splash_dialog (si);
1196 main_loop (si); /* doesn't return */
1201 /* Processing ClientMessage events.
1205 static Bool error_handler_hit_p = False;
1208 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1210 error_handler_hit_p = True;
1214 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1215 them. We only look up the atom names for printing warning messages,
1216 so don't bomb out when it happens...
1219 XGetAtomName_safe (Display *dpy, Atom atom)
1222 XErrorHandler old_handler;
1223 if (!atom) return 0;
1226 error_handler_hit_p = False;
1227 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1228 result = XGetAtomName (dpy, atom);
1230 XSetErrorHandler (old_handler);
1232 if (error_handler_hit_p) result = 0;
1239 sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned long) atom);
1240 return strdup (buf);
1246 clientmessage_response (saver_info *si, Window w, Bool error,
1247 const char *stderr_msg,
1248 const char *protocol_msg)
1252 saver_preferences *p = &si->prefs;
1253 XErrorHandler old_handler;
1255 if (error || p->verbose_p)
1256 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1258 L = strlen(protocol_msg);
1259 proto = (char *) malloc (L + 2);
1260 proto[0] = (error ? '-' : '+');
1261 strcpy (proto+1, protocol_msg);
1264 /* Ignore all X errors while sending a response to a ClientMessage.
1265 Pretty much the only way we could get an error here is if the
1266 window we're trying to send the reply on has been deleted, in
1267 which case, the sender of the ClientMessage won't see our response
1270 XSync (si->dpy, False);
1271 error_handler_hit_p = False;
1272 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1274 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1275 PropModeReplace, (unsigned char *) proto, L);
1277 XSync (si->dpy, False);
1278 XSetErrorHandler (old_handler);
1279 XSync (si->dpy, False);
1285 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1287 saver_preferences *p = &si->prefs;
1289 Window window = event->xclient.window;
1291 /* Preferences might affect our handling of client messages. */
1292 maybe_reload_init_file (si);
1294 if (event->xclient.message_type != XA_SCREENSAVER)
1297 str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1298 fprintf (stderr, "%s: unrecognised ClientMessage type %s received\n",
1299 blurb(), (str ? str : "(null)"));
1300 if (str) XFree (str);
1303 if (event->xclient.format != 32)
1305 fprintf (stderr, "%s: ClientMessage of format %d received, not 32\n",
1306 blurb(), event->xclient.format);
1310 type = event->xclient.data.l[0];
1311 if (type == XA_ACTIVATE)
1315 clientmessage_response(si, window, False,
1316 "ACTIVATE ClientMessage received.",
1318 si->selection_mode = 0;
1319 si->demoing_p = False;
1321 if (si->throttled_p && p->verbose_p)
1322 fprintf (stderr, "%s: unthrottled.\n", blurb());
1323 si->throttled_p = False;
1325 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1327 XForceScreenSaver (si->dpy, ScreenSaverActive);
1335 clientmessage_response(si, window, True,
1336 "ClientMessage ACTIVATE received while already active.",
1339 else if (type == XA_DEACTIVATE)
1343 if (si->throttled_p && p->verbose_p)
1344 fprintf (stderr, "%s: unthrottled.\n", blurb());
1345 si->throttled_p = False;
1347 clientmessage_response(si, window, False,
1348 "DEACTIVATE ClientMessage received.",
1350 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1352 XForceScreenSaver (si->dpy, ScreenSaverReset);
1360 clientmessage_response(si, window, False,
1361 "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
1362 "not active: idle timer reset.");
1365 else if (type == XA_CYCLE)
1369 clientmessage_response(si, window, False,
1370 "CYCLE ClientMessage received.",
1372 si->selection_mode = 0; /* 0 means randomize when its time. */
1373 si->demoing_p = False;
1375 if (si->throttled_p && p->verbose_p)
1376 fprintf (stderr, "%s: unthrottled.\n", blurb());
1377 si->throttled_p = False;
1380 XtRemoveTimeOut (si->cycle_id);
1382 cycle_timer ((XtPointer) si, 0);
1385 clientmessage_response(si, window, True,
1386 "ClientMessage CYCLE received while inactive.",
1389 else if (type == XA_NEXT || type == XA_PREV)
1391 clientmessage_response(si, window, False,
1393 ? "NEXT ClientMessage received."
1394 : "PREV ClientMessage received."),
1396 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1397 si->demoing_p = False;
1399 if (si->throttled_p && p->verbose_p)
1400 fprintf (stderr, "%s: unthrottled.\n", blurb());
1401 si->throttled_p = False;
1406 XtRemoveTimeOut (si->cycle_id);
1408 cycle_timer ((XtPointer) si, 0);
1413 else if (type == XA_SELECT)
1417 long which = event->xclient.data.l[1];
1419 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1420 sprintf (buf2, "activating (%ld).", which);
1421 clientmessage_response (si, window, False, buf, buf2);
1423 if (which < 0) which = 0; /* 0 == "random" */
1424 si->selection_mode = which;
1425 si->demoing_p = False;
1427 if (si->throttled_p && p->verbose_p)
1428 fprintf (stderr, "%s: unthrottled.\n", blurb());
1429 si->throttled_p = False;
1434 XtRemoveTimeOut (si->cycle_id);
1436 cycle_timer ((XtPointer) si, 0);
1441 else if (type == XA_EXIT)
1443 /* Ignore EXIT message if the screen is locked. */
1444 if (until_idle_p || !si->locked_p)
1446 clientmessage_response (si, window, False,
1447 "EXIT ClientMessage received.",
1451 unblank_screen (si);
1452 kill_screenhack (si);
1453 XSync (si->dpy, False);
1455 saver_exit (si, 0, 0);
1458 clientmessage_response (si, window, True,
1459 "EXIT ClientMessage received while locked.",
1460 "screen is locked.");
1462 else if (type == XA_RESTART)
1464 /* The RESTART message works whether the screensaver is active or not,
1465 unless the screen is locked, in which case it doesn't work.
1467 if (until_idle_p || !si->locked_p)
1469 clientmessage_response (si, window, False,
1470 "RESTART ClientMessage received.",
1474 unblank_screen (si);
1475 kill_screenhack (si);
1476 XSync (si->dpy, False);
1481 if (real_stdout) fflush (real_stdout);
1482 if (real_stderr) fflush (real_stderr);
1483 /* make sure error message shows up before exit. */
1484 if (real_stderr && stderr != real_stderr)
1485 dup2 (fileno(real_stderr), fileno(stderr));
1487 restart_process (si);
1488 exit (1); /* shouldn't get here; but if restarting didn't work,
1489 make this command be the same as EXIT. */
1492 clientmessage_response (si, window, True,
1493 "RESTART ClientMessage received while locked.",
1494 "screen is locked.");
1496 else if (type == XA_DEMO)
1498 long arg = event->xclient.data.l[1];
1499 Bool demo_one_hack_p = (arg == 300);
1501 if (demo_one_hack_p)
1505 long which = event->xclient.data.l[2];
1508 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1509 sprintf (buf2, "demoing (%ld).", which);
1510 clientmessage_response (si, window, False, buf, buf2);
1512 if (which < 0) which = 0; /* 0 == "random" */
1513 si->selection_mode = which;
1514 si->demoing_p = True;
1516 if (si->throttled_p && p->verbose_p)
1517 fprintf (stderr, "%s: unthrottled.\n", blurb());
1518 si->throttled_p = False;
1523 clientmessage_response (si, window, True,
1524 "DEMO ClientMessage received while active.",
1529 clientmessage_response (si, window, True,
1530 "obsolete form of DEMO ClientMessage.",
1531 "obsolete form of DEMO ClientMessage.");
1534 else if (type == XA_PREFS)
1536 clientmessage_response (si, window, True,
1537 "the PREFS client-message is obsolete.",
1538 "the PREFS client-message is obsolete.");
1540 else if (type == XA_LOCK)
1543 clientmessage_response (si, window, True,
1544 "not compiled with support for locking.",
1545 "locking not enabled.");
1546 #else /* !NO_LOCKING */
1547 if (si->locking_disabled_p)
1548 clientmessage_response (si, window, True,
1549 "LOCK ClientMessage received, but locking is disabled.",
1550 "locking not enabled.");
1551 else if (si->locked_p)
1552 clientmessage_response (si, window, True,
1553 "LOCK ClientMessage received while already locked.",
1558 char *response = (until_idle_p
1559 ? "activating and locking."
1561 sprintf (buf, "LOCK ClientMessage received; %s", response);
1562 clientmessage_response (si, window, False, buf, response);
1563 set_locked_p (si, True);
1564 si->selection_mode = 0;
1565 si->demoing_p = False;
1567 if (si->lock_id) /* we're doing it now, so lose the timeout */
1569 XtRemoveTimeOut (si->lock_id);
1575 if (si->using_mit_saver_extension ||
1576 si->using_sgi_saver_extension)
1578 XForceScreenSaver (si->dpy, ScreenSaverActive);
1587 #endif /* !NO_LOCKING */
1589 else if (type == XA_THROTTLE)
1591 if (si->throttled_p)
1592 clientmessage_response (si, window, True,
1593 "THROTTLE ClientMessage received, but "
1594 "already throttled.",
1595 "already throttled.");
1599 char *response = "throttled.";
1600 si->throttled_p = True;
1601 si->selection_mode = 0;
1602 si->demoing_p = False;
1603 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
1604 clientmessage_response (si, window, False, buf, response);
1609 XtRemoveTimeOut (si->cycle_id);
1611 cycle_timer ((XtPointer) si, 0);
1615 else if (type == XA_UNTHROTTLE)
1617 if (! si->throttled_p)
1618 clientmessage_response (si, window, True,
1619 "UNTHROTTLE ClientMessage received, but "
1625 char *response = "unthrottled.";
1626 si->throttled_p = False;
1627 si->selection_mode = 0;
1628 si->demoing_p = False;
1629 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
1630 clientmessage_response (si, window, False, buf, response);
1635 XtRemoveTimeOut (si->cycle_id);
1637 cycle_timer ((XtPointer) si, 0);
1645 str = XGetAtomName_safe (si->dpy, type);
1649 if (strlen (str) > 80)
1650 strcpy (str+70, "...");
1651 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
1658 "unrecognised screensaver ClientMessage 0x%x received.",
1659 (unsigned int) event->xclient.data.l[0]);
1662 clientmessage_response (si, window, True, buf, buf);
1668 /* Some random diagnostics printed in -verbose mode.
1672 analyze_display (saver_info *si)
1676 const char *name; const char *desc; Bool useful_p;
1679 { "SCREEN_SAVER", /* underscore */ "SGI Screen-Saver",
1680 # ifdef HAVE_SGI_SAVER_EXTENSION
1685 }, { "SCREEN-SAVER", /* dash */ "SGI Screen-Saver",
1686 # ifdef HAVE_SGI_SAVER_EXTENSION
1691 }, { "MIT-SCREEN-SAVER", "MIT Screen-Saver",
1692 # ifdef HAVE_MIT_SAVER_EXTENSION
1697 }, { "XIDLE", "XIdle",
1698 # ifdef HAVE_XIDLE_EXTENSION
1703 }, { "SGI-VIDEO-CONTROL", "SGI Video-Control",
1704 # ifdef HAVE_SGI_VC_EXTENSION
1709 }, { "READDISPLAY", "SGI Read-Display",
1710 # ifdef HAVE_READ_DISPLAY_EXTENSION
1715 }, { "MIT-SHM", "Shared Memory",
1716 # ifdef HAVE_XSHM_EXTENSION
1721 }, { "DOUBLE-BUFFER", "Double-Buffering",
1722 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
1727 }, { "DPMS", "Power Management",
1728 # ifdef HAVE_DPMS_EXTENSION
1739 }, { "XFree86-VidModeExtension", "XF86 Video-Mode",
1740 # ifdef HAVE_XF86VMODE
1745 }, { "XINERAMA", "Xinerama",
1750 fprintf (stderr, "%s: running on display \"%s\" (%d screen%s).\n",
1752 DisplayString(si->dpy),
1753 si->nscreens, (si->nscreens == 1 ? "" : "s"));
1754 fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
1755 ServerVendor(si->dpy), VendorRelease(si->dpy));
1757 fprintf (stderr, "%s: useful extensions:\n", blurb());
1758 for (i = 0; i < countof(exts); i++)
1760 int op = 0, event = 0, error = 0;
1763 if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
1765 sprintf (buf, "%s: ", blurb());
1767 strcat (buf, exts[i].desc);
1768 if (!exts[i].useful_p)
1771 while (strlen (buf) < k) strcat (buf, " ");
1772 strcat (buf, "<-- not supported at compile time!");
1774 fprintf (stderr, "%s\n", buf);
1777 for (i = 0; i < si->nscreens; i++)
1779 unsigned long colormapped_depths = 0;
1780 unsigned long non_mapped_depths = 0;
1781 XVisualInfo vi_in, *vi_out;
1784 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
1785 if (!vi_out) continue;
1786 for (j = 0; j < out_count; j++)
1787 if (vi_out[j].class == PseudoColor)
1788 colormapped_depths |= (1 << vi_out[j].depth);
1790 non_mapped_depths |= (1 << vi_out[j].depth);
1791 XFree ((char *) vi_out);
1793 if (colormapped_depths)
1795 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(), i);
1796 for (j = 0; j < 32; j++)
1797 if (colormapped_depths & (1 << j))
1798 fprintf (stderr, " %d", j);
1799 fprintf (stderr, ".\n");
1801 if (non_mapped_depths)
1803 fprintf (stderr, "%s: screen %d non-colormapped depths:",
1805 for (j = 0; j < 32; j++)
1806 if (non_mapped_depths & (1 << j))
1807 fprintf (stderr, " %d", j);
1808 fprintf (stderr, ".\n");
1814 display_is_on_console_p (saver_info *si)
1816 Bool not_on_console = True;
1817 char *dpystr = DisplayString (si->dpy);
1818 char *tail = (char *) strchr (dpystr, ':');
1819 if (! tail || strncmp (tail, ":0", 2))
1820 not_on_console = True;
1823 char dpyname[255], localname[255];
1824 strncpy (dpyname, dpystr, tail-dpystr);
1825 dpyname [tail-dpystr] = 0;
1827 !strcmp(dpyname, "unix") ||
1828 !strcmp(dpyname, "localhost"))
1829 not_on_console = False;
1830 else if (gethostname (localname, sizeof (localname)))
1831 not_on_console = True; /* can't find hostname? */
1834 /* We have to call gethostbyname() on the result of gethostname()
1835 because the two aren't guarenteed to be the same name for the
1836 same host: on some losing systems, one is a FQDN and the other
1837 is not. Here in the wide wonderful world of Unix it's rocket
1838 science to obtain the local hostname in a portable fashion.
1840 And don't forget, gethostbyname() reuses the structure it
1841 returns, so we have to copy the fucker before calling it again.
1842 Thank you master, may I have another.
1844 struct hostent *h = gethostbyname (dpyname);
1846 not_on_console = True;
1851 strcpy (hn, h->h_name);
1852 l = gethostbyname (localname);
1853 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
1857 return !not_on_console;