1 /* xscreensaver, Copyright (c) 1991-2012 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\
302 Bool in_signal_handler_p = 0; /* I hate C so much... */
307 if (in_signal_handler_p)
309 /* Turns out that ctime() and even localtime_r() call malloc() on Linux!
310 So we can't call them from inside SIGCHLD. WTF.
313 strcpy (buf, "... ... .. signal ....");
318 time_t now = time ((time_t *) 0);
319 char *str = (char *) ctime (&now);
320 char *nl = (char *) strchr (str, '\n');
321 if (nl) *nl = 0; /* take off that dang newline */
326 static Bool blurb_timestamp_p = True; /* kludge */
331 if (!blurb_timestamp_p)
335 static char buf[255];
336 char *ct = timestring();
337 int n = strlen(progname);
339 strncpy(buf, progname, n);
342 strncpy(buf+n, ct+11, 8);
343 strcpy(buf+n+9, ": ");
350 saver_ehandler (Display *dpy, XErrorEvent *error)
352 saver_info *si = global_si_kludge; /* I hate C so much... */
356 if (!real_stderr) real_stderr = stderr;
358 fprintf (real_stderr, "\n"
359 "#######################################"
360 "#######################################\n\n"
361 "%s: X Error! PLEASE REPORT THIS BUG.\n",
364 for (i = 0; i < si->nscreens; i++)
366 saver_screen_info *ssi = &si->screens[i];
367 fprintf (real_stderr, "%s: screen %d/%d: 0x%x, 0x%x, 0x%x\n",
368 blurb(), ssi->real_screen_number, ssi->number,
369 (unsigned int) RootWindowOfScreen (si->screens[i].screen),
370 (unsigned int) si->screens[i].real_vroot,
371 (unsigned int) si->screens[i].screensaver_window);
374 fprintf (real_stderr, "\n"
375 "#######################################"
376 "#######################################\n\n");
378 fatal_p = XmuPrintDefaultErrorMessage (dpy, error, real_stderr);
380 fatal_p = True; /* The only time I've ever seen a supposedly nonfatal error,
381 it has been BadImplementation / Xlib sequence lost, which
382 are in truth pretty damned fatal.
385 fprintf (real_stderr, "\n");
388 fprintf (real_stderr, "%s: nonfatal error.\n\n", blurb());
391 if (si->prefs.xsync_p)
393 saver_exit (si, -1, "because of synchronous X Error");
398 __extension__ /* don't warn about "string length is greater than the
399 length ISO C89 compilers are required to support". */
401 fprintf (real_stderr,
402 "#######################################################################\n"
404 " If at all possible, please re-run xscreensaver with the command\n"
405 " line arguments `-sync -verbose -log log.txt', and reproduce this\n"
406 " bug. That will cause xscreensaver to dump a `core' file to the\n"
407 " current directory. Please include the stack trace from that core\n"
408 " file in your bug report. *DO NOT* mail the core file itself! That\n"
409 " won't work. A \"log.txt\" file will also be written. Please *do*\n"
410 " include the complete \"log.txt\" file with your bug report.\n"
412 " http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
413 " the most useful bug reports, and how to examine core files.\n"
415 " The more information you can provide, the better. But please\n"
416 " report this bug, regardless!\n"
418 "#######################################################################\n"
422 saver_exit (si, -1, 0);
430 /* This error handler is used only while the X connection is being set up;
431 after we've got a connection, we don't use this handler again. The only
432 reason for having this is so that we can present a more idiot-proof error
433 message than "cannot open display."
436 startup_ehandler (String name, String type, String class,
437 String defalt, /* one can't even spel properly
438 in this joke of a language */
439 String *av, Cardinal *ac)
443 saver_info *si = global_si_kludge; /* I hate C so much... */
444 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
446 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
447 fmt, sizeof(fmt)-1, *db);
449 fprintf (stderr, "%s: ", blurb());
451 memset (p, 0, sizeof(p));
452 if (*ac > countof (p)) *ac = countof (p);
453 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
454 fprintf (stderr, fmt, /* Did I mention that I hate C? */
455 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
456 fprintf (stderr, "\n");
458 describe_uids (si, stderr);
460 if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
462 fprintf (stderr, "\n"
463 "%s: This is probably because you're logging in as root. You\n"
464 " shouldn't log in as root: you should log in as a normal user,\n"
465 " and then `su' as needed. If you insist on logging in as\n"
466 " root, you will have to turn off X's security features before\n"
467 " xscreensaver will work.\n"
469 " Please read the manual and FAQ for more information:\n",
474 fprintf (stderr, "\n"
475 "%s: Errors at startup are usually authorization problems.\n"
476 " But you're not logging in as root (good!) so something\n"
477 " else must be wrong. Did you read the manual and the FAQ?\n",
481 fprintf (stderr, "\n"
482 " http://www.jwz.org/xscreensaver/faq.html\n"
483 " http://www.jwz.org/xscreensaver/man.html\n"
492 /* The zillions of initializations.
495 /* Set progname, version, etc. This is done very early.
498 set_version_string (saver_info *si, int *argc, char **argv)
500 progclass = "XScreenSaver";
502 /* progname is reset later, after we connect to X. */
503 progname = strrchr(argv[0], '/');
504 if (progname) progname++;
505 else progname = argv[0];
507 if (strlen(progname) > 100) /* keep it short. */
510 /* The X resource database blows up if argv[0] has a "." in it. */
513 while ((s = strchr (s, '.')))
517 si->version = (char *) malloc (5);
518 memcpy (si->version, screensaver_id + 17, 4);
523 /* Initializations that potentially take place as a priveleged user:
524 If the xscreensaver executable is setuid root, then these initializations
525 are run as root, before discarding privileges.
528 privileged_initialization (saver_info *si, int *argc, char **argv)
531 /* before hack_uid() for proper permissions */
532 lock_priv_init (*argc, argv, si->prefs.verbose_p);
533 #endif /* NO_LOCKING */
539 /* Figure out what locking mechanisms are supported.
542 lock_initialization (saver_info *si, int *argc, char **argv)
545 si->locking_disabled_p = True;
546 si->nolock_reason = "not compiled with locking support";
547 #else /* !NO_LOCKING */
549 /* Finish initializing locking, now that we're out of privileged code. */
550 if (! lock_init (*argc, argv, si->prefs.verbose_p))
552 si->locking_disabled_p = True;
553 si->nolock_reason = "error getting password";
556 /* If locking is currently enabled, but the environment indicates that
557 we have been launched as GDM's "Background" program, then disable
558 locking just in case.
560 if (!si->locking_disabled_p && getenv ("RUNNING_UNDER_GDM"))
562 si->locking_disabled_p = True;
563 si->nolock_reason = "running under GDM";
566 /* If the server is XDarwin (MacOS X) then disable locking.
567 (X grabs only affect X programs, so you can use Command-Tab
568 to bring any other Mac program to the front, e.g., Terminal.)
570 if (!si->locking_disabled_p)
572 int op = 0, event = 0, error = 0;
573 Bool macos_p = False;
576 /* Disable locking if *running* on Apple hardware, since we have no
577 reliable way to determine whether the server is running on MacOS.
578 Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
579 but I'm not really sure about that.
585 /* This extension exists on the Apple X11 server, but not
586 on earlier versions of the XDarwin server. */
587 macos_p = XQueryExtension (si->dpy, "Apple-DRI", &op, &event, &error);
591 si->locking_disabled_p = True;
592 si->nolock_reason = "Cannot lock securely on MacOS X";
596 if (si->prefs.debug_p) /* But allow locking anyway in debug mode. */
597 si->locking_disabled_p = False;
599 #endif /* NO_LOCKING */
603 /* Open the connection to the X server, and intern our Atoms.
606 connect_to_server (saver_info *si, int *argc, char **argv)
608 Widget toplevel_shell;
611 char *d = getenv ("DISPLAY");
614 char *ndpy = strdup("DISPLAY=:0.0");
615 /* if (si->prefs.verbose_p) */ /* sigh, too early to test this... */
617 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
621 /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
622 glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
623 do not. So we must leak it (and/or the previous setting). Yay.
626 #endif /* HAVE_PUTENV */
628 XSetErrorHandler (saver_ehandler);
630 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
631 toplevel_shell = XtAppInitialize (&si->app, progclass,
632 options, XtNumber (options),
633 argc, argv, defaults, 0, 0);
634 XtAppSetErrorMsgHandler (si->app, 0);
636 si->dpy = XtDisplay (toplevel_shell);
637 si->prefs.db = XtDatabase (si->dpy);
638 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
640 if(strlen(progname) > 100) /* keep it short. */
643 db = si->prefs.db; /* resources.c needs this */
645 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
646 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
647 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
648 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
649 XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
650 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
652 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
653 XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False);
654 XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False);
655 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
656 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
657 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
658 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
659 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
660 XA_PREV = XInternAtom (si->dpy, "PREV", False);
661 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
662 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
663 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
664 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
665 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
666 XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
667 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
668 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
670 return toplevel_shell;
674 /* Handle the command-line arguments that were not handled for us by Xt.
675 Issue an error message and exit if there are unknown options.
678 process_command_line (saver_info *si, int *argc, char **argv)
681 for (i = 1; i < *argc; i++)
683 if (!strcmp (argv[i], "-debug"))
684 /* no resource for this one, out of paranoia. */
685 si->prefs.debug_p = True;
687 else if (!strcmp (argv[i], "-h") ||
688 !strcmp (argv[i], "-help") ||
689 !strcmp (argv[i], "--help"))
694 const char *s = argv[i];
695 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
698 if (s[0] == '-' && s[1] == '-') s++;
699 if (!strcmp (s, "-activate") ||
700 !strcmp (s, "-deactivate") ||
701 !strcmp (s, "-cycle") ||
702 !strcmp (s, "-next") ||
703 !strcmp (s, "-prev") ||
704 !strcmp (s, "-exit") ||
705 !strcmp (s, "-restart") ||
706 !strcmp (s, "-demo") ||
707 !strcmp (s, "-prefs") ||
708 !strcmp (s, "-preferences") ||
709 !strcmp (s, "-lock") ||
710 !strcmp (s, "-version") ||
711 !strcmp (s, "-time"))
714 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
715 fprintf (stderr, "\n\
716 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
718 fprintf (stderr, "\n\
719 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
722 The `xscreensaver' program is a daemon that runs in the background.\n\
723 You control a running xscreensaver process by sending it messages\n\
724 with `xscreensaver-demo' or `xscreensaver-command'.\n\
725 . See the man pages for details, or check the web page:\n\
726 http://www.jwz.org/xscreensaver/\n\n");
735 /* Print out the xscreensaver banner to the tty if applicable;
736 Issue any other warnings that are called for at this point.
739 print_banner (saver_info *si)
741 saver_preferences *p = &si->prefs;
743 /* This resource gets set some time before the others, so that we know
744 whether to print the banner (and so that the banner gets printed before
745 any resource-database-related error messages.)
747 p->verbose_p = (p->debug_p ||
748 get_boolean_resource (si->dpy, "verbose", "Boolean"));
750 /* Ditto, for the locking_disabled_p message. */
751 p->lock_p = get_boolean_resource (si->dpy, "lock", "Boolean");
755 "%s %s, copyright (c) 1991-2008 "
756 "by Jamie Zawinski <jwz@jwz.org>.\n",
757 progname, si->version);
760 fprintf (stderr, "\n"
761 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
763 "\tNote that in debug mode, the xscreensaver window will only\n"
764 "\tcover the left half of the screen. (The idea is that you\n"
765 "\tcan still see debugging output in a shell, if you position\n"
766 "\tit on the right side of the screen.)\n"
768 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
769 "\tuntrusted environments.\n"
775 if (!si->uid_message || !*si->uid_message)
776 describe_uids (si, stderr);
779 if (si->orig_uid && *si->orig_uid)
780 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
781 blurb(), si->orig_uid);
782 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
785 fprintf (stderr, "%s: in process %lu.\n", blurb(),
786 (unsigned long) getpid());
791 print_lock_failure_banner (saver_info *si)
793 saver_preferences *p = &si->prefs;
795 /* If locking was not able to be initalized for some reason, explain why.
796 (This has to be done after we've read the lock_p resource.)
798 if (si->locking_disabled_p)
801 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
803 if (strstr (si->nolock_reason, "passw"))
804 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
805 "consult the manual.\n", blurb());
806 else if (strstr (si->nolock_reason, "running as "))
808 "%s: locking only works when xscreensaver is launched\n"
809 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
810 "\t See the manual for details.\n",
817 /* called from screens.c so that all the Xt crud is here. */
819 initialize_screen_root_widget (saver_screen_info *ssi)
821 saver_info *si = ssi->global;
822 if (ssi->toplevel_shell)
823 XtDestroyWidget (ssi->toplevel_shell);
824 ssi->toplevel_shell =
825 XtVaAppCreateShell (progname, progclass,
826 applicationShellWidgetClass,
828 XtNscreen, ssi->screen,
829 XtNvisual, ssi->current_visual,
830 XtNdepth, visual_depth (ssi->screen,
831 ssi->current_visual),
836 /* Examine all of the display's screens, and populate the `saver_screen_info'
837 structures. Make sure this is called after hack_environment() sets $PATH.
840 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
844 update_screen_layout (si);
846 /* Check to see whether fading is ever possible -- if any of the
847 screens on the display has a PseudoColor visual, then fading can
848 work (on at least some screens.) If no screen has a PseudoColor
849 visual, then don't bother ever trying to fade, because it will
850 just cause a delay without causing any visible effect.
852 for (i = 0; i < si->nscreens; i++)
854 saver_screen_info *ssi = &si->screens[i];
855 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
856 get_visual (ssi->screen, "PseudoColor", True, False) ||
857 get_visual (ssi->screen, "GrayScale", True, False))
859 si->fading_possible_p = True;
864 #ifdef HAVE_XF86VMODE_GAMMA
865 si->fading_possible_p = True; /* if we can gamma fade, go for it */
870 /* If any server extensions have been requested, try and initialize them.
871 Issue warnings if requests can't be honored.
874 initialize_server_extensions (saver_info *si)
876 saver_preferences *p = &si->prefs;
878 Bool server_has_xidle_extension_p = False;
879 Bool server_has_sgi_saver_extension_p = False;
880 Bool server_has_mit_saver_extension_p = False;
881 Bool system_has_proc_interrupts_p = False;
882 Bool server_has_xinput_extension_p = False;
883 const char *piwhy = 0;
885 si->using_xidle_extension = p->use_xidle_extension;
886 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
887 si->using_mit_saver_extension = p->use_mit_saver_extension;
888 si->using_proc_interrupts = p->use_proc_interrupts;
889 si->using_xinput_extension = p->use_xinput_extension;
891 #ifdef HAVE_XIDLE_EXTENSION
894 server_has_xidle_extension_p = XidleQueryExtension (si->dpy, &ev, &er);
897 #ifdef HAVE_SGI_SAVER_EXTENSION
898 server_has_sgi_saver_extension_p =
899 XScreenSaverQueryExtension (si->dpy,
900 &si->sgi_saver_ext_event_number,
901 &si->sgi_saver_ext_error_number);
903 #ifdef HAVE_MIT_SAVER_EXTENSION
904 server_has_mit_saver_extension_p =
905 XScreenSaverQueryExtension (si->dpy,
906 &si->mit_saver_ext_event_number,
907 &si->mit_saver_ext_error_number);
909 #ifdef HAVE_PROC_INTERRUPTS
910 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
914 server_has_xinput_extension_p = query_xinput_extension (si);
917 if (!server_has_xidle_extension_p)
918 si->using_xidle_extension = False;
919 else if (p->verbose_p)
921 if (si->using_xidle_extension)
922 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
924 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
927 if (!server_has_sgi_saver_extension_p)
928 si->using_sgi_saver_extension = False;
929 else if (p->verbose_p)
931 if (si->using_sgi_saver_extension)
932 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
935 "%s: not using server's SGI SCREEN_SAVER extension.\n",
939 if (!server_has_mit_saver_extension_p)
940 si->using_mit_saver_extension = False;
941 else if (p->verbose_p)
943 if (si->using_mit_saver_extension)
944 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
948 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
953 if (XRRQueryExtension (si->dpy,
954 &si->randr_event_number, &si->randr_error_number))
956 int nscreens = ScreenCount (si->dpy); /* number of *real* screens */
959 si->using_randr_extension = TRUE;
962 fprintf (stderr, "%s: selecting RANDR events\n", blurb());
963 for (i = 0; i < nscreens; i++)
964 # ifdef RRScreenChangeNotifyMask /* randr.h 1.5, 2002/09/29 */
965 XRRSelectInput (si->dpy, RootWindow (si->dpy, i),
966 RRScreenChangeNotifyMask);
967 # else /* !RRScreenChangeNotifyMask */ /* Xrandr.h 1.4, 2001/06/07 */
968 XRRScreenChangeSelectInput (si->dpy, RootWindow (si->dpy, i), True);
969 # endif /* !RRScreenChangeNotifyMask */
971 # endif /* HAVE_RANDR */
974 if (!server_has_xinput_extension_p)
975 si->using_xinput_extension = False;
978 if (si->using_xinput_extension)
979 init_xinput_extension(si);
983 if (si->using_xinput_extension)
985 "%s: selecting events from %d XInputExtension devices.\n",
986 blurb(), si->num_xinput_devices);
989 "%s: not using XInputExtension.\n",
995 if (!system_has_proc_interrupts_p)
997 si->using_proc_interrupts = False;
998 if (p->verbose_p && piwhy)
999 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
1002 else if (p->verbose_p)
1004 if (si->using_proc_interrupts)
1006 "%s: consulting /proc/interrupts for keyboard activity.\n",
1010 "%s: not consulting /proc/interrupts for keyboard activity.\n",
1016 #ifdef DEBUG_MULTISCREEN
1018 debug_multiscreen_timer (XtPointer closure, XtIntervalId *id)
1020 saver_info *si = (saver_info *) closure;
1021 saver_preferences *p = &si->prefs;
1022 if (update_screen_layout (si))
1026 fprintf (stderr, "%s: new layout:\n", blurb());
1027 describe_monitor_layout (si);
1029 resize_screensaver_window (si);
1031 XtAppAddTimeOut (si->app, 1000*4, debug_multiscreen_timer, (XtPointer) si);
1033 #endif /* DEBUG_MULTISCREEN */
1036 /* For the case where we aren't using an server extensions, select user events
1037 on all the existing windows, and launch timers to select events on
1038 newly-created windows as well.
1040 If a server extension is being used, this does nothing.
1043 select_events (saver_info *si)
1045 saver_preferences *p = &si->prefs;
1048 if (si->using_xidle_extension ||
1049 si->using_mit_saver_extension ||
1050 si->using_sgi_saver_extension)
1053 if (p->initial_delay)
1057 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
1058 (int) p->initial_delay/1000,
1059 (p->initial_delay == 1000 ? "" : "s"));
1063 usleep (p->initial_delay);
1065 fprintf (stderr, " done.\n");
1070 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
1075 /* Select events on the root windows of every screen. This also selects
1076 for window creation events, so that new subwindows will be noticed.
1078 for (i = 0; i < si->nscreens; i++)
1080 saver_screen_info *ssi = &si->screens[i];
1081 if (ssi->real_screen_p)
1082 start_notice_events_timer (si,
1083 RootWindowOfScreen (si->screens[i].screen), False);
1087 fprintf (stderr, " done.\n");
1089 # ifdef DEBUG_MULTISCREEN
1090 if (p->debug_p) debug_multiscreen_timer ((XtPointer) si, 0);
1096 maybe_reload_init_file (saver_info *si)
1098 saver_preferences *p = &si->prefs;
1099 if (init_file_changed_p (p))
1102 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
1103 blurb(), init_file_name());
1105 load_init_file (si->dpy, p);
1107 /* If a server extension is in use, and p->timeout has changed,
1108 we need to inform the server of the new timeout. */
1109 disable_builtin_screensaver (si, False);
1111 /* If the DPMS settings in the init file have changed,
1112 change the settings on the server to match. */
1113 sync_server_dpms_settings (si->dpy,
1114 (p->dpms_enabled_p &&
1115 p->mode != DONT_BLANK),
1116 p->dpms_standby / 1000,
1117 p->dpms_suspend / 1000,
1126 - wait until the user is idle;
1128 - wait until the user is active;
1129 - unblank the screen;
1134 main_loop (saver_info *si)
1136 saver_preferences *p = &si->prefs;
1142 Bool was_locked = False;
1145 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1147 check_for_leaks ("unblanked A");
1148 sleep_until_idle (si, True);
1149 check_for_leaks ("unblanked B");
1154 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
1155 si->selection_mode, timestring());
1157 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
1161 maybe_reload_init_file (si);
1163 if (p->mode == DONT_BLANK)
1166 fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
1167 blurb(), timestring());
1169 /* Go around the loop and wait for the next bout of idleness,
1170 or for the init file to change, or for a remote command to
1171 come in, or something.
1173 But, if locked_p is true, go ahead. This can only happen
1174 if we're in "disabled" mode but a "lock" clientmessage came
1175 in: in that case, we should go ahead and blank/lock the screen.
1181 /* Since we're about to blank the screen, kill the de-race timer,
1182 if any. It might still be running if we have unblanked and then
1183 re-blanked in a short period (e.g., when using the "next" button
1184 in xscreensaver-demo.)
1189 fprintf (stderr, "%s: stopping de-race timer (%d remaining.)\n",
1190 blurb(), si->de_race_ticks);
1191 XtRemoveTimeOut (si->de_race_id);
1196 /* Now, try to blank.
1199 if (! blank_screen (si))
1201 /* We were unable to grab either the keyboard or mouse.
1202 This means we did not (and must not) blank the screen.
1203 If we were to blank the screen while some other program
1204 is holding both the mouse and keyboard grabbed, then
1205 we would never be able to un-blank it! We would never
1206 see any events, and the display would be wedged.
1208 In particular, without that keyboard grab, we will be
1209 unable to ever read keypresses on the unlock dialog.
1210 You can't unlock if you can't type your password.
1212 So, just go around the loop again and wait for the
1213 next bout of idleness. (If the user remains idle, we
1214 will next try to blank the screen again in no more than
1217 Time retry = 60 * 1000;
1218 if (p->timeout < retry)
1224 "%s: DEBUG MODE: unable to grab -- BLANKING ANYWAY.\n",
1230 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
1233 schedule_wakeup_event (si, retry, p->debug_p);
1238 for (i = 0; i < si->nscreens; i++)
1239 kill_screenhack (&si->screens[i]);
1241 raise_window (si, True, True, False);
1242 if (si->throttled_p)
1243 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
1245 for (i = 0; i < si->nscreens; i++)
1246 spawn_screenhack (&si->screens[i]);
1248 /* If we are blanking only, optionally power down monitor right now.
1249 To do this, we might need to temporarily re-enable DPMS first.
1251 if (p->mode == BLANK_ONLY &&
1252 p->dpms_enabled_p &&
1255 sync_server_dpms_settings (si->dpy, True,
1256 p->dpms_standby / 1000,
1257 p->dpms_suspend / 1000,
1259 ? (p->dpms_off / 1000)
1262 monitor_power_on (si, False);
1265 /* Don't start the cycle timer in demo mode. */
1266 if (!si->demoing_p && p->cycle)
1267 si->cycle_id = XtAppAddTimeOut (si->app,
1269 /* see comment in cycle_timer() */
1277 /* Maybe start locking the screen.
1280 Time lock_timeout = p->lock_timeout;
1282 if (si->emergency_lock_p && p->lock_p && lock_timeout)
1284 int secs = p->lock_timeout / 1000;
1287 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1289 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1293 si->emergency_lock_p = False;
1295 if (!si->demoing_p && /* if not going into demo mode */
1296 p->lock_p && /* and locking is enabled */
1297 !si->locking_disabled_p && /* and locking is possible */
1298 lock_timeout == 0) /* and locking is not timer-deferred */
1299 set_locked_p (si, True); /* then lock right now. */
1301 /* locked_p might be true already because of the above, or because of
1302 the LOCK ClientMessage. But if not, and if we're supposed to lock
1303 after some time, set up a timer to do so.
1308 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1309 activate_lock_timer,
1312 #endif /* !NO_LOCKING */
1315 ok_to_unblank = True;
1318 check_for_leaks ("blanked A");
1319 sleep_until_idle (si, False); /* until not idle */
1320 check_for_leaks ("blanked B");
1322 maybe_reload_init_file (si);
1325 /* Maybe unlock the screen.
1329 saver_screen_info *ssi = si->default_screen;
1330 if (si->locking_disabled_p) abort ();
1333 si->dbox_up_p = True;
1334 for (i = 0; i < si->nscreens; i++)
1335 suspend_screenhack (&si->screens[i], True); /* suspend */
1336 XUndefineCursor (si->dpy, ssi->screensaver_window);
1338 ok_to_unblank = unlock_p (si);
1340 si->dbox_up_p = False;
1341 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1342 for (i = 0; i < si->nscreens; i++)
1343 suspend_screenhack (&si->screens[i], False); /* resume */
1345 if (!ok_to_unblank &&
1346 !screenhack_running_p (si))
1348 /* If the lock dialog has been dismissed and we're not about to
1349 unlock the screen, and there is currently no hack running,
1350 then launch one. (There might be no hack running if DPMS
1351 had kicked in. But DPMS is off now, so bring back the hack)
1354 XtRemoveTimeOut (si->cycle_id);
1356 cycle_timer ((XtPointer) si, 0);
1359 #endif /* !NO_LOCKING */
1361 } while (!ok_to_unblank);
1365 fprintf (stderr, "%s: unblanking screen at %s.\n",
1366 blurb(), timestring ());
1368 /* Kill before unblanking, to stop drawing as soon as possible. */
1369 for (i = 0; i < si->nscreens; i++)
1370 kill_screenhack (&si->screens[i]);
1371 unblank_screen (si);
1373 set_locked_p (si, False);
1374 si->emergency_lock_p = False;
1376 si->selection_mode = 0;
1378 /* If we're throttled, and the user has explicitly unlocked the screen,
1379 then unthrottle. If we weren't locked, then don't unthrottle
1380 automatically, because someone might have just bumped the desk... */
1383 if (si->throttled_p && p->verbose_p)
1384 fprintf (stderr, "%s: unthrottled.\n", blurb());
1385 si->throttled_p = False;
1390 XtRemoveTimeOut (si->cycle_id);
1396 XtRemoveTimeOut (si->lock_id);
1400 /* Since we're unblanked now, break race conditions and make
1401 sure we stay that way (see comment in timers.c.) */
1402 if (! si->de_race_id)
1403 de_race_timer ((XtPointer) si, 0);
1407 static void analyze_display (saver_info *si);
1408 static void fix_fds (void);
1411 main (int argc, char **argv)
1415 saver_info *si = &the_si;
1416 saver_preferences *p = &si->prefs;
1417 struct passwd *spasswd;
1420 /* It turns out that if we do setlocale (LC_ALL, "") here, people
1421 running in Japanese locales get font craziness on the password
1422 dialog, presumably because it is displaying Japanese characters
1423 in a non-Japanese font. However, if we don't call setlocale()
1424 at all, then XLookupString() never returns multi-byte UTF-8
1425 characters when people type non-Latin1 characters on the
1428 The current theory (and at this point, I'm really guessing!) is
1429 that using LC_CTYPE instead of LC_ALL will make XLookupString()
1430 behave usefully, without having the side-effect of screwing up
1431 the fonts on the unlock dialog.
1433 See https://bugs.launchpad.net/ubuntu/+source/xscreensaver/+bug/671923
1434 from comment #20 onward.
1439 if (!setlocale (LC_CTYPE, ""))
1440 fprintf (stderr, "%s: warning: could not set default locale\n",
1443 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1444 textdomain (GETTEXT_PACKAGE);
1445 #endif /* ENABLE_NLS */
1447 memset(si, 0, sizeof(*si));
1448 global_si_kludge = si; /* I hate C so much... */
1452 # undef ya_rand_init
1455 save_argv (argc, argv);
1456 set_version_string (si, &argc, argv);
1457 privileged_initialization (si, &argc, argv);
1458 hack_environment (si);
1460 spasswd = getpwuid(getuid());
1463 fprintf(stderr, "Could not figure out who the current user is!\n");
1467 si->user = strdup(spasswd->pw_name ? spasswd->pw_name : "(unknown)");
1470 si->unlock_cb = gui_auth_conv;
1471 si->auth_finished_cb = auth_finished_cb;
1472 # endif /* !NO_LOCKING */
1474 shell = connect_to_server (si, &argc, argv);
1475 process_command_line (si, &argc, argv);
1476 stderr_log_file (si);
1479 load_init_file(si->dpy, p); /* must be before initialize_per_screen_info() */
1480 blurb_timestamp_p = p->timestamp_p; /* kludge */
1481 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1483 /* We can only issue this warning now. */
1484 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1486 "%s: there are no PseudoColor or GrayScale visuals.\n"
1487 "%s: ignoring the request for fading/unfading.\n",
1490 for (i = 0; i < si->nscreens; i++)
1492 saver_screen_info *ssi = &si->screens[i];
1493 if (ssi->real_screen_p)
1494 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1498 lock_initialization (si, &argc, argv);
1499 print_lock_failure_banner (si);
1501 if (p->xsync_p) XSynchronize (si->dpy, True);
1503 if (p->verbose_p) analyze_display (si);
1504 initialize_server_extensions (si);
1506 si->blank_time = time ((time_t) 0); /* must be before ..._window */
1507 initialize_screensaver_window (si);
1512 disable_builtin_screensaver (si, True);
1513 sync_server_dpms_settings (si->dpy,
1514 (p->dpms_enabled_p &&
1515 p->mode != DONT_BLANK),
1516 p->dpms_standby / 1000,
1517 p->dpms_suspend / 1000,
1521 initialize_stderr (si);
1522 handle_signals (si);
1524 make_splash_dialog (si);
1526 main_loop (si); /* doesn't return */
1533 /* Bad Things Happen if stdin, stdout, and stderr have been closed
1534 (as by the `sh incantation "xscreensaver >&- 2>&-"). When you do
1535 that, the X connection gets allocated to one of these fds, and
1536 then some random library writes to stderr, and random bits get
1537 stuffed down the X pipe, causing "Xlib: sequence lost" errors.
1538 So, we cause the first three file descriptors to be open to
1539 /dev/null if they aren't open to something else already. This
1540 must be done before any other files are opened (or the closing
1541 of that other file will again free up one of the "magic" first
1544 We do this by opening /dev/null three times, and then closing
1545 those fds, *unless* any of them got allocated as #0, #1, or #2,
1546 in which case we leave them open. Gag.
1548 Really, this crap is technically required of *every* X program,
1549 if you want it to be robust in the face of "2>&-".
1551 int fd0 = open ("/dev/null", O_RDWR);
1552 int fd1 = open ("/dev/null", O_RDWR);
1553 int fd2 = open ("/dev/null", O_RDWR);
1554 if (fd0 > 2) close (fd0);
1555 if (fd1 > 2) close (fd1);
1556 if (fd2 > 2) close (fd2);
1561 /* Processing ClientMessage events.
1565 static Bool error_handler_hit_p = False;
1568 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1570 error_handler_hit_p = True;
1574 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1575 them. We only look up the atom names for printing warning messages,
1576 so don't bomb out when it happens...
1579 XGetAtomName_safe (Display *dpy, Atom atom)
1582 XErrorHandler old_handler;
1583 if (!atom) return 0;
1586 error_handler_hit_p = False;
1587 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1588 result = XGetAtomName (dpy, atom);
1590 XSetErrorHandler (old_handler);
1592 if (error_handler_hit_p) result = 0;
1599 sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
1600 return strdup (buf);
1606 clientmessage_response (saver_info *si, Window w, Bool error,
1607 const char *stderr_msg,
1608 const char *protocol_msg)
1612 saver_preferences *p = &si->prefs;
1613 XErrorHandler old_handler;
1615 if (error || p->verbose_p)
1616 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1618 L = strlen(protocol_msg);
1619 proto = (char *) malloc (L + 2);
1620 proto[0] = (error ? '-' : '+');
1621 strcpy (proto+1, protocol_msg);
1624 /* Ignore all X errors while sending a response to a ClientMessage.
1625 Pretty much the only way we could get an error here is if the
1626 window we're trying to send the reply on has been deleted, in
1627 which case, the sender of the ClientMessage won't see our response
1630 XSync (si->dpy, False);
1631 error_handler_hit_p = False;
1632 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1634 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1635 PropModeReplace, (unsigned char *) proto, L);
1637 XSync (si->dpy, False);
1638 XSetErrorHandler (old_handler);
1639 XSync (si->dpy, False);
1646 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1648 #if 0 /* Oh, fuck it. GNOME likes to spew random ClientMessages at us
1649 all the time. This is presumably indicative of an error in
1650 the sender of that ClientMessage: if we're getting it and
1651 ignoring it, then it's not reaching the intended recipient.
1652 But people complain to me about this all the time ("waaah!
1653 xscreensaver is printing to it's stderr and that gets my
1654 panties all in a bunch!") And I'm sick of hearing about it.
1655 So we'll just ignore these messages and let GNOME go right
1656 ahead and continue to stumble along in its malfunction.
1659 saver_preferences *p = &si->prefs;
1660 char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1661 Window w = event->xclient.window;
1664 Bool root_p = False;
1667 for (screen = 0; screen < si->nscreens; screen++)
1668 if (w == si->screens[screen].screensaver_window)
1670 strcpy (wdesc, "xscreensaver");
1673 else if (w == RootWindow (si->dpy, screen))
1675 strcpy (wdesc, "root");
1680 /* If this ClientMessage was sent to the real root window instead of to the
1681 xscreensaver window, then it might be intended for someone else who is
1682 listening on the root window (e.g., the window manager). So only print
1683 the warning if: we are in debug mode; or if the bogus message was
1684 actually sent to one of the xscreensaver-created windows.
1686 if (root_p && !p->debug_p)
1691 XErrorHandler old_handler;
1693 XWindowAttributes xgwa;
1694 memset (&hint, 0, sizeof(hint));
1695 memset (&xgwa, 0, sizeof(xgwa));
1697 XSync (si->dpy, False);
1698 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1699 XGetClassHint (si->dpy, w, &hint);
1700 XGetWindowAttributes (si->dpy, w, &xgwa);
1701 XSync (si->dpy, False);
1702 XSetErrorHandler (old_handler);
1703 XSync (si->dpy, False);
1705 screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1707 sprintf (wdesc, "%.20s / %.20s",
1708 (hint.res_name ? hint.res_name : "(null)"),
1709 (hint.res_class ? hint.res_class : "(null)"));
1710 if (hint.res_name) XFree (hint.res_name);
1711 if (hint.res_class) XFree (hint.res_class);
1714 fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1715 blurb(), screen, (str ? str : "(null)"));
1716 fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1717 blurb(), screen, (unsigned long) w, wdesc);
1718 if (str) XFree (str);
1725 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1727 saver_preferences *p = &si->prefs;
1729 Window window = event->xclient.window;
1731 /* Preferences might affect our handling of client messages. */
1732 maybe_reload_init_file (si);
1734 if (event->xclient.message_type != XA_SCREENSAVER ||
1735 event->xclient.format != 32)
1737 bogus_clientmessage_warning (si, event);
1741 type = event->xclient.data.l[0];
1742 if (type == XA_ACTIVATE)
1746 if (p->mode == DONT_BLANK)
1748 clientmessage_response(si, window, True,
1749 "ACTIVATE ClientMessage received in DONT_BLANK mode.",
1750 "screen blanking is currently disabled.");
1754 clientmessage_response(si, window, False,
1755 "ACTIVATE ClientMessage received.",
1757 si->selection_mode = 0;
1758 si->demoing_p = False;
1760 if (si->throttled_p && p->verbose_p)
1761 fprintf (stderr, "%s: unthrottled.\n", blurb());
1762 si->throttled_p = False;
1764 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1766 XForceScreenSaver (si->dpy, ScreenSaverActive);
1774 clientmessage_response(si, window, True,
1775 "ClientMessage ACTIVATE received while already active.",
1778 else if (type == XA_DEACTIVATE)
1782 if (si->throttled_p && p->verbose_p)
1783 fprintf (stderr, "%s: unthrottled.\n", blurb());
1784 si->throttled_p = False;
1786 clientmessage_response(si, window, False,
1787 "DEACTIVATE ClientMessage received.",
1789 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1791 XForceScreenSaver (si->dpy, ScreenSaverReset);
1799 clientmessage_response(si, window, False,
1800 "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
1801 "not active: idle timer reset.");
1804 else if (type == XA_CYCLE)
1808 clientmessage_response(si, window, False,
1809 "CYCLE ClientMessage received.",
1811 si->selection_mode = 0; /* 0 means randomize when its time. */
1812 si->demoing_p = False;
1814 if (si->throttled_p && p->verbose_p)
1815 fprintf (stderr, "%s: unthrottled.\n", blurb());
1816 si->throttled_p = False;
1819 XtRemoveTimeOut (si->cycle_id);
1821 cycle_timer ((XtPointer) si, 0);
1824 clientmessage_response(si, window, True,
1825 "ClientMessage CYCLE received while inactive.",
1828 else if (type == XA_NEXT || type == XA_PREV)
1830 clientmessage_response(si, window, False,
1832 ? "NEXT ClientMessage received."
1833 : "PREV ClientMessage received."),
1835 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1836 si->demoing_p = False;
1838 if (si->throttled_p && p->verbose_p)
1839 fprintf (stderr, "%s: unthrottled.\n", blurb());
1840 si->throttled_p = False;
1845 XtRemoveTimeOut (si->cycle_id);
1847 cycle_timer ((XtPointer) si, 0);
1852 else if (type == XA_SELECT)
1856 long which = event->xclient.data.l[1];
1858 if (p->mode == DONT_BLANK)
1860 clientmessage_response(si, window, True,
1861 "SELECT ClientMessage received in DONT_BLANK mode.",
1862 "screen blanking is currently disabled.");
1866 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1867 sprintf (buf2, "activating (%ld).", which);
1868 clientmessage_response (si, window, False, buf, buf2);
1870 if (which < 0) which = 0; /* 0 == "random" */
1871 si->selection_mode = which;
1872 si->demoing_p = False;
1874 if (si->throttled_p && p->verbose_p)
1875 fprintf (stderr, "%s: unthrottled.\n", blurb());
1876 si->throttled_p = False;
1881 XtRemoveTimeOut (si->cycle_id);
1883 cycle_timer ((XtPointer) si, 0);
1888 else if (type == XA_EXIT)
1890 /* Ignore EXIT message if the screen is locked. */
1891 if (until_idle_p || !si->locked_p)
1893 clientmessage_response (si, window, False,
1894 "EXIT ClientMessage received.",
1899 for (i = 0; i < si->nscreens; i++)
1900 kill_screenhack (&si->screens[i]);
1901 unblank_screen (si);
1902 XSync (si->dpy, False);
1904 saver_exit (si, 0, 0);
1907 clientmessage_response (si, window, True,
1908 "EXIT ClientMessage received while locked.",
1909 "screen is locked.");
1911 else if (type == XA_RESTART)
1913 /* The RESTART message works whether the screensaver is active or not,
1914 unless the screen is locked, in which case it doesn't work.
1916 if (until_idle_p || !si->locked_p)
1918 clientmessage_response (si, window, False,
1919 "RESTART ClientMessage received.",
1924 for (i = 0; i < si->nscreens; i++)
1925 kill_screenhack (&si->screens[i]);
1926 unblank_screen (si);
1927 XSync (si->dpy, False);
1930 restart_process (si); /* does not return */
1934 clientmessage_response (si, window, True,
1935 "RESTART ClientMessage received while locked.",
1936 "screen is locked.");
1938 else if (type == XA_DEMO)
1940 long arg = event->xclient.data.l[1];
1941 Bool demo_one_hack_p = (arg == 5000);
1943 if (demo_one_hack_p)
1947 long which = event->xclient.data.l[2];
1950 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1951 sprintf (buf2, "demoing (%ld).", which);
1952 clientmessage_response (si, window, False, buf, buf2);
1954 if (which < 0) which = 0; /* 0 == "random" */
1955 si->selection_mode = which;
1956 si->demoing_p = True;
1958 if (si->throttled_p && p->verbose_p)
1959 fprintf (stderr, "%s: unthrottled.\n", blurb());
1960 si->throttled_p = False;
1965 clientmessage_response (si, window, True,
1966 "DEMO ClientMessage received while active.",
1971 clientmessage_response (si, window, True,
1972 "obsolete form of DEMO ClientMessage.",
1973 "obsolete form of DEMO ClientMessage.");
1976 else if (type == XA_PREFS)
1978 clientmessage_response (si, window, True,
1979 "the PREFS client-message is obsolete.",
1980 "the PREFS client-message is obsolete.");
1982 else if (type == XA_LOCK)
1985 clientmessage_response (si, window, True,
1986 "not compiled with support for locking.",
1987 "locking not enabled.");
1988 #else /* !NO_LOCKING */
1989 if (si->locking_disabled_p)
1990 clientmessage_response (si, window, True,
1991 "LOCK ClientMessage received, but locking is disabled.",
1992 "locking not enabled.");
1993 else if (si->locked_p)
1994 clientmessage_response (si, window, True,
1995 "LOCK ClientMessage received while already locked.",
2000 char *response = (until_idle_p
2001 ? "activating and locking."
2003 sprintf (buf, "LOCK ClientMessage received; %s", response);
2004 clientmessage_response (si, window, False, buf, response);
2005 set_locked_p (si, True);
2006 si->selection_mode = 0;
2007 si->demoing_p = False;
2009 if (si->lock_id) /* we're doing it now, so lose the timeout */
2011 XtRemoveTimeOut (si->lock_id);
2017 if (si->using_mit_saver_extension ||
2018 si->using_sgi_saver_extension)
2020 XForceScreenSaver (si->dpy, ScreenSaverActive);
2029 #endif /* !NO_LOCKING */
2031 else if (type == XA_THROTTLE)
2033 /* The THROTTLE command is deprecated -- it predates the XDPMS
2034 extension. Instead of using -throttle, users should instead
2035 just power off the monitor (e.g., "xset dpms force off".)
2036 In a few minutes, xscreensaver will notice that the monitor
2037 is off, and cease running hacks.
2039 if (si->throttled_p)
2040 clientmessage_response (si, window, True,
2041 "THROTTLE ClientMessage received, but "
2042 "already throttled.",
2043 "already throttled.");
2047 char *response = "throttled.";
2048 si->throttled_p = True;
2049 si->selection_mode = 0;
2050 si->demoing_p = False;
2051 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
2052 clientmessage_response (si, window, False, buf, response);
2057 XtRemoveTimeOut (si->cycle_id);
2059 cycle_timer ((XtPointer) si, 0);
2063 else if (type == XA_UNTHROTTLE)
2065 if (! si->throttled_p)
2066 clientmessage_response (si, window, True,
2067 "UNTHROTTLE ClientMessage received, but "
2073 char *response = "unthrottled.";
2074 si->throttled_p = False;
2075 si->selection_mode = 0;
2076 si->demoing_p = False;
2077 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
2078 clientmessage_response (si, window, False, buf, response);
2083 XtRemoveTimeOut (si->cycle_id);
2085 cycle_timer ((XtPointer) si, 0);
2093 str = XGetAtomName_safe (si->dpy, type);
2097 if (strlen (str) > 80)
2098 strcpy (str+70, "...");
2099 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
2106 "unrecognised screensaver ClientMessage 0x%x received.",
2107 (unsigned int) event->xclient.data.l[0]);
2110 clientmessage_response (si, window, True, buf, buf);
2116 /* Some random diagnostics printed in -verbose mode.
2120 analyze_display (saver_info *si)
2124 const char *name; const char *desc;
2126 Status (*version_fn) (Display *, int *majP, int *minP);
2129 { "SCREEN_SAVER", /* underscore */ "SGI Screen-Saver",
2130 # ifdef HAVE_SGI_SAVER_EXTENSION
2135 }, { "SCREEN-SAVER", /* dash */ "SGI Screen-Saver",
2136 # ifdef HAVE_SGI_SAVER_EXTENSION
2141 }, { "MIT-SCREEN-SAVER", "MIT Screen-Saver",
2142 # ifdef HAVE_MIT_SAVER_EXTENSION
2143 True, XScreenSaverQueryVersion
2147 }, { "XIDLE", "XIdle",
2148 # ifdef HAVE_XIDLE_EXTENSION
2153 }, { "SGI-VIDEO-CONTROL", "SGI Video-Control",
2154 # ifdef HAVE_SGI_VC_EXTENSION
2155 True, XSGIvcQueryVersion
2159 }, { "READDISPLAY", "SGI Read-Display",
2160 # ifdef HAVE_READ_DISPLAY_EXTENSION
2161 True, XReadDisplayQueryVersion
2165 }, { "MIT-SHM", "Shared Memory",
2166 # ifdef HAVE_XSHM_EXTENSION
2167 True, (Status (*) (Display*,int*,int*)) XShmQueryVersion /* 4 args */
2171 }, { "DOUBLE-BUFFER", "Double-Buffering",
2172 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
2173 True, XdbeQueryExtension
2177 }, { "DPMS", "Power Management",
2178 # ifdef HAVE_DPMS_EXTENSION
2179 True, DPMSGetVersion
2189 }, { "XFree86-VidModeExtension", "XF86 Video-Mode",
2190 # ifdef HAVE_XF86VMODE
2191 True, XF86VidModeQueryVersion
2195 }, { "XC-VidModeExtension", "XC Video-Mode",
2196 # ifdef HAVE_XF86VMODE
2197 True, XF86VidModeQueryVersion
2201 }, { "XFree86-MISC", "XF86 Misc",
2202 # ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2203 True, XF86MiscQueryVersion
2207 }, { "XC-MISC", "XC Misc",
2208 # ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2209 True, XF86MiscQueryVersion
2213 }, { "XINERAMA", "Xinerama",
2214 # ifdef HAVE_XINERAMA
2215 True, XineramaQueryVersion
2219 }, { "RANDR", "Resize-and-Rotate",
2221 True, XRRQueryVersion
2227 }, { "NV-CONTROL", "NVidia",
2229 }, { "NV-GLX", "NVidia GLX",
2231 }, { "Apple-DRI", "Apple-DRI (XDarwin)",
2233 }, { "XInputExtension", "XInput",
2238 fprintf (stderr, "%s: running on display \"%s\"\n", blurb(),
2239 DisplayString(si->dpy));
2240 fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
2241 ServerVendor(si->dpy), VendorRelease(si->dpy));
2243 fprintf (stderr, "%s: useful extensions:\n", blurb());
2244 for (i = 0; i < countof(exts); i++)
2246 int op = 0, event = 0, error = 0;
2248 int maj = 0, min = 0;
2249 int dummy1, dummy2, dummy3;
2252 /* Most of the extension version functions take 3 args,
2253 writing results into args 2 and 3, but some take more.
2254 We only ever care about the first two results, but we
2255 pass in three extra pointers just in case.
2257 Status (*version_fn_2) (Display*,int*,int*,int*,int*,int*) =
2258 (Status (*) (Display*,int*,int*,int*,int*,int*)) exts[i].version_fn;
2260 if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
2262 sprintf (buf, "%s: ", blurb());
2264 strcat (buf, exts[i].desc);
2268 else if (version_fn_2 (si->dpy, &maj, &min, &dummy1, &dummy2, &dummy3))
2269 sprintf (buf+strlen(buf), " (%d.%d)", maj, min);
2271 strcat (buf, " (unavailable)");
2273 if (!exts[i].useful_p)
2274 strcat (buf, " (disabled at compile time)");
2275 fprintf (stderr, "%s\n", buf);
2278 for (i = 0; i < si->nscreens; i++)
2280 saver_screen_info *ssi = &si->screens[i];
2281 unsigned long colormapped_depths = 0;
2282 unsigned long non_mapped_depths = 0;
2283 XVisualInfo vi_in, *vi_out;
2286 if (!ssi->real_screen_p) continue;
2288 vi_in.screen = ssi->real_screen_number;
2289 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
2290 if (!vi_out) continue;
2291 for (j = 0; j < out_count; j++)
2292 if (vi_out[j].class == PseudoColor)
2293 colormapped_depths |= (1 << vi_out[j].depth);
2295 non_mapped_depths |= (1 << vi_out[j].depth);
2296 XFree ((char *) vi_out);
2298 if (colormapped_depths)
2300 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(),
2301 ssi->real_screen_number);
2302 for (j = 0; j < 32; j++)
2303 if (colormapped_depths & (1 << j))
2304 fprintf (stderr, " %d", j);
2305 fprintf (stderr, ".\n");
2307 if (non_mapped_depths)
2309 fprintf (stderr, "%s: screen %d non-colormapped depths:",
2310 blurb(), ssi->real_screen_number);
2311 for (j = 0; j < 32; j++)
2312 if (non_mapped_depths & (1 << j))
2313 fprintf (stderr, " %d", j);
2314 fprintf (stderr, ".\n");
2318 describe_monitor_layout (si);
2323 display_is_on_console_p (saver_info *si)
2325 Bool not_on_console = True;
2326 char *dpystr = DisplayString (si->dpy);
2327 char *tail = (char *) strchr (dpystr, ':');
2328 if (! tail || strncmp (tail, ":0", 2))
2329 not_on_console = True;
2332 char dpyname[255], localname[255];
2333 strncpy (dpyname, dpystr, tail-dpystr);
2334 dpyname [tail-dpystr] = 0;
2336 !strcmp(dpyname, "unix") ||
2337 !strcmp(dpyname, "localhost"))
2338 not_on_console = False;
2339 else if (gethostname (localname, sizeof (localname)))
2340 not_on_console = True; /* can't find hostname? */
2341 else if (!strncmp (dpyname, "/tmp/launch-", 12)) /* MacOS X launchd */
2342 not_on_console = False;
2345 /* We have to call gethostbyname() on the result of gethostname()
2346 because the two aren't guarenteed to be the same name for the
2347 same host: on some losing systems, one is a FQDN and the other
2348 is not. Here in the wide wonderful world of Unix it's rocket
2349 science to obtain the local hostname in a portable fashion.
2351 And don't forget, gethostbyname() reuses the structure it
2352 returns, so we have to copy the fucker before calling it again.
2353 Thank you master, may I have another.
2355 struct hostent *h = gethostbyname (dpyname);
2357 not_on_console = True;
2362 strcpy (hn, h->h_name);
2363 l = gethostbyname (localname);
2364 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
2368 return !not_on_console;
2372 /* Do a little bit of heap introspection...
2375 check_for_leaks (const char *where)
2377 #if defined(HAVE_SBRK) && defined(LEAK_PARANOIA)
2378 static unsigned long last_brk = 0;
2379 int b = (unsigned long) sbrk(0);
2380 if (last_brk && last_brk < b)
2381 fprintf (stderr, "%s: %s: brk grew by %luK.\n",
2383 (((b - last_brk) + 1023) / 1024));
2385 #endif /* HAVE_SBRK */