1 /* xscreensaver, Copyright (c) 1991-1999 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_EXIT, XA_RESTART, XA_LOCK, XA_SELECT;
167 static Atom XA_THROTTLE, XA_UNTHROTTLE;
168 Atom XA_DEMO, XA_PREFS;
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-1999 by Jamie Zawinski <jwz@jwz.org>\n\
223 The standard Xt command-line options are accepted; other options include:\n\
225 -timeout <minutes> When the screensaver should activate.\n\
226 -cycle <minutes> How long to let each hack run before switching.\n\
227 -lock-mode Require a password before deactivating.\n\
228 -lock-timeout <minutes> Grace period before locking; default 0.\n\
229 -visual <id-or-class> Which X visual to run on.\n\
230 -install Install a private colormap.\n\
232 -no-splash Don't display a splash-screen at startup.\n\
233 -help This message.\n\
235 See the manual for other options and X resources.\n\
237 The `xscreensaver' program should be left running in the background.\n\
238 Use the `xscreensaver-demo' and `xscreensaver-command' programs to\n\
239 manipulate a running xscreensaver.\n\
241 The `*programs' resource controls which graphics demos will be launched by\n\
242 the screensaver. See `man xscreensaver' or the web page for more details.\n\
244 Just getting started? Try this:\n\
249 For updates, check http://www.jwz.org/xscreensaver/\n\
261 time_t now = time ((time_t *) 0);
262 char *str = (char *) ctime (&now);
263 char *nl = (char *) strchr (str, '\n');
264 if (nl) *nl = 0; /* take off that dang newline */
268 static Bool blurb_timestamp_p = False; /* kludge */
273 if (!blurb_timestamp_p)
277 static char buf[255];
278 char *ct = timestring();
279 int n = strlen(progname);
281 strncpy(buf, progname, n);
284 strncpy(buf+n, ct+11, 8);
285 strcpy(buf+n+9, ": ");
292 saver_ehandler (Display *dpy, XErrorEvent *error)
294 saver_info *si = global_si_kludge; /* I hate C so much... */
296 if (!real_stderr) real_stderr = stderr;
298 fprintf (real_stderr, "\n"
299 "#######################################"
300 "#######################################\n\n"
301 "%s: X Error! PLEASE REPORT THIS BUG.\n\n"
302 "#######################################"
303 "#######################################\n\n",
305 if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
307 fprintf (real_stderr, "\n");
308 if (si->prefs.xsync_p)
310 saver_exit (si, -1, "because of synchronous X Error");
314 fprintf (real_stderr,
315 "#######################################"
316 "#######################################\n\n");
317 fprintf (real_stderr,
318 " If at all possible, please re-run xscreensaver with the command line\n"
319 " arguments `-sync -verbose', and reproduce this bug. That will cause\n"
320 " xscreensaver to dump a `core' file to the current directory. Please\n"
321 " include the stack trace from that core file in your bug report.\n"
322 " Do NOT mail the core file itself! That won't work.\n"
324 " http://www.jwz.org/xscreensaver/bugs.html explains how to create the\n"
325 " most useful bug reports, and how to examine core files.\n"
327 " The more information you can provide, the better. But please report\n"
328 " report this bug, regardless!\n"
330 fprintf (real_stderr,
331 "#######################################"
332 "#######################################\n\n");
334 saver_exit (si, -1, 0);
338 fprintf (real_stderr, " (nonfatal.)\n");
343 /* This error handler is used only while the X connection is being set up;
344 after we've got a connection, we don't use this handler again. The only
345 reason for having this is so that we can present a more idiot-proof error
346 message than "cannot open display."
349 startup_ehandler (String name, String type, String class,
350 String defalt, /* one can't even spel properly
351 in this joke of a language */
352 String *av, Cardinal *ac)
356 saver_info *si = global_si_kludge; /* I hate C so much... */
357 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
359 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
360 fmt, sizeof(fmt)-1, *db);
362 fprintf (stderr, "%s: ", blurb());
364 memset (p, 0, sizeof(p));
365 if (*ac > countof (p)) *ac = countof (p);
366 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
367 fprintf (stderr, fmt, /* Did I mention that I hate C? */
368 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
369 fprintf (stderr, "\n");
371 describe_uids (si, stderr);
372 fprintf (stderr, "\n"
373 "%s: Errors at startup are usually authorization problems.\n"
374 " Did you read the manual? Specifically, the parts\n"
375 " that talk about XAUTH, XDM, and root logins?\n"
377 " http://www.jwz.org/xscreensaver/man.html\n"
387 /* The zillions of initializations.
390 /* Set progname, version, etc. This is done very early.
393 set_version_string (saver_info *si, int *argc, char **argv)
395 progclass = "XScreenSaver";
397 /* progname is reset later, after we connect to X. */
398 progname = strrchr(argv[0], '/');
399 if (progname) progname++;
400 else progname = argv[0];
402 if (strlen(progname) > 100) /* keep it short. */
405 /* The X resource database blows up if argv[0] has a "." in it. */
408 while ((s = strchr (s, '.')))
412 si->version = (char *) malloc (5);
413 memcpy (si->version, screensaver_id + 17, 4);
418 /* Initializations that potentially take place as a priveleged user:
419 If the xscreensaver executable is setuid root, then these initializations
420 are run as root, before discarding privileges.
423 privileged_initialization (saver_info *si, int *argc, char **argv)
426 /* before hack_uid() for proper permissions */
427 lock_priv_init (*argc, argv, si->prefs.verbose_p);
428 #endif /* NO_LOCKING */
434 /* Figure out what locking mechanisms are supported.
437 lock_initialization (saver_info *si, int *argc, char **argv)
440 si->locking_disabled_p = True;
441 si->nolock_reason = "not compiled with locking support";
442 #else /* !NO_LOCKING */
444 /* Finish initializing locking, now that we're out of privileged code. */
445 if (! lock_init (*argc, argv, si->prefs.verbose_p))
447 si->locking_disabled_p = True;
448 si->nolock_reason = "error getting password";
450 #endif /* NO_LOCKING */
456 /* Open the connection to the X server, and intern our Atoms.
459 connect_to_server (saver_info *si, int *argc, char **argv)
461 Widget toplevel_shell;
463 XSetErrorHandler (saver_ehandler);
465 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
466 toplevel_shell = XtAppInitialize (&si->app, progclass,
467 options, XtNumber (options),
468 argc, argv, defaults, 0, 0);
469 XtAppSetErrorMsgHandler (si->app, 0);
471 si->dpy = XtDisplay (toplevel_shell);
472 si->prefs.db = XtDatabase (si->dpy);
473 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
475 if(strlen(progname) > 100) /* keep it short. */
478 db = si->prefs.db; /* resources.c needs this */
480 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
481 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
482 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
483 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
484 XA_SCREENSAVER_TIME = XInternAtom (si->dpy, "_SCREENSAVER_TIME", False);
485 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
487 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
488 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
489 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
490 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
491 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
492 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
493 XA_PREV = XInternAtom (si->dpy, "PREV", False);
494 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
495 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
496 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
497 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
498 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
499 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
500 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
502 return toplevel_shell;
506 /* Handle the command-line arguments that were not handled for us by Xt.
507 Issue an error message and exit if there are unknown options.
510 process_command_line (saver_info *si, int *argc, char **argv)
513 for (i = 1; i < *argc; i++)
515 if (!strcmp (argv[i], "-debug"))
516 /* no resource for this one, out of paranoia. */
517 si->prefs.debug_p = True;
519 else if (!strcmp (argv[i], "-h") ||
520 !strcmp (argv[i], "-help") ||
521 !strcmp (argv[i], "--help"))
526 const char *s = argv[i];
527 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
530 if (s[0] == '-' && s[1] == '-') s++;
531 if (!strcmp (s, "-activate") ||
532 !strcmp (s, "-deactivate") ||
533 !strcmp (s, "-cycle") ||
534 !strcmp (s, "-next") ||
535 !strcmp (s, "-prev") ||
536 !strcmp (s, "-exit") ||
537 !strcmp (s, "-restart") ||
538 !strcmp (s, "-demo") ||
539 !strcmp (s, "-prefs") ||
540 !strcmp (s, "-preferences") ||
541 !strcmp (s, "-lock") ||
542 !strcmp (s, "-version") ||
543 !strcmp (s, "-time"))
546 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
547 fprintf (stderr, "\n\
548 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
550 fprintf (stderr, "\n\
551 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
554 The `xscreensaver' program is a daemon that runs in the background.\n\
555 You control a running xscreensaver process by sending it messages\n\
556 with `xscreensaver-demo' or `xscreensaver-command'.\n\
557 . See the man pages for details, or check the web page:\n\
558 http://www.jwz.org/xscreensaver/\n\n");
560 /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
561 suggest that explicitly. */
562 if (!strcmp (s, "-lock"))
564 Or perhaps you meant either the \"-lock-mode\" or the\n\
565 \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
574 /* Print out the xscreensaver banner to the tty if applicable;
575 Issue any other warnings that are called for at this point.
578 print_banner (saver_info *si)
580 saver_preferences *p = &si->prefs;
582 /* This resource gets set some time before the others, so that we know
583 whether to print the banner (and so that the banner gets printed before
584 any resource-database-related error messages.)
586 p->verbose_p = (p->debug_p || get_boolean_resource ("verbose", "Boolean"));
588 /* Ditto, for the locking_disabled_p message. */
589 p->lock_p = get_boolean_resource ("lock", "Boolean");
593 "%s %s, copyright (c) 1991-1999 "
594 "by Jamie Zawinski <jwz@jwz.org>.\n",
595 progname, si->version);
598 fprintf (stderr, "\n"
599 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
601 "\tNote that in debug mode, the xscreensaver window will only\n"
602 "\tcover the left half of the screen. (The idea is that you\n"
603 "\tcan still see debugging output in a shell, if you position\n"
604 "\tit on the right side of the screen.)\n"
606 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
607 "\tuntrusted environments.\n"
613 if (!si->uid_message || !*si->uid_message)
614 describe_uids (si, stderr);
617 if (si->orig_uid && *si->orig_uid)
618 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
619 blurb(), si->orig_uid);
620 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
623 fprintf (stderr, "%s: in process %lu.\n", blurb(),
624 (unsigned long) getpid());
627 /* If locking was not able to be initalized for some reason, explain why.
628 (This has to be done after we've read the lock_p resource.)
630 if (p->lock_p && si->locking_disabled_p)
633 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
635 if (strstr (si->nolock_reason, "passw"))
636 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
637 "consult the manual.\n", blurb());
638 else if (strstr (si->nolock_reason, "running as "))
640 "%s: locking only works when xscreensaver is launched\n"
641 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
642 "\t See the manual for details.\n",
648 /* Examine all of the display's screens, and populate the `saver_screen_info'
652 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
654 Bool found_any_writable_cells = False;
657 si->nscreens = ScreenCount(si->dpy);
658 si->screens = (saver_screen_info *)
659 calloc(sizeof(saver_screen_info), si->nscreens);
661 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
663 for (i = 0; i < si->nscreens; i++)
665 saver_screen_info *ssi = &si->screens[i];
667 ssi->screen = ScreenOfDisplay (si->dpy, i);
669 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
670 ssi->default_visual =
671 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
673 ssi->current_visual = ssi->default_visual;
674 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
676 if (ssi == si->default_screen)
677 /* Since this is the default screen, use the one already created. */
678 ssi->toplevel_shell = toplevel_shell;
680 /* Otherwise, each screen must have its own unmapped root widget. */
681 ssi->toplevel_shell =
682 XtVaAppCreateShell (progname, progclass, applicationShellWidgetClass,
684 XtNscreen, ssi->screen,
685 XtNvisual, ssi->current_visual,
686 XtNdepth, visual_depth (ssi->screen,
687 ssi->current_visual),
690 if (! found_any_writable_cells)
692 /* Check to see whether fading is ever possible -- if any of the
693 screens on the display has a PseudoColor visual, then fading can
694 work (on at least some screens.) If no screen has a PseudoColor
695 visual, then don't bother ever trying to fade, because it will
696 just cause a delay without causing any visible effect.
698 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
699 get_visual (ssi->screen, "PseudoColor", True, False) ||
700 get_visual (ssi->screen, "GrayScale", True, False))
701 found_any_writable_cells = True;
705 si->fading_possible_p = found_any_writable_cells;
709 /* If any server extensions have been requested, try and initialize them.
710 Issue warnings if requests can't be honored.
713 initialize_server_extensions (saver_info *si)
715 saver_preferences *p = &si->prefs;
717 Bool server_has_xidle_extension_p = False;
718 Bool server_has_sgi_saver_extension_p = False;
719 Bool server_has_mit_saver_extension_p = False;
720 Bool system_has_proc_interrupts_p = False;
721 const char *piwhy = 0;
723 si->using_xidle_extension = p->use_xidle_extension;
724 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
725 si->using_mit_saver_extension = p->use_mit_saver_extension;
726 si->using_proc_interrupts = p->use_proc_interrupts;
728 #ifdef HAVE_XIDLE_EXTENSION
729 server_has_xidle_extension_p = query_xidle_extension (si);
731 #ifdef HAVE_SGI_SAVER_EXTENSION
732 server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
734 #ifdef HAVE_MIT_SAVER_EXTENSION
735 server_has_mit_saver_extension_p = query_mit_saver_extension (si);
737 #ifdef HAVE_PROC_INTERRUPTS
738 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
741 if (!server_has_xidle_extension_p)
742 si->using_xidle_extension = False;
743 else if (p->verbose_p)
745 if (si->using_xidle_extension)
746 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
748 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
751 if (!server_has_sgi_saver_extension_p)
752 si->using_sgi_saver_extension = False;
753 else if (p->verbose_p)
755 if (si->using_sgi_saver_extension)
756 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
759 "%s: not using server's SGI SCREEN_SAVER extension.\n",
763 if (!server_has_mit_saver_extension_p)
764 si->using_mit_saver_extension = False;
765 else if (p->verbose_p)
767 if (si->using_mit_saver_extension)
768 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
772 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
776 if (!system_has_proc_interrupts_p)
778 si->using_proc_interrupts = False;
779 if (p->verbose_p && piwhy)
780 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
783 else if (p->verbose_p)
785 if (si->using_proc_interrupts)
787 "%s: consulting /proc/interrupts for keyboard activity.\n",
791 "%s: not consulting /proc/interrupts for keyboard activity.\n",
797 /* For the case where we aren't using an server extensions, select user events
798 on all the existing windows, and launch timers to select events on
799 newly-created windows as well.
801 If a server extension is being used, this does nothing.
804 select_events (saver_info *si)
806 saver_preferences *p = &si->prefs;
809 if (si->using_xidle_extension ||
810 si->using_mit_saver_extension ||
811 si->using_sgi_saver_extension)
814 if (p->initial_delay)
818 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
819 (int) p->initial_delay/1000,
820 (p->initial_delay == 1000 ? "" : "s"));
824 usleep (p->initial_delay);
826 fprintf (stderr, " done.\n");
831 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
836 /* Select events on the root windows of every screen. This also selects
837 for window creation events, so that new subwindows will be noticed.
839 for (i = 0; i < si->nscreens; i++)
840 start_notice_events_timer (si, RootWindowOfScreen (si->screens[i].screen),
844 fprintf (stderr, " done.\n");
849 maybe_reload_init_file (saver_info *si)
851 saver_preferences *p = &si->prefs;
852 if (init_file_changed_p (p))
855 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
856 blurb(), init_file_name());
860 /* If a server extension is in use, and p->timeout has changed,
861 we need to inform the server of the new timeout. */
862 disable_builtin_screensaver (si, False);
869 - wait until the user is idle;
871 - wait until the user is active;
872 - unblank the screen;
877 main_loop (saver_info *si)
879 saver_preferences *p = &si->prefs;
884 Bool was_locked = False;
885 sleep_until_idle (si, True);
890 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
891 si->selection_mode, timestring());
894 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
898 maybe_reload_init_file (si);
900 if (! blank_screen (si))
902 /* We were unable to grab either the keyboard or mouse.
903 This means we did not (and must not) blank the screen.
904 If we were to blank the screen while some other program
905 is holding both the mouse and keyboard grabbed, then
906 we would never be able to un-blank it! We would never
907 see any events, and the display would be wedged.
909 So, just go around the loop again and wait for the
910 next bout of idleness.
914 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
919 kill_screenhack (si);
921 if (!si->throttled_p)
922 spawn_screenhack (si, True);
923 else if (p->verbose_p)
924 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
926 /* Don't start the cycle timer in demo mode. */
927 if (!si->demoing_p && p->cycle)
928 si->cycle_id = XtAppAddTimeOut (si->app,
930 /* see comment in cycle_timer() */
939 Time lock_timeout = p->lock_timeout;
941 if (si->emergency_lock_p && p->lock_p && lock_timeout)
943 int secs = p->lock_timeout / 1000;
946 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
948 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
952 si->emergency_lock_p = False;
954 if (!si->demoing_p && /* if not going into demo mode */
955 p->lock_p && /* and locking is enabled */
956 !si->locking_disabled_p && /* and locking is possible */
957 lock_timeout == 0) /* and locking is not timer-deferred */
958 set_locked_p (si, True); /* then lock right now. */
960 /* locked_p might be true already because of the above, or because of
961 the LOCK ClientMessage. But if not, and if we're supposed to lock
962 after some time, set up a timer to do so.
967 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
971 #endif /* !NO_LOCKING */
974 ok_to_unblank = True;
977 sleep_until_idle (si, False); /* until not idle */
978 maybe_reload_init_file (si);
983 saver_screen_info *ssi = si->default_screen;
984 if (si->locking_disabled_p) abort ();
987 si->dbox_up_p = True;
988 suspend_screenhack (si, True);
989 XUndefineCursor (si->dpy, ssi->screensaver_window);
991 ok_to_unblank = unlock_p (si);
993 si->dbox_up_p = False;
994 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
995 suspend_screenhack (si, False); /* resume */
997 #endif /* !NO_LOCKING */
999 } while (!ok_to_unblank);
1003 fprintf (stderr, "%s: unblanking screen at %s.\n",
1004 blurb(), timestring ());
1006 /* Kill before unblanking, to stop drawing as soon as possible. */
1007 kill_screenhack (si);
1008 unblank_screen (si);
1010 set_locked_p (si, False);
1011 si->emergency_lock_p = False;
1013 si->selection_mode = 0;
1015 /* If we're throttled, and the user has explicitly unlocked the screen,
1016 then unthrottle. If we weren't locked, then don't unthrottle
1017 automatically, because someone might have just bumped the desk... */
1020 if (si->throttled_p && p->verbose_p)
1021 fprintf (stderr, "%s: unthrottled.\n", blurb());
1022 si->throttled_p = False;
1027 XtRemoveTimeOut (si->cycle_id);
1033 XtRemoveTimeOut (si->lock_id);
1038 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1042 static void analyze_display (saver_info *si);
1045 main (int argc, char **argv)
1049 saver_info *si = &the_si;
1050 saver_preferences *p = &si->prefs;
1053 memset(si, 0, sizeof(*si));
1054 global_si_kludge = si; /* I hate C so much... */
1056 srandom ((int) time ((time_t *) 0));
1058 save_argv (argc, argv);
1059 set_version_string (si, &argc, argv);
1060 privileged_initialization (si, &argc, argv);
1061 hack_environment (si);
1063 shell = connect_to_server (si, &argc, argv);
1064 process_command_line (si, &argc, argv);
1067 load_init_file (p); /* must be before initialize_per_screen_info() */
1068 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1070 /* We can only issue this warnings now. */
1071 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1073 "%s: there are no PseudoColor or GrayScale visuals.\n"
1074 "%s: ignoring the request for fading/unfading.\n",
1077 for (i = 0; i < si->nscreens; i++)
1078 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1081 lock_initialization (si, &argc, argv);
1083 if (p->xsync_p) XSynchronize (si->dpy, True);
1084 blurb_timestamp_p = p->timestamp_p; /* kludge */
1086 if (p->verbose_p) analyze_display (si);
1087 initialize_server_extensions (si);
1088 initialize_screensaver_window (si);
1091 disable_builtin_screensaver (si, True);
1092 initialize_stderr (si);
1094 make_splash_dialog (si);
1096 main_loop (si); /* doesn't return */
1101 /* Processing ClientMessage events.
1105 clientmessage_response (saver_info *si, Window w, Bool error,
1106 const char *stderr_msg,
1107 const char *protocol_msg)
1111 saver_preferences *p = &si->prefs;
1112 if (error || p->verbose_p)
1113 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1115 L = strlen(protocol_msg);
1116 proto = (char *) malloc (L + 2);
1117 proto[0] = (error ? '-' : '+');
1118 strcpy (proto+1, protocol_msg);
1121 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1122 PropModeReplace, (unsigned char *) proto, L);
1123 XSync (si->dpy, False);
1128 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1130 saver_preferences *p = &si->prefs;
1132 Window window = event->xclient.window;
1134 /* Preferences might affect our handling of client messages. */
1135 maybe_reload_init_file (si);
1137 if (event->xclient.message_type != XA_SCREENSAVER)
1140 str = XGetAtomName (si->dpy, event->xclient.message_type);
1141 fprintf (stderr, "%s: unrecognised ClientMessage type %s received\n",
1142 blurb(), (str ? str : "(null)"));
1143 if (str) XFree (str);
1146 if (event->xclient.format != 32)
1148 fprintf (stderr, "%s: ClientMessage of format %d received, not 32\n",
1149 blurb(), event->xclient.format);
1153 type = event->xclient.data.l[0];
1154 if (type == XA_ACTIVATE)
1158 clientmessage_response(si, window, False,
1159 "ACTIVATE ClientMessage received.",
1161 si->selection_mode = 0;
1162 si->demoing_p = False;
1164 if (si->throttled_p && p->verbose_p)
1165 fprintf (stderr, "%s: unthrottled.\n", blurb());
1166 si->throttled_p = False;
1168 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1170 XForceScreenSaver (si->dpy, ScreenSaverActive);
1178 clientmessage_response(si, window, True,
1179 "ClientMessage ACTIVATE received while already active.",
1182 else if (type == XA_DEACTIVATE)
1186 if (si->throttled_p && p->verbose_p)
1187 fprintf (stderr, "%s: unthrottled.\n", blurb());
1188 si->throttled_p = False;
1190 clientmessage_response(si, window, False,
1191 "DEACTIVATE ClientMessage received.",
1193 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1195 XForceScreenSaver (si->dpy, ScreenSaverReset);
1203 clientmessage_response(si, window, True,
1204 "ClientMessage DEACTIVATE received while inactive.",
1207 else if (type == XA_CYCLE)
1211 clientmessage_response(si, window, False,
1212 "CYCLE ClientMessage received.",
1214 si->selection_mode = 0; /* 0 means randomize when its time. */
1215 si->demoing_p = False;
1217 if (si->throttled_p && p->verbose_p)
1218 fprintf (stderr, "%s: unthrottled.\n", blurb());
1219 si->throttled_p = False;
1222 XtRemoveTimeOut (si->cycle_id);
1224 cycle_timer ((XtPointer) si, 0);
1227 clientmessage_response(si, window, True,
1228 "ClientMessage CYCLE received while inactive.",
1231 else if (type == XA_NEXT || type == XA_PREV)
1233 clientmessage_response(si, window, False,
1235 ? "NEXT ClientMessage received."
1236 : "PREV ClientMessage received."),
1238 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1239 si->demoing_p = False;
1241 if (si->throttled_p && p->verbose_p)
1242 fprintf (stderr, "%s: unthrottled.\n", blurb());
1243 si->throttled_p = False;
1248 XtRemoveTimeOut (si->cycle_id);
1250 cycle_timer ((XtPointer) si, 0);
1255 else if (type == XA_SELECT)
1259 long which = event->xclient.data.l[1];
1261 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1262 sprintf (buf2, "activating (%ld).", which);
1263 clientmessage_response (si, window, False, buf, buf2);
1265 if (which < 0) which = 0; /* 0 == "random" */
1266 si->selection_mode = which;
1267 si->demoing_p = False;
1269 if (si->throttled_p && p->verbose_p)
1270 fprintf (stderr, "%s: unthrottled.\n", blurb());
1271 si->throttled_p = False;
1276 XtRemoveTimeOut (si->cycle_id);
1278 cycle_timer ((XtPointer) si, 0);
1283 else if (type == XA_EXIT)
1285 /* Ignore EXIT message if the screen is locked. */
1286 if (until_idle_p || !si->locked_p)
1288 clientmessage_response (si, window, False,
1289 "EXIT ClientMessage received.",
1293 unblank_screen (si);
1294 kill_screenhack (si);
1295 XSync (si->dpy, False);
1297 saver_exit (si, 0, 0);
1300 clientmessage_response (si, window, True,
1301 "EXIT ClientMessage received while locked.",
1302 "screen is locked.");
1304 else if (type == XA_RESTART)
1306 /* The RESTART message works whether the screensaver is active or not,
1307 unless the screen is locked, in which case it doesn't work.
1309 if (until_idle_p || !si->locked_p)
1311 clientmessage_response (si, window, False,
1312 "RESTART ClientMessage received.",
1316 unblank_screen (si);
1317 kill_screenhack (si);
1318 XSync (si->dpy, False);
1323 if (real_stdout) fflush (real_stdout);
1324 if (real_stderr) fflush (real_stderr);
1325 /* make sure error message shows up before exit. */
1326 if (real_stderr && stderr != real_stderr)
1327 dup2 (fileno(real_stderr), fileno(stderr));
1329 restart_process (si);
1330 exit (1); /* shouldn't get here; but if restarting didn't work,
1331 make this command be the same as EXIT. */
1334 clientmessage_response (si, window, True,
1335 "RESTART ClientMessage received while locked.",
1336 "screen is locked.");
1338 else if (type == XA_DEMO)
1340 long arg = event->xclient.data.l[1];
1341 Bool demo_one_hack_p = (arg == 300);
1343 if (demo_one_hack_p)
1347 long which = event->xclient.data.l[2];
1350 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1351 sprintf (buf2, "demoing (%ld).", which);
1352 clientmessage_response (si, window, False, buf, buf2);
1354 if (which < 0) which = 0; /* 0 == "random" */
1355 si->selection_mode = which;
1356 si->demoing_p = True;
1358 if (si->throttled_p && p->verbose_p)
1359 fprintf (stderr, "%s: unthrottled.\n", blurb());
1360 si->throttled_p = False;
1365 clientmessage_response (si, window, True,
1366 "DEMO ClientMessage received while active.",
1371 clientmessage_response (si, window, True,
1372 "obsolete form of DEMO ClientMessage.",
1373 "obsolete form of DEMO ClientMessage.");
1376 else if (type == XA_PREFS)
1378 clientmessage_response (si, window, True,
1379 "the PREFS client-message is obsolete.",
1380 "the PREFS client-message is obsolete.");
1382 else if (type == XA_LOCK)
1385 clientmessage_response (si, window, True,
1386 "not compiled with support for locking.",
1387 "locking not enabled.");
1388 #else /* !NO_LOCKING */
1389 if (si->locking_disabled_p)
1390 clientmessage_response (si, window, True,
1391 "LOCK ClientMessage received, but locking is disabled.",
1392 "locking not enabled.");
1393 else if (si->locked_p)
1394 clientmessage_response (si, window, True,
1395 "LOCK ClientMessage received while already locked.",
1400 char *response = (until_idle_p
1401 ? "activating and locking."
1403 sprintf (buf, "LOCK ClientMessage received; %s", response);
1404 clientmessage_response (si, window, False, buf, response);
1405 set_locked_p (si, True);
1406 si->selection_mode = 0;
1407 si->demoing_p = False;
1409 if (si->lock_id) /* we're doing it now, so lose the timeout */
1411 XtRemoveTimeOut (si->lock_id);
1417 if (si->using_mit_saver_extension ||
1418 si->using_sgi_saver_extension)
1420 XForceScreenSaver (si->dpy, ScreenSaverActive);
1429 #endif /* !NO_LOCKING */
1431 else if (type == XA_THROTTLE)
1433 if (si->throttled_p)
1434 clientmessage_response (si, window, True,
1435 "THROTTLE ClientMessage received, but "
1436 "already throttled.",
1437 "already throttled.");
1441 char *response = "throttled.";
1442 si->throttled_p = True;
1443 si->selection_mode = 0;
1444 si->demoing_p = False;
1445 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
1446 clientmessage_response (si, window, False, buf, response);
1451 XtRemoveTimeOut (si->cycle_id);
1453 cycle_timer ((XtPointer) si, 0);
1457 else if (type == XA_UNTHROTTLE)
1459 if (! si->throttled_p)
1460 clientmessage_response (si, window, True,
1461 "UNTHROTTLE ClientMessage received, but "
1467 char *response = "unthrottled.";
1468 si->throttled_p = False;
1469 si->selection_mode = 0;
1470 si->demoing_p = False;
1471 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
1472 clientmessage_response (si, window, False, buf, response);
1477 XtRemoveTimeOut (si->cycle_id);
1479 cycle_timer ((XtPointer) si, 0);
1487 str = (type ? XGetAtomName(si->dpy, type) : 0);
1491 if (strlen (str) > 80)
1492 strcpy (str+70, "...");
1493 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
1500 "unrecognised screensaver ClientMessage 0x%x received.",
1501 (unsigned int) event->xclient.data.l[0]);
1504 clientmessage_response (si, window, True, buf, buf);
1510 /* Some random diagnostics printed in -verbose mode.
1514 analyze_display (saver_info *si)
1517 static const char *exts[][2] = {
1518 { "SCREEN_SAVER", "SGI Screen-Saver" },
1519 { "SCREEN-SAVER", "SGI Screen-Saver" },
1520 { "MIT-SCREEN-SAVER", "MIT Screen-Saver" },
1521 { "XIDLE", "XIdle" },
1522 { "SGI-VIDEO-CONTROL", "SGI Video-Control" },
1523 { "READDISPLAY", "SGI Read-Display" },
1524 { "MIT-SHM", "Shared Memory" },
1525 { "DOUBLE-BUFFER", "Double-Buffering" },
1526 { "DPMS", "Power Management" },
1528 { "XFree86-VidModeExtension", "XF86 Video-Mode" },
1529 { "XINERAMA", "Xinerama" }
1532 fprintf (stderr, "%s: running on display \"%s\"\n", blurb(),
1533 DisplayString(si->dpy));
1534 fprintf (stderr, "%s: vendor is %s, %d\n", blurb(),
1535 ServerVendor(si->dpy), VendorRelease(si->dpy));
1537 fprintf (stderr, "%s: useful extensions:\n", blurb());
1538 for (i = 0; i < countof(exts); i++)
1540 int op = 0, event = 0, error = 0;
1541 if (XQueryExtension (si->dpy, exts[i][0], &op, &event, &error))
1542 fprintf (stderr, "%s: %s\n", blurb(), exts[i][1]);
1545 for (i = 0; i < si->nscreens; i++)
1547 unsigned long colormapped_depths = 0;
1548 unsigned long non_mapped_depths = 0;
1549 XVisualInfo vi_in, *vi_out;
1552 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
1553 if (!vi_out) continue;
1554 for (j = 0; j < out_count; j++)
1555 if (vi_out[j].class == PseudoColor)
1556 colormapped_depths |= (1 << vi_out[j].depth);
1558 non_mapped_depths |= (1 << vi_out[j].depth);
1559 XFree ((char *) vi_out);
1561 if (colormapped_depths)
1563 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(), i);
1564 for (j = 0; j < 32; j++)
1565 if (colormapped_depths & (1 << j))
1566 fprintf (stderr, " %d", j);
1567 fprintf (stderr, "\n");
1569 if (non_mapped_depths)
1571 fprintf (stderr, "%s: screen %d non-mapped depths:", blurb(), i);
1572 for (j = 0; j < 32; j++)
1573 if (non_mapped_depths & (1 << j))
1574 fprintf (stderr, " %d", j);
1575 fprintf (stderr, "\n");
1581 display_is_on_console_p (saver_info *si)
1583 Bool not_on_console = True;
1584 char *dpystr = DisplayString (si->dpy);
1585 char *tail = (char *) strchr (dpystr, ':');
1586 if (! tail || strncmp (tail, ":0", 2))
1587 not_on_console = True;
1590 char dpyname[255], localname[255];
1591 strncpy (dpyname, dpystr, tail-dpystr);
1592 dpyname [tail-dpystr] = 0;
1594 !strcmp(dpyname, "unix") ||
1595 !strcmp(dpyname, "localhost"))
1596 not_on_console = False;
1597 else if (gethostname (localname, sizeof (localname)))
1598 not_on_console = True; /* can't find hostname? */
1601 /* We have to call gethostbyname() on the result of gethostname()
1602 because the two aren't guarenteed to be the same name for the
1603 same host: on some losing systems, one is a FQDN and the other
1604 is not. Here in the wide wonderful world of Unix it's rocket
1605 science to obtain the local hostname in a portable fashion.
1607 And don't forget, gethostbyname() reuses the structure it
1608 returns, so we have to copy the fucker before calling it again.
1609 Thank you master, may I have another.
1611 struct hostent *h = gethostbyname (dpyname);
1613 not_on_console = True;
1618 strcpy (hn, h->h_name);
1619 l = gethostbyname (localname);
1620 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
1624 return !not_on_console;