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 that's probably pretty rare.
75 * The reason that we can't select KeyPresses on windows that don't have
76 * them already is that, when dispatching a KeyPress event, X finds the
77 * lowest (leafmost) window in the hierarchy on which *any* client selects
78 * for KeyPress, and sends the event to that window. This means that if a
79 * client had a window with subwindows, and expected to receive KeyPress
80 * events on the parent window instead of the subwindows, then that client
81 * would malfunction if some other client selected KeyPress events on the
82 * subwindows. It is an incredible misdesign that one client can make
83 * another client malfunction in this way.
85 * To detect mouse motion, we periodically wake up and poll the mouse
86 * position and button/modifier state, and notice when something has
87 * changed. We make this check every five seconds by default, and since the
88 * screensaver timeout has a granularity of one minute, this makes the
89 * chance of a false positive very small. We could detect mouse motion in
90 * the same way as keyboard activity, but that would suffer from the same
91 * "client changing event mask" problem that the KeyPress events hack does.
92 * I think polling is more reliable.
94 * None of this crap happens if we're using one of the extensions, so install
95 * one of them if the description above sounds just too flaky to live. It
96 * is, but those are your choices.
98 * A third idle-detection option could be implemented (but is not): when
99 * running on the console display ($DISPLAY is `localhost`:0) and we're on a
100 * machine where /dev/tty and /dev/mouse have reasonable last-modification
101 * times, we could just stat() those. But the incremental benefit of
102 * implementing this is really small, so forget I said anything.
105 * - Have a second terminal handy.
106 * - Be careful where you set your breakpoints, you don't want this to
107 * stop under the debugger with the keyboard grabbed or the blackout
109 * - If you run your debugger under XEmacs, try M-ESC (x-grab-keyboard)
110 * to keep your emacs window alive even when xscreensaver has grabbed.
111 * - Go read the code related to `debug_p'.
112 * - You probably can't set breakpoints in functions that are called on
113 * the other side of a call to fork() -- if your clients are dying
114 * with signal 5, Trace/BPT Trap, you're losing in this way.
115 * - If you aren't using a server extension, don't leave this stopped
116 * under the debugger for very long, or the X input buffer will get
117 * huge because of the keypress events it's selecting for. This can
118 * make your X server wedge with "no more input buffers."
120 * ======================================================================== */
128 #include <X11/Xlib.h>
129 #include <X11/Xatom.h>
130 #include <X11/Intrinsic.h>
131 #include <X11/StringDefs.h>
132 #include <X11/Shell.h>
134 #include <netdb.h> /* for gethostbyname() */
137 # include <X11/Xmu/Error.h>
139 # include <Xmu/Error.h>
141 #else /* !HAVE_XMU */
143 #endif /* !HAVE_XMU */
145 #ifdef HAVE_XIDLE_EXTENSION
146 # include <X11/extensions/xidle.h>
147 #endif /* HAVE_XIDLE_EXTENSION */
149 #include "xscreensaver.h"
151 #include "yarandom.h"
152 #include "resources.h"
156 saver_info *global_si_kludge = 0; /* I hate C so much... */
163 static Atom XA_SCREENSAVER_RESPONSE;
164 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
165 static Atom XA_EXIT, XA_RESTART, XA_LOCK, XA_SELECT;
166 static Atom XA_THROTTLE, XA_UNTHROTTLE;
167 Atom XA_DEMO, XA_PREFS;
170 static XrmOptionDescRec options [] = {
171 { "-timeout", ".timeout", XrmoptionSepArg, 0 },
172 { "-cycle", ".cycle", XrmoptionSepArg, 0 },
173 { "-lock-mode", ".lock", XrmoptionNoArg, "on" },
174 { "-no-lock-mode", ".lock", XrmoptionNoArg, "off" },
175 { "-no-lock", ".lock", XrmoptionNoArg, "off" },
176 { "-lock-timeout", ".lockTimeout", XrmoptionSepArg, 0 },
177 { "-lock-vts", ".lockVTs", XrmoptionNoArg, "on" },
178 { "-no-lock-vts", ".lockVTs", XrmoptionNoArg, "off" },
179 { "-visual", ".visualID", XrmoptionSepArg, 0 },
180 { "-install", ".installColormap", XrmoptionNoArg, "on" },
181 { "-no-install", ".installColormap", XrmoptionNoArg, "off" },
182 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
183 { "-silent", ".verbose", XrmoptionNoArg, "off" },
184 { "-timestamp", ".timestamp", XrmoptionNoArg, "on" },
185 { "-capture-stderr", ".captureStderr", XrmoptionNoArg, "on" },
186 { "-no-capture-stderr", ".captureStderr", XrmoptionNoArg, "off" },
187 { "-xidle-extension", ".xidleExtension", XrmoptionNoArg, "on" },
188 { "-no-xidle-extension", ".xidleExtension", XrmoptionNoArg, "off" },
189 { "-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "on" },
190 { "-no-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "off" },
191 { "-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "on" },
192 { "-no-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "off" },
193 { "-proc-interrupts", ".procInterrupts", XrmoptionNoArg, "on" },
194 { "-no-proc-interrupts", ".procInterrupts", XrmoptionNoArg, "off" },
195 { "-splash", ".splash", XrmoptionNoArg, "on" },
196 { "-no-splash", ".splash", XrmoptionNoArg, "off" },
197 { "-nosplash", ".splash", XrmoptionNoArg, "off" },
198 { "-idelay", ".initialDelay", XrmoptionSepArg, 0 },
199 { "-nice", ".nice", XrmoptionSepArg, 0 },
201 /* Actually these are built in to Xt, but just to be sure... */
202 { "-synchronous", ".synchronous", XrmoptionNoArg, "on" },
203 { "-xrm", NULL, XrmoptionResArg, NULL }
206 static char *defaults[] = {
207 #include "XScreenSaver_ad.h"
212 ERROR! You must not include vroot.h in this file.
216 do_help (saver_info *si)
221 xscreensaver %s, copyright (c) 1991-1999 by Jamie Zawinski <jwz@jwz.org>\n\
222 The standard Xt command-line options are accepted; other options include:\n\
224 -timeout <minutes> When the screensaver should activate.\n\
225 -cycle <minutes> How long to let each hack run before switching.\n\
226 -lock-mode Require a password before deactivating.\n\
227 -lock-timeout <minutes> Grace period before locking; default 0.\n\
228 -visual <id-or-class> Which X visual to run on.\n\
229 -install Install a private colormap.\n\
231 -no-splash Don't display a splash-screen at startup.\n\
232 -help This message.\n\
234 See the manual for other options and X resources.\n\
236 The `xscreensaver' program should be left running in the background.\n\
237 Use the `xscreensaver-demo' and `xscreensaver-command' programs to\n\
238 manipulate a running xscreensaver.\n\
240 The `*programs' resource controls which graphics demos will be launched by\n\
241 the screensaver. See `man xscreensaver' or the web page for more details.\n\
243 Just getting started? Try this:\n\
248 For updates, check http://www.jwz.org/xscreensaver/\n\
260 time_t now = time ((time_t *) 0);
261 char *str = (char *) ctime (&now);
262 char *nl = (char *) strchr (str, '\n');
263 if (nl) *nl = 0; /* take off that dang newline */
267 static Bool blurb_timestamp_p = False; /* kludge */
272 if (!blurb_timestamp_p)
276 static char buf[255];
277 char *ct = timestring();
278 int n = strlen(progname);
280 strncpy(buf, progname, n);
283 strncpy(buf+n, ct+11, 8);
284 strcpy(buf+n+9, ": ");
291 saver_ehandler (Display *dpy, XErrorEvent *error)
293 saver_info *si = global_si_kludge; /* I hate C so much... */
295 if (!real_stderr) real_stderr = stderr;
297 fprintf (real_stderr, "\n"
298 "#######################################"
299 "#######################################\n\n"
300 "%s: X Error! PLEASE REPORT THIS BUG.\n\n"
301 "#######################################"
302 "#######################################\n\n",
304 if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
306 fprintf (real_stderr, "\n");
307 if (si->prefs.xsync_p)
309 saver_exit (si, -1, "because of synchronous X Error");
313 fprintf (real_stderr,
314 "#######################################"
315 "#######################################\n\n");
316 fprintf (real_stderr,
317 " If at all possible, please re-run xscreensaver with the command line\n"
318 " arguments `-sync -verbose', and reproduce this bug. That will cause\n"
319 " xscreensaver to dump a `core' file to the current directory. Please\n"
320 " include the stack trace from that core file in your bug report.\n"
322 " http://www.jwz.org/xscreensaver/bugs.html explains how to create the\n"
323 " most useful bug reports, and how to examine core files.\n"
325 " The more information you can provide, the better. But please report\n"
326 " report this bug, regardless!\n"
328 fprintf (real_stderr,
329 "#######################################"
330 "#######################################\n\n");
332 saver_exit (si, -1, 0);
336 fprintf (real_stderr, " (nonfatal.)\n");
341 /* This error handler is used only while the X connection is being set up;
342 after we've got a connection, we don't use this handler again. The only
343 reason for having this is so that we can present a more idiot-proof error
344 message than "cannot open display."
347 startup_ehandler (String name, String type, String class,
348 String defalt, /* one can't even spel properly
349 in this joke of a language */
350 String *av, Cardinal *ac)
354 saver_info *si = global_si_kludge; /* I hate C so much... */
355 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
357 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
358 fmt, sizeof(fmt)-1, *db);
360 fprintf (stderr, "%s: ", blurb());
362 memset (p, 0, sizeof(p));
363 if (*ac > countof (p)) *ac = countof (p);
364 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
365 fprintf (stderr, fmt, /* Did I mention that I hate C? */
366 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
367 fprintf (stderr, "\n");
369 describe_uids (si, stderr);
370 fprintf (stderr, "\n"
371 "%s: Errors at startup are usually authorization problems.\n"
372 " Did you read the manual? Specifically, the parts\n"
373 " that talk about XAUTH, XDM, and root logins?\n"
375 " http://www.jwz.org/xscreensaver/man.html\n"
385 /* The zillions of initializations.
388 /* Set progname, version, etc. This is done very early.
391 set_version_string (saver_info *si, int *argc, char **argv)
393 progclass = "XScreenSaver";
395 /* progname is reset later, after we connect to X. */
396 progname = strrchr(argv[0], '/');
397 if (progname) progname++;
398 else progname = argv[0];
400 if (strlen(progname) > 100) /* keep it short. */
403 /* The X resource database blows up if argv[0] has a "." in it. */
406 while ((s = strchr (s, '.')))
410 si->version = (char *) malloc (5);
411 memcpy (si->version, screensaver_id + 17, 4);
416 /* Initializations that potentially take place as a priveleged user:
417 If the xscreensaver executable is setuid root, then these initializations
418 are run as root, before discarding privileges.
421 privileged_initialization (saver_info *si, int *argc, char **argv)
424 /* before hack_uid() for proper permissions */
425 lock_priv_init (*argc, argv, si->prefs.verbose_p);
426 #endif /* NO_LOCKING */
432 /* Figure out what locking mechanisms are supported.
435 lock_initialization (saver_info *si, int *argc, char **argv)
438 si->locking_disabled_p = True;
439 si->nolock_reason = "not compiled with locking support";
440 #else /* !NO_LOCKING */
442 /* Finish initializing locking, now that we're out of privileged code. */
443 if (! lock_init (*argc, argv, si->prefs.verbose_p))
445 si->locking_disabled_p = True;
446 si->nolock_reason = "error getting password";
448 #endif /* NO_LOCKING */
454 /* Open the connection to the X server, and intern our Atoms.
457 connect_to_server (saver_info *si, int *argc, char **argv)
459 Widget toplevel_shell;
461 XSetErrorHandler (saver_ehandler);
463 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
464 toplevel_shell = XtAppInitialize (&si->app, progclass,
465 options, XtNumber (options),
466 argc, argv, defaults, 0, 0);
467 XtAppSetErrorMsgHandler (si->app, 0);
469 si->dpy = XtDisplay (toplevel_shell);
470 si->prefs.db = XtDatabase (si->dpy);
471 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
473 if(strlen(progname) > 100) /* keep it short. */
476 db = si->prefs.db; /* resources.c needs this */
478 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
479 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
480 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
481 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
482 XA_SCREENSAVER_TIME = XInternAtom (si->dpy, "_SCREENSAVER_TIME", False);
483 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
485 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
486 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
487 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
488 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
489 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
490 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
491 XA_PREV = XInternAtom (si->dpy, "PREV", False);
492 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
493 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
494 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
495 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
496 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
497 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
498 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
500 return toplevel_shell;
504 /* Handle the command-line arguments that were not handled for us by Xt.
505 Issue an error message and exit if there are unknown options.
508 process_command_line (saver_info *si, int *argc, char **argv)
511 for (i = 1; i < *argc; i++)
513 if (!strcmp (argv[i], "-debug"))
514 /* no resource for this one, out of paranoia. */
515 si->prefs.debug_p = True;
517 else if (!strcmp (argv[i], "-h") ||
518 !strcmp (argv[i], "-help") ||
519 !strcmp (argv[i], "--help"))
524 const char *s = argv[i];
525 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
528 if (s[0] == '-' && s[1] == '-') s++;
529 if (!strcmp (s, "-activate") ||
530 !strcmp (s, "-deactivate") ||
531 !strcmp (s, "-cycle") ||
532 !strcmp (s, "-next") ||
533 !strcmp (s, "-prev") ||
534 !strcmp (s, "-exit") ||
535 !strcmp (s, "-restart") ||
536 !strcmp (s, "-demo") ||
537 !strcmp (s, "-prefs") ||
538 !strcmp (s, "-preferences") ||
539 !strcmp (s, "-lock") ||
540 !strcmp (s, "-version") ||
541 !strcmp (s, "-time"))
544 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
545 fprintf (stderr, "\n\
546 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
548 fprintf (stderr, "\n\
549 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
552 The `xscreensaver' program is a daemon that runs in the background.\n\
553 You control a running xscreensaver process by sending it messages\n\
554 with `xscreensaver-demo' or `xscreensaver-command'.\n\
555 . See the man pages for details, or check the web page:\n\
556 http://www.jwz.org/xscreensaver/\n\n");
558 /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
559 suggest that explicitly. */
560 if (!strcmp (s, "-lock"))
562 Or perhaps you meant either the \"-lock-mode\" or the\n\
563 \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
572 /* Print out the xscreensaver banner to the tty if applicable;
573 Issue any other warnings that are called for at this point.
576 print_banner (saver_info *si)
578 saver_preferences *p = &si->prefs;
580 /* This resource gets set some time before the others, so that we know
581 whether to print the banner (and so that the banner gets printed before
582 any resource-database-related error messages.)
584 p->verbose_p = (p->debug_p || get_boolean_resource ("verbose", "Boolean"));
586 /* Ditto, for the locking_disabled_p message. */
587 p->lock_p = get_boolean_resource ("lock", "Boolean");
591 "%s %s, copyright (c) 1991-1999 "
592 "by Jamie Zawinski <jwz@jwz.org>.\n",
593 progname, si->version);
596 fprintf (stderr, "\n"
597 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
599 "\tNote that in debug mode, the xscreensaver window will only\n"
600 "\tcover the left half of the screen. (The idea is that you\n"
601 "\tcan still see debugging output in a shell, if you position\n"
602 "\tit on the right side of the screen.)\n"
604 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
605 "\tuntrusted environments.\n"
611 if (!si->uid_message || !*si->uid_message)
612 describe_uids (si, stderr);
615 if (si->orig_uid && *si->orig_uid)
616 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
617 blurb(), si->orig_uid);
618 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
621 fprintf (stderr, "%s: in process %lu.\n", blurb(),
622 (unsigned long) getpid());
625 /* If locking was not able to be initalized for some reason, explain why.
626 (This has to be done after we've read the lock_p resource.)
628 if (p->lock_p && si->locking_disabled_p)
631 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
633 if (strstr (si->nolock_reason, "passw"))
634 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
635 "consult the manual.\n", blurb());
636 else if (strstr (si->nolock_reason, "running as "))
638 "%s: locking only works when xscreensaver is launched\n"
639 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
640 "\t See the manual for details.\n",
646 /* Examine all of the display's screens, and populate the `saver_screen_info'
650 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
652 Bool found_any_writable_cells = False;
655 si->nscreens = ScreenCount(si->dpy);
656 si->screens = (saver_screen_info *)
657 calloc(sizeof(saver_screen_info), si->nscreens);
659 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
661 for (i = 0; i < si->nscreens; i++)
663 saver_screen_info *ssi = &si->screens[i];
665 ssi->screen = ScreenOfDisplay (si->dpy, i);
667 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
668 ssi->default_visual =
669 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
671 ssi->current_visual = ssi->default_visual;
672 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
674 if (ssi == si->default_screen)
675 /* Since this is the default screen, use the one already created. */
676 ssi->toplevel_shell = toplevel_shell;
678 /* Otherwise, each screen must have its own unmapped root widget. */
679 ssi->toplevel_shell =
680 XtVaAppCreateShell (progname, progclass, applicationShellWidgetClass,
682 XtNscreen, ssi->screen,
683 XtNvisual, ssi->current_visual,
684 XtNdepth, visual_depth (ssi->screen,
685 ssi->current_visual),
688 if (! found_any_writable_cells)
690 /* Check to see whether fading is ever possible -- if any of the
691 screens on the display has a PseudoColor visual, then fading can
692 work (on at least some screens.) If no screen has a PseudoColor
693 visual, then don't bother ever trying to fade, because it will
694 just cause a delay without causing any visible effect.
696 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
697 get_visual (ssi->screen, "PseudoColor", True, False) ||
698 get_visual (ssi->screen, "GrayScale", True, False))
699 found_any_writable_cells = True;
703 si->fading_possible_p = found_any_writable_cells;
707 /* If any server extensions have been requested, try and initialize them.
708 Issue warnings if requests can't be honored.
711 initialize_server_extensions (saver_info *si)
713 saver_preferences *p = &si->prefs;
715 Bool server_has_xidle_extension_p = False;
716 Bool server_has_sgi_saver_extension_p = False;
717 Bool server_has_mit_saver_extension_p = False;
718 Bool system_has_proc_interrupts_p = False;
719 const char *piwhy = 0;
721 si->using_xidle_extension = p->use_xidle_extension;
722 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
723 si->using_mit_saver_extension = p->use_mit_saver_extension;
724 si->using_proc_interrupts = p->use_proc_interrupts;
726 #ifdef HAVE_XIDLE_EXTENSION
727 server_has_xidle_extension_p = query_xidle_extension (si);
729 #ifdef HAVE_SGI_SAVER_EXTENSION
730 server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
732 #ifdef HAVE_MIT_SAVER_EXTENSION
733 server_has_mit_saver_extension_p = query_mit_saver_extension (si);
735 #ifdef HAVE_PROC_INTERRUPTS
736 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
739 if (!server_has_xidle_extension_p)
740 si->using_xidle_extension = False;
741 else if (p->verbose_p)
743 if (si->using_xidle_extension)
744 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
746 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
749 if (!server_has_sgi_saver_extension_p)
750 si->using_sgi_saver_extension = False;
751 else if (p->verbose_p)
753 if (si->using_sgi_saver_extension)
754 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
757 "%s: not using server's SGI SCREEN_SAVER extension.\n",
761 if (!server_has_mit_saver_extension_p)
762 si->using_mit_saver_extension = False;
763 else if (p->verbose_p)
765 if (si->using_mit_saver_extension)
766 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
770 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
774 if (!system_has_proc_interrupts_p)
776 si->using_proc_interrupts = False;
777 if (p->verbose_p && piwhy)
778 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
781 else if (p->verbose_p)
783 if (si->using_proc_interrupts)
785 "%s: consulting /proc/interrupts for keyboard activity.\n",
789 "%s: not consulting /proc/interrupts for keyboard activity.\n",
795 /* For the case where we aren't using an server extensions, select user events
796 on all the existing windows, and launch timers to select events on
797 newly-created windows as well.
799 If a server extension is being used, this does nothing.
802 select_events (saver_info *si)
804 saver_preferences *p = &si->prefs;
807 if (si->using_xidle_extension ||
808 si->using_mit_saver_extension ||
809 si->using_sgi_saver_extension)
812 if (p->initial_delay)
816 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
817 (int) p->initial_delay/1000,
818 (p->initial_delay == 1000 ? "" : "s"));
822 usleep (p->initial_delay);
824 fprintf (stderr, " done.\n");
829 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
834 /* Select events on the root windows of every screen. This also selects
835 for window creation events, so that new subwindows will be noticed.
837 for (i = 0; i < si->nscreens; i++)
838 start_notice_events_timer (si, RootWindowOfScreen (si->screens[i].screen),
842 fprintf (stderr, " done.\n");
847 maybe_reload_init_file (saver_info *si)
849 saver_preferences *p = &si->prefs;
850 if (init_file_changed_p (p))
853 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
854 blurb(), init_file_name());
858 /* If a server extension is in use, and p->timeout has changed,
859 we need to inform the server of the new timeout. */
860 disable_builtin_screensaver (si, False);
867 - wait until the user is idle;
869 - wait until the user is active;
870 - unblank the screen;
875 main_loop (saver_info *si)
877 saver_preferences *p = &si->prefs;
882 Bool was_locked = False;
883 sleep_until_idle (si, True);
888 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
889 si->selection_mode, timestring());
892 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
896 maybe_reload_init_file (si);
898 if (! blank_screen (si))
900 /* We were unable to grab either the keyboard or mouse.
901 This means we did not (and must not) blank the screen.
902 If we were to blank the screen while some other program
903 is holding both the mouse and keyboard grabbed, then
904 we would never be able to un-blank it! We would never
905 see any events, and the display would be wedged.
907 So, just go around the loop again and wait for the
908 next bout of idleness.
912 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
917 kill_screenhack (si);
919 if (!si->throttled_p)
920 spawn_screenhack (si, True);
921 else if (p->verbose_p)
922 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
924 /* Don't start the cycle timer in demo mode. */
925 if (!si->demoing_p && p->cycle)
926 si->cycle_id = XtAppAddTimeOut (si->app,
928 /* see comment in cycle_timer() */
937 Time lock_timeout = p->lock_timeout;
939 if (si->emergency_lock_p && p->lock_p && lock_timeout)
941 int secs = p->lock_timeout / 1000;
944 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
946 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
950 si->emergency_lock_p = False;
952 if (!si->demoing_p && /* if not going into demo mode */
953 p->lock_p && /* and locking is enabled */
954 !si->locking_disabled_p && /* and locking is possible */
955 lock_timeout == 0) /* and locking is not timer-deferred */
956 set_locked_p (si, True); /* then lock right now. */
958 /* locked_p might be true already because of the above, or because of
959 the LOCK ClientMessage. But if not, and if we're supposed to lock
960 after some time, set up a timer to do so.
965 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
969 #endif /* !NO_LOCKING */
972 ok_to_unblank = True;
975 sleep_until_idle (si, False); /* until not idle */
976 maybe_reload_init_file (si);
981 saver_screen_info *ssi = si->default_screen;
982 if (si->locking_disabled_p) abort ();
985 si->dbox_up_p = True;
986 suspend_screenhack (si, True);
987 XUndefineCursor (si->dpy, ssi->screensaver_window);
989 ok_to_unblank = unlock_p (si);
991 si->dbox_up_p = False;
992 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
993 suspend_screenhack (si, False); /* resume */
995 #endif /* !NO_LOCKING */
997 } while (!ok_to_unblank);
1001 fprintf (stderr, "%s: unblanking screen at %s.\n",
1002 blurb(), timestring ());
1004 /* Kill before unblanking, to stop drawing as soon as possible. */
1005 kill_screenhack (si);
1006 unblank_screen (si);
1008 set_locked_p (si, False);
1009 si->emergency_lock_p = False;
1011 si->selection_mode = 0;
1013 /* If we're throttled, and the user has explicitly unlocked the screen,
1014 then unthrottle. If we weren't locked, then don't unthrottle
1015 automatically, because someone might have just bumped the desk... */
1018 if (si->throttled_p && p->verbose_p)
1019 fprintf (stderr, "%s: unthrottled.\n", blurb());
1020 si->throttled_p = False;
1025 XtRemoveTimeOut (si->cycle_id);
1031 XtRemoveTimeOut (si->lock_id);
1036 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1040 static void analyze_display (saver_info *si);
1043 main (int argc, char **argv)
1047 saver_info *si = &the_si;
1048 saver_preferences *p = &si->prefs;
1051 memset(si, 0, sizeof(*si));
1052 global_si_kludge = si; /* I hate C so much... */
1054 srandom ((int) time ((time_t *) 0));
1056 save_argv (argc, argv);
1057 set_version_string (si, &argc, argv);
1058 privileged_initialization (si, &argc, argv);
1059 hack_environment (si);
1061 shell = connect_to_server (si, &argc, argv);
1062 process_command_line (si, &argc, argv);
1065 load_init_file (p); /* must be before initialize_per_screen_info() */
1066 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1068 /* We can only issue this warnings now. */
1069 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1071 "%s: there are no PseudoColor or GrayScale visuals.\n"
1072 "%s: ignoring the request for fading/unfading.\n",
1075 for (i = 0; i < si->nscreens; i++)
1076 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1079 lock_initialization (si, &argc, argv);
1081 if (p->xsync_p) XSynchronize (si->dpy, True);
1082 blurb_timestamp_p = p->timestamp_p; /* kludge */
1084 if (p->verbose_p) analyze_display (si);
1085 initialize_server_extensions (si);
1086 initialize_screensaver_window (si);
1089 disable_builtin_screensaver (si, True);
1090 initialize_stderr (si);
1092 make_splash_dialog (si);
1094 main_loop (si); /* doesn't return */
1099 /* Processing ClientMessage events.
1103 clientmessage_response (saver_info *si, Window w, Bool error,
1104 const char *stderr_msg,
1105 const char *protocol_msg)
1109 saver_preferences *p = &si->prefs;
1110 if (error || p->verbose_p)
1111 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1113 L = strlen(protocol_msg);
1114 proto = (char *) malloc (L + 2);
1115 proto[0] = (error ? '-' : '+');
1116 strcpy (proto+1, protocol_msg);
1119 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1120 PropModeReplace, (unsigned char *) proto, L);
1121 XSync (si->dpy, False);
1126 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1128 saver_preferences *p = &si->prefs;
1130 Window window = event->xclient.window;
1132 /* Preferences might affect our handling of client messages. */
1133 maybe_reload_init_file (si);
1135 if (event->xclient.message_type != XA_SCREENSAVER)
1138 str = XGetAtomName (si->dpy, event->xclient.message_type);
1139 fprintf (stderr, "%s: unrecognised ClientMessage type %s received\n",
1140 blurb(), (str ? str : "(null)"));
1141 if (str) XFree (str);
1144 if (event->xclient.format != 32)
1146 fprintf (stderr, "%s: ClientMessage of format %d received, not 32\n",
1147 blurb(), event->xclient.format);
1151 type = event->xclient.data.l[0];
1152 if (type == XA_ACTIVATE)
1156 clientmessage_response(si, window, False,
1157 "ACTIVATE ClientMessage received.",
1159 si->selection_mode = 0;
1160 si->demoing_p = False;
1162 if (si->throttled_p && p->verbose_p)
1163 fprintf (stderr, "%s: unthrottled.\n", blurb());
1164 si->throttled_p = False;
1166 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1168 XForceScreenSaver (si->dpy, ScreenSaverActive);
1176 clientmessage_response(si, window, True,
1177 "ClientMessage ACTIVATE received while already active.",
1180 else if (type == XA_DEACTIVATE)
1184 if (si->throttled_p && p->verbose_p)
1185 fprintf (stderr, "%s: unthrottled.\n", blurb());
1186 si->throttled_p = False;
1188 clientmessage_response(si, window, False,
1189 "DEACTIVATE ClientMessage received.",
1191 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1193 XForceScreenSaver (si->dpy, ScreenSaverReset);
1201 clientmessage_response(si, window, True,
1202 "ClientMessage DEACTIVATE received while inactive.",
1205 else if (type == XA_CYCLE)
1209 clientmessage_response(si, window, False,
1210 "CYCLE ClientMessage received.",
1212 si->selection_mode = 0; /* 0 means randomize when its time. */
1213 si->demoing_p = False;
1215 if (si->throttled_p && p->verbose_p)
1216 fprintf (stderr, "%s: unthrottled.\n", blurb());
1217 si->throttled_p = False;
1220 XtRemoveTimeOut (si->cycle_id);
1222 cycle_timer ((XtPointer) si, 0);
1225 clientmessage_response(si, window, True,
1226 "ClientMessage CYCLE received while inactive.",
1229 else if (type == XA_NEXT || type == XA_PREV)
1231 clientmessage_response(si, window, False,
1233 ? "NEXT ClientMessage received."
1234 : "PREV ClientMessage received."),
1236 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1237 si->demoing_p = False;
1239 if (si->throttled_p && p->verbose_p)
1240 fprintf (stderr, "%s: unthrottled.\n", blurb());
1241 si->throttled_p = False;
1246 XtRemoveTimeOut (si->cycle_id);
1248 cycle_timer ((XtPointer) si, 0);
1253 else if (type == XA_SELECT)
1257 long which = event->xclient.data.l[1];
1259 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1260 sprintf (buf2, "activating (%ld).", which);
1261 clientmessage_response (si, window, False, buf, buf2);
1263 if (which < 0) which = 0; /* 0 == "random" */
1264 si->selection_mode = which;
1265 si->demoing_p = False;
1267 if (si->throttled_p && p->verbose_p)
1268 fprintf (stderr, "%s: unthrottled.\n", blurb());
1269 si->throttled_p = False;
1274 XtRemoveTimeOut (si->cycle_id);
1276 cycle_timer ((XtPointer) si, 0);
1281 else if (type == XA_EXIT)
1283 /* Ignore EXIT message if the screen is locked. */
1284 if (until_idle_p || !si->locked_p)
1286 clientmessage_response (si, window, False,
1287 "EXIT ClientMessage received.",
1291 unblank_screen (si);
1292 kill_screenhack (si);
1293 XSync (si->dpy, False);
1295 saver_exit (si, 0, 0);
1298 clientmessage_response (si, window, True,
1299 "EXIT ClientMessage received while locked.",
1300 "screen is locked.");
1302 else if (type == XA_RESTART)
1304 /* The RESTART message works whether the screensaver is active or not,
1305 unless the screen is locked, in which case it doesn't work.
1307 if (until_idle_p || !si->locked_p)
1309 clientmessage_response (si, window, False,
1310 "RESTART ClientMessage received.",
1314 unblank_screen (si);
1315 kill_screenhack (si);
1316 XSync (si->dpy, False);
1321 if (real_stdout) fflush (real_stdout);
1322 if (real_stderr) fflush (real_stderr);
1323 /* make sure error message shows up before exit. */
1324 if (real_stderr && stderr != real_stderr)
1325 dup2 (fileno(real_stderr), fileno(stderr));
1327 restart_process (si);
1328 exit (1); /* shouldn't get here; but if restarting didn't work,
1329 make this command be the same as EXIT. */
1332 clientmessage_response (si, window, True,
1333 "RESTART ClientMessage received while locked.",
1334 "screen is locked.");
1336 else if (type == XA_DEMO)
1338 long arg = event->xclient.data.l[1];
1339 Bool demo_one_hack_p = (arg == 300);
1341 if (demo_one_hack_p)
1345 long which = event->xclient.data.l[2];
1348 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1349 sprintf (buf2, "demoing (%ld).", which);
1350 clientmessage_response (si, window, False, buf, buf2);
1352 if (which < 0) which = 0; /* 0 == "random" */
1353 si->selection_mode = which;
1354 si->demoing_p = True;
1356 if (si->throttled_p && p->verbose_p)
1357 fprintf (stderr, "%s: unthrottled.\n", blurb());
1358 si->throttled_p = False;
1363 clientmessage_response (si, window, True,
1364 "DEMO ClientMessage received while active.",
1369 clientmessage_response (si, window, True,
1370 "obsolete form of DEMO ClientMessage.",
1371 "obsolete form of DEMO ClientMessage.");
1374 else if (type == XA_PREFS)
1376 clientmessage_response (si, window, True,
1377 "the PREFS client-message is obsolete.",
1378 "the PREFS client-message is obsolete.");
1380 else if (type == XA_LOCK)
1383 clientmessage_response (si, window, True,
1384 "not compiled with support for locking.",
1385 "locking not enabled.");
1386 #else /* !NO_LOCKING */
1387 if (si->locking_disabled_p)
1388 clientmessage_response (si, window, True,
1389 "LOCK ClientMessage received, but locking is disabled.",
1390 "locking not enabled.");
1391 else if (si->locked_p)
1392 clientmessage_response (si, window, True,
1393 "LOCK ClientMessage received while already locked.",
1398 char *response = (until_idle_p
1399 ? "activating and locking."
1401 sprintf (buf, "LOCK ClientMessage received; %s", response);
1402 clientmessage_response (si, window, False, buf, response);
1403 set_locked_p (si, True);
1404 si->selection_mode = 0;
1405 si->demoing_p = False;
1407 if (si->lock_id) /* we're doing it now, so lose the timeout */
1409 XtRemoveTimeOut (si->lock_id);
1415 if (si->using_mit_saver_extension ||
1416 si->using_sgi_saver_extension)
1418 XForceScreenSaver (si->dpy, ScreenSaverActive);
1427 #endif /* !NO_LOCKING */
1429 else if (type == XA_THROTTLE)
1431 if (si->throttled_p)
1432 clientmessage_response (si, window, True,
1433 "THROTTLE ClientMessage received, but "
1434 "already throttled.",
1435 "already throttled.");
1439 char *response = "throttled.";
1440 si->throttled_p = True;
1441 si->selection_mode = 0;
1442 si->demoing_p = False;
1443 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
1444 clientmessage_response (si, window, False, buf, response);
1449 XtRemoveTimeOut (si->cycle_id);
1451 cycle_timer ((XtPointer) si, 0);
1455 else if (type == XA_UNTHROTTLE)
1457 if (! si->throttled_p)
1458 clientmessage_response (si, window, True,
1459 "UNTHROTTLE ClientMessage received, but "
1465 char *response = "unthrottled.";
1466 si->throttled_p = False;
1467 si->selection_mode = 0;
1468 si->demoing_p = False;
1469 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
1470 clientmessage_response (si, window, False, buf, response);
1475 XtRemoveTimeOut (si->cycle_id);
1477 cycle_timer ((XtPointer) si, 0);
1485 str = (type ? XGetAtomName(si->dpy, type) : 0);
1489 if (strlen (str) > 80)
1490 strcpy (str+70, "...");
1491 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
1498 "unrecognised screensaver ClientMessage 0x%x received.",
1499 (unsigned int) event->xclient.data.l[0]);
1502 clientmessage_response (si, window, True, buf, buf);
1508 /* Some random diagnostics printed in -verbose mode.
1512 analyze_display (saver_info *si)
1515 static const char *exts[][2] = {
1516 { "SCREEN_SAVER", "SGI Screen-Saver" },
1517 { "SCREEN-SAVER", "SGI Screen-Saver" },
1518 { "MIT-SCREEN-SAVER", "MIT Screen-Saver" },
1519 { "XIDLE", "XIdle" },
1520 { "SGI-VIDEO-CONTROL", "SGI Video-Control" },
1521 { "READDISPLAY", "SGI Read-Display" },
1522 { "MIT-SHM", "Shared Memory" },
1523 { "DOUBLE-BUFFER", "Double-Buffering" },
1524 { "DPMS", "Power Management" },
1526 { "XFree86-VidModeExtension", "XF86 Video-Mode" },
1527 { "XINERAMA", "Xinerama" }
1530 fprintf (stderr, "%s: running on display \"%s\"\n", blurb(),
1531 DisplayString(si->dpy));
1532 fprintf (stderr, "%s: vendor is %s, %d\n", blurb(),
1533 ServerVendor(si->dpy), VendorRelease(si->dpy));
1535 fprintf (stderr, "%s: useful extensions:\n", blurb());
1536 for (i = 0; i < countof(exts); i++)
1538 int op = 0, event = 0, error = 0;
1539 if (XQueryExtension (si->dpy, exts[i][0], &op, &event, &error))
1540 fprintf (stderr, "%s: %s\n", blurb(), exts[i][1]);
1543 for (i = 0; i < si->nscreens; i++)
1545 unsigned long colormapped_depths = 0;
1546 unsigned long non_mapped_depths = 0;
1547 XVisualInfo vi_in, *vi_out;
1550 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
1551 if (!vi_out) continue;
1552 for (j = 0; j < out_count; j++)
1553 if (vi_out[j].class == PseudoColor)
1554 colormapped_depths |= (1 << vi_out[j].depth);
1556 non_mapped_depths |= (1 << vi_out[j].depth);
1557 XFree ((char *) vi_out);
1559 if (colormapped_depths)
1561 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(), i);
1562 for (j = 0; j < 32; j++)
1563 if (colormapped_depths & (1 << j))
1564 fprintf (stderr, " %d", j);
1565 fprintf (stderr, "\n");
1567 if (non_mapped_depths)
1569 fprintf (stderr, "%s: screen %d non-mapped depths:", blurb(), i);
1570 for (j = 0; j < 32; j++)
1571 if (non_mapped_depths & (1 << j))
1572 fprintf (stderr, " %d", j);
1573 fprintf (stderr, "\n");
1579 display_is_on_console_p (saver_info *si)
1581 Bool not_on_console = True;
1582 char *dpystr = DisplayString (si->dpy);
1583 char *tail = (char *) strchr (dpystr, ':');
1584 if (! tail || strncmp (tail, ":0", 2))
1585 not_on_console = True;
1588 char dpyname[255], localname[255];
1589 strncpy (dpyname, dpystr, tail-dpystr);
1590 dpyname [tail-dpystr] = 0;
1592 !strcmp(dpyname, "unix") ||
1593 !strcmp(dpyname, "localhost"))
1594 not_on_console = False;
1595 else if (gethostname (localname, sizeof (localname)))
1596 not_on_console = True; /* can't find hostname? */
1599 /* We have to call gethostbyname() on the result of gethostname()
1600 because the two aren't guarenteed to be the same name for the
1601 same host: on some losing systems, one is a FQDN and the other
1602 is not. Here in the wide wonderful world of Unix it's rocket
1603 science to obtain the local hostname in a portable fashion.
1605 And don't forget, gethostbyname() reuses the structure it
1606 returns, so we have to copy the fucker before calling it again.
1607 Thank you master, may I have another.
1609 struct hostent *h = gethostbyname (dpyname);
1611 not_on_console = True;
1616 strcpy (hn, h->h_name);
1617 l = gethostbyname (localname);
1618 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
1622 return !not_on_console;