1 /* xscreensaver, Copyright (c) 1991-1998 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>
136 # include <X11/Xmu/Error.h>
138 # include <Xmu/Error.h>
140 #else /* !HAVE_XMU */
142 #endif /* !HAVE_XMU */
144 #ifdef HAVE_XIDLE_EXTENSION
145 #include <X11/extensions/xidle.h>
146 #endif /* HAVE_XIDLE_EXTENSION */
148 #include "xscreensaver.h"
150 #include "yarandom.h"
151 #include "resources.h"
154 saver_info *global_si_kludge = 0; /* I hate C so much... */
161 static Atom XA_SCREENSAVER_RESPONSE;
162 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
163 static Atom XA_EXIT, XA_RESTART, XA_LOCK, XA_SELECT;
164 Atom XA_DEMO, XA_PREFS;
167 static XrmOptionDescRec options [] = {
168 { "-timeout", ".timeout", XrmoptionSepArg, 0 },
169 { "-cycle", ".cycle", XrmoptionSepArg, 0 },
170 { "-lock-mode", ".lock", XrmoptionNoArg, "on" },
171 { "-no-lock-mode", ".lock", XrmoptionNoArg, "off" },
172 { "-no-lock", ".lock", XrmoptionNoArg, "off" },
173 { "-lock-timeout", ".lockTimeout", XrmoptionSepArg, 0 },
174 { "-lock-vts", ".lockVTs", XrmoptionNoArg, "on" },
175 { "-no-lock-vts", ".lockVTs", XrmoptionNoArg, "off" },
176 { "-visual", ".visualID", XrmoptionSepArg, 0 },
177 { "-install", ".installColormap", XrmoptionNoArg, "on" },
178 { "-no-install", ".installColormap", XrmoptionNoArg, "off" },
179 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
180 { "-silent", ".verbose", XrmoptionNoArg, "off" },
181 { "-timestamp", ".timestamp", XrmoptionNoArg, "on" },
182 { "-capture-stderr", ".captureStderr", XrmoptionNoArg, "on" },
183 { "-no-capture-stderr", ".captureStderr", XrmoptionNoArg, "off" },
184 { "-xidle-extension", ".xidleExtension", XrmoptionNoArg, "on" },
185 { "-no-xidle-extension", ".xidleExtension", XrmoptionNoArg, "off" },
186 { "-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "on" },
187 { "-no-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "off" },
188 { "-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "on" },
189 { "-no-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "off" },
190 { "-splash", ".splash", XrmoptionNoArg, "on" },
191 { "-no-splash", ".splash", XrmoptionNoArg, "off" },
192 { "-nosplash", ".splash", XrmoptionNoArg, "off" },
193 { "-idelay", ".initialDelay", XrmoptionSepArg, 0 },
194 { "-nice", ".nice", XrmoptionSepArg, 0 },
196 /* Actually these are built in to Xt, but just to be sure... */
197 { "-synchronous", ".synchronous", XrmoptionNoArg, "on" },
198 { "-xrm", NULL, XrmoptionResArg, NULL }
201 static char *defaults[] = {
202 #include "XScreenSaver_ad.h"
207 ERROR! You must not include vroot.h in this file.
211 do_help (saver_info *si)
216 xscreensaver %s, copyright (c) 1991-1998 by Jamie Zawinski <jwz@jwz.org>\n\
217 The standard Xt command-line options are accepted; other options include:\n\
219 -timeout <minutes> When the screensaver should activate.\n\
220 -cycle <minutes> How long to let each hack run before switching.\n\
221 -lock-mode Require a password before deactivating.\n\
222 -lock-timeout <minutes> Grace period before locking; default 0.\n\
223 -visual <id-or-class> Which X visual to run on.\n\
224 -install Install a private colormap.\n\
226 -no-splash Don't display a splash-screen at startup.\n\
227 -help This message.\n\
229 See the manual for other options and X resources.\n\
231 The `xscreensaver' program should be left running in the background.\n\
232 Use the `xscreensaver-demo' and `xscreensaver-command' programs to\n\
233 manipulate a running xscreensaver.\n\
235 The `*programs' resource controls which graphics demos will be launched by\n\
236 the screensaver. See `man xscreensaver' or the web page for more details.\n\
238 Just getting started? Try this:\n\
243 For updates, check 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... */
290 fprintf (real_stderr, "\n"
291 "#######################################"
292 "#######################################\n\n"
293 "%s: X Error! PLEASE REPORT THIS BUG.\n\n"
294 "#######################################"
295 "#######################################\n\n",
297 if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
299 fprintf (real_stderr, "\n");
300 if (si->prefs.xsync_p)
302 saver_exit (si, -1, "because of synchronous X Error");
307 "%s: to dump a core file, re-run with `-sync'.\n"
308 "%s: see http://www.jwz.org/xscreensaver/bugs.html\n"
309 "\t\tfor bug reporting information.\n\n",
311 saver_exit (si, -1, 0);
315 fprintf (real_stderr, " (nonfatal.)\n");
320 /* The zillions of initializations.
323 /* Set progname, version, etc. This is done very early.
326 set_version_string (saver_info *si, int *argc, char **argv)
328 progclass = "XScreenSaver";
330 /* progname is reset later, after we connect to X. */
331 progname = strrchr(argv[0], '/');
332 if (progname) progname++;
333 else progname = argv[0];
335 if (strlen(progname) > 100) /* keep it short. */
338 /* The X resource database blows up if argv[0] has a "." in it. */
341 while ((s = strchr (s, '.')))
345 si->version = (char *) malloc (5);
346 memcpy (si->version, screensaver_id + 17, 4);
351 /* Initializations that potentially take place as a priveleged user:
352 If the xscreensaver executable is setuid root, then these initializations
353 are run as root, before discarding privileges.
356 privileged_initialization (saver_info *si, int *argc, char **argv)
359 si->locking_disabled_p = True;
360 si->nolock_reason = "not compiled with locking support";
361 #else /* !NO_LOCKING */
362 si->locking_disabled_p = False;
363 if (! lock_init (*argc, argv)) /* before hack_uid() for proper permissions */
365 si->locking_disabled_p = True;
366 si->nolock_reason = "error getting password";
368 #endif /* NO_LOCKING */
372 #endif /* NO_SETUID */
376 /* Open the connection to the X server, and intern our Atoms.
379 connect_to_server (saver_info *si, int *argc, char **argv)
381 Widget toplevel_shell;
383 XSetErrorHandler (saver_ehandler);
384 toplevel_shell = XtAppInitialize (&si->app, progclass,
385 options, XtNumber (options),
386 argc, argv, defaults, 0, 0);
388 si->dpy = XtDisplay (toplevel_shell);
389 si->prefs.db = XtDatabase (si->dpy);
390 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
392 if(strlen(progname) > 100) /* keep it short. */
395 db = si->prefs.db; /* resources.c needs this */
397 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
398 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
399 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
400 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
401 XA_SCREENSAVER_TIME = XInternAtom (si->dpy, "_SCREENSAVER_TIME", False);
402 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
404 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
405 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
406 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
407 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
408 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
409 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
410 XA_PREV = XInternAtom (si->dpy, "PREV", False);
411 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
412 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
413 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
414 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
415 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
417 return toplevel_shell;
421 /* Handle the command-line arguments that were not handled for us by Xt.
422 Issue an error message and exit if there are unknown options.
425 process_command_line (saver_info *si, int *argc, char **argv)
428 for (i = 1; i < *argc; i++)
430 if (!strcmp (argv[i], "-debug"))
431 /* no resource for this one, out of paranoia. */
432 si->prefs.debug_p = True;
434 else if (!strcmp (argv[i], "-h") ||
435 !strcmp (argv[i], "-help") ||
436 !strcmp (argv[i], "--help"))
441 const char *s = argv[i];
442 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
445 if (s[0] == '-' && s[1] == '-') s++;
446 if (!strcmp (s, "-activate") ||
447 !strcmp (s, "-deactivate") ||
448 !strcmp (s, "-cycle") ||
449 !strcmp (s, "-next") ||
450 !strcmp (s, "-prev") ||
451 !strcmp (s, "-exit") ||
452 !strcmp (s, "-restart") ||
453 !strcmp (s, "-demo") ||
454 !strcmp (s, "-prefs") ||
455 !strcmp (s, "-preferences") ||
456 !strcmp (s, "-lock") ||
457 !strcmp (s, "-version") ||
458 !strcmp (s, "-time"))
461 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
462 fprintf (stderr, "\n\
463 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
465 fprintf (stderr, "\n\
466 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
469 The `xscreensaver' program is a daemon that runs in the background.\n\
470 You control a running xscreensaver process by sending it messages\n\
471 with `xscreensaver-demo' or `xscreensaver-command'.\n\
472 . See the man pages for details, or check the web page:\n\
473 http://www.jwz.org/xscreensaver/\n\n");
475 /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
476 suggest that explicitly. */
477 if (!strcmp (s, "-lock"))
479 Or perhaps you meant either the \"-lock-mode\" or the\n\
480 \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
489 /* Print out the xscreensaver banner to the tty if applicable;
490 Issue any other warnings that are called for at this point.
493 print_banner (saver_info *si)
495 saver_preferences *p = &si->prefs;
497 /* This resource gets set some time before the others, so that we know
498 whether to print the banner (and so that the banner gets printed before
499 any resource-database-related error messages.)
501 p->verbose_p = (p->debug_p || get_boolean_resource ("verbose", "Boolean"));
503 /* Ditto, for the locking_disabled_p message. */
504 p->lock_p = get_boolean_resource ("lock", "Boolean");
508 "%s %s, copyright (c) 1991-1998 "
509 "by Jamie Zawinski <jwz@jwz.org>.\n",
510 progname, si->version);
513 fprintf (stderr, "\n"
514 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
516 "\tNote that in debug mode, the xscreensaver window will only\n"
517 "\tcover the left half of the screen. (The idea is that you\n"
518 "\tcan still see debugging output in a shell, if you position\n"
519 "\tit on the right side of the screen.)\n"
521 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
522 "\tuntrusted environments.\n"
528 if (!si->uid_message || !*si->uid_message)
529 describe_uids (si, stderr);
532 if (si->orig_uid && *si->orig_uid)
533 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
534 blurb(), si->orig_uid);
535 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
538 fprintf (stderr, "%s: in process %lu.\n", blurb(),
539 (unsigned long) getpid());
542 /* If locking was not able to be initalized for some reason, explain why.
543 (This has to be done after we've read the lock_p resource.)
545 if (p->lock_p && si->locking_disabled_p)
548 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
550 if (strstr (si->nolock_reason, "passw"))
551 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
552 "consult the manual.\n", blurb());
553 else if (strstr (si->nolock_reason, "running as "))
555 "%s: locking only works when xscreensaver is launched\n"
556 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
557 "\t See the manual for details.\n",
563 /* Examine all of the display's screens, and populate the `saver_screen_info'
567 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
569 Bool found_any_writable_cells = False;
572 si->nscreens = ScreenCount(si->dpy);
573 si->screens = (saver_screen_info *)
574 calloc(sizeof(saver_screen_info), si->nscreens);
576 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
578 for (i = 0; i < si->nscreens; i++)
580 saver_screen_info *ssi = &si->screens[i];
582 ssi->screen = ScreenOfDisplay (si->dpy, i);
584 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
585 ssi->default_visual =
586 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
588 ssi->current_visual = ssi->default_visual;
589 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
591 if (ssi == si->default_screen)
592 /* Since this is the default screen, use the one already created. */
593 ssi->toplevel_shell = toplevel_shell;
595 /* Otherwise, each screen must have its own unmapped root widget. */
596 ssi->toplevel_shell =
597 XtVaAppCreateShell (progname, progclass, applicationShellWidgetClass,
599 XtNscreen, ssi->screen,
600 XtNvisual, ssi->current_visual,
601 XtNdepth, visual_depth (ssi->screen,
602 ssi->current_visual),
605 if (! found_any_writable_cells)
607 /* Check to see whether fading is ever possible -- if any of the
608 screens on the display has a PseudoColor visual, then fading can
609 work (on at least some screens.) If no screen has a PseudoColor
610 visual, then don't bother ever trying to fade, because it will
611 just cause a delay without causing any visible effect.
613 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
614 get_visual (ssi->screen, "PseudoColor", True, False) ||
615 get_visual (ssi->screen, "GrayScale", True, False))
616 found_any_writable_cells = True;
620 si->prefs.fading_possible_p = found_any_writable_cells;
624 /* If any server extensions have been requested, try and initialize them.
625 Issue warnings if requests can't be honored.
628 initialize_server_extensions (saver_info *si)
630 saver_preferences *p = &si->prefs;
632 Bool server_has_xidle_extension_p = False;
633 Bool server_has_sgi_saver_extension_p = False;
634 Bool server_has_mit_saver_extension_p = False;
636 #ifdef HAVE_XIDLE_EXTENSION
637 server_has_xidle_extension_p = query_xidle_extension (si);
639 #ifdef HAVE_SGI_SAVER_EXTENSION
640 server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
642 #ifdef HAVE_MIT_SAVER_EXTENSION
643 server_has_mit_saver_extension_p = query_mit_saver_extension (si);
646 if (!server_has_xidle_extension_p)
647 p->use_xidle_extension = False;
648 else if (p->verbose_p)
650 if (p->use_xidle_extension)
651 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
653 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
656 if (!server_has_sgi_saver_extension_p)
657 p->use_sgi_saver_extension = False;
658 else if (p->verbose_p)
660 if (p->use_sgi_saver_extension)
661 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
664 "%s: not using server's SGI SCREEN_SAVER extension.\n",
668 if (!server_has_mit_saver_extension_p)
669 p->use_mit_saver_extension = False;
670 else if (p->verbose_p)
672 if (p->use_mit_saver_extension)
673 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
677 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
683 /* For the case where we aren't using an server extensions, select user events
684 on all the existing windows, and launch timers to select events on
685 newly-created windows as well.
687 If a server extension is being used, this does nothing.
690 select_events (saver_info *si)
692 saver_preferences *p = &si->prefs;
695 if (p->use_xidle_extension ||
696 p->use_mit_saver_extension ||
697 p->use_sgi_saver_extension)
700 if (p->initial_delay)
704 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
705 (int) p->initial_delay/1000,
706 (p->initial_delay == 1000 ? "" : "s"));
710 usleep (p->initial_delay);
712 fprintf (stderr, " done.\n");
717 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
722 /* Select events on the root windows of every screen. This also selects
723 for window creation events, so that new subwindows will be noticed.
725 for (i = 0; i < si->nscreens; i++)
726 start_notice_events_timer (si, RootWindowOfScreen (si->screens[i].screen));
729 fprintf (stderr, " done.\n");
734 maybe_reload_init_file (saver_info *si)
736 saver_preferences *p = &si->prefs;
737 if (init_file_changed_p (p))
740 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
741 blurb(), init_file_name());
745 /* If a server extension is in use, and p->timeout has changed,
746 we need to inform the server of the new timeout. */
747 disable_builtin_screensaver (si, False);
754 - wait until the user is idle;
756 - wait until the user is active;
757 - unblank the screen;
762 main_loop (saver_info *si)
764 saver_preferences *p = &si->prefs;
769 sleep_until_idle (si, True);
774 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
775 si->selection_mode, timestring());
778 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
782 maybe_reload_init_file (si);
785 kill_screenhack (si);
786 spawn_screenhack (si, True);
788 /* Don't start the cycle timer in demo mode. */
789 if (!si->demoing_p && p->cycle)
790 si->cycle_id = XtAppAddTimeOut (si->app, p->cycle, cycle_timer,
795 if (!si->demoing_p && /* if not going into demo mode */
796 p->lock_p && /* and locking is enabled */
797 !si->locking_disabled_p && /* and locking is possible */
798 p->lock_timeout == 0) /* and locking is not timer-deferred */
799 si->locked_p = True; /* then lock right now. */
801 /* locked_p might be true already because of the above, or because of
802 the LOCK ClientMessage. But if not, and if we're supposed to lock
803 after some time, set up a timer to do so.
808 si->lock_id = XtAppAddTimeOut (si->app, p->lock_timeout,
811 #endif /* !NO_LOCKING */
814 ok_to_unblank = True;
817 sleep_until_idle (si, False); /* until not idle */
818 maybe_reload_init_file (si);
823 saver_screen_info *ssi = si->default_screen;
824 if (si->locking_disabled_p) abort ();
826 si->dbox_up_p = True;
827 suspend_screenhack (si, True);
828 XUndefineCursor (si->dpy, ssi->screensaver_window);
830 ok_to_unblank = unlock_p (si);
832 si->dbox_up_p = False;
833 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
834 suspend_screenhack (si, False); /* resume */
836 #endif /* !NO_LOCKING */
838 } while (!ok_to_unblank);
842 fprintf (stderr, "%s: unblanking screen at %s.\n",
843 blurb(), timestring ());
845 /* Kill before unblanking, to stop drawing as soon as possible. */
846 kill_screenhack (si);
849 si->locked_p = False;
851 si->selection_mode = 0;
855 XtRemoveTimeOut (si->cycle_id);
861 XtRemoveTimeOut (si->lock_id);
866 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
870 static void analyze_display (saver_info *si);
873 main (int argc, char **argv)
877 saver_info *si = &the_si;
878 saver_preferences *p = &si->prefs;
881 memset(si, 0, sizeof(*si));
882 global_si_kludge = si; /* I hate C so much... */
884 srandom ((int) time ((time_t *) 0));
886 save_argv (argc, argv);
887 set_version_string (si, &argc, argv);
888 privileged_initialization (si, &argc, argv);
889 hack_environment (si);
891 shell = connect_to_server (si, &argc, argv);
892 process_command_line (si, &argc, argv);
895 initialize_per_screen_info (si, shell); /* also sets p->fading_possible_p */
897 for (i = 0; i < si->nscreens; i++)
898 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
903 if (p->xsync_p) XSynchronize (si->dpy, True);
904 blurb_timestamp_p = p->timestamp_p; /* kludge */
906 if (p->verbose_p) analyze_display (si);
907 initialize_server_extensions (si);
908 initialize_screensaver_window (si);
911 disable_builtin_screensaver (si, True);
912 initialize_stderr (si);
914 make_splash_dialog (si);
916 main_loop (si); /* doesn't return */
921 /* Processing ClientMessage events.
925 clientmessage_response (saver_info *si, Window w, Bool error,
926 const char *stderr_msg,
927 const char *protocol_msg)
931 saver_preferences *p = &si->prefs;
932 if (error || p->verbose_p)
933 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
935 L = strlen(protocol_msg);
936 proto = (char *) malloc (L + 2);
937 proto[0] = (error ? '-' : '+');
938 strcpy (proto+1, protocol_msg);
941 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
942 PropModeReplace, proto, L);
943 XSync (si->dpy, False);
948 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
950 saver_preferences *p = &si->prefs;
952 Window window = event->xclient.window;
954 /* Preferences might affect our handling of client messages. */
955 maybe_reload_init_file (si);
957 if (event->xclient.message_type != XA_SCREENSAVER)
960 str = XGetAtomName (si->dpy, event->xclient.message_type);
961 fprintf (stderr, "%s: unrecognised ClientMessage type %s received\n",
962 blurb(), (str ? str : "(null)"));
963 if (str) XFree (str);
966 if (event->xclient.format != 32)
968 fprintf (stderr, "%s: ClientMessage of format %d received, not 32\n",
969 blurb(), event->xclient.format);
973 type = event->xclient.data.l[0];
974 if (type == XA_ACTIVATE)
978 clientmessage_response(si, window, False,
979 "ACTIVATE ClientMessage received.",
981 si->selection_mode = 0;
982 si->demoing_p = False;
983 if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
985 XForceScreenSaver (si->dpy, ScreenSaverActive);
993 clientmessage_response(si, window, True,
994 "ClientMessage ACTIVATE received while already active.",
997 else if (type == XA_DEACTIVATE)
1001 clientmessage_response(si, window, False,
1002 "DEACTIVATE ClientMessage received.",
1004 if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
1006 XForceScreenSaver (si->dpy, ScreenSaverReset);
1014 clientmessage_response(si, window, True,
1015 "ClientMessage DEACTIVATE received while inactive.",
1018 else if (type == XA_CYCLE)
1022 clientmessage_response(si, window, False,
1023 "CYCLE ClientMessage received.",
1025 si->selection_mode = 0; /* 0 means randomize when its time. */
1026 si->demoing_p = False;
1028 XtRemoveTimeOut (si->cycle_id);
1030 cycle_timer ((XtPointer) si, 0);
1033 clientmessage_response(si, window, True,
1034 "ClientMessage CYCLE received while inactive.",
1037 else if (type == XA_NEXT || type == XA_PREV)
1039 clientmessage_response(si, window, False,
1041 ? "NEXT ClientMessage received."
1042 : "PREV ClientMessage received."),
1044 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1045 si->demoing_p = False;
1050 XtRemoveTimeOut (si->cycle_id);
1052 cycle_timer ((XtPointer) si, 0);
1057 else if (type == XA_SELECT)
1061 long which = event->xclient.data.l[1];
1063 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1064 sprintf (buf2, "activating (%ld).", which);
1065 clientmessage_response (si, window, False, buf, buf2);
1067 if (which < 0) which = 0; /* 0 == "random" */
1068 si->selection_mode = which;
1069 si->demoing_p = False;
1074 XtRemoveTimeOut (si->cycle_id);
1076 cycle_timer ((XtPointer) si, 0);
1081 else if (type == XA_EXIT)
1083 /* Ignore EXIT message if the screen is locked. */
1084 if (until_idle_p || !si->locked_p)
1086 clientmessage_response (si, window, False,
1087 "EXIT ClientMessage received.",
1091 unblank_screen (si);
1092 kill_screenhack (si);
1093 XSync (si->dpy, False);
1095 saver_exit (si, 0, 0);
1098 clientmessage_response (si, window, True,
1099 "EXIT ClientMessage received while locked.",
1100 "screen is locked.");
1102 else if (type == XA_RESTART)
1104 /* The RESTART message works whether the screensaver is active or not,
1105 unless the screen is locked, in which case it doesn't work.
1107 if (until_idle_p || !si->locked_p)
1109 clientmessage_response (si, window, False,
1110 "RESTART ClientMessage received.",
1114 unblank_screen (si);
1115 kill_screenhack (si);
1116 XSync (si->dpy, False);
1119 /* make sure error message shows up before exit. */
1120 if (real_stderr && stderr != real_stderr)
1121 dup2 (fileno(real_stderr), fileno(stderr));
1123 restart_process (si);
1124 exit (1); /* shouldn't get here; but if restarting didn't work,
1125 make this command be the same as EXIT. */
1128 clientmessage_response (si, window, True,
1129 "RESTART ClientMessage received while locked.",
1130 "screen is locked.");
1132 else if (type == XA_DEMO)
1134 long arg = event->xclient.data.l[1];
1135 Bool demo_one_hack_p = (arg == 300);
1137 if (demo_one_hack_p)
1141 long which = event->xclient.data.l[2];
1144 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1145 sprintf (buf2, "demoing (%ld).", which);
1146 clientmessage_response (si, window, False, buf, buf2);
1148 if (which < 0) which = 0; /* 0 == "random" */
1149 si->selection_mode = which;
1150 si->demoing_p = True;
1155 clientmessage_response (si, window, True,
1156 "DEMO ClientMessage received while active.",
1161 clientmessage_response (si, window, True,
1162 "obsolete form of DEMO ClientMessage.",
1163 "obsolete form of DEMO ClientMessage.");
1166 else if (type == XA_PREFS)
1168 clientmessage_response (si, window, True,
1169 "the PREFS client-message is obsolete.",
1170 "the PREFS client-message is obsolete.");
1172 else if (type == XA_LOCK)
1175 clientmessage_response (si, window, True,
1176 "not compiled with support for locking.",
1177 "locking not enabled.");
1178 #else /* !NO_LOCKING */
1179 if (si->locking_disabled_p)
1180 clientmessage_response (si, window, True,
1181 "LOCK ClientMessage received, but locking is disabled.",
1182 "locking not enabled.");
1183 else if (si->locked_p)
1184 clientmessage_response (si, window, True,
1185 "LOCK ClientMessage received while already locked.",
1190 char *response = (until_idle_p
1191 ? "activating and locking."
1193 si->locked_p = True;
1194 si->selection_mode = 0;
1195 si->demoing_p = False;
1196 sprintf (buf, "LOCK ClientMessage received; %s", response);
1197 clientmessage_response (si, window, False, buf, response);
1199 if (si->lock_id) /* we're doing it now, so lose the timeout */
1201 XtRemoveTimeOut (si->lock_id);
1207 if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
1209 XForceScreenSaver (si->dpy, ScreenSaverActive);
1218 #endif /* !NO_LOCKING */
1224 str = (type ? XGetAtomName(si->dpy, type) : 0);
1228 if (strlen (str) > 80)
1229 strcpy (str+70, "...");
1230 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
1237 "unrecognised screensaver ClientMessage 0x%x received.",
1238 (unsigned int) event->xclient.data.l[0]);
1241 clientmessage_response (si, window, True, buf, buf);
1247 /* Some random diagnostics printed in -verbose mode.
1251 analyze_display (saver_info *si)
1254 static const char *exts[][2] = {
1255 { "SCREEN_SAVER", "SGI Screen-Saver" },
1256 { "SCREEN-SAVER", "SGI Screen-Saver" },
1257 { "MIT-SCREEN-SAVER", "MIT Screen-Saver" },
1258 { "XIDLE", "XIdle" },
1259 { "SGI-VIDEO-CONTROL", "SGI Video-Control" },
1260 { "READDISPLAY", "SGI Read-Display" },
1261 { "MIT-SHM", "Shared Memory" },
1262 { "DOUBLE-BUFFER", "Double-Buffering" },
1263 { "DPMS", "Power Management" },
1267 fprintf (stderr, "%s: running on display \"%s\"\n", blurb(),
1268 DisplayString(si->dpy));
1269 fprintf (stderr, "%s: vendor is %s, %d\n", blurb(),
1270 ServerVendor(si->dpy), VendorRelease(si->dpy));
1272 fprintf (stderr, "%s: useful extensions:\n", blurb());
1273 for (i = 0; i < countof(exts); i++)
1275 int op = 0, event = 0, error = 0;
1276 if (XQueryExtension (si->dpy, exts[i][0], &op, &event, &error))
1277 fprintf (stderr, "%s: %s\n", blurb(), exts[i][1]);
1280 for (i = 0; i < si->nscreens; i++)
1282 unsigned long colormapped_depths = 0;
1283 unsigned long non_mapped_depths = 0;
1284 XVisualInfo vi_in, *vi_out;
1287 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
1288 if (!vi_out) continue;
1289 for (j = 0; j < out_count; j++)
1290 if (vi_out[j].class == PseudoColor)
1291 colormapped_depths |= (1 << vi_out[j].depth);
1293 non_mapped_depths |= (1 << vi_out[j].depth);
1294 XFree ((char *) vi_out);
1296 if (colormapped_depths)
1298 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(), i);
1299 for (j = 0; j < 32; j++)
1300 if (colormapped_depths & (1 << j))
1301 fprintf (stderr, " %d", j);
1302 fprintf (stderr, "\n");
1304 if (non_mapped_depths)
1306 fprintf (stderr, "%s: screen %d non-mapped depths:", blurb(), i);
1307 for (j = 0; j < 32; j++)
1308 if (non_mapped_depths & (1 << j))
1309 fprintf (stderr, " %d", j);
1310 fprintf (stderr, "\n");