1 /* xscreensaver, Copyright (c) 1991-2011 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 * (See comments in screens.c for more details about Xinerama/RANDR stuff.)
51 * While we are waiting for user activity, we also set up timers so that,
52 * after a certain amount of time has passed, we can start a different
53 * screenhack. We do this by killing the running child process with
54 * SIGTERM, and then starting a new one in the same way.
56 * If there was a real virtual root, meaning that we removed the __SWM_VROOT
57 * property from it, meaning we must (absolutely must) restore it before we
58 * exit, then we set up signal handlers for most signals (SIGINT, SIGTERM,
59 * etc.) that do this. Most Xlib and Xt routines are not reentrant, so it
60 * is not generally safe to call them from signal handlers; however, this
61 * program spends most of its time waiting, so the window of opportunity
62 * when code could be called reentrantly is fairly small; and also, the worst
63 * that could happen is that the call would fail. If we've gotten one of
64 * these signals, then we're on our way out anyway. If we didn't restore the
65 * __SWM_VROOT property, that would be very bad, so it's worth a shot. Note
66 * that this means that, if you're using a virtual-root window manager, you
67 * can really fuck up the world by killing this process with "kill -9".
69 * This program accepts ClientMessages of type SCREENSAVER; these messages
70 * may contain the atoms ACTIVATE, DEACTIVATE, etc, meaning to turn the
71 * screensaver on or off now, regardless of the idleness of the user,
72 * and a few other things. The included "xscreensaver-command" program
73 * sends these messsages.
75 * If we don't have the XIdle, MIT-SCREEN-SAVER, or SGI SCREEN_SAVER
76 * extensions, then we do the XAutoLock trick: notice every window that
77 * gets created, and wait 30 seconds or so until its creating process has
78 * settled down, and then select KeyPress events on those windows which
79 * already select for KeyPress events. It's important that we not select
80 * KeyPress on windows which don't select them, because that would
81 * interfere with event propagation. This will break if any program
82 * changes its event mask to contain KeyRelease or PointerMotion more than
83 * 30 seconds after creating the window, but such programs do not seem to
84 * occur in nature (I've never seen it happen in all these years.)
86 * The reason that we can't select KeyPresses on windows that don't have
87 * them already is that, when dispatching a KeyPress event, X finds the
88 * lowest (leafmost) window in the hierarchy on which *any* client selects
89 * for KeyPress, and sends the event to that window. This means that if a
90 * client had a window with subwindows, and expected to receive KeyPress
91 * events on the parent window instead of the subwindows, then that client
92 * would malfunction if some other client selected KeyPress events on the
93 * subwindows. It is an incredible misdesign that one client can make
94 * another client malfunction in this way.
96 * To detect mouse motion, we periodically wake up and poll the mouse
97 * position and button/modifier state, and notice when something has
98 * changed. We make this check every five seconds by default, and since the
99 * screensaver timeout has a granularity of one minute, this makes the
100 * chance of a false positive very small. We could detect mouse motion in
101 * the same way as keyboard activity, but that would suffer from the same
102 * "client changing event mask" problem that the KeyPress events hack does.
103 * I think polling is more reliable.
105 * On systems with /proc/interrupts (Linux) we poll that file and note when
106 * the interrupt counter numbers on the "keyboard" and "PS/2" lines change.
107 * (There is no reliable way, using /proc/interrupts, to detect non-PS/2
108 * mice, so it doesn't help for serial or USB mice.)
110 * None of this crap happens if we're using one of the extensions. Sadly,
111 * the XIdle extension hasn't been available for many years; the SGI
112 * extension only exists on SGIs; and the MIT extension, while widely
113 * deployed, is garbage in several ways.
115 * A third idle-detection option could be implemented (but is not): when
116 * running on the console display ($DISPLAY is `localhost`:0) and we're on a
117 * machine where /dev/tty and /dev/mouse have reasonable last-modification
118 * times, we could just stat() those. But the incremental benefit of
119 * implementing this is really small, so forget I said anything.
122 * - Have a second terminal handy.
123 * - Be careful where you set your breakpoints, you don't want this to
124 * stop under the debugger with the keyboard grabbed or the blackout
126 * - If you run your debugger under XEmacs, try M-ESC (x-grab-keyboard)
127 * to keep your emacs window alive even when xscreensaver has grabbed.
128 * - Go read the code related to `debug_p'.
129 * - You probably can't set breakpoints in functions that are called on
130 * the other side of a call to fork() -- if your subprocesses are
131 * dying with signal 5, Trace/BPT Trap, you're losing in this way.
132 * - If you aren't using a server extension, don't leave this stopped
133 * under the debugger for very long, or the X input buffer will get
134 * huge because of the keypress events it's selecting for. This can
135 * make your X server wedge with "no more input buffers."
137 * ======================================================================== */
145 #include <X11/Xlib.h>
149 # include <libintl.h>
150 #endif /* ENABLE_NLS */
152 #include <X11/Xlibint.h>
154 #include <X11/Xatom.h>
155 #include <X11/Intrinsic.h>
156 #include <X11/StringDefs.h>
157 #include <X11/Shell.h>
160 #include <sys/time.h>
161 #include <netdb.h> /* for gethostbyname() */
162 #include <sys/types.h>
166 # include <X11/Xmu/Error.h>
168 # include <Xmu/Error.h>
170 #else /* !HAVE_XMU */
172 #endif /* !HAVE_XMU */
174 #ifdef HAVE_MIT_SAVER_EXTENSION
175 #include <X11/extensions/scrnsaver.h>
176 #endif /* HAVE_MIT_SAVER_EXTENSION */
178 #ifdef HAVE_XIDLE_EXTENSION
179 # include <X11/extensions/xidle.h>
180 #endif /* HAVE_XIDLE_EXTENSION */
182 #ifdef HAVE_SGI_VC_EXTENSION
183 # include <X11/extensions/XSGIvc.h>
184 #endif /* HAVE_SGI_VC_EXTENSION */
186 #ifdef HAVE_READ_DISPLAY_EXTENSION
187 # include <X11/extensions/readdisplay.h>
188 #endif /* HAVE_READ_DISPLAY_EXTENSION */
190 #ifdef HAVE_XSHM_EXTENSION
191 # include <X11/extensions/XShm.h>
192 #endif /* HAVE_XSHM_EXTENSION */
194 #ifdef HAVE_DPMS_EXTENSION
195 # include <X11/extensions/dpms.h>
196 #endif /* HAVE_DPMS_EXTENSION */
199 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
200 # include <X11/extensions/Xdbe.h>
201 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
203 #ifdef HAVE_XF86VMODE
204 # include <X11/extensions/xf86vmode.h>
205 #endif /* HAVE_XF86VMODE */
207 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
208 # include <X11/extensions/xf86misc.h>
209 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
212 # include <X11/extensions/Xinerama.h>
213 #endif /* HAVE_XINERAMA */
216 # include <X11/extensions/Xrandr.h>
217 #endif /* HAVE_RANDR */
220 #include "xscreensaver.h"
222 #include "yarandom.h"
223 #include "resources.h"
228 saver_info *global_si_kludge = 0; /* I hate C so much... */
235 static Atom XA_SCREENSAVER_RESPONSE;
236 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
237 static Atom XA_RESTART, XA_SELECT;
238 static Atom XA_THROTTLE, XA_UNTHROTTLE;
239 Atom XA_DEMO, XA_PREFS, XA_EXIT, XA_LOCK, XA_BLANK;
242 static XrmOptionDescRec options [] = {
244 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
245 { "-silent", ".verbose", XrmoptionNoArg, "off" },
247 /* xscreensaver-demo uses this one */
248 { "-nosplash", ".splash", XrmoptionNoArg, "off" },
249 { "-no-splash", ".splash", XrmoptionNoArg, "off" },
251 /* useful for debugging */
252 { "-no-capture-stderr", ".captureStderr", XrmoptionNoArg, "off" },
253 { "-log", ".logFile", XrmoptionSepArg, 0 },
257 __extension__ /* shut up about "string length is greater than the length
258 ISO C89 compilers are required to support" when including
262 static char *defaults[] = {
263 #include "XScreenSaver_ad.h"
268 ERROR! You must not include vroot.h in this file.
272 do_help (saver_info *si)
277 xscreensaver %s, copyright (c) 1991-2008 by Jamie Zawinski <jwz@jwz.org>\n\
279 All xscreensaver configuration is via the `~/.xscreensaver' file.\n\
280 Rather than editing that file by hand, just run `xscreensaver-demo':\n\
281 that program lets you configure the screen saver graphically,\n\
282 including timeouts, locking, and display modes.\n\
286 Just getting started? Try this:\n\
291 For updates, online manual, and FAQ, please see the web page:\n\
293 http://www.jwz.org/xscreensaver/\n\
305 time_t now = time ((time_t *) 0);
306 char *str = (char *) ctime (&now);
307 char *nl = (char *) strchr (str, '\n');
308 if (nl) *nl = 0; /* take off that dang newline */
312 static Bool blurb_timestamp_p = True; /* kludge */
317 if (!blurb_timestamp_p)
321 static char buf[255];
322 char *ct = timestring();
323 int n = strlen(progname);
325 strncpy(buf, progname, n);
328 strncpy(buf+n, ct+11, 8);
329 strcpy(buf+n+9, ": ");
336 saver_ehandler (Display *dpy, XErrorEvent *error)
338 saver_info *si = global_si_kludge; /* I hate C so much... */
342 if (!real_stderr) real_stderr = stderr;
344 fprintf (real_stderr, "\n"
345 "#######################################"
346 "#######################################\n\n"
347 "%s: X Error! PLEASE REPORT THIS BUG.\n",
350 for (i = 0; i < si->nscreens; i++)
352 saver_screen_info *ssi = &si->screens[i];
353 fprintf (real_stderr, "%s: screen %d/%d: 0x%x, 0x%x, 0x%x\n",
354 blurb(), ssi->real_screen_number, ssi->number,
355 (unsigned int) RootWindowOfScreen (si->screens[i].screen),
356 (unsigned int) si->screens[i].real_vroot,
357 (unsigned int) si->screens[i].screensaver_window);
360 fprintf (real_stderr, "\n"
361 "#######################################"
362 "#######################################\n\n");
364 fatal_p = XmuPrintDefaultErrorMessage (dpy, error, real_stderr);
366 fatal_p = True; /* The only time I've ever seen a supposedly nonfatal error,
367 it has been BadImplementation / Xlib sequence lost, which
368 are in truth pretty damned fatal.
371 fprintf (real_stderr, "\n");
374 fprintf (real_stderr, "%s: nonfatal error.\n\n", blurb());
377 if (si->prefs.xsync_p)
379 saver_exit (si, -1, "because of synchronous X Error");
384 __extension__ /* don't warn about "string length is greater than the
385 length ISO C89 compilers are required to support". */
387 fprintf (real_stderr,
388 "#######################################################################\n"
390 " If at all possible, please re-run xscreensaver with the command\n"
391 " line arguments `-sync -verbose -log log.txt', and reproduce this\n"
392 " bug. That will cause xscreensaver to dump a `core' file to the\n"
393 " current directory. Please include the stack trace from that core\n"
394 " file in your bug report. *DO NOT* mail the core file itself! That\n"
395 " won't work. A \"log.txt\" file will also be written. Please *do*\n"
396 " include the complete \"log.txt\" file with your bug report.\n"
398 " http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
399 " the most useful bug reports, and how to examine core files.\n"
401 " The more information you can provide, the better. But please\n"
402 " report this bug, regardless!\n"
404 "#######################################################################\n"
408 saver_exit (si, -1, 0);
416 /* This error handler is used only while the X connection is being set up;
417 after we've got a connection, we don't use this handler again. The only
418 reason for having this is so that we can present a more idiot-proof error
419 message than "cannot open display."
422 startup_ehandler (String name, String type, String class,
423 String defalt, /* one can't even spel properly
424 in this joke of a language */
425 String *av, Cardinal *ac)
429 saver_info *si = global_si_kludge; /* I hate C so much... */
430 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
432 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
433 fmt, sizeof(fmt)-1, *db);
435 fprintf (stderr, "%s: ", blurb());
437 memset (p, 0, sizeof(p));
438 if (*ac > countof (p)) *ac = countof (p);
439 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
440 fprintf (stderr, fmt, /* Did I mention that I hate C? */
441 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
442 fprintf (stderr, "\n");
444 describe_uids (si, stderr);
446 if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
448 fprintf (stderr, "\n"
449 "%s: This is probably because you're logging in as root. You\n"
450 " shouldn't log in as root: you should log in as a normal user,\n"
451 " and then `su' as needed. If you insist on logging in as\n"
452 " root, you will have to turn off X's security features before\n"
453 " xscreensaver will work.\n"
455 " Please read the manual and FAQ for more information:\n",
460 fprintf (stderr, "\n"
461 "%s: Errors at startup are usually authorization problems.\n"
462 " But you're not logging in as root (good!) so something\n"
463 " else must be wrong. Did you read the manual and the FAQ?\n",
467 fprintf (stderr, "\n"
468 " http://www.jwz.org/xscreensaver/faq.html\n"
469 " http://www.jwz.org/xscreensaver/man.html\n"
478 /* The zillions of initializations.
481 /* Set progname, version, etc. This is done very early.
484 set_version_string (saver_info *si, int *argc, char **argv)
486 progclass = "XScreenSaver";
488 /* progname is reset later, after we connect to X. */
489 progname = strrchr(argv[0], '/');
490 if (progname) progname++;
491 else progname = argv[0];
493 if (strlen(progname) > 100) /* keep it short. */
496 /* The X resource database blows up if argv[0] has a "." in it. */
499 while ((s = strchr (s, '.')))
503 si->version = (char *) malloc (5);
504 memcpy (si->version, screensaver_id + 17, 4);
509 /* Initializations that potentially take place as a priveleged user:
510 If the xscreensaver executable is setuid root, then these initializations
511 are run as root, before discarding privileges.
514 privileged_initialization (saver_info *si, int *argc, char **argv)
517 /* before hack_uid() for proper permissions */
518 lock_priv_init (*argc, argv, si->prefs.verbose_p);
519 #endif /* NO_LOCKING */
525 /* Figure out what locking mechanisms are supported.
528 lock_initialization (saver_info *si, int *argc, char **argv)
531 si->locking_disabled_p = True;
532 si->nolock_reason = "not compiled with locking support";
533 #else /* !NO_LOCKING */
535 /* Finish initializing locking, now that we're out of privileged code. */
536 if (! lock_init (*argc, argv, si->prefs.verbose_p))
538 si->locking_disabled_p = True;
539 si->nolock_reason = "error getting password";
542 /* If locking is currently enabled, but the environment indicates that
543 we have been launched as GDM's "Background" program, then disable
544 locking just in case.
546 if (!si->locking_disabled_p && getenv ("RUNNING_UNDER_GDM"))
548 si->locking_disabled_p = True;
549 si->nolock_reason = "running under GDM";
552 /* If the server is XDarwin (MacOS X) then disable locking.
553 (X grabs only affect X programs, so you can use Command-Tab
554 to bring any other Mac program to the front, e.g., Terminal.)
556 if (!si->locking_disabled_p)
558 int op = 0, event = 0, error = 0;
559 Bool macos_p = False;
562 /* Disable locking if *running* on Apple hardware, since we have no
563 reliable way to determine whether the server is running on MacOS.
564 Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
565 but I'm not really sure about that.
571 /* This extension exists on the Apple X11 server, but not
572 on earlier versions of the XDarwin server. */
573 macos_p = XQueryExtension (si->dpy, "Apple-DRI", &op, &event, &error);
577 si->locking_disabled_p = True;
578 si->nolock_reason = "Cannot lock securely on MacOS X";
582 if (si->prefs.debug_p) /* But allow locking anyway in debug mode. */
583 si->locking_disabled_p = False;
585 #endif /* NO_LOCKING */
589 /* Open the connection to the X server, and intern our Atoms.
592 connect_to_server (saver_info *si, int *argc, char **argv)
594 Widget toplevel_shell;
597 char *d = getenv ("DISPLAY");
600 char *ndpy = strdup("DISPLAY=:0.0");
601 /* if (si->prefs.verbose_p) */ /* sigh, too early to test this... */
603 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
607 /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
608 glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
609 do not. So we must leak it (and/or the previous setting). Yay.
612 #endif /* HAVE_PUTENV */
614 XSetErrorHandler (saver_ehandler);
616 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
617 toplevel_shell = XtAppInitialize (&si->app, progclass,
618 options, XtNumber (options),
619 argc, argv, defaults, 0, 0);
620 XtAppSetErrorMsgHandler (si->app, 0);
622 si->dpy = XtDisplay (toplevel_shell);
623 si->prefs.db = XtDatabase (si->dpy);
624 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
626 if(strlen(progname) > 100) /* keep it short. */
629 db = si->prefs.db; /* resources.c needs this */
631 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
632 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
633 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
634 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
635 XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
636 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
638 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
639 XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False);
640 XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False);
641 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
642 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
643 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
644 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
645 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
646 XA_PREV = XInternAtom (si->dpy, "PREV", False);
647 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
648 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
649 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
650 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
651 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
652 XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
653 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
654 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
656 return toplevel_shell;
660 /* Handle the command-line arguments that were not handled for us by Xt.
661 Issue an error message and exit if there are unknown options.
664 process_command_line (saver_info *si, int *argc, char **argv)
667 for (i = 1; i < *argc; i++)
669 if (!strcmp (argv[i], "-debug"))
670 /* no resource for this one, out of paranoia. */
671 si->prefs.debug_p = True;
673 else if (!strcmp (argv[i], "-h") ||
674 !strcmp (argv[i], "-help") ||
675 !strcmp (argv[i], "--help"))
680 const char *s = argv[i];
681 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
684 if (s[0] == '-' && s[1] == '-') s++;
685 if (!strcmp (s, "-activate") ||
686 !strcmp (s, "-deactivate") ||
687 !strcmp (s, "-cycle") ||
688 !strcmp (s, "-next") ||
689 !strcmp (s, "-prev") ||
690 !strcmp (s, "-exit") ||
691 !strcmp (s, "-restart") ||
692 !strcmp (s, "-demo") ||
693 !strcmp (s, "-prefs") ||
694 !strcmp (s, "-preferences") ||
695 !strcmp (s, "-lock") ||
696 !strcmp (s, "-version") ||
697 !strcmp (s, "-time"))
700 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
701 fprintf (stderr, "\n\
702 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
704 fprintf (stderr, "\n\
705 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
708 The `xscreensaver' program is a daemon that runs in the background.\n\
709 You control a running xscreensaver process by sending it messages\n\
710 with `xscreensaver-demo' or `xscreensaver-command'.\n\
711 . See the man pages for details, or check the web page:\n\
712 http://www.jwz.org/xscreensaver/\n\n");
721 /* Print out the xscreensaver banner to the tty if applicable;
722 Issue any other warnings that are called for at this point.
725 print_banner (saver_info *si)
727 saver_preferences *p = &si->prefs;
729 /* This resource gets set some time before the others, so that we know
730 whether to print the banner (and so that the banner gets printed before
731 any resource-database-related error messages.)
733 p->verbose_p = (p->debug_p ||
734 get_boolean_resource (si->dpy, "verbose", "Boolean"));
736 /* Ditto, for the locking_disabled_p message. */
737 p->lock_p = get_boolean_resource (si->dpy, "lock", "Boolean");
741 "%s %s, copyright (c) 1991-2008 "
742 "by Jamie Zawinski <jwz@jwz.org>.\n",
743 progname, si->version);
746 fprintf (stderr, "\n"
747 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
749 "\tNote that in debug mode, the xscreensaver window will only\n"
750 "\tcover the left half of the screen. (The idea is that you\n"
751 "\tcan still see debugging output in a shell, if you position\n"
752 "\tit on the right side of the screen.)\n"
754 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
755 "\tuntrusted environments.\n"
761 if (!si->uid_message || !*si->uid_message)
762 describe_uids (si, stderr);
765 if (si->orig_uid && *si->orig_uid)
766 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
767 blurb(), si->orig_uid);
768 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
771 fprintf (stderr, "%s: in process %lu.\n", blurb(),
772 (unsigned long) getpid());
777 print_lock_failure_banner (saver_info *si)
779 saver_preferences *p = &si->prefs;
781 /* If locking was not able to be initalized for some reason, explain why.
782 (This has to be done after we've read the lock_p resource.)
784 if (si->locking_disabled_p)
787 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
789 if (strstr (si->nolock_reason, "passw"))
790 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
791 "consult the manual.\n", blurb());
792 else if (strstr (si->nolock_reason, "running as "))
794 "%s: locking only works when xscreensaver is launched\n"
795 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
796 "\t See the manual for details.\n",
803 /* called from screens.c so that all the Xt crud is here. */
805 initialize_screen_root_widget (saver_screen_info *ssi)
807 saver_info *si = ssi->global;
808 if (ssi->toplevel_shell)
809 XtDestroyWidget (ssi->toplevel_shell);
810 ssi->toplevel_shell =
811 XtVaAppCreateShell (progname, progclass,
812 applicationShellWidgetClass,
814 XtNscreen, ssi->screen,
815 XtNvisual, ssi->current_visual,
816 XtNdepth, visual_depth (ssi->screen,
817 ssi->current_visual),
822 /* Examine all of the display's screens, and populate the `saver_screen_info'
823 structures. Make sure this is called after hack_environment() sets $PATH.
826 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
830 update_screen_layout (si);
832 /* Check to see whether fading is ever possible -- if any of the
833 screens on the display has a PseudoColor visual, then fading can
834 work (on at least some screens.) If no screen has a PseudoColor
835 visual, then don't bother ever trying to fade, because it will
836 just cause a delay without causing any visible effect.
838 for (i = 0; i < si->nscreens; i++)
840 saver_screen_info *ssi = &si->screens[i];
841 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
842 get_visual (ssi->screen, "PseudoColor", True, False) ||
843 get_visual (ssi->screen, "GrayScale", True, False))
845 si->fading_possible_p = True;
850 #ifdef HAVE_XF86VMODE_GAMMA
851 si->fading_possible_p = True; /* if we can gamma fade, go for it */
856 /* If any server extensions have been requested, try and initialize them.
857 Issue warnings if requests can't be honored.
860 initialize_server_extensions (saver_info *si)
862 saver_preferences *p = &si->prefs;
864 Bool server_has_xidle_extension_p = False;
865 Bool server_has_sgi_saver_extension_p = False;
866 Bool server_has_mit_saver_extension_p = False;
867 Bool system_has_proc_interrupts_p = False;
868 Bool server_has_xinput_extension_p = False;
869 const char *piwhy = 0;
871 si->using_xidle_extension = p->use_xidle_extension;
872 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
873 si->using_mit_saver_extension = p->use_mit_saver_extension;
874 si->using_proc_interrupts = p->use_proc_interrupts;
875 si->using_xinput_extension = p->use_xinput_extension;
877 #ifdef HAVE_XIDLE_EXTENSION
880 server_has_xidle_extension_p = XidleQueryExtension (si->dpy, &ev, &er);
883 #ifdef HAVE_SGI_SAVER_EXTENSION
884 server_has_sgi_saver_extension_p =
885 XScreenSaverQueryExtension (si->dpy,
886 &si->sgi_saver_ext_event_number,
887 &si->sgi_saver_ext_error_number);
889 #ifdef HAVE_MIT_SAVER_EXTENSION
890 server_has_mit_saver_extension_p =
891 XScreenSaverQueryExtension (si->dpy,
892 &si->mit_saver_ext_event_number,
893 &si->mit_saver_ext_error_number);
895 #ifdef HAVE_PROC_INTERRUPTS
896 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
900 server_has_xinput_extension_p = query_xinput_extension (si);
903 if (!server_has_xidle_extension_p)
904 si->using_xidle_extension = False;
905 else if (p->verbose_p)
907 if (si->using_xidle_extension)
908 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
910 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
913 if (!server_has_sgi_saver_extension_p)
914 si->using_sgi_saver_extension = False;
915 else if (p->verbose_p)
917 if (si->using_sgi_saver_extension)
918 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
921 "%s: not using server's SGI SCREEN_SAVER extension.\n",
925 if (!server_has_mit_saver_extension_p)
926 si->using_mit_saver_extension = False;
927 else if (p->verbose_p)
929 if (si->using_mit_saver_extension)
930 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
934 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
939 if (XRRQueryExtension (si->dpy,
940 &si->randr_event_number, &si->randr_error_number))
942 int nscreens = ScreenCount (si->dpy); /* number of *real* screens */
945 si->using_randr_extension = TRUE;
948 fprintf (stderr, "%s: selecting RANDR events\n", blurb());
949 for (i = 0; i < nscreens; i++)
950 # ifdef RRScreenChangeNotifyMask /* randr.h 1.5, 2002/09/29 */
951 XRRSelectInput (si->dpy, RootWindow (si->dpy, i),
952 RRScreenChangeNotifyMask);
953 # else /* !RRScreenChangeNotifyMask */ /* Xrandr.h 1.4, 2001/06/07 */
954 XRRScreenChangeSelectInput (si->dpy, RootWindow (si->dpy, i), True);
955 # endif /* !RRScreenChangeNotifyMask */
957 # endif /* HAVE_RANDR */
960 if (!server_has_xinput_extension_p)
961 si->using_xinput_extension = False;
964 if (si->using_xinput_extension)
965 init_xinput_extension(si);
969 if (si->using_xinput_extension)
971 "%s: selecting events from %d XInputExtension devices.\n",
972 blurb(), si->num_xinput_devices);
975 "%s: not using XInputExtension.\n",
981 if (!system_has_proc_interrupts_p)
983 si->using_proc_interrupts = False;
984 if (p->verbose_p && piwhy)
985 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
988 else if (p->verbose_p)
990 if (si->using_proc_interrupts)
992 "%s: consulting /proc/interrupts for keyboard activity.\n",
996 "%s: not consulting /proc/interrupts for keyboard activity.\n",
1002 #ifdef DEBUG_MULTISCREEN
1004 debug_multiscreen_timer (XtPointer closure, XtIntervalId *id)
1006 saver_info *si = (saver_info *) closure;
1007 saver_preferences *p = &si->prefs;
1008 if (update_screen_layout (si))
1012 fprintf (stderr, "%s: new layout:\n", blurb());
1013 describe_monitor_layout (si);
1015 resize_screensaver_window (si);
1017 XtAppAddTimeOut (si->app, 1000*4, debug_multiscreen_timer, (XtPointer) si);
1019 #endif /* DEBUG_MULTISCREEN */
1022 /* For the case where we aren't using an server extensions, select user events
1023 on all the existing windows, and launch timers to select events on
1024 newly-created windows as well.
1026 If a server extension is being used, this does nothing.
1029 select_events (saver_info *si)
1031 saver_preferences *p = &si->prefs;
1034 if (si->using_xidle_extension ||
1035 si->using_mit_saver_extension ||
1036 si->using_sgi_saver_extension)
1039 if (p->initial_delay)
1043 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
1044 (int) p->initial_delay/1000,
1045 (p->initial_delay == 1000 ? "" : "s"));
1049 usleep (p->initial_delay);
1051 fprintf (stderr, " done.\n");
1056 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
1061 /* Select events on the root windows of every screen. This also selects
1062 for window creation events, so that new subwindows will be noticed.
1064 for (i = 0; i < si->nscreens; i++)
1066 saver_screen_info *ssi = &si->screens[i];
1067 if (ssi->real_screen_p)
1068 start_notice_events_timer (si,
1069 RootWindowOfScreen (si->screens[i].screen), False);
1073 fprintf (stderr, " done.\n");
1075 # ifdef DEBUG_MULTISCREEN
1076 if (p->debug_p) debug_multiscreen_timer ((XtPointer) si, 0);
1082 maybe_reload_init_file (saver_info *si)
1084 saver_preferences *p = &si->prefs;
1085 if (init_file_changed_p (p))
1088 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
1089 blurb(), init_file_name());
1091 load_init_file (si->dpy, p);
1093 /* If a server extension is in use, and p->timeout has changed,
1094 we need to inform the server of the new timeout. */
1095 disable_builtin_screensaver (si, False);
1097 /* If the DPMS settings in the init file have changed,
1098 change the settings on the server to match. */
1099 sync_server_dpms_settings (si->dpy,
1100 (p->dpms_enabled_p &&
1101 p->mode != DONT_BLANK),
1102 p->dpms_standby / 1000,
1103 p->dpms_suspend / 1000,
1112 - wait until the user is idle;
1114 - wait until the user is active;
1115 - unblank the screen;
1120 main_loop (saver_info *si)
1122 saver_preferences *p = &si->prefs;
1128 Bool was_locked = False;
1131 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1133 check_for_leaks ("unblanked A");
1134 sleep_until_idle (si, True);
1135 check_for_leaks ("unblanked B");
1140 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
1141 si->selection_mode, timestring());
1143 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
1147 maybe_reload_init_file (si);
1149 if (p->mode == DONT_BLANK)
1152 fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
1153 blurb(), timestring());
1155 /* Go around the loop and wait for the next bout of idleness,
1156 or for the init file to change, or for a remote command to
1157 come in, or something.
1159 But, if locked_p is true, go ahead. This can only happen
1160 if we're in "disabled" mode but a "lock" clientmessage came
1161 in: in that case, we should go ahead and blank/lock the screen.
1167 /* Since we're about to blank the screen, kill the de-race timer,
1168 if any. It might still be running if we have unblanked and then
1169 re-blanked in a short period (e.g., when using the "next" button
1170 in xscreensaver-demo.)
1175 fprintf (stderr, "%s: stopping de-race timer (%d remaining.)\n",
1176 blurb(), si->de_race_ticks);
1177 XtRemoveTimeOut (si->de_race_id);
1182 /* Now, try to blank.
1185 if (! blank_screen (si))
1187 /* We were unable to grab either the keyboard or mouse.
1188 This means we did not (and must not) blank the screen.
1189 If we were to blank the screen while some other program
1190 is holding both the mouse and keyboard grabbed, then
1191 we would never be able to un-blank it! We would never
1192 see any events, and the display would be wedged.
1194 In particular, without that keyboard grab, we will be
1195 unable to ever read keypresses on the unlock dialog.
1196 You can't unlock if you can't type your password.
1198 So, just go around the loop again and wait for the
1199 next bout of idleness. (If the user remains idle, we
1200 will next try to blank the screen again in no more than
1203 Time retry = 60 * 1000;
1204 if (p->timeout < retry)
1210 "%s: DEBUG MODE: unable to grab -- BLANKING ANYWAY.\n",
1216 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
1219 schedule_wakeup_event (si, retry, p->debug_p);
1224 for (i = 0; i < si->nscreens; i++)
1225 kill_screenhack (&si->screens[i]);
1227 raise_window (si, True, True, False);
1228 if (si->throttled_p)
1229 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
1231 for (i = 0; i < si->nscreens; i++)
1232 spawn_screenhack (&si->screens[i]);
1234 /* If we are blanking only, optionally power down monitor right now.
1235 To do this, we might need to temporarily re-enable DPMS first.
1237 if (p->mode == BLANK_ONLY &&
1238 p->dpms_enabled_p &&
1241 sync_server_dpms_settings (si->dpy, True,
1242 p->dpms_standby / 1000,
1243 p->dpms_suspend / 1000,
1245 ? (p->dpms_off / 1000)
1248 monitor_power_on (si, False);
1251 /* Don't start the cycle timer in demo mode. */
1252 if (!si->demoing_p && p->cycle)
1253 si->cycle_id = XtAppAddTimeOut (si->app,
1255 /* see comment in cycle_timer() */
1263 /* Maybe start locking the screen.
1266 Time lock_timeout = p->lock_timeout;
1268 if (si->emergency_lock_p && p->lock_p && lock_timeout)
1270 int secs = p->lock_timeout / 1000;
1273 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1275 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1279 si->emergency_lock_p = False;
1281 if (!si->demoing_p && /* if not going into demo mode */
1282 p->lock_p && /* and locking is enabled */
1283 !si->locking_disabled_p && /* and locking is possible */
1284 lock_timeout == 0) /* and locking is not timer-deferred */
1285 set_locked_p (si, True); /* then lock right now. */
1287 /* locked_p might be true already because of the above, or because of
1288 the LOCK ClientMessage. But if not, and if we're supposed to lock
1289 after some time, set up a timer to do so.
1294 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1295 activate_lock_timer,
1298 #endif /* !NO_LOCKING */
1301 ok_to_unblank = True;
1304 check_for_leaks ("blanked A");
1305 sleep_until_idle (si, False); /* until not idle */
1306 check_for_leaks ("blanked B");
1308 maybe_reload_init_file (si);
1311 /* Maybe unlock the screen.
1315 saver_screen_info *ssi = si->default_screen;
1316 if (si->locking_disabled_p) abort ();
1319 si->dbox_up_p = True;
1320 for (i = 0; i < si->nscreens; i++)
1321 suspend_screenhack (&si->screens[i], True); /* suspend */
1322 XUndefineCursor (si->dpy, ssi->screensaver_window);
1324 ok_to_unblank = unlock_p (si);
1326 si->dbox_up_p = False;
1327 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1328 for (i = 0; i < si->nscreens; i++)
1329 suspend_screenhack (&si->screens[i], False); /* resume */
1331 if (!ok_to_unblank &&
1332 !screenhack_running_p (si))
1334 /* If the lock dialog has been dismissed and we're not about to
1335 unlock the screen, and there is currently no hack running,
1336 then launch one. (There might be no hack running if DPMS
1337 had kicked in. But DPMS is off now, so bring back the hack)
1340 XtRemoveTimeOut (si->cycle_id);
1342 cycle_timer ((XtPointer) si, 0);
1345 #endif /* !NO_LOCKING */
1347 } while (!ok_to_unblank);
1351 fprintf (stderr, "%s: unblanking screen at %s.\n",
1352 blurb(), timestring ());
1354 /* Kill before unblanking, to stop drawing as soon as possible. */
1355 for (i = 0; i < si->nscreens; i++)
1356 kill_screenhack (&si->screens[i]);
1357 unblank_screen (si);
1359 set_locked_p (si, False);
1360 si->emergency_lock_p = False;
1362 si->selection_mode = 0;
1364 /* If we're throttled, and the user has explicitly unlocked the screen,
1365 then unthrottle. If we weren't locked, then don't unthrottle
1366 automatically, because someone might have just bumped the desk... */
1369 if (si->throttled_p && p->verbose_p)
1370 fprintf (stderr, "%s: unthrottled.\n", blurb());
1371 si->throttled_p = False;
1376 XtRemoveTimeOut (si->cycle_id);
1382 XtRemoveTimeOut (si->lock_id);
1386 /* Since we're unblanked now, break race conditions and make
1387 sure we stay that way (see comment in timers.c.) */
1388 if (! si->de_race_id)
1389 de_race_timer ((XtPointer) si, 0);
1393 static void analyze_display (saver_info *si);
1394 static void fix_fds (void);
1397 main (int argc, char **argv)
1401 saver_info *si = &the_si;
1402 saver_preferences *p = &si->prefs;
1403 struct passwd *spasswd;
1406 /* It turns out that if we do setlocale (LC_ALL, "") here, people
1407 running in Japanese locales get font craziness on the password
1408 dialog, presumably because it is displaying Japanese characters
1409 in a non-Japanese font. However, if we don't call setlocale()
1410 at all, then XLookupString() never returns multi-byte UTF-8
1411 characters when people type non-Latin1 characters on the
1414 The current theory (and at this point, I'm really guessing!) is
1415 that using LC_CTYPE instead of LC_ALL will make XLookupString()
1416 behave usefully, without having the side-effect of screwing up
1417 the fonts on the unlock dialog.
1419 See https://bugs.launchpad.net/ubuntu/+source/xscreensaver/+bug/671923
1420 from comment #20 onward.
1425 if (!setlocale (LC_CTYPE, ""))
1426 fprintf (stderr, "%s: warning: could not set default locale\n",
1429 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1430 textdomain (GETTEXT_PACKAGE);
1431 #endif /* ENABLE_NLS */
1433 memset(si, 0, sizeof(*si));
1434 global_si_kludge = si; /* I hate C so much... */
1438 # undef ya_rand_init
1441 save_argv (argc, argv);
1442 set_version_string (si, &argc, argv);
1443 privileged_initialization (si, &argc, argv);
1444 hack_environment (si);
1446 spasswd = getpwuid(getuid());
1449 fprintf(stderr, "Could not figure out who the current user is!\n");
1453 si->user = strdup(spasswd->pw_name ? spasswd->pw_name : "(unknown)");
1456 si->unlock_cb = gui_auth_conv;
1457 si->auth_finished_cb = auth_finished_cb;
1458 # endif /* !NO_LOCKING */
1460 shell = connect_to_server (si, &argc, argv);
1461 process_command_line (si, &argc, argv);
1462 stderr_log_file (si);
1465 load_init_file(si->dpy, p); /* must be before initialize_per_screen_info() */
1466 blurb_timestamp_p = p->timestamp_p; /* kludge */
1467 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1469 /* We can only issue this warning now. */
1470 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1472 "%s: there are no PseudoColor or GrayScale visuals.\n"
1473 "%s: ignoring the request for fading/unfading.\n",
1476 for (i = 0; i < si->nscreens; i++)
1478 saver_screen_info *ssi = &si->screens[i];
1479 if (ssi->real_screen_p)
1480 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1484 lock_initialization (si, &argc, argv);
1485 print_lock_failure_banner (si);
1487 if (p->xsync_p) XSynchronize (si->dpy, True);
1489 if (p->verbose_p) analyze_display (si);
1490 initialize_server_extensions (si);
1492 si->blank_time = time ((time_t) 0); /* must be before ..._window */
1493 initialize_screensaver_window (si);
1498 disable_builtin_screensaver (si, True);
1499 sync_server_dpms_settings (si->dpy,
1500 (p->dpms_enabled_p &&
1501 p->mode != DONT_BLANK),
1502 p->dpms_standby / 1000,
1503 p->dpms_suspend / 1000,
1507 initialize_stderr (si);
1508 handle_signals (si);
1510 make_splash_dialog (si);
1512 main_loop (si); /* doesn't return */
1519 /* Bad Things Happen if stdin, stdout, and stderr have been closed
1520 (as by the `sh incantation "xscreensaver >&- 2>&-"). When you do
1521 that, the X connection gets allocated to one of these fds, and
1522 then some random library writes to stderr, and random bits get
1523 stuffed down the X pipe, causing "Xlib: sequence lost" errors.
1524 So, we cause the first three file descriptors to be open to
1525 /dev/null if they aren't open to something else already. This
1526 must be done before any other files are opened (or the closing
1527 of that other file will again free up one of the "magic" first
1530 We do this by opening /dev/null three times, and then closing
1531 those fds, *unless* any of them got allocated as #0, #1, or #2,
1532 in which case we leave them open. Gag.
1534 Really, this crap is technically required of *every* X program,
1535 if you want it to be robust in the face of "2>&-".
1537 int fd0 = open ("/dev/null", O_RDWR);
1538 int fd1 = open ("/dev/null", O_RDWR);
1539 int fd2 = open ("/dev/null", O_RDWR);
1540 if (fd0 > 2) close (fd0);
1541 if (fd1 > 2) close (fd1);
1542 if (fd2 > 2) close (fd2);
1547 /* Processing ClientMessage events.
1551 static Bool error_handler_hit_p = False;
1554 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1556 error_handler_hit_p = True;
1560 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1561 them. We only look up the atom names for printing warning messages,
1562 so don't bomb out when it happens...
1565 XGetAtomName_safe (Display *dpy, Atom atom)
1568 XErrorHandler old_handler;
1569 if (!atom) return 0;
1572 error_handler_hit_p = False;
1573 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1574 result = XGetAtomName (dpy, atom);
1576 XSetErrorHandler (old_handler);
1578 if (error_handler_hit_p) result = 0;
1585 sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
1586 return strdup (buf);
1592 clientmessage_response (saver_info *si, Window w, Bool error,
1593 const char *stderr_msg,
1594 const char *protocol_msg)
1598 saver_preferences *p = &si->prefs;
1599 XErrorHandler old_handler;
1601 if (error || p->verbose_p)
1602 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1604 L = strlen(protocol_msg);
1605 proto = (char *) malloc (L + 2);
1606 proto[0] = (error ? '-' : '+');
1607 strcpy (proto+1, protocol_msg);
1610 /* Ignore all X errors while sending a response to a ClientMessage.
1611 Pretty much the only way we could get an error here is if the
1612 window we're trying to send the reply on has been deleted, in
1613 which case, the sender of the ClientMessage won't see our response
1616 XSync (si->dpy, False);
1617 error_handler_hit_p = False;
1618 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1620 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1621 PropModeReplace, (unsigned char *) proto, L);
1623 XSync (si->dpy, False);
1624 XSetErrorHandler (old_handler);
1625 XSync (si->dpy, False);
1632 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1634 #if 0 /* Oh, fuck it. GNOME likes to spew random ClientMessages at us
1635 all the time. This is presumably indicative of an error in
1636 the sender of that ClientMessage: if we're getting it and
1637 ignoring it, then it's not reaching the intended recipient.
1638 But people complain to me about this all the time ("waaah!
1639 xscreensaver is printing to it's stderr and that gets my
1640 panties all in a bunch!") And I'm sick of hearing about it.
1641 So we'll just ignore these messages and let GNOME go right
1642 ahead and continue to stumble along in its malfunction.
1645 saver_preferences *p = &si->prefs;
1646 char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1647 Window w = event->xclient.window;
1650 Bool root_p = False;
1653 for (screen = 0; screen < si->nscreens; screen++)
1654 if (w == si->screens[screen].screensaver_window)
1656 strcpy (wdesc, "xscreensaver");
1659 else if (w == RootWindow (si->dpy, screen))
1661 strcpy (wdesc, "root");
1666 /* If this ClientMessage was sent to the real root window instead of to the
1667 xscreensaver window, then it might be intended for someone else who is
1668 listening on the root window (e.g., the window manager). So only print
1669 the warning if: we are in debug mode; or if the bogus message was
1670 actually sent to one of the xscreensaver-created windows.
1672 if (root_p && !p->debug_p)
1677 XErrorHandler old_handler;
1679 XWindowAttributes xgwa;
1680 memset (&hint, 0, sizeof(hint));
1681 memset (&xgwa, 0, sizeof(xgwa));
1683 XSync (si->dpy, False);
1684 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1685 XGetClassHint (si->dpy, w, &hint);
1686 XGetWindowAttributes (si->dpy, w, &xgwa);
1687 XSync (si->dpy, False);
1688 XSetErrorHandler (old_handler);
1689 XSync (si->dpy, False);
1691 screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1693 sprintf (wdesc, "%.20s / %.20s",
1694 (hint.res_name ? hint.res_name : "(null)"),
1695 (hint.res_class ? hint.res_class : "(null)"));
1696 if (hint.res_name) XFree (hint.res_name);
1697 if (hint.res_class) XFree (hint.res_class);
1700 fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1701 blurb(), screen, (str ? str : "(null)"));
1702 fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1703 blurb(), screen, (unsigned long) w, wdesc);
1704 if (str) XFree (str);
1711 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1713 saver_preferences *p = &si->prefs;
1715 Window window = event->xclient.window;
1717 /* Preferences might affect our handling of client messages. */
1718 maybe_reload_init_file (si);
1720 if (event->xclient.message_type != XA_SCREENSAVER ||
1721 event->xclient.format != 32)
1723 bogus_clientmessage_warning (si, event);
1727 type = event->xclient.data.l[0];
1728 if (type == XA_ACTIVATE)
1732 if (p->mode == DONT_BLANK)
1734 clientmessage_response(si, window, True,
1735 "ACTIVATE ClientMessage received in DONT_BLANK mode.",
1736 "screen blanking is currently disabled.");
1740 clientmessage_response(si, window, False,
1741 "ACTIVATE ClientMessage received.",
1743 si->selection_mode = 0;
1744 si->demoing_p = False;
1746 if (si->throttled_p && p->verbose_p)
1747 fprintf (stderr, "%s: unthrottled.\n", blurb());
1748 si->throttled_p = False;
1750 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1752 XForceScreenSaver (si->dpy, ScreenSaverActive);
1760 clientmessage_response(si, window, True,
1761 "ClientMessage ACTIVATE received while already active.",
1764 else if (type == XA_DEACTIVATE)
1768 if (si->throttled_p && p->verbose_p)
1769 fprintf (stderr, "%s: unthrottled.\n", blurb());
1770 si->throttled_p = False;
1772 clientmessage_response(si, window, False,
1773 "DEACTIVATE ClientMessage received.",
1775 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1777 XForceScreenSaver (si->dpy, ScreenSaverReset);
1785 clientmessage_response(si, window, False,
1786 "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
1787 "not active: idle timer reset.");
1790 else if (type == XA_CYCLE)
1794 clientmessage_response(si, window, False,
1795 "CYCLE ClientMessage received.",
1797 si->selection_mode = 0; /* 0 means randomize when its time. */
1798 si->demoing_p = False;
1800 if (si->throttled_p && p->verbose_p)
1801 fprintf (stderr, "%s: unthrottled.\n", blurb());
1802 si->throttled_p = False;
1805 XtRemoveTimeOut (si->cycle_id);
1807 cycle_timer ((XtPointer) si, 0);
1810 clientmessage_response(si, window, True,
1811 "ClientMessage CYCLE received while inactive.",
1814 else if (type == XA_NEXT || type == XA_PREV)
1816 clientmessage_response(si, window, False,
1818 ? "NEXT ClientMessage received."
1819 : "PREV ClientMessage received."),
1821 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1822 si->demoing_p = False;
1824 if (si->throttled_p && p->verbose_p)
1825 fprintf (stderr, "%s: unthrottled.\n", blurb());
1826 si->throttled_p = False;
1831 XtRemoveTimeOut (si->cycle_id);
1833 cycle_timer ((XtPointer) si, 0);
1838 else if (type == XA_SELECT)
1842 long which = event->xclient.data.l[1];
1844 if (p->mode == DONT_BLANK)
1846 clientmessage_response(si, window, True,
1847 "SELECT ClientMessage received in DONT_BLANK mode.",
1848 "screen blanking is currently disabled.");
1852 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1853 sprintf (buf2, "activating (%ld).", which);
1854 clientmessage_response (si, window, False, buf, buf2);
1856 if (which < 0) which = 0; /* 0 == "random" */
1857 si->selection_mode = which;
1858 si->demoing_p = False;
1860 if (si->throttled_p && p->verbose_p)
1861 fprintf (stderr, "%s: unthrottled.\n", blurb());
1862 si->throttled_p = False;
1867 XtRemoveTimeOut (si->cycle_id);
1869 cycle_timer ((XtPointer) si, 0);
1874 else if (type == XA_EXIT)
1876 /* Ignore EXIT message if the screen is locked. */
1877 if (until_idle_p || !si->locked_p)
1879 clientmessage_response (si, window, False,
1880 "EXIT ClientMessage received.",
1885 for (i = 0; i < si->nscreens; i++)
1886 kill_screenhack (&si->screens[i]);
1887 unblank_screen (si);
1888 XSync (si->dpy, False);
1890 saver_exit (si, 0, 0);
1893 clientmessage_response (si, window, True,
1894 "EXIT ClientMessage received while locked.",
1895 "screen is locked.");
1897 else if (type == XA_RESTART)
1899 /* The RESTART message works whether the screensaver is active or not,
1900 unless the screen is locked, in which case it doesn't work.
1902 if (until_idle_p || !si->locked_p)
1904 clientmessage_response (si, window, False,
1905 "RESTART ClientMessage received.",
1910 for (i = 0; i < si->nscreens; i++)
1911 kill_screenhack (&si->screens[i]);
1912 unblank_screen (si);
1913 XSync (si->dpy, False);
1916 restart_process (si); /* does not return */
1920 clientmessage_response (si, window, True,
1921 "RESTART ClientMessage received while locked.",
1922 "screen is locked.");
1924 else if (type == XA_DEMO)
1926 long arg = event->xclient.data.l[1];
1927 Bool demo_one_hack_p = (arg == 5000);
1929 if (demo_one_hack_p)
1933 long which = event->xclient.data.l[2];
1936 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1937 sprintf (buf2, "demoing (%ld).", which);
1938 clientmessage_response (si, window, False, buf, buf2);
1940 if (which < 0) which = 0; /* 0 == "random" */
1941 si->selection_mode = which;
1942 si->demoing_p = True;
1944 if (si->throttled_p && p->verbose_p)
1945 fprintf (stderr, "%s: unthrottled.\n", blurb());
1946 si->throttled_p = False;
1951 clientmessage_response (si, window, True,
1952 "DEMO ClientMessage received while active.",
1957 clientmessage_response (si, window, True,
1958 "obsolete form of DEMO ClientMessage.",
1959 "obsolete form of DEMO ClientMessage.");
1962 else if (type == XA_PREFS)
1964 clientmessage_response (si, window, True,
1965 "the PREFS client-message is obsolete.",
1966 "the PREFS client-message is obsolete.");
1968 else if (type == XA_LOCK)
1971 clientmessage_response (si, window, True,
1972 "not compiled with support for locking.",
1973 "locking not enabled.");
1974 #else /* !NO_LOCKING */
1975 if (si->locking_disabled_p)
1976 clientmessage_response (si, window, True,
1977 "LOCK ClientMessage received, but locking is disabled.",
1978 "locking not enabled.");
1979 else if (si->locked_p)
1980 clientmessage_response (si, window, True,
1981 "LOCK ClientMessage received while already locked.",
1986 char *response = (until_idle_p
1987 ? "activating and locking."
1989 sprintf (buf, "LOCK ClientMessage received; %s", response);
1990 clientmessage_response (si, window, False, buf, response);
1991 set_locked_p (si, True);
1992 si->selection_mode = 0;
1993 si->demoing_p = False;
1995 if (si->lock_id) /* we're doing it now, so lose the timeout */
1997 XtRemoveTimeOut (si->lock_id);
2003 if (si->using_mit_saver_extension ||
2004 si->using_sgi_saver_extension)
2006 XForceScreenSaver (si->dpy, ScreenSaverActive);
2015 #endif /* !NO_LOCKING */
2017 else if (type == XA_THROTTLE)
2019 /* The THROTTLE command is deprecated -- it predates the XDPMS
2020 extension. Instead of using -throttle, users should instead
2021 just power off the monitor (e.g., "xset dpms force off".)
2022 In a few minutes, xscreensaver will notice that the monitor
2023 is off, and cease running hacks.
2025 if (si->throttled_p)
2026 clientmessage_response (si, window, True,
2027 "THROTTLE ClientMessage received, but "
2028 "already throttled.",
2029 "already throttled.");
2033 char *response = "throttled.";
2034 si->throttled_p = True;
2035 si->selection_mode = 0;
2036 si->demoing_p = False;
2037 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
2038 clientmessage_response (si, window, False, buf, response);
2043 XtRemoveTimeOut (si->cycle_id);
2045 cycle_timer ((XtPointer) si, 0);
2049 else if (type == XA_UNTHROTTLE)
2051 if (! si->throttled_p)
2052 clientmessage_response (si, window, True,
2053 "UNTHROTTLE ClientMessage received, but "
2059 char *response = "unthrottled.";
2060 si->throttled_p = False;
2061 si->selection_mode = 0;
2062 si->demoing_p = False;
2063 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
2064 clientmessage_response (si, window, False, buf, response);
2069 XtRemoveTimeOut (si->cycle_id);
2071 cycle_timer ((XtPointer) si, 0);
2079 str = XGetAtomName_safe (si->dpy, type);
2083 if (strlen (str) > 80)
2084 strcpy (str+70, "...");
2085 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
2092 "unrecognised screensaver ClientMessage 0x%x received.",
2093 (unsigned int) event->xclient.data.l[0]);
2096 clientmessage_response (si, window, True, buf, buf);
2102 /* Some random diagnostics printed in -verbose mode.
2106 analyze_display (saver_info *si)
2110 const char *name; const char *desc;
2112 Status (*version_fn) (Display *, int *majP, int *minP);
2115 { "SCREEN_SAVER", /* underscore */ "SGI Screen-Saver",
2116 # ifdef HAVE_SGI_SAVER_EXTENSION
2121 }, { "SCREEN-SAVER", /* dash */ "SGI Screen-Saver",
2122 # ifdef HAVE_SGI_SAVER_EXTENSION
2127 }, { "MIT-SCREEN-SAVER", "MIT Screen-Saver",
2128 # ifdef HAVE_MIT_SAVER_EXTENSION
2129 True, XScreenSaverQueryVersion
2133 }, { "XIDLE", "XIdle",
2134 # ifdef HAVE_XIDLE_EXTENSION
2139 }, { "SGI-VIDEO-CONTROL", "SGI Video-Control",
2140 # ifdef HAVE_SGI_VC_EXTENSION
2141 True, XSGIvcQueryVersion
2145 }, { "READDISPLAY", "SGI Read-Display",
2146 # ifdef HAVE_READ_DISPLAY_EXTENSION
2147 True, XReadDisplayQueryVersion
2151 }, { "MIT-SHM", "Shared Memory",
2152 # ifdef HAVE_XSHM_EXTENSION
2153 True, (Status (*) (Display*,int*,int*)) XShmQueryVersion /* 4 args */
2157 }, { "DOUBLE-BUFFER", "Double-Buffering",
2158 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
2159 True, XdbeQueryExtension
2163 }, { "DPMS", "Power Management",
2164 # ifdef HAVE_DPMS_EXTENSION
2165 True, DPMSGetVersion
2175 }, { "XFree86-VidModeExtension", "XF86 Video-Mode",
2176 # ifdef HAVE_XF86VMODE
2177 True, XF86VidModeQueryVersion
2181 }, { "XC-VidModeExtension", "XC Video-Mode",
2182 # ifdef HAVE_XF86VMODE
2183 True, XF86VidModeQueryVersion
2187 }, { "XFree86-MISC", "XF86 Misc",
2188 # ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2189 True, XF86MiscQueryVersion
2193 }, { "XC-MISC", "XC Misc",
2194 # ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2195 True, XF86MiscQueryVersion
2199 }, { "XINERAMA", "Xinerama",
2200 # ifdef HAVE_XINERAMA
2201 True, XineramaQueryVersion
2205 }, { "RANDR", "Resize-and-Rotate",
2207 True, XRRQueryVersion
2213 }, { "NV-CONTROL", "NVidia",
2215 }, { "NV-GLX", "NVidia GLX",
2217 }, { "Apple-DRI", "Apple-DRI (XDarwin)",
2219 }, { "XInputExtension", "XInput",
2224 fprintf (stderr, "%s: running on display \"%s\"\n", blurb(),
2225 DisplayString(si->dpy));
2226 fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
2227 ServerVendor(si->dpy), VendorRelease(si->dpy));
2229 fprintf (stderr, "%s: useful extensions:\n", blurb());
2230 for (i = 0; i < countof(exts); i++)
2232 int op = 0, event = 0, error = 0;
2234 int maj = 0, min = 0;
2235 int dummy1, dummy2, dummy3;
2238 /* Most of the extension version functions take 3 args,
2239 writing results into args 2 and 3, but some take more.
2240 We only ever care about the first two results, but we
2241 pass in three extra pointers just in case.
2243 Status (*version_fn_2) (Display*,int*,int*,int*,int*,int*) =
2244 (Status (*) (Display*,int*,int*,int*,int*,int*)) exts[i].version_fn;
2246 if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
2248 sprintf (buf, "%s: ", blurb());
2250 strcat (buf, exts[i].desc);
2254 else if (version_fn_2 (si->dpy, &maj, &min, &dummy1, &dummy2, &dummy3))
2255 sprintf (buf+strlen(buf), " (%d.%d)", maj, min);
2257 strcat (buf, " (unavailable)");
2259 if (!exts[i].useful_p)
2260 strcat (buf, " (disabled at compile time)");
2261 fprintf (stderr, "%s\n", buf);
2264 for (i = 0; i < si->nscreens; i++)
2266 saver_screen_info *ssi = &si->screens[i];
2267 unsigned long colormapped_depths = 0;
2268 unsigned long non_mapped_depths = 0;
2269 XVisualInfo vi_in, *vi_out;
2272 if (!ssi->real_screen_p) continue;
2274 vi_in.screen = ssi->real_screen_number;
2275 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
2276 if (!vi_out) continue;
2277 for (j = 0; j < out_count; j++)
2278 if (vi_out[j].class == PseudoColor)
2279 colormapped_depths |= (1 << vi_out[j].depth);
2281 non_mapped_depths |= (1 << vi_out[j].depth);
2282 XFree ((char *) vi_out);
2284 if (colormapped_depths)
2286 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(),
2287 ssi->real_screen_number);
2288 for (j = 0; j < 32; j++)
2289 if (colormapped_depths & (1 << j))
2290 fprintf (stderr, " %d", j);
2291 fprintf (stderr, ".\n");
2293 if (non_mapped_depths)
2295 fprintf (stderr, "%s: screen %d non-colormapped depths:",
2296 blurb(), ssi->real_screen_number);
2297 for (j = 0; j < 32; j++)
2298 if (non_mapped_depths & (1 << j))
2299 fprintf (stderr, " %d", j);
2300 fprintf (stderr, ".\n");
2304 describe_monitor_layout (si);
2309 display_is_on_console_p (saver_info *si)
2311 Bool not_on_console = True;
2312 char *dpystr = DisplayString (si->dpy);
2313 char *tail = (char *) strchr (dpystr, ':');
2314 if (! tail || strncmp (tail, ":0", 2))
2315 not_on_console = True;
2318 char dpyname[255], localname[255];
2319 strncpy (dpyname, dpystr, tail-dpystr);
2320 dpyname [tail-dpystr] = 0;
2322 !strcmp(dpyname, "unix") ||
2323 !strcmp(dpyname, "localhost"))
2324 not_on_console = False;
2325 else if (gethostname (localname, sizeof (localname)))
2326 not_on_console = True; /* can't find hostname? */
2327 else if (!strncmp (dpyname, "/tmp/launch-", 12)) /* MacOS X launchd */
2328 not_on_console = False;
2331 /* We have to call gethostbyname() on the result of gethostname()
2332 because the two aren't guarenteed to be the same name for the
2333 same host: on some losing systems, one is a FQDN and the other
2334 is not. Here in the wide wonderful world of Unix it's rocket
2335 science to obtain the local hostname in a portable fashion.
2337 And don't forget, gethostbyname() reuses the structure it
2338 returns, so we have to copy the fucker before calling it again.
2339 Thank you master, may I have another.
2341 struct hostent *h = gethostbyname (dpyname);
2343 not_on_console = True;
2348 strcpy (hn, h->h_name);
2349 l = gethostbyname (localname);
2350 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
2354 return !not_on_console;
2358 /* Do a little bit of heap introspection...
2361 check_for_leaks (const char *where)
2363 #if defined(HAVE_SBRK) && defined(LEAK_PARANOIA)
2364 static unsigned long last_brk = 0;
2365 int b = (unsigned long) sbrk(0);
2366 if (last_brk && last_brk < b)
2367 fprintf (stderr, "%s: %s: brk grew by %luK.\n",
2369 (((b - last_brk) + 1023) / 1024));
2371 #endif /* HAVE_SBRK */