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>
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"
155 saver_info *global_si_kludge = 0; /* I hate C so much... */
162 static Atom XA_SCREENSAVER_RESPONSE;
163 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
164 static Atom XA_EXIT, XA_RESTART, XA_LOCK, XA_SELECT;
165 Atom XA_DEMO, XA_PREFS;
168 static XrmOptionDescRec options [] = {
169 { "-timeout", ".timeout", XrmoptionSepArg, 0 },
170 { "-cycle", ".cycle", XrmoptionSepArg, 0 },
171 { "-lock-mode", ".lock", XrmoptionNoArg, "on" },
172 { "-no-lock-mode", ".lock", XrmoptionNoArg, "off" },
173 { "-no-lock", ".lock", XrmoptionNoArg, "off" },
174 { "-lock-timeout", ".lockTimeout", XrmoptionSepArg, 0 },
175 { "-lock-vts", ".lockVTs", XrmoptionNoArg, "on" },
176 { "-no-lock-vts", ".lockVTs", XrmoptionNoArg, "off" },
177 { "-visual", ".visualID", XrmoptionSepArg, 0 },
178 { "-install", ".installColormap", XrmoptionNoArg, "on" },
179 { "-no-install", ".installColormap", XrmoptionNoArg, "off" },
180 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
181 { "-silent", ".verbose", XrmoptionNoArg, "off" },
182 { "-timestamp", ".timestamp", XrmoptionNoArg, "on" },
183 { "-capture-stderr", ".captureStderr", XrmoptionNoArg, "on" },
184 { "-no-capture-stderr", ".captureStderr", XrmoptionNoArg, "off" },
185 { "-xidle-extension", ".xidleExtension", XrmoptionNoArg, "on" },
186 { "-no-xidle-extension", ".xidleExtension", XrmoptionNoArg, "off" },
187 { "-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "on" },
188 { "-no-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "off" },
189 { "-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "on" },
190 { "-no-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "off" },
191 { "-splash", ".splash", XrmoptionNoArg, "on" },
192 { "-no-splash", ".splash", XrmoptionNoArg, "off" },
193 { "-nosplash", ".splash", XrmoptionNoArg, "off" },
194 { "-idelay", ".initialDelay", XrmoptionSepArg, 0 },
195 { "-nice", ".nice", XrmoptionSepArg, 0 },
197 /* Actually these are built in to Xt, but just to be sure... */
198 { "-synchronous", ".synchronous", XrmoptionNoArg, "on" },
199 { "-xrm", NULL, XrmoptionResArg, NULL }
202 static char *defaults[] = {
203 #include "XScreenSaver_ad.h"
208 ERROR! You must not include vroot.h in this file.
212 do_help (saver_info *si)
217 xscreensaver %s, copyright (c) 1991-1998 by Jamie Zawinski <jwz@jwz.org>\n\
218 The standard Xt command-line options are accepted; other options include:\n\
220 -timeout <minutes> When the screensaver should activate.\n\
221 -cycle <minutes> How long to let each hack run before switching.\n\
222 -lock-mode Require a password before deactivating.\n\
223 -lock-timeout <minutes> Grace period before locking; default 0.\n\
224 -visual <id-or-class> Which X visual to run on.\n\
225 -install Install a private colormap.\n\
227 -no-splash Don't display a splash-screen at startup.\n\
228 -help This message.\n\
230 See the manual for other options and X resources.\n\
232 The `xscreensaver' program should be left running in the background.\n\
233 Use the `xscreensaver-demo' and `xscreensaver-command' programs to\n\
234 manipulate a running xscreensaver.\n\
236 The `*programs' resource controls which graphics demos will be launched by\n\
237 the screensaver. See `man xscreensaver' or the web page for more details.\n\
239 Just getting started? Try this:\n\
244 For updates, check http://www.jwz.org/xscreensaver/\n\
256 time_t now = time ((time_t *) 0);
257 char *str = (char *) ctime (&now);
258 char *nl = (char *) strchr (str, '\n');
259 if (nl) *nl = 0; /* take off that dang newline */
263 static Bool blurb_timestamp_p = False; /* kludge */
268 if (!blurb_timestamp_p)
272 static char buf[255];
273 char *ct = timestring();
274 int n = strlen(progname);
276 strncpy(buf, progname, n);
279 strncpy(buf+n, ct+11, 8);
280 strcpy(buf+n+9, ": ");
287 saver_ehandler (Display *dpy, XErrorEvent *error)
289 saver_info *si = global_si_kludge; /* I hate C so much... */
291 if (!real_stderr) real_stderr = stderr;
293 fprintf (real_stderr, "\n"
294 "#######################################"
295 "#######################################\n\n"
296 "%s: X Error! PLEASE REPORT THIS BUG.\n\n"
297 "#######################################"
298 "#######################################\n\n",
300 if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
302 fprintf (real_stderr, "\n");
303 if (si->prefs.xsync_p)
305 saver_exit (si, -1, "because of synchronous X Error");
309 fprintf (real_stderr,
310 "#######################################"
311 "#######################################\n\n");
312 fprintf (real_stderr,
313 " If at all possible, please re-run xscreensaver with the command line\n"
314 " arguments `-sync -verbose', and reproduce this bug. That will cause\n"
315 " xscreensaver to dump a `core' file to the current directory. Please\n"
316 " include the stack trace from that core file in your bug report.\n"
318 " http://www.jwz.org/xscreensaver/bugs.html explains how to create the\n"
319 " most useful bug reports, and how to examine core files.\n"
321 " The more information you can provide, the better. But please report\n"
322 " report this bug, regardless!\n"
324 fprintf (real_stderr,
325 "#######################################"
326 "#######################################\n\n");
328 saver_exit (si, -1, 0);
332 fprintf (real_stderr, " (nonfatal.)\n");
337 /* This error handler is used only while the X connection is being set up;
338 after we've got a connection, we don't use this handler again. The only
339 reason for having this is so that we can present a more idiot-proof error
340 message than "cannot open display."
343 startup_ehandler (String name, String type, String class,
344 String defalt, /* one can't even spel properly
345 in this joke of a language */
346 String *av, Cardinal *ac)
350 saver_info *si = global_si_kludge; /* I hate C so much... */
351 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
353 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
354 fmt, sizeof(fmt)-1, *db);
356 fprintf (stderr, "%s: ", blurb());
358 memset (p, 0, sizeof(p));
359 if (*ac > countof (p)) *ac = countof (p);
360 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
361 fprintf (stderr, fmt, /* Did I mention that I hate C? */
362 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
363 fprintf (stderr, "\n");
365 describe_uids (si, stderr);
366 fprintf (stderr, "\n"
367 "%s: Errors at startup are usually authorization problems.\n"
368 " Did you read the manual? Specifically, the parts\n"
369 " that talk about XAUTH, XDM, and root logins?\n"
371 " http://www.jwz.org/xscreensaver/man.html\n"
381 /* The zillions of initializations.
384 /* Set progname, version, etc. This is done very early.
387 set_version_string (saver_info *si, int *argc, char **argv)
389 progclass = "XScreenSaver";
391 /* progname is reset later, after we connect to X. */
392 progname = strrchr(argv[0], '/');
393 if (progname) progname++;
394 else progname = argv[0];
396 if (strlen(progname) > 100) /* keep it short. */
399 /* The X resource database blows up if argv[0] has a "." in it. */
402 while ((s = strchr (s, '.')))
406 si->version = (char *) malloc (5);
407 memcpy (si->version, screensaver_id + 17, 4);
412 /* Initializations that potentially take place as a priveleged user:
413 If the xscreensaver executable is setuid root, then these initializations
414 are run as root, before discarding privileges.
417 privileged_initialization (saver_info *si, int *argc, char **argv)
420 si->locking_disabled_p = True;
421 si->nolock_reason = "not compiled with locking support";
422 #else /* !NO_LOCKING */
423 si->locking_disabled_p = False;
424 /* before hack_uid() for proper permissions */
425 if (! lock_init (*argc, argv, si->prefs.verbose_p))
427 si->locking_disabled_p = True;
428 si->nolock_reason = "error getting password";
430 #endif /* NO_LOCKING */
434 #endif /* NO_SETUID */
438 /* Open the connection to the X server, and intern our Atoms.
441 connect_to_server (saver_info *si, int *argc, char **argv)
443 Widget toplevel_shell;
445 XSetErrorHandler (saver_ehandler);
447 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
448 toplevel_shell = XtAppInitialize (&si->app, progclass,
449 options, XtNumber (options),
450 argc, argv, defaults, 0, 0);
451 XtAppSetErrorMsgHandler (si->app, 0);
453 si->dpy = XtDisplay (toplevel_shell);
454 si->prefs.db = XtDatabase (si->dpy);
455 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
457 if(strlen(progname) > 100) /* keep it short. */
460 db = si->prefs.db; /* resources.c needs this */
462 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
463 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
464 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
465 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
466 XA_SCREENSAVER_TIME = XInternAtom (si->dpy, "_SCREENSAVER_TIME", False);
467 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
469 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
470 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
471 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
472 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
473 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
474 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
475 XA_PREV = XInternAtom (si->dpy, "PREV", False);
476 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
477 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
478 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
479 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
480 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
482 return toplevel_shell;
486 /* Handle the command-line arguments that were not handled for us by Xt.
487 Issue an error message and exit if there are unknown options.
490 process_command_line (saver_info *si, int *argc, char **argv)
493 for (i = 1; i < *argc; i++)
495 if (!strcmp (argv[i], "-debug"))
496 /* no resource for this one, out of paranoia. */
497 si->prefs.debug_p = True;
499 else if (!strcmp (argv[i], "-h") ||
500 !strcmp (argv[i], "-help") ||
501 !strcmp (argv[i], "--help"))
506 const char *s = argv[i];
507 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
510 if (s[0] == '-' && s[1] == '-') s++;
511 if (!strcmp (s, "-activate") ||
512 !strcmp (s, "-deactivate") ||
513 !strcmp (s, "-cycle") ||
514 !strcmp (s, "-next") ||
515 !strcmp (s, "-prev") ||
516 !strcmp (s, "-exit") ||
517 !strcmp (s, "-restart") ||
518 !strcmp (s, "-demo") ||
519 !strcmp (s, "-prefs") ||
520 !strcmp (s, "-preferences") ||
521 !strcmp (s, "-lock") ||
522 !strcmp (s, "-version") ||
523 !strcmp (s, "-time"))
526 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
527 fprintf (stderr, "\n\
528 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
530 fprintf (stderr, "\n\
531 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
534 The `xscreensaver' program is a daemon that runs in the background.\n\
535 You control a running xscreensaver process by sending it messages\n\
536 with `xscreensaver-demo' or `xscreensaver-command'.\n\
537 . See the man pages for details, or check the web page:\n\
538 http://www.jwz.org/xscreensaver/\n\n");
540 /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
541 suggest that explicitly. */
542 if (!strcmp (s, "-lock"))
544 Or perhaps you meant either the \"-lock-mode\" or the\n\
545 \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
554 /* Print out the xscreensaver banner to the tty if applicable;
555 Issue any other warnings that are called for at this point.
558 print_banner (saver_info *si)
560 saver_preferences *p = &si->prefs;
562 /* This resource gets set some time before the others, so that we know
563 whether to print the banner (and so that the banner gets printed before
564 any resource-database-related error messages.)
566 p->verbose_p = (p->debug_p || get_boolean_resource ("verbose", "Boolean"));
568 /* Ditto, for the locking_disabled_p message. */
569 p->lock_p = get_boolean_resource ("lock", "Boolean");
573 "%s %s, copyright (c) 1991-1998 "
574 "by Jamie Zawinski <jwz@jwz.org>.\n",
575 progname, si->version);
578 fprintf (stderr, "\n"
579 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
581 "\tNote that in debug mode, the xscreensaver window will only\n"
582 "\tcover the left half of the screen. (The idea is that you\n"
583 "\tcan still see debugging output in a shell, if you position\n"
584 "\tit on the right side of the screen.)\n"
586 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
587 "\tuntrusted environments.\n"
593 if (!si->uid_message || !*si->uid_message)
594 describe_uids (si, stderr);
597 if (si->orig_uid && *si->orig_uid)
598 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
599 blurb(), si->orig_uid);
600 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
603 fprintf (stderr, "%s: in process %lu.\n", blurb(),
604 (unsigned long) getpid());
607 /* If locking was not able to be initalized for some reason, explain why.
608 (This has to be done after we've read the lock_p resource.)
610 if (p->lock_p && si->locking_disabled_p)
613 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
615 if (strstr (si->nolock_reason, "passw"))
616 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
617 "consult the manual.\n", blurb());
618 else if (strstr (si->nolock_reason, "running as "))
620 "%s: locking only works when xscreensaver is launched\n"
621 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
622 "\t See the manual for details.\n",
628 /* Examine all of the display's screens, and populate the `saver_screen_info'
632 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
634 Bool found_any_writable_cells = False;
637 si->nscreens = ScreenCount(si->dpy);
638 si->screens = (saver_screen_info *)
639 calloc(sizeof(saver_screen_info), si->nscreens);
641 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
643 for (i = 0; i < si->nscreens; i++)
645 saver_screen_info *ssi = &si->screens[i];
647 ssi->screen = ScreenOfDisplay (si->dpy, i);
649 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
650 ssi->default_visual =
651 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
653 ssi->current_visual = ssi->default_visual;
654 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
656 if (ssi == si->default_screen)
657 /* Since this is the default screen, use the one already created. */
658 ssi->toplevel_shell = toplevel_shell;
660 /* Otherwise, each screen must have its own unmapped root widget. */
661 ssi->toplevel_shell =
662 XtVaAppCreateShell (progname, progclass, applicationShellWidgetClass,
664 XtNscreen, ssi->screen,
665 XtNvisual, ssi->current_visual,
666 XtNdepth, visual_depth (ssi->screen,
667 ssi->current_visual),
670 if (! found_any_writable_cells)
672 /* Check to see whether fading is ever possible -- if any of the
673 screens on the display has a PseudoColor visual, then fading can
674 work (on at least some screens.) If no screen has a PseudoColor
675 visual, then don't bother ever trying to fade, because it will
676 just cause a delay without causing any visible effect.
678 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
679 get_visual (ssi->screen, "PseudoColor", True, False) ||
680 get_visual (ssi->screen, "GrayScale", True, False))
681 found_any_writable_cells = True;
685 si->prefs.fading_possible_p = found_any_writable_cells;
689 /* If any server extensions have been requested, try and initialize them.
690 Issue warnings if requests can't be honored.
693 initialize_server_extensions (saver_info *si)
695 saver_preferences *p = &si->prefs;
697 Bool server_has_xidle_extension_p = False;
698 Bool server_has_sgi_saver_extension_p = False;
699 Bool server_has_mit_saver_extension_p = False;
701 si->using_xidle_extension = p->use_xidle_extension;
702 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
703 si->using_mit_saver_extension = p->use_mit_saver_extension;
705 #ifdef HAVE_XIDLE_EXTENSION
706 server_has_xidle_extension_p = query_xidle_extension (si);
708 #ifdef HAVE_SGI_SAVER_EXTENSION
709 server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
711 #ifdef HAVE_MIT_SAVER_EXTENSION
712 server_has_mit_saver_extension_p = query_mit_saver_extension (si);
715 if (!server_has_xidle_extension_p)
716 si->using_xidle_extension = False;
717 else if (p->verbose_p)
719 if (si->using_xidle_extension)
720 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
722 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
725 if (!server_has_sgi_saver_extension_p)
726 si->using_sgi_saver_extension = False;
727 else if (p->verbose_p)
729 if (si->using_sgi_saver_extension)
730 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
733 "%s: not using server's SGI SCREEN_SAVER extension.\n",
737 if (!server_has_mit_saver_extension_p)
738 si->using_mit_saver_extension = False;
739 else if (p->verbose_p)
741 if (si->using_mit_saver_extension)
742 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
746 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
752 /* For the case where we aren't using an server extensions, select user events
753 on all the existing windows, and launch timers to select events on
754 newly-created windows as well.
756 If a server extension is being used, this does nothing.
759 select_events (saver_info *si)
761 saver_preferences *p = &si->prefs;
764 if (si->using_xidle_extension ||
765 si->using_mit_saver_extension ||
766 si->using_sgi_saver_extension)
769 if (p->initial_delay)
773 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
774 (int) p->initial_delay/1000,
775 (p->initial_delay == 1000 ? "" : "s"));
779 usleep (p->initial_delay);
781 fprintf (stderr, " done.\n");
786 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
791 /* Select events on the root windows of every screen. This also selects
792 for window creation events, so that new subwindows will be noticed.
794 for (i = 0; i < si->nscreens; i++)
795 start_notice_events_timer (si, RootWindowOfScreen (si->screens[i].screen));
798 fprintf (stderr, " done.\n");
803 maybe_reload_init_file (saver_info *si)
805 saver_preferences *p = &si->prefs;
806 if (init_file_changed_p (p))
809 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
810 blurb(), init_file_name());
814 /* If a server extension is in use, and p->timeout has changed,
815 we need to inform the server of the new timeout. */
816 disable_builtin_screensaver (si, False);
823 - wait until the user is idle;
825 - wait until the user is active;
826 - unblank the screen;
831 main_loop (saver_info *si)
833 saver_preferences *p = &si->prefs;
838 sleep_until_idle (si, True);
843 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
844 si->selection_mode, timestring());
847 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
851 maybe_reload_init_file (si);
854 kill_screenhack (si);
855 spawn_screenhack (si, True);
857 /* Don't start the cycle timer in demo mode. */
858 if (!si->demoing_p && p->cycle)
859 si->cycle_id = XtAppAddTimeOut (si->app, p->cycle, cycle_timer,
864 if (!si->demoing_p && /* if not going into demo mode */
865 p->lock_p && /* and locking is enabled */
866 !si->locking_disabled_p && /* and locking is possible */
867 p->lock_timeout == 0) /* and locking is not timer-deferred */
868 si->locked_p = True; /* then lock right now. */
870 /* locked_p might be true already because of the above, or because of
871 the LOCK ClientMessage. But if not, and if we're supposed to lock
872 after some time, set up a timer to do so.
877 si->lock_id = XtAppAddTimeOut (si->app, p->lock_timeout,
880 #endif /* !NO_LOCKING */
883 ok_to_unblank = True;
886 sleep_until_idle (si, False); /* until not idle */
887 maybe_reload_init_file (si);
892 saver_screen_info *ssi = si->default_screen;
893 if (si->locking_disabled_p) abort ();
895 si->dbox_up_p = True;
896 suspend_screenhack (si, True);
897 XUndefineCursor (si->dpy, ssi->screensaver_window);
899 ok_to_unblank = unlock_p (si);
901 si->dbox_up_p = False;
902 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
903 suspend_screenhack (si, False); /* resume */
905 #endif /* !NO_LOCKING */
907 } while (!ok_to_unblank);
911 fprintf (stderr, "%s: unblanking screen at %s.\n",
912 blurb(), timestring ());
914 /* Kill before unblanking, to stop drawing as soon as possible. */
915 kill_screenhack (si);
918 si->locked_p = False;
920 si->selection_mode = 0;
924 XtRemoveTimeOut (si->cycle_id);
930 XtRemoveTimeOut (si->lock_id);
935 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
939 static void analyze_display (saver_info *si);
942 main (int argc, char **argv)
946 saver_info *si = &the_si;
947 saver_preferences *p = &si->prefs;
950 memset(si, 0, sizeof(*si));
951 global_si_kludge = si; /* I hate C so much... */
953 srandom ((int) time ((time_t *) 0));
955 save_argv (argc, argv);
956 set_version_string (si, &argc, argv);
957 privileged_initialization (si, &argc, argv);
958 hack_environment (si);
960 shell = connect_to_server (si, &argc, argv);
961 process_command_line (si, &argc, argv);
964 initialize_per_screen_info (si, shell); /* also sets p->fading_possible_p */
966 for (i = 0; i < si->nscreens; i++)
967 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
972 if (p->xsync_p) XSynchronize (si->dpy, True);
973 blurb_timestamp_p = p->timestamp_p; /* kludge */
975 if (p->verbose_p) analyze_display (si);
976 initialize_server_extensions (si);
977 initialize_screensaver_window (si);
980 disable_builtin_screensaver (si, True);
981 initialize_stderr (si);
983 make_splash_dialog (si);
985 main_loop (si); /* doesn't return */
990 /* Processing ClientMessage events.
994 clientmessage_response (saver_info *si, Window w, Bool error,
995 const char *stderr_msg,
996 const char *protocol_msg)
1000 saver_preferences *p = &si->prefs;
1001 if (error || p->verbose_p)
1002 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1004 L = strlen(protocol_msg);
1005 proto = (char *) malloc (L + 2);
1006 proto[0] = (error ? '-' : '+');
1007 strcpy (proto+1, protocol_msg);
1010 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1011 PropModeReplace, proto, L);
1012 XSync (si->dpy, False);
1017 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1020 Window window = event->xclient.window;
1022 /* Preferences might affect our handling of client messages. */
1023 maybe_reload_init_file (si);
1025 if (event->xclient.message_type != XA_SCREENSAVER)
1028 str = XGetAtomName (si->dpy, event->xclient.message_type);
1029 fprintf (stderr, "%s: unrecognised ClientMessage type %s received\n",
1030 blurb(), (str ? str : "(null)"));
1031 if (str) XFree (str);
1034 if (event->xclient.format != 32)
1036 fprintf (stderr, "%s: ClientMessage of format %d received, not 32\n",
1037 blurb(), event->xclient.format);
1041 type = event->xclient.data.l[0];
1042 if (type == XA_ACTIVATE)
1046 clientmessage_response(si, window, False,
1047 "ACTIVATE ClientMessage received.",
1049 si->selection_mode = 0;
1050 si->demoing_p = False;
1051 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1053 XForceScreenSaver (si->dpy, ScreenSaverActive);
1061 clientmessage_response(si, window, True,
1062 "ClientMessage ACTIVATE received while already active.",
1065 else if (type == XA_DEACTIVATE)
1069 clientmessage_response(si, window, False,
1070 "DEACTIVATE ClientMessage received.",
1072 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1074 XForceScreenSaver (si->dpy, ScreenSaverReset);
1082 clientmessage_response(si, window, True,
1083 "ClientMessage DEACTIVATE received while inactive.",
1086 else if (type == XA_CYCLE)
1090 clientmessage_response(si, window, False,
1091 "CYCLE ClientMessage received.",
1093 si->selection_mode = 0; /* 0 means randomize when its time. */
1094 si->demoing_p = False;
1096 XtRemoveTimeOut (si->cycle_id);
1098 cycle_timer ((XtPointer) si, 0);
1101 clientmessage_response(si, window, True,
1102 "ClientMessage CYCLE received while inactive.",
1105 else if (type == XA_NEXT || type == XA_PREV)
1107 clientmessage_response(si, window, False,
1109 ? "NEXT ClientMessage received."
1110 : "PREV ClientMessage received."),
1112 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1113 si->demoing_p = False;
1118 XtRemoveTimeOut (si->cycle_id);
1120 cycle_timer ((XtPointer) si, 0);
1125 else if (type == XA_SELECT)
1129 long which = event->xclient.data.l[1];
1131 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1132 sprintf (buf2, "activating (%ld).", which);
1133 clientmessage_response (si, window, False, buf, buf2);
1135 if (which < 0) which = 0; /* 0 == "random" */
1136 si->selection_mode = which;
1137 si->demoing_p = False;
1142 XtRemoveTimeOut (si->cycle_id);
1144 cycle_timer ((XtPointer) si, 0);
1149 else if (type == XA_EXIT)
1151 /* Ignore EXIT message if the screen is locked. */
1152 if (until_idle_p || !si->locked_p)
1154 clientmessage_response (si, window, False,
1155 "EXIT ClientMessage received.",
1159 unblank_screen (si);
1160 kill_screenhack (si);
1161 XSync (si->dpy, False);
1163 saver_exit (si, 0, 0);
1166 clientmessage_response (si, window, True,
1167 "EXIT ClientMessage received while locked.",
1168 "screen is locked.");
1170 else if (type == XA_RESTART)
1172 /* The RESTART message works whether the screensaver is active or not,
1173 unless the screen is locked, in which case it doesn't work.
1175 if (until_idle_p || !si->locked_p)
1177 clientmessage_response (si, window, False,
1178 "RESTART ClientMessage received.",
1182 unblank_screen (si);
1183 kill_screenhack (si);
1184 XSync (si->dpy, False);
1187 /* make sure error message shows up before exit. */
1188 if (real_stderr && stderr != real_stderr)
1189 dup2 (fileno(real_stderr), fileno(stderr));
1191 restart_process (si);
1192 exit (1); /* shouldn't get here; but if restarting didn't work,
1193 make this command be the same as EXIT. */
1196 clientmessage_response (si, window, True,
1197 "RESTART ClientMessage received while locked.",
1198 "screen is locked.");
1200 else if (type == XA_DEMO)
1202 long arg = event->xclient.data.l[1];
1203 Bool demo_one_hack_p = (arg == 300);
1205 if (demo_one_hack_p)
1209 long which = event->xclient.data.l[2];
1212 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1213 sprintf (buf2, "demoing (%ld).", which);
1214 clientmessage_response (si, window, False, buf, buf2);
1216 if (which < 0) which = 0; /* 0 == "random" */
1217 si->selection_mode = which;
1218 si->demoing_p = True;
1223 clientmessage_response (si, window, True,
1224 "DEMO ClientMessage received while active.",
1229 clientmessage_response (si, window, True,
1230 "obsolete form of DEMO ClientMessage.",
1231 "obsolete form of DEMO ClientMessage.");
1234 else if (type == XA_PREFS)
1236 clientmessage_response (si, window, True,
1237 "the PREFS client-message is obsolete.",
1238 "the PREFS client-message is obsolete.");
1240 else if (type == XA_LOCK)
1243 clientmessage_response (si, window, True,
1244 "not compiled with support for locking.",
1245 "locking not enabled.");
1246 #else /* !NO_LOCKING */
1247 if (si->locking_disabled_p)
1248 clientmessage_response (si, window, True,
1249 "LOCK ClientMessage received, but locking is disabled.",
1250 "locking not enabled.");
1251 else if (si->locked_p)
1252 clientmessage_response (si, window, True,
1253 "LOCK ClientMessage received while already locked.",
1258 char *response = (until_idle_p
1259 ? "activating and locking."
1261 si->locked_p = True;
1262 si->selection_mode = 0;
1263 si->demoing_p = False;
1264 sprintf (buf, "LOCK ClientMessage received; %s", response);
1265 clientmessage_response (si, window, False, buf, response);
1267 if (si->lock_id) /* we're doing it now, so lose the timeout */
1269 XtRemoveTimeOut (si->lock_id);
1275 if (si->using_mit_saver_extension ||
1276 si->using_sgi_saver_extension)
1278 XForceScreenSaver (si->dpy, ScreenSaverActive);
1287 #endif /* !NO_LOCKING */
1293 str = (type ? XGetAtomName(si->dpy, type) : 0);
1297 if (strlen (str) > 80)
1298 strcpy (str+70, "...");
1299 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
1306 "unrecognised screensaver ClientMessage 0x%x received.",
1307 (unsigned int) event->xclient.data.l[0]);
1310 clientmessage_response (si, window, True, buf, buf);
1316 /* Some random diagnostics printed in -verbose mode.
1320 analyze_display (saver_info *si)
1323 static const char *exts[][2] = {
1324 { "SCREEN_SAVER", "SGI Screen-Saver" },
1325 { "SCREEN-SAVER", "SGI Screen-Saver" },
1326 { "MIT-SCREEN-SAVER", "MIT Screen-Saver" },
1327 { "XIDLE", "XIdle" },
1328 { "SGI-VIDEO-CONTROL", "SGI Video-Control" },
1329 { "READDISPLAY", "SGI Read-Display" },
1330 { "MIT-SHM", "Shared Memory" },
1331 { "DOUBLE-BUFFER", "Double-Buffering" },
1332 { "DPMS", "Power Management" },
1336 fprintf (stderr, "%s: running on display \"%s\"\n", blurb(),
1337 DisplayString(si->dpy));
1338 fprintf (stderr, "%s: vendor is %s, %d\n", blurb(),
1339 ServerVendor(si->dpy), VendorRelease(si->dpy));
1341 fprintf (stderr, "%s: useful extensions:\n", blurb());
1342 for (i = 0; i < countof(exts); i++)
1344 int op = 0, event = 0, error = 0;
1345 if (XQueryExtension (si->dpy, exts[i][0], &op, &event, &error))
1346 fprintf (stderr, "%s: %s\n", blurb(), exts[i][1]);
1349 for (i = 0; i < si->nscreens; i++)
1351 unsigned long colormapped_depths = 0;
1352 unsigned long non_mapped_depths = 0;
1353 XVisualInfo vi_in, *vi_out;
1356 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
1357 if (!vi_out) continue;
1358 for (j = 0; j < out_count; j++)
1359 if (vi_out[j].class == PseudoColor)
1360 colormapped_depths |= (1 << vi_out[j].depth);
1362 non_mapped_depths |= (1 << vi_out[j].depth);
1363 XFree ((char *) vi_out);
1365 if (colormapped_depths)
1367 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(), i);
1368 for (j = 0; j < 32; j++)
1369 if (colormapped_depths & (1 << j))
1370 fprintf (stderr, " %d", j);
1371 fprintf (stderr, "\n");
1373 if (non_mapped_depths)
1375 fprintf (stderr, "%s: screen %d non-mapped depths:", blurb(), i);
1376 for (j = 0; j < 32; j++)
1377 if (non_mapped_depths & (1 << j))
1378 fprintf (stderr, " %d", j);
1379 fprintf (stderr, "\n");