1 /* xscreensaver, Copyright (c) 1991-2003 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.
21 * We place a __SWM_VROOT property on this window, so that newly-started
22 * clients will think that this window is a "virtual root" window (as per
23 * the logic in the historical "vroot.h" header.)
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 * On multi-screen systems, we do the above on each screen, and start
42 * multiple programs, each with a different value of $DISPLAY.
44 * On Xinerama systems, we do a similar thing, but instead create multiple
45 * windows on the (only) display, and tell the subprocess which one to use
46 * via the $XSCREENSAVER_WINDOW environment variable -- this trick requires
47 * a recent (Aug 2003) revision of vroot.h.
49 * While we are waiting for user activity, we also set up timers so that,
50 * after a certain amount of time has passed, we can start a different
51 * screenhack. We do this by killing the running child process with
52 * SIGTERM, and then starting a new one in the same way.
54 * If there was a real virtual root, meaning that we removed the __SWM_VROOT
55 * property from it, meaning we must (absolutely must) restore it before we
56 * exit, then we set up signal handlers for most signals (SIGINT, SIGTERM,
57 * etc.) that do this. Most Xlib and Xt routines are not reentrant, so it
58 * is not generally safe to call them from signal handlers; however, this
59 * program spends most of its time waiting, so the window of opportunity
60 * when code could be called reentrantly is fairly small; and also, the worst
61 * that could happen is that the call would fail. If we've gotten one of
62 * these signals, then we're on our way out anyway. If we didn't restore the
63 * __SWM_VROOT property, that would be very bad, so it's worth a shot. Note
64 * that this means that, if you're using a virtual-root window manager, you
65 * can really fuck up the world by killing this process with "kill -9".
67 * This program accepts ClientMessages of type SCREENSAVER; these messages
68 * may contain the atoms ACTIVATE, DEACTIVATE, etc, meaning to turn the
69 * screensaver on or off now, regardless of the idleness of the user,
70 * and a few other things. The included "xscreensaver-command" program
71 * sends these messsages.
73 * If we don't have the XIdle, MIT-SCREEN-SAVER, or SGI SCREEN_SAVER
74 * extensions, then we do the XAutoLock trick: notice every window that
75 * gets created, and wait 30 seconds or so until its creating process has
76 * settled down, and then select KeyPress events on those windows which
77 * already select for KeyPress events. It's important that we not select
78 * KeyPress on windows which don't select them, because that would
79 * interfere with event propagation. This will break if any program
80 * changes its event mask to contain KeyRelease or PointerMotion more than
81 * 30 seconds after creating the window, but such programs do not seem to
82 * occur in nature (I've never seen it happen in all these years.)
84 * The reason that we can't select KeyPresses on windows that don't have
85 * them already is that, when dispatching a KeyPress event, X finds the
86 * lowest (leafmost) window in the hierarchy on which *any* client selects
87 * for KeyPress, and sends the event to that window. This means that if a
88 * client had a window with subwindows, and expected to receive KeyPress
89 * events on the parent window instead of the subwindows, then that client
90 * would malfunction if some other client selected KeyPress events on the
91 * subwindows. It is an incredible misdesign that one client can make
92 * another client malfunction in this way.
94 * To detect mouse motion, we periodically wake up and poll the mouse
95 * position and button/modifier state, and notice when something has
96 * changed. We make this check every five seconds by default, and since the
97 * screensaver timeout has a granularity of one minute, this makes the
98 * chance of a false positive very small. We could detect mouse motion in
99 * the same way as keyboard activity, but that would suffer from the same
100 * "client changing event mask" problem that the KeyPress events hack does.
101 * I think polling is more reliable.
103 * On systems with /proc/interrupts (Linux) we poll that file and note when
104 * the interrupt counter numbers on the "keyboard" and "PS/2" lines change.
105 * (There is no reliable way, using /proc/interrupts, to detect non-PS/2
106 * mice, so it doesn't help for serial or USB mice.)
108 * None of this crap happens if we're using one of the extensions. Sadly,
109 * the XIdle extension hasn't been available for many years; the SGI
110 * extension only exists on SGIs; and the MIT extension, while widely
111 * deployed, is garbage in several ways.
113 * A third idle-detection option could be implemented (but is not): when
114 * running on the console display ($DISPLAY is `localhost`:0) and we're on a
115 * machine where /dev/tty and /dev/mouse have reasonable last-modification
116 * times, we could just stat() those. But the incremental benefit of
117 * implementing this is really small, so forget I said anything.
120 * - Have a second terminal handy.
121 * - Be careful where you set your breakpoints, you don't want this to
122 * stop under the debugger with the keyboard grabbed or the blackout
124 * - If you run your debugger under XEmacs, try M-ESC (x-grab-keyboard)
125 * to keep your emacs window alive even when xscreensaver has grabbed.
126 * - Go read the code related to `debug_p'.
127 * - You probably can't set breakpoints in functions that are called on
128 * the other side of a call to fork() -- if your subprocesses are
129 * dying with signal 5, Trace/BPT Trap, you're losing in this way.
130 * - If you aren't using a server extension, don't leave this stopped
131 * under the debugger for very long, or the X input buffer will get
132 * huge because of the keypress events it's selecting for. This can
133 * make your X server wedge with "no more input buffers."
135 * ======================================================================== */
143 #include <X11/Xlib.h>
145 #include <X11/Xlibint.h>
147 #include <X11/Xatom.h>
148 #include <X11/Intrinsic.h>
149 #include <X11/StringDefs.h>
150 #include <X11/Shell.h>
153 #include <sys/time.h>
154 #include <netdb.h> /* for gethostbyname() */
157 # include <X11/Xmu/Error.h>
159 # include <Xmu/Error.h>
161 #else /* !HAVE_XMU */
163 #endif /* !HAVE_XMU */
165 #ifdef HAVE_XIDLE_EXTENSION
166 # include <X11/extensions/xidle.h>
167 #endif /* HAVE_XIDLE_EXTENSION */
170 # include <X11/extensions/Xinerama.h>
171 #endif /* HAVE_XINERAMA */
173 #include "xscreensaver.h"
175 #include "yarandom.h"
176 #include "resources.h"
180 saver_info *global_si_kludge = 0; /* I hate C so much... */
187 static Atom XA_SCREENSAVER_RESPONSE;
188 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
189 static Atom XA_RESTART, XA_SELECT;
190 static Atom XA_THROTTLE, XA_UNTHROTTLE;
191 Atom XA_DEMO, XA_PREFS, XA_EXIT, XA_LOCK, XA_BLANK;
194 static XrmOptionDescRec options [] = {
196 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
197 { "-silent", ".verbose", XrmoptionNoArg, "off" },
199 /* xscreensaver-demo uses this one */
200 { "-nosplash", ".splash", XrmoptionNoArg, "off" },
201 { "-no-splash", ".splash", XrmoptionNoArg, "off" },
203 /* useful for debugging */
204 { "-no-capture-stderr", ".captureStderr", XrmoptionNoArg, "off" },
206 /* There's really no reason to have these command-line args; they just
207 lead to confusion when the .xscreensaver file has conflicting values.
210 { "-splash", ".splash", XrmoptionNoArg, "on" },
211 { "-capture-stderr", ".captureStderr", XrmoptionNoArg, "on" },
212 { "-timeout", ".timeout", XrmoptionSepArg, 0 },
213 { "-cycle", ".cycle", XrmoptionSepArg, 0 },
214 { "-lock-mode", ".lock", XrmoptionNoArg, "on" },
215 { "-no-lock-mode", ".lock", XrmoptionNoArg, "off" },
216 { "-no-lock", ".lock", XrmoptionNoArg, "off" },
217 { "-lock-timeout", ".lockTimeout", XrmoptionSepArg, 0 },
218 { "-lock-vts", ".lockVTs", XrmoptionNoArg, "on" },
219 { "-no-lock-vts", ".lockVTs", XrmoptionNoArg, "off" },
220 { "-visual", ".visualID", XrmoptionSepArg, 0 },
221 { "-install", ".installColormap", XrmoptionNoArg, "on" },
222 { "-no-install", ".installColormap", XrmoptionNoArg, "off" },
223 { "-timestamp", ".timestamp", XrmoptionNoArg, "on" },
224 { "-xidle-extension", ".xidleExtension", XrmoptionNoArg, "on" },
225 { "-no-xidle-extension", ".xidleExtension", XrmoptionNoArg, "off" },
226 { "-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "on" },
227 { "-no-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "off" },
228 { "-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "on" },
229 { "-no-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "off" },
230 { "-proc-interrupts", ".procInterrupts", XrmoptionNoArg, "on" },
231 { "-no-proc-interrupts", ".procInterrupts", XrmoptionNoArg, "off" },
232 { "-idelay", ".initialDelay", XrmoptionSepArg, 0 },
233 { "-nice", ".nice", XrmoptionSepArg, 0 },
238 __extension__ /* shut up about "string length is greater than the length
239 ISO C89 compilers are required to support" when including
243 static char *defaults[] = {
244 #include "XScreenSaver_ad.h"
249 ERROR! You must not include vroot.h in this file.
253 do_help (saver_info *si)
258 xscreensaver %s, copyright (c) 1991-2004 by Jamie Zawinski <jwz@jwz.org>\n\
260 All xscreensaver configuration is via the `~/.xscreensaver' file.\n\
261 Rather than editing that file by hand, just run `xscreensaver-demo':\n\
262 that program lets you configure the screen saver graphically,\n\
263 including timeouts, locking, and display modes.\n\
267 Just getting started? Try this:\n\
272 For updates, online manual, and FAQ, please see the web page:\n\
274 http://www.jwz.org/xscreensaver/\n\
286 time_t now = time ((time_t *) 0);
287 char *str = (char *) ctime (&now);
288 char *nl = (char *) strchr (str, '\n');
289 if (nl) *nl = 0; /* take off that dang newline */
293 static Bool blurb_timestamp_p = False; /* kludge */
298 if (!blurb_timestamp_p)
302 static char buf[255];
303 char *ct = timestring();
304 int n = strlen(progname);
306 strncpy(buf, progname, n);
309 strncpy(buf+n, ct+11, 8);
310 strcpy(buf+n+9, ": ");
317 saver_ehandler (Display *dpy, XErrorEvent *error)
319 saver_info *si = global_si_kludge; /* I hate C so much... */
323 if (!real_stderr) real_stderr = stderr;
325 fprintf (real_stderr, "\n"
326 "#######################################"
327 "#######################################\n\n"
328 "%s: X Error! PLEASE REPORT THIS BUG.\n",
331 for (i = 0; i < si->nscreens; i++)
333 saver_screen_info *ssi = &si->screens[i];
334 fprintf (real_stderr, "%s: screen %d/%d: 0x%x, 0x%x, 0x%x\n",
335 blurb(), ssi->real_screen_number, ssi->number,
336 (unsigned int) RootWindowOfScreen (si->screens[i].screen),
337 (unsigned int) si->screens[i].real_vroot,
338 (unsigned int) si->screens[i].screensaver_window);
341 fprintf (real_stderr, "\n"
342 "#######################################"
343 "#######################################\n\n");
345 fatal_p = XmuPrintDefaultErrorMessage (dpy, error, real_stderr);
347 fatal_p = True; /* The only time I've ever seen a supposedly nonfatal error,
348 it has been BadImplementation / Xlib sequence lost, which
349 are in truth pretty damned fatal.
352 fprintf (real_stderr, "\n");
355 fprintf (real_stderr, "%s: nonfatal error.\n\n", blurb());
358 if (si->prefs.xsync_p)
360 saver_exit (si, -1, "because of synchronous X Error");
364 fprintf (real_stderr,
365 "#######################################"
366 "#######################################\n\n");
367 fprintf (real_stderr,
368 " If at all possible, please re-run xscreensaver with the command\n"
369 " line arguments `-sync -verbose -no-capture', and reproduce this\n"
370 " bug. That will cause xscreensaver to dump a `core' file to the\n"
371 " current directory. Please include the stack trace from that core\n"
372 " file in your bug report. *DO NOT* mail the core file itself!\n"
373 " That won't work.\n");
374 fprintf (real_stderr,
376 " http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
377 " the most useful bug reports, and how to examine core files.\n"
379 " The more information you can provide, the better. But please\n"
380 " report this bug, regardless!\n"
382 fprintf (real_stderr,
383 "#######################################"
384 "#######################################\n\n");
386 saver_exit (si, -1, 0);
394 /* This error handler is used only while the X connection is being set up;
395 after we've got a connection, we don't use this handler again. The only
396 reason for having this is so that we can present a more idiot-proof error
397 message than "cannot open display."
400 startup_ehandler (String name, String type, String class,
401 String defalt, /* one can't even spel properly
402 in this joke of a language */
403 String *av, Cardinal *ac)
407 saver_info *si = global_si_kludge; /* I hate C so much... */
408 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
410 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
411 fmt, sizeof(fmt)-1, *db);
413 fprintf (stderr, "%s: ", blurb());
415 memset (p, 0, sizeof(p));
416 if (*ac > countof (p)) *ac = countof (p);
417 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
418 fprintf (stderr, fmt, /* Did I mention that I hate C? */
419 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
420 fprintf (stderr, "\n");
422 describe_uids (si, stderr);
424 if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
426 fprintf (stderr, "\n"
427 "%s: This is probably because you're logging in as root. You\n"
428 " shouldn't log in as root: you should log in as a normal user,\n"
429 " and then `su' as needed. If you insist on logging in as\n"
430 " root, you will have to turn off X's security features before\n"
431 " xscreensaver will work.\n"
433 " Please read the manual and FAQ for more information:\n",
438 fprintf (stderr, "\n"
439 "%s: Errors at startup are usually authorization problems.\n"
440 " But you're not logging in as root (good!) so something\n"
441 " else must be wrong. Did you read the manual and the FAQ?\n",
445 fprintf (stderr, "\n"
446 " http://www.jwz.org/xscreensaver/faq.html\n"
447 " http://www.jwz.org/xscreensaver/man.html\n"
456 /* The zillions of initializations.
459 /* Set progname, version, etc. This is done very early.
462 set_version_string (saver_info *si, int *argc, char **argv)
464 progclass = "XScreenSaver";
466 /* progname is reset later, after we connect to X. */
467 progname = strrchr(argv[0], '/');
468 if (progname) progname++;
469 else progname = argv[0];
471 if (strlen(progname) > 100) /* keep it short. */
474 /* The X resource database blows up if argv[0] has a "." in it. */
477 while ((s = strchr (s, '.')))
481 si->version = (char *) malloc (5);
482 memcpy (si->version, screensaver_id + 17, 4);
487 /* Initializations that potentially take place as a priveleged user:
488 If the xscreensaver executable is setuid root, then these initializations
489 are run as root, before discarding privileges.
492 privileged_initialization (saver_info *si, int *argc, char **argv)
495 /* before hack_uid() for proper permissions */
496 lock_priv_init (*argc, argv, si->prefs.verbose_p);
497 #endif /* NO_LOCKING */
503 /* Figure out what locking mechanisms are supported.
506 lock_initialization (saver_info *si, int *argc, char **argv)
509 si->locking_disabled_p = True;
510 si->nolock_reason = "not compiled with locking support";
511 #else /* !NO_LOCKING */
513 /* Finish initializing locking, now that we're out of privileged code. */
514 if (! lock_init (*argc, argv, si->prefs.verbose_p))
516 si->locking_disabled_p = True;
517 si->nolock_reason = "error getting password";
520 /* If locking is currently enabled, but the environment indicates that
521 we have been launched as GDM's "Background" program, then disable
522 locking just in case.
524 if (!si->locking_disabled_p && getenv ("RUNNING_UNDER_GDM"))
526 si->locking_disabled_p = True;
527 si->nolock_reason = "running under GDM";
530 /* If the server is XDarwin (MacOS X) then disable locking.
531 (X grabs only affect X programs, so you can use Command-Tab
532 to bring any other Mac program to the front, e.g., Terminal.)
534 if (!si->locking_disabled_p)
536 int op = 0, event = 0, error = 0;
537 Bool macos_p = False;
540 /* Disable locking if *running* on Apple hardware, since we have no
541 reliable way to determine whether the server is running on MacOS.
542 Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
543 but I'm not really sure about that.
549 /* This extension exists on the Apple X11 server, but not
550 on earlier versions of the XDarwin server. */
551 macos_p = XQueryExtension (si->dpy, "Apple-DRI", &op, &event, &error);
555 si->locking_disabled_p = True;
556 si->nolock_reason = "Cannot lock securely on MacOS X";
560 #endif /* NO_LOCKING */
564 /* Open the connection to the X server, and intern our Atoms.
567 connect_to_server (saver_info *si, int *argc, char **argv)
569 Widget toplevel_shell;
572 char *d = getenv ("DISPLAY");
575 char *ndpy = strdup("DISPLAY=:0.0");
576 /* if (si->prefs.verbose_p) */ /* sigh, too early to test this... */
578 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
582 /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
583 glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
584 do not. So we must leak it (and/or the previous setting). Yay.
587 #endif /* HAVE_PUTENV */
589 XSetErrorHandler (saver_ehandler);
591 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
592 toplevel_shell = XtAppInitialize (&si->app, progclass,
593 options, XtNumber (options),
594 argc, argv, defaults, 0, 0);
595 XtAppSetErrorMsgHandler (si->app, 0);
597 si->dpy = XtDisplay (toplevel_shell);
598 si->prefs.db = XtDatabase (si->dpy);
599 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
601 if(strlen(progname) > 100) /* keep it short. */
604 db = si->prefs.db; /* resources.c needs this */
606 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
607 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
608 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
609 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
610 XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
611 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
613 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
614 XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False);
615 XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False);
616 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
617 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
618 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
619 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
620 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
621 XA_PREV = XInternAtom (si->dpy, "PREV", False);
622 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
623 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
624 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
625 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
626 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
627 XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
628 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
629 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
631 return toplevel_shell;
635 /* Handle the command-line arguments that were not handled for us by Xt.
636 Issue an error message and exit if there are unknown options.
639 process_command_line (saver_info *si, int *argc, char **argv)
642 for (i = 1; i < *argc; i++)
644 if (!strcmp (argv[i], "-debug"))
645 /* no resource for this one, out of paranoia. */
646 si->prefs.debug_p = True;
648 else if (!strcmp (argv[i], "-h") ||
649 !strcmp (argv[i], "-help") ||
650 !strcmp (argv[i], "--help"))
655 const char *s = argv[i];
656 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
659 if (s[0] == '-' && s[1] == '-') s++;
660 if (!strcmp (s, "-activate") ||
661 !strcmp (s, "-deactivate") ||
662 !strcmp (s, "-cycle") ||
663 !strcmp (s, "-next") ||
664 !strcmp (s, "-prev") ||
665 !strcmp (s, "-exit") ||
666 !strcmp (s, "-restart") ||
667 !strcmp (s, "-demo") ||
668 !strcmp (s, "-prefs") ||
669 !strcmp (s, "-preferences") ||
670 !strcmp (s, "-lock") ||
671 !strcmp (s, "-version") ||
672 !strcmp (s, "-time"))
675 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
676 fprintf (stderr, "\n\
677 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
679 fprintf (stderr, "\n\
680 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
683 The `xscreensaver' program is a daemon that runs in the background.\n\
684 You control a running xscreensaver process by sending it messages\n\
685 with `xscreensaver-demo' or `xscreensaver-command'.\n\
686 . See the man pages for details, or check the web page:\n\
687 http://www.jwz.org/xscreensaver/\n\n");
689 /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
690 suggest that explicitly. */
691 if (!strcmp (s, "-lock"))
693 Or perhaps you meant either the \"-lock-mode\" or the\n\
694 \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
703 /* Print out the xscreensaver banner to the tty if applicable;
704 Issue any other warnings that are called for at this point.
707 print_banner (saver_info *si)
709 saver_preferences *p = &si->prefs;
711 /* This resource gets set some time before the others, so that we know
712 whether to print the banner (and so that the banner gets printed before
713 any resource-database-related error messages.)
715 p->verbose_p = (p->debug_p || get_boolean_resource ("verbose", "Boolean"));
717 /* Ditto, for the locking_disabled_p message. */
718 p->lock_p = get_boolean_resource ("lock", "Boolean");
722 "%s %s, copyright (c) 1991-2004 "
723 "by Jamie Zawinski <jwz@jwz.org>.\n",
724 progname, si->version);
727 fprintf (stderr, "\n"
728 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
730 "\tNote that in debug mode, the xscreensaver window will only\n"
731 "\tcover the left half of the screen. (The idea is that you\n"
732 "\tcan still see debugging output in a shell, if you position\n"
733 "\tit on the right side of the screen.)\n"
735 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
736 "\tuntrusted environments.\n"
742 if (!si->uid_message || !*si->uid_message)
743 describe_uids (si, stderr);
746 if (si->orig_uid && *si->orig_uid)
747 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
748 blurb(), si->orig_uid);
749 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
752 fprintf (stderr, "%s: in process %lu.\n", blurb(),
753 (unsigned long) getpid());
758 print_lock_failure_banner (saver_info *si)
760 saver_preferences *p = &si->prefs;
762 /* If locking was not able to be initalized for some reason, explain why.
763 (This has to be done after we've read the lock_p resource.)
765 if (si->locking_disabled_p)
768 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
770 if (strstr (si->nolock_reason, "passw"))
771 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
772 "consult the manual.\n", blurb());
773 else if (strstr (si->nolock_reason, "running as "))
775 "%s: locking only works when xscreensaver is launched\n"
776 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
777 "\t See the manual for details.\n",
784 /* Examine all of the display's screens, and populate the `saver_screen_info'
785 structures. Make sure this is called after hack_environment() sets $PATH.
788 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
790 Bool found_any_writable_cells = False;
793 # ifdef HAVE_XINERAMA
796 si->xinerama_p = (XineramaQueryExtension (si->dpy, &event, &error) &&
797 XineramaIsActive (si->dpy));
800 if (si->xinerama_p && ScreenCount (si->dpy) != 1)
802 si->xinerama_p = False;
803 if (si->prefs.verbose_p)
805 "%s: Xinerama AND %d screens? Disabling Xinerama support!\n",
806 blurb(), ScreenCount(si->dpy));
811 XineramaScreenInfo *xsi = XineramaQueryScreens (si->dpy, &si->nscreens);
813 si->xinerama_p = False;
816 si->screens = (saver_screen_info *)
817 calloc(sizeof(saver_screen_info), si->nscreens);
818 for (i = 0; i < si->nscreens; i++)
820 si->screens[i].x = xsi[i].x_org;
821 si->screens[i].y = xsi[i].y_org;
822 si->screens[i].width = xsi[i].width;
823 si->screens[i].height = xsi[i].height;
827 si->default_screen = &si->screens[0];
828 si->default_screen->real_screen_p = True;
830 # endif /* !HAVE_XINERAMA */
834 si->nscreens = ScreenCount(si->dpy);
835 si->screens = (saver_screen_info *)
836 calloc(sizeof(saver_screen_info), si->nscreens);
837 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
839 for (i = 0; i < si->nscreens; i++)
841 saver_screen_info *ssi = &si->screens[i];
842 ssi->width = DisplayWidth (si->dpy, i);
843 ssi->height = DisplayHeight (si->dpy, i);
844 ssi->real_screen_p = True;
845 ssi->real_screen_number = i;
850 /* In "quad mode", we use the Xinerama code to pretend that there are 4
851 screens for every physical screen, and run four times as many hacks...
853 if (si->prefs.quad_p)
855 int ns2 = si->nscreens * 4;
856 saver_screen_info *ssi2 = (saver_screen_info *)
857 calloc(sizeof(saver_screen_info), ns2);
859 for (i = 0; i < si->nscreens; i++)
861 saver_screen_info *old = &si->screens[i];
863 if (si->prefs.debug_p) old->width = old->width / 2;
870 ssi2[i*4 ].width /= 2;
871 ssi2[i*4 ].height /= 2;
873 ssi2[i*4+1].x += ssi2[i*4 ].width;
874 ssi2[i*4+1].width -= ssi2[i*4 ].width;
875 ssi2[i*4+1].height /= 2;
877 ssi2[i*4+2].y += ssi2[i*4 ].height;
878 ssi2[i*4+2].width /= 2;
879 ssi2[i*4+2].height -= ssi2[i*4 ].height;
881 ssi2[i*4+3].x += ssi2[i*4+2].width;
882 ssi2[i*4+3].y += ssi2[i*4+2].height;
883 ssi2[i*4+3].width -= ssi2[i*4+2].width;
884 ssi2[i*4+3].height -= ssi2[i*4+2].height;
886 ssi2[i*4+1].real_screen_p = False;
887 ssi2[i*4+2].real_screen_p = False;
888 ssi2[i*4+3].real_screen_p = False;
894 si->default_screen = &si->screens[DefaultScreen(si->dpy) * 4];
895 si->xinerama_p = True;
898 /* finish initializing the screens.
900 for (i = 0; i < si->nscreens; i++)
902 saver_screen_info *ssi = &si->screens[i];
906 ssi->screen = ScreenOfDisplay (si->dpy, ssi->real_screen_number);
910 ssi->width = WidthOfScreen (ssi->screen);
911 ssi->height = HeightOfScreen (ssi->screen);
914 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
915 ssi->default_visual =
916 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
918 ssi->current_visual = ssi->default_visual;
919 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
921 /* Execute a subprocess to find the GL visual. */
922 ssi->best_gl_visual = get_best_gl_visual (ssi);
924 if (ssi == si->default_screen)
925 /* Since this is the default screen, use the one already created. */
926 ssi->toplevel_shell = toplevel_shell;
928 /* Otherwise, each screen must have its own unmapped root widget. */
929 ssi->toplevel_shell =
930 XtVaAppCreateShell (progname, progclass, applicationShellWidgetClass,
932 XtNscreen, ssi->screen,
933 XtNvisual, ssi->current_visual,
934 XtNdepth, visual_depth (ssi->screen,
935 ssi->current_visual),
938 if (! found_any_writable_cells)
940 /* Check to see whether fading is ever possible -- if any of the
941 screens on the display has a PseudoColor visual, then fading can
942 work (on at least some screens.) If no screen has a PseudoColor
943 visual, then don't bother ever trying to fade, because it will
944 just cause a delay without causing any visible effect.
946 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
947 get_visual (ssi->screen, "PseudoColor", True, False) ||
948 get_visual (ssi->screen, "GrayScale", True, False))
949 found_any_writable_cells = True;
953 si->fading_possible_p = found_any_writable_cells;
955 #ifdef HAVE_XF86VMODE_GAMMA
956 si->fading_possible_p = True; /* if we can gamma fade, go for it */
961 /* If any server extensions have been requested, try and initialize them.
962 Issue warnings if requests can't be honored.
965 initialize_server_extensions (saver_info *si)
967 saver_preferences *p = &si->prefs;
969 Bool server_has_xidle_extension_p = False;
970 Bool server_has_sgi_saver_extension_p = False;
971 Bool server_has_mit_saver_extension_p = False;
972 Bool system_has_proc_interrupts_p = False;
973 const char *piwhy = 0;
975 si->using_xidle_extension = p->use_xidle_extension;
976 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
977 si->using_mit_saver_extension = p->use_mit_saver_extension;
978 si->using_proc_interrupts = p->use_proc_interrupts;
980 #ifdef HAVE_XIDLE_EXTENSION
981 server_has_xidle_extension_p = query_xidle_extension (si);
983 #ifdef HAVE_SGI_SAVER_EXTENSION
984 server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
986 #ifdef HAVE_MIT_SAVER_EXTENSION
987 server_has_mit_saver_extension_p = query_mit_saver_extension (si);
989 #ifdef HAVE_PROC_INTERRUPTS
990 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
993 if (!server_has_xidle_extension_p)
994 si->using_xidle_extension = False;
995 else if (p->verbose_p)
997 if (si->using_xidle_extension)
998 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
1000 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
1003 if (!server_has_sgi_saver_extension_p)
1004 si->using_sgi_saver_extension = False;
1005 else if (p->verbose_p)
1007 if (si->using_sgi_saver_extension)
1008 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
1011 "%s: not using server's SGI SCREEN_SAVER extension.\n",
1015 if (!server_has_mit_saver_extension_p)
1016 si->using_mit_saver_extension = False;
1017 else if (p->verbose_p)
1019 if (si->using_mit_saver_extension)
1020 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
1024 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
1028 /* These are incompatible (or at least, our support for them is...) */
1029 if (si->xinerama_p && si->using_mit_saver_extension)
1031 si->using_mit_saver_extension = False;
1033 fprintf (stderr, "%s: Xinerama in use: disabling MIT-SCREEN-SAVER.\n",
1038 query_randr_extension (si);
1041 if (!system_has_proc_interrupts_p)
1043 si->using_proc_interrupts = False;
1044 if (p->verbose_p && piwhy)
1045 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
1048 else if (p->verbose_p)
1050 if (si->using_proc_interrupts)
1052 "%s: consulting /proc/interrupts for keyboard activity.\n",
1056 "%s: not consulting /proc/interrupts for keyboard activity.\n",
1062 /* For the case where we aren't using an server extensions, select user events
1063 on all the existing windows, and launch timers to select events on
1064 newly-created windows as well.
1066 If a server extension is being used, this does nothing.
1069 select_events (saver_info *si)
1071 saver_preferences *p = &si->prefs;
1074 if (si->using_xidle_extension ||
1075 si->using_mit_saver_extension ||
1076 si->using_sgi_saver_extension)
1079 if (p->initial_delay)
1083 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
1084 (int) p->initial_delay/1000,
1085 (p->initial_delay == 1000 ? "" : "s"));
1089 usleep (p->initial_delay);
1091 fprintf (stderr, " done.\n");
1096 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
1101 /* Select events on the root windows of every screen. This also selects
1102 for window creation events, so that new subwindows will be noticed.
1104 for (i = 0; i < si->nscreens; i++)
1106 saver_screen_info *ssi = &si->screens[i];
1107 if (ssi->real_screen_p)
1108 start_notice_events_timer (si,
1109 RootWindowOfScreen (si->screens[i].screen), False);
1113 fprintf (stderr, " done.\n");
1118 maybe_reload_init_file (saver_info *si)
1120 saver_preferences *p = &si->prefs;
1121 if (init_file_changed_p (p))
1124 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
1125 blurb(), init_file_name());
1129 /* If a server extension is in use, and p->timeout has changed,
1130 we need to inform the server of the new timeout. */
1131 disable_builtin_screensaver (si, False);
1133 /* If the DPMS settings in the init file have changed,
1134 change the settings on the server to match. */
1135 sync_server_dpms_settings (si->dpy,
1136 (p->dpms_enabled_p &&
1137 p->mode != DONT_BLANK),
1138 p->dpms_standby / 1000,
1139 p->dpms_suspend / 1000,
1148 - wait until the user is idle;
1150 - wait until the user is active;
1151 - unblank the screen;
1156 main_loop (saver_info *si)
1158 saver_preferences *p = &si->prefs;
1163 Bool was_locked = False;
1166 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1168 check_for_leaks ("unblanked A");
1169 sleep_until_idle (si, True);
1170 check_for_leaks ("unblanked B");
1175 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
1176 si->selection_mode, timestring());
1178 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
1182 maybe_reload_init_file (si);
1184 if (p->mode == DONT_BLANK)
1187 fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
1188 blurb(), timestring());
1190 /* Go around the loop and wait for the next bout of idleness,
1191 or for the init file to change, or for a remote command to
1192 come in, or something.
1197 if (! blank_screen (si))
1199 /* We were unable to grab either the keyboard or mouse.
1200 This means we did not (and must not) blank the screen.
1201 If we were to blank the screen while some other program
1202 is holding both the mouse and keyboard grabbed, then
1203 we would never be able to un-blank it! We would never
1204 see any events, and the display would be wedged.
1206 So, just go around the loop again and wait for the
1207 next bout of idleness.
1211 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
1216 kill_screenhack (si);
1218 if (!si->throttled_p)
1219 spawn_screenhack (si, True);
1220 else if (p->verbose_p)
1221 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
1223 /* Don't start the cycle timer in demo mode. */
1224 if (!si->demoing_p && p->cycle)
1225 si->cycle_id = XtAppAddTimeOut (si->app,
1227 /* see comment in cycle_timer() */
1235 /* Maybe start locking the screen.
1238 Time lock_timeout = p->lock_timeout;
1240 if (si->emergency_lock_p && p->lock_p && lock_timeout)
1242 int secs = p->lock_timeout / 1000;
1245 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1247 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1251 si->emergency_lock_p = False;
1253 if (!si->demoing_p && /* if not going into demo mode */
1254 p->lock_p && /* and locking is enabled */
1255 !si->locking_disabled_p && /* and locking is possible */
1256 lock_timeout == 0) /* and locking is not timer-deferred */
1257 set_locked_p (si, True); /* then lock right now. */
1259 /* locked_p might be true already because of the above, or because of
1260 the LOCK ClientMessage. But if not, and if we're supposed to lock
1261 after some time, set up a timer to do so.
1266 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1267 activate_lock_timer,
1270 #endif /* !NO_LOCKING */
1273 ok_to_unblank = True;
1276 check_for_leaks ("blanked A");
1277 sleep_until_idle (si, False); /* until not idle */
1278 check_for_leaks ("blanked B");
1280 maybe_reload_init_file (si);
1283 /* Maybe unlock the screen.
1287 saver_screen_info *ssi = si->default_screen;
1288 if (si->locking_disabled_p) abort ();
1291 si->dbox_up_p = True;
1292 suspend_screenhack (si, True);
1293 XUndefineCursor (si->dpy, ssi->screensaver_window);
1295 ok_to_unblank = unlock_p (si);
1297 si->dbox_up_p = False;
1298 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1299 suspend_screenhack (si, False); /* resume */
1301 if (!ok_to_unblank &&
1302 !screenhack_running_p (si))
1304 /* If the lock dialog has been dismissed and we're not about to
1305 unlock the screen, and there is currently no hack running,
1306 then launch one. (There might be no hack running if DPMS
1307 had kicked in. But DPMS is off now, so bring back the hack)
1310 XtRemoveTimeOut (si->cycle_id);
1312 cycle_timer ((XtPointer) si, 0);
1315 #endif /* !NO_LOCKING */
1317 } while (!ok_to_unblank);
1321 fprintf (stderr, "%s: unblanking screen at %s.\n",
1322 blurb(), timestring ());
1324 /* Kill before unblanking, to stop drawing as soon as possible. */
1325 kill_screenhack (si);
1326 unblank_screen (si);
1328 set_locked_p (si, False);
1329 si->emergency_lock_p = False;
1331 si->selection_mode = 0;
1333 /* If we're throttled, and the user has explicitly unlocked the screen,
1334 then unthrottle. If we weren't locked, then don't unthrottle
1335 automatically, because someone might have just bumped the desk... */
1338 if (si->throttled_p && p->verbose_p)
1339 fprintf (stderr, "%s: unthrottled.\n", blurb());
1340 si->throttled_p = False;
1345 XtRemoveTimeOut (si->cycle_id);
1351 XtRemoveTimeOut (si->lock_id);
1355 /* It's possible that a race condition could have led to the saver
1356 window being unexpectedly still mapped. This can happen like so:
1360 - that hack tries to grab a screen image (it does this by
1361 first unmapping the saver window, then remapping it.)
1362 - hack unmaps window
1364 - user becomes active
1365 - hack re-maps window (*)
1366 - driver kills subprocess
1367 - driver unmaps window (**)
1369 The race is that (*) might have been sent to the server before
1370 the client process was killed, but, due to scheduling randomness,
1371 might not have been received by the server until after (**).
1372 In other words, (*) and (**) might happen out of order, meaning
1373 the driver will unmap the window, and then after that, the
1374 recently-dead client will re-map it. This leaves the user
1375 locked out (it looks like a desktop, but it's not!)
1377 To avoid this: after un-blanking the screen, sleep for a second,
1378 and then really make sure the window is unmapped.
1380 Update: actually, let's do that once a second for 8 seconds,
1381 because sometimes the machine is slow, and we miss...
1385 for (j = 0; j < 8; j++)
1387 XSync (si->dpy, False);
1389 for (i = 0; i < si->nscreens; i++)
1391 saver_screen_info *ssi = &si->screens[i];
1392 Window w = ssi->screensaver_window;
1393 XWindowAttributes xgwa;
1394 XGetWindowAttributes (si->dpy, w, &xgwa);
1395 if (xgwa.map_state != IsUnmapped)
1399 "%s: %d: client race! emergency unmap 0x%lx.\n",
1400 blurb(), i, (unsigned long) w);
1401 XUnmapWindow (si->dpy, w);
1404 XSync (si->dpy, False);
1410 static void analyze_display (saver_info *si);
1411 static void fix_fds (void);
1414 main (int argc, char **argv)
1418 saver_info *si = &the_si;
1419 saver_preferences *p = &si->prefs;
1422 memset(si, 0, sizeof(*si));
1423 global_si_kludge = si; /* I hate C so much... */
1427 # undef ya_rand_init
1430 save_argv (argc, argv);
1431 set_version_string (si, &argc, argv);
1432 privileged_initialization (si, &argc, argv);
1433 hack_environment (si);
1435 shell = connect_to_server (si, &argc, argv);
1436 process_command_line (si, &argc, argv);
1439 load_init_file (p); /* must be before initialize_per_screen_info() */
1440 blurb_timestamp_p = p->timestamp_p; /* kludge */
1441 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1443 /* We can only issue this warning now. */
1444 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1446 "%s: there are no PseudoColor or GrayScale visuals.\n"
1447 "%s: ignoring the request for fading/unfading.\n",
1450 for (i = 0; i < si->nscreens; i++)
1452 saver_screen_info *ssi = &si->screens[i];
1453 if (ssi->real_screen_p)
1454 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1458 lock_initialization (si, &argc, argv);
1459 print_lock_failure_banner (si);
1461 if (p->xsync_p) XSynchronize (si->dpy, True);
1463 if (p->verbose_p) analyze_display (si);
1464 initialize_server_extensions (si);
1466 si->blank_time = time ((time_t) 0); /* must be before ..._window */
1467 initialize_screensaver_window (si);
1472 disable_builtin_screensaver (si, True);
1473 sync_server_dpms_settings (si->dpy,
1474 (p->dpms_enabled_p &&
1475 p->mode != DONT_BLANK),
1476 p->dpms_standby / 1000,
1477 p->dpms_suspend / 1000,
1481 initialize_stderr (si);
1482 handle_signals (si);
1484 make_splash_dialog (si);
1486 main_loop (si); /* doesn't return */
1493 /* Bad Things Happen if stdin, stdout, and stderr have been closed
1494 (as by the `sh incantation "xscreensaver >&- 2>&-"). When you do
1495 that, the X connection gets allocated to one of these fds, and
1496 then some random library writes to stderr, and random bits get
1497 stuffed down the X pipe, causing "Xlib: sequence lost" errors.
1498 So, we cause the first three file descriptors to be open to
1499 /dev/null if they aren't open to something else already. This
1500 must be done before any other files are opened (or the closing
1501 of that other file will again free up one of the "magic" first
1504 We do this by opening /dev/null three times, and then closing
1505 those fds, *unless* any of them got allocated as #0, #1, or #2,
1506 in which case we leave them open. Gag.
1508 Really, this crap is technically required of *every* X program,
1509 if you want it to be robust in the face of "2>&-".
1511 int fd0 = open ("/dev/null", O_RDWR);
1512 int fd1 = open ("/dev/null", O_RDWR);
1513 int fd2 = open ("/dev/null", O_RDWR);
1514 if (fd0 > 2) close (fd0);
1515 if (fd1 > 2) close (fd1);
1516 if (fd2 > 2) close (fd2);
1521 /* Processing ClientMessage events.
1525 static Bool error_handler_hit_p = False;
1528 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1530 error_handler_hit_p = True;
1534 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1535 them. We only look up the atom names for printing warning messages,
1536 so don't bomb out when it happens...
1539 XGetAtomName_safe (Display *dpy, Atom atom)
1542 XErrorHandler old_handler;
1543 if (!atom) return 0;
1546 error_handler_hit_p = False;
1547 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1548 result = XGetAtomName (dpy, atom);
1550 XSetErrorHandler (old_handler);
1552 if (error_handler_hit_p) result = 0;
1559 sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
1560 return strdup (buf);
1566 clientmessage_response (saver_info *si, Window w, Bool error,
1567 const char *stderr_msg,
1568 const char *protocol_msg)
1572 saver_preferences *p = &si->prefs;
1573 XErrorHandler old_handler;
1575 if (error || p->verbose_p)
1576 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1578 L = strlen(protocol_msg);
1579 proto = (char *) malloc (L + 2);
1580 proto[0] = (error ? '-' : '+');
1581 strcpy (proto+1, protocol_msg);
1584 /* Ignore all X errors while sending a response to a ClientMessage.
1585 Pretty much the only way we could get an error here is if the
1586 window we're trying to send the reply on has been deleted, in
1587 which case, the sender of the ClientMessage won't see our response
1590 XSync (si->dpy, False);
1591 error_handler_hit_p = False;
1592 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1594 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1595 PropModeReplace, (unsigned char *) proto, L);
1597 XSync (si->dpy, False);
1598 XSetErrorHandler (old_handler);
1599 XSync (si->dpy, False);
1606 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1608 char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1609 Window w = event->xclient.window;
1614 for (screen = 0; screen < si->nscreens; screen++)
1615 if (w == si->screens[screen].screensaver_window)
1617 strcpy (wdesc, "xscreensaver");
1620 else if (w == RootWindow (si->dpy, screen))
1622 strcpy (wdesc, "root");
1628 XErrorHandler old_handler;
1630 XWindowAttributes xgwa;
1631 memset (&hint, 0, sizeof(hint));
1632 memset (&xgwa, 0, sizeof(xgwa));
1634 XSync (si->dpy, False);
1635 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1636 XGetClassHint (si->dpy, w, &hint);
1637 XGetWindowAttributes (si->dpy, w, &xgwa);
1638 XSync (si->dpy, False);
1639 XSetErrorHandler (old_handler);
1640 XSync (si->dpy, False);
1642 screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1644 sprintf (wdesc, "%.20s / %.20s",
1645 (hint.res_name ? hint.res_name : "(null)"),
1646 (hint.res_class ? hint.res_class : "(null)"));
1647 if (hint.res_name) XFree (hint.res_name);
1648 if (hint.res_class) XFree (hint.res_class);
1651 fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1652 blurb(), screen, (str ? str : "(null)"));
1653 fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1654 blurb(), screen, (unsigned long) w, wdesc);
1655 if (str) XFree (str);
1659 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1661 saver_preferences *p = &si->prefs;
1663 Window window = event->xclient.window;
1665 /* Preferences might affect our handling of client messages. */
1666 maybe_reload_init_file (si);
1668 if (event->xclient.message_type != XA_SCREENSAVER ||
1669 event->xclient.format != 32)
1671 bogus_clientmessage_warning (si, event);
1675 type = event->xclient.data.l[0];
1676 if (type == XA_ACTIVATE)
1680 if (p->mode == DONT_BLANK)
1682 clientmessage_response(si, window, True,
1683 "ACTIVATE ClientMessage received in DONT_BLANK mode.",
1684 "screen blanking is currently disabled.");
1688 clientmessage_response(si, window, False,
1689 "ACTIVATE ClientMessage received.",
1691 si->selection_mode = 0;
1692 si->demoing_p = False;
1694 if (si->throttled_p && p->verbose_p)
1695 fprintf (stderr, "%s: unthrottled.\n", blurb());
1696 si->throttled_p = False;
1698 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1700 XForceScreenSaver (si->dpy, ScreenSaverActive);
1708 clientmessage_response(si, window, True,
1709 "ClientMessage ACTIVATE received while already active.",
1712 else if (type == XA_DEACTIVATE)
1716 if (si->throttled_p && p->verbose_p)
1717 fprintf (stderr, "%s: unthrottled.\n", blurb());
1718 si->throttled_p = False;
1720 clientmessage_response(si, window, False,
1721 "DEACTIVATE ClientMessage received.",
1723 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1725 XForceScreenSaver (si->dpy, ScreenSaverReset);
1733 clientmessage_response(si, window, False,
1734 "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
1735 "not active: idle timer reset.");
1738 else if (type == XA_CYCLE)
1742 clientmessage_response(si, window, False,
1743 "CYCLE ClientMessage received.",
1745 si->selection_mode = 0; /* 0 means randomize when its time. */
1746 si->demoing_p = False;
1748 if (si->throttled_p && p->verbose_p)
1749 fprintf (stderr, "%s: unthrottled.\n", blurb());
1750 si->throttled_p = False;
1753 XtRemoveTimeOut (si->cycle_id);
1755 cycle_timer ((XtPointer) si, 0);
1758 clientmessage_response(si, window, True,
1759 "ClientMessage CYCLE received while inactive.",
1762 else if (type == XA_NEXT || type == XA_PREV)
1764 clientmessage_response(si, window, False,
1766 ? "NEXT ClientMessage received."
1767 : "PREV ClientMessage received."),
1769 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1770 si->demoing_p = False;
1772 if (si->throttled_p && p->verbose_p)
1773 fprintf (stderr, "%s: unthrottled.\n", blurb());
1774 si->throttled_p = False;
1779 XtRemoveTimeOut (si->cycle_id);
1781 cycle_timer ((XtPointer) si, 0);
1786 else if (type == XA_SELECT)
1790 long which = event->xclient.data.l[1];
1792 if (p->mode == DONT_BLANK)
1794 clientmessage_response(si, window, True,
1795 "SELECT ClientMessage received in DONT_BLANK mode.",
1796 "screen blanking is currently disabled.");
1800 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1801 sprintf (buf2, "activating (%ld).", which);
1802 clientmessage_response (si, window, False, buf, buf2);
1804 if (which < 0) which = 0; /* 0 == "random" */
1805 si->selection_mode = which;
1806 si->demoing_p = False;
1808 if (si->throttled_p && p->verbose_p)
1809 fprintf (stderr, "%s: unthrottled.\n", blurb());
1810 si->throttled_p = False;
1815 XtRemoveTimeOut (si->cycle_id);
1817 cycle_timer ((XtPointer) si, 0);
1822 else if (type == XA_EXIT)
1824 /* Ignore EXIT message if the screen is locked. */
1825 if (until_idle_p || !si->locked_p)
1827 clientmessage_response (si, window, False,
1828 "EXIT ClientMessage received.",
1832 unblank_screen (si);
1833 kill_screenhack (si);
1834 XSync (si->dpy, False);
1836 saver_exit (si, 0, 0);
1839 clientmessage_response (si, window, True,
1840 "EXIT ClientMessage received while locked.",
1841 "screen is locked.");
1843 else if (type == XA_RESTART)
1845 /* The RESTART message works whether the screensaver is active or not,
1846 unless the screen is locked, in which case it doesn't work.
1848 if (until_idle_p || !si->locked_p)
1850 clientmessage_response (si, window, False,
1851 "RESTART ClientMessage received.",
1855 unblank_screen (si);
1856 kill_screenhack (si);
1857 XSync (si->dpy, False);
1860 restart_process (si); /* does not return */
1864 clientmessage_response (si, window, True,
1865 "RESTART ClientMessage received while locked.",
1866 "screen is locked.");
1868 else if (type == XA_DEMO)
1870 long arg = event->xclient.data.l[1];
1871 Bool demo_one_hack_p = (arg == 300);
1873 if (demo_one_hack_p)
1877 long which = event->xclient.data.l[2];
1880 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1881 sprintf (buf2, "demoing (%ld).", which);
1882 clientmessage_response (si, window, False, buf, buf2);
1884 if (which < 0) which = 0; /* 0 == "random" */
1885 si->selection_mode = which;
1886 si->demoing_p = True;
1888 if (si->throttled_p && p->verbose_p)
1889 fprintf (stderr, "%s: unthrottled.\n", blurb());
1890 si->throttled_p = False;
1895 clientmessage_response (si, window, True,
1896 "DEMO ClientMessage received while active.",
1901 clientmessage_response (si, window, True,
1902 "obsolete form of DEMO ClientMessage.",
1903 "obsolete form of DEMO ClientMessage.");
1906 else if (type == XA_PREFS)
1908 clientmessage_response (si, window, True,
1909 "the PREFS client-message is obsolete.",
1910 "the PREFS client-message is obsolete.");
1912 else if (type == XA_LOCK)
1915 clientmessage_response (si, window, True,
1916 "not compiled with support for locking.",
1917 "locking not enabled.");
1918 #else /* !NO_LOCKING */
1919 if (p->mode == DONT_BLANK)
1920 clientmessage_response(si, window, True,
1921 "LOCK ClientMessage received in DONT_BLANK mode.",
1922 "screen blanking is currently disabled.");
1923 else if (si->locking_disabled_p)
1924 clientmessage_response (si, window, True,
1925 "LOCK ClientMessage received, but locking is disabled.",
1926 "locking not enabled.");
1927 else if (si->locked_p)
1928 clientmessage_response (si, window, True,
1929 "LOCK ClientMessage received while already locked.",
1934 char *response = (until_idle_p
1935 ? "activating and locking."
1937 sprintf (buf, "LOCK ClientMessage received; %s", response);
1938 clientmessage_response (si, window, False, buf, response);
1939 set_locked_p (si, True);
1940 si->selection_mode = 0;
1941 si->demoing_p = False;
1943 if (si->lock_id) /* we're doing it now, so lose the timeout */
1945 XtRemoveTimeOut (si->lock_id);
1951 if (si->using_mit_saver_extension ||
1952 si->using_sgi_saver_extension)
1954 XForceScreenSaver (si->dpy, ScreenSaverActive);
1963 #endif /* !NO_LOCKING */
1965 else if (type == XA_THROTTLE)
1967 if (si->throttled_p)
1968 clientmessage_response (si, window, True,
1969 "THROTTLE ClientMessage received, but "
1970 "already throttled.",
1971 "already throttled.");
1975 char *response = "throttled.";
1976 si->throttled_p = True;
1977 si->selection_mode = 0;
1978 si->demoing_p = False;
1979 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
1980 clientmessage_response (si, window, False, buf, response);
1985 XtRemoveTimeOut (si->cycle_id);
1987 cycle_timer ((XtPointer) si, 0);
1991 else if (type == XA_UNTHROTTLE)
1993 if (! si->throttled_p)
1994 clientmessage_response (si, window, True,
1995 "UNTHROTTLE ClientMessage received, but "
2001 char *response = "unthrottled.";
2002 si->throttled_p = False;
2003 si->selection_mode = 0;
2004 si->demoing_p = False;
2005 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
2006 clientmessage_response (si, window, False, buf, response);
2011 XtRemoveTimeOut (si->cycle_id);
2013 cycle_timer ((XtPointer) si, 0);
2021 str = XGetAtomName_safe (si->dpy, type);
2025 if (strlen (str) > 80)
2026 strcpy (str+70, "...");
2027 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
2034 "unrecognised screensaver ClientMessage 0x%x received.",
2035 (unsigned int) event->xclient.data.l[0]);
2038 clientmessage_response (si, window, True, buf, buf);
2044 /* Some random diagnostics printed in -verbose mode.
2048 analyze_display (saver_info *si)
2052 const char *name; const char *desc; Bool useful_p;
2055 { "SCREEN_SAVER", /* underscore */ "SGI Screen-Saver",
2056 # ifdef HAVE_SGI_SAVER_EXTENSION
2061 }, { "SCREEN-SAVER", /* dash */ "SGI Screen-Saver",
2062 # ifdef HAVE_SGI_SAVER_EXTENSION
2067 }, { "MIT-SCREEN-SAVER", "MIT Screen-Saver",
2068 # ifdef HAVE_MIT_SAVER_EXTENSION
2073 }, { "XIDLE", "XIdle",
2074 # ifdef HAVE_XIDLE_EXTENSION
2079 }, { "SGI-VIDEO-CONTROL", "SGI Video-Control",
2080 # ifdef HAVE_SGI_VC_EXTENSION
2085 }, { "READDISPLAY", "SGI Read-Display",
2086 # ifdef HAVE_READ_DISPLAY_EXTENSION
2091 }, { "MIT-SHM", "Shared Memory",
2092 # ifdef HAVE_XSHM_EXTENSION
2097 }, { "DOUBLE-BUFFER", "Double-Buffering",
2098 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
2103 }, { "DPMS", "Power Management",
2104 # ifdef HAVE_DPMS_EXTENSION
2115 }, { "XFree86-VidModeExtension", "XF86 Video-Mode",
2116 # ifdef HAVE_XF86VMODE
2121 }, { "XINERAMA", "Xinerama",
2122 # ifdef HAVE_XINERAMA
2127 }, { "RANDR", "Resize-and-Rotate",
2133 }, { "Apple-DRI", "Apple-DRI (XDarwin)",
2138 fprintf (stderr, "%s: running on display \"%s\" (%d %sscreen%s).\n",
2140 DisplayString(si->dpy),
2142 (si->xinerama_p ? "Xinerama " : ""),
2143 (si->nscreens == 1 ? "" : "s"));
2144 fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
2145 ServerVendor(si->dpy), VendorRelease(si->dpy));
2147 fprintf (stderr, "%s: useful extensions:\n", blurb());
2148 for (i = 0; i < countof(exts); i++)
2150 int op = 0, event = 0, error = 0;
2153 if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
2155 sprintf (buf, "%s: ", blurb());
2157 strcat (buf, exts[i].desc);
2158 if (!exts[i].useful_p)
2161 while (strlen (buf) < k) strcat (buf, " ");
2162 strcat (buf, "<-- not supported at compile time!");
2164 fprintf (stderr, "%s\n", buf);
2167 for (i = 0; i < si->nscreens; i++)
2169 saver_screen_info *ssi = &si->screens[i];
2170 unsigned long colormapped_depths = 0;
2171 unsigned long non_mapped_depths = 0;
2172 XVisualInfo vi_in, *vi_out;
2175 if (!ssi->real_screen_p) continue;
2177 vi_in.screen = ssi->real_screen_number;
2178 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
2179 if (!vi_out) continue;
2180 for (j = 0; j < out_count; j++)
2181 if (vi_out[j].class == PseudoColor)
2182 colormapped_depths |= (1 << vi_out[j].depth);
2184 non_mapped_depths |= (1 << vi_out[j].depth);
2185 XFree ((char *) vi_out);
2187 if (colormapped_depths)
2189 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(),
2190 ssi->real_screen_number);
2191 for (j = 0; j < 32; j++)
2192 if (colormapped_depths & (1 << j))
2193 fprintf (stderr, " %d", j);
2194 fprintf (stderr, ".\n");
2196 if (non_mapped_depths)
2198 fprintf (stderr, "%s: screen %d non-colormapped depths:",
2199 blurb(), ssi->real_screen_number);
2200 for (j = 0; j < 32; j++)
2201 if (non_mapped_depths & (1 << j))
2202 fprintf (stderr, " %d", j);
2203 fprintf (stderr, ".\n");
2209 fprintf (stderr, "%s: Xinerama layout:\n", blurb());
2210 for (i = 0; i < si->nscreens; i++)
2212 saver_screen_info *ssi = &si->screens[i];
2213 fprintf (stderr, "%s: %c %d/%d: %dx%d+%d+%d\n",
2215 (ssi->real_screen_p ? '+' : ' '),
2216 ssi->number, ssi->real_screen_number,
2217 ssi->width, ssi->height, ssi->x, ssi->y);
2223 display_is_on_console_p (saver_info *si)
2225 Bool not_on_console = True;
2226 char *dpystr = DisplayString (si->dpy);
2227 char *tail = (char *) strchr (dpystr, ':');
2228 if (! tail || strncmp (tail, ":0", 2))
2229 not_on_console = True;
2232 char dpyname[255], localname[255];
2233 strncpy (dpyname, dpystr, tail-dpystr);
2234 dpyname [tail-dpystr] = 0;
2236 !strcmp(dpyname, "unix") ||
2237 !strcmp(dpyname, "localhost"))
2238 not_on_console = False;
2239 else if (gethostname (localname, sizeof (localname)))
2240 not_on_console = True; /* can't find hostname? */
2243 /* We have to call gethostbyname() on the result of gethostname()
2244 because the two aren't guarenteed to be the same name for the
2245 same host: on some losing systems, one is a FQDN and the other
2246 is not. Here in the wide wonderful world of Unix it's rocket
2247 science to obtain the local hostname in a portable fashion.
2249 And don't forget, gethostbyname() reuses the structure it
2250 returns, so we have to copy the fucker before calling it again.
2251 Thank you master, may I have another.
2253 struct hostent *h = gethostbyname (dpyname);
2255 not_on_console = True;
2260 strcpy (hn, h->h_name);
2261 l = gethostbyname (localname);
2262 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
2266 return !not_on_console;
2270 /* Do a little bit of heap introspection...
2273 check_for_leaks (const char *where)
2276 static unsigned long last_brk = 0;
2277 int b = (unsigned long) sbrk(0);
2278 if (last_brk && last_brk < b)
2279 fprintf (stderr, "%s: %s: brk grew by %luK.\n",
2281 (((b - last_brk) + 1023) / 1024));
2283 #endif /* HAVE_SBRK */