1 /* xscreensaver, Copyright (c) 1991-2016 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 * But here's a new kink that started showing up in late 2014: GNOME programs
97 * don't actually select for or receive KeyPress events! They do it behind
98 * the scenes through some kind of Input Method magic, even when running in
99 * an en_US locale. However, in that case, those applications *do* seem to
100 * update the _NET_WM_USER_TIME on their own windows every time they have
101 * received a secret KeyPress, so we *also* monitor that property on every
102 * window, and treat changes to it as identical to KeyPress.
104 * To detect mouse motion, we periodically wake up and poll the mouse
105 * position and button/modifier state, and notice when something has
106 * changed. We make this check every five seconds by default, and since the
107 * screensaver timeout has a granularity of one minute, this makes the
108 * chance of a false positive very small. We could detect mouse motion in
109 * the same way as keyboard activity, but that would suffer from the same
110 * "client changing event mask" problem that the KeyPress events hack does.
111 * I think polling is more reliable.
113 * On systems with /proc/interrupts (Linux) we poll that file and note when
114 * the interrupt counter numbers on the "keyboard" and "PS/2" lines change.
115 * (There is no reliable way, using /proc/interrupts, to detect non-PS/2
116 * mice, so it doesn't help for serial or USB mice.)
118 * None of this crap happens if we're using one of the extensions. Sadly,
119 * the XIdle extension hasn't been available for many years; the SGI
120 * extension only exists on SGIs; and the MIT extension, while widely
121 * deployed, is garbage in several ways.
123 * A third idle-detection option could be implemented (but is not): when
124 * running on the console display ($DISPLAY is `localhost`:0) and we're on a
125 * machine where /dev/tty and /dev/mouse have reasonable last-modification
126 * times, we could just stat() those. But the incremental benefit of
127 * implementing this is really small, so forget I said anything.
130 * - Have a second terminal handy.
131 * - Be careful where you set your breakpoints, you don't want this to
132 * stop under the debugger with the keyboard grabbed or the blackout
134 * - If you run your debugger under XEmacs, try M-ESC (x-grab-keyboard)
135 * to keep your emacs window alive even when xscreensaver has grabbed.
136 * - Go read the code related to `debug_p'.
137 * - You probably can't set breakpoints in functions that are called on
138 * the other side of a call to fork() -- if your subprocesses are
139 * dying with signal 5, Trace/BPT Trap, you're losing in this way.
140 * - If you aren't using a server extension, don't leave this stopped
141 * under the debugger for very long, or the X input buffer will get
142 * huge because of the keypress events it's selecting for. This can
143 * make your X server wedge with "no more input buffers."
145 * ======================================================================== */
153 #include <X11/Xlib.h>
157 # include <libintl.h>
158 #endif /* ENABLE_NLS */
160 #include <X11/Xlibint.h>
162 #include <X11/Xatom.h>
163 #include <X11/Intrinsic.h>
164 #include <X11/StringDefs.h>
165 #include <X11/Shell.h>
168 #include <sys/time.h>
169 #include <netdb.h> /* for gethostbyname() */
170 #include <sys/types.h>
174 # include <X11/Xmu/Error.h>
176 # include <Xmu/Error.h>
178 #else /* !HAVE_XMU */
180 #endif /* !HAVE_XMU */
182 #ifdef HAVE_MIT_SAVER_EXTENSION
183 #include <X11/extensions/scrnsaver.h>
184 #endif /* HAVE_MIT_SAVER_EXTENSION */
186 #ifdef HAVE_XIDLE_EXTENSION
187 # include <X11/extensions/xidle.h>
188 #endif /* HAVE_XIDLE_EXTENSION */
190 #ifdef HAVE_SGI_VC_EXTENSION
191 # include <X11/extensions/XSGIvc.h>
192 #endif /* HAVE_SGI_VC_EXTENSION */
194 #ifdef HAVE_READ_DISPLAY_EXTENSION
195 # include <X11/extensions/readdisplay.h>
196 #endif /* HAVE_READ_DISPLAY_EXTENSION */
198 #ifdef HAVE_XSHM_EXTENSION
199 # include <X11/extensions/XShm.h>
200 #endif /* HAVE_XSHM_EXTENSION */
202 #ifdef HAVE_DPMS_EXTENSION
203 # include <X11/extensions/dpms.h>
204 #endif /* HAVE_DPMS_EXTENSION */
207 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
208 # include <X11/extensions/Xdbe.h>
209 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
211 #ifdef HAVE_XF86VMODE
212 # include <X11/extensions/xf86vmode.h>
213 #endif /* HAVE_XF86VMODE */
215 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
216 # include <X11/extensions/xf86misc.h>
217 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
220 # include <X11/extensions/Xinerama.h>
221 #endif /* HAVE_XINERAMA */
224 # include <X11/extensions/Xrandr.h>
225 #endif /* HAVE_RANDR */
228 #include "xscreensaver.h"
230 #include "yarandom.h"
231 #include "resources.h"
236 saver_info *global_si_kludge = 0; /* I hate C so much... */
243 static Atom XA_SCREENSAVER_RESPONSE;
244 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
245 static Atom XA_RESTART, XA_SELECT;
246 static Atom XA_THROTTLE, XA_UNTHROTTLE;
247 Atom XA_DEMO, XA_PREFS, XA_EXIT, XA_LOCK, XA_BLANK;
250 static XrmOptionDescRec options [] = {
252 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
253 { "-silent", ".verbose", XrmoptionNoArg, "off" },
255 /* xscreensaver-demo uses this one */
256 { "-nosplash", ".splash", XrmoptionNoArg, "off" },
257 { "-no-splash", ".splash", XrmoptionNoArg, "off" },
259 /* useful for debugging */
260 { "-no-capture-stderr", ".captureStderr", XrmoptionNoArg, "off" },
261 { "-log", ".logFile", XrmoptionSepArg, 0 },
265 __extension__ /* shut up about "string length is greater than the length
266 ISO C89 compilers are required to support" when including
270 static char *defaults[] = {
271 #include "XScreenSaver_ad.h"
276 ERROR! You must not include vroot.h in this file.
280 do_help (saver_info *si)
283 s = strchr (screensaver_id, '-');
284 s = strrchr (s, '-');
286 strncpy (year, s, 4);
292 xscreensaver %s, copyright (c) 1991-%s by Jamie Zawinski <jwz@jwz.org>\n\
294 All xscreensaver configuration is via the `~/.xscreensaver' file.\n\
295 Rather than editing that file by hand, just run `xscreensaver-demo':\n\
296 that program lets you configure the screen saver graphically,\n\
297 including timeouts, locking, and display modes.\n\
301 Just getting started? Try this:\n\
306 For updates, online manual, and FAQ, please see the web page:\n\
308 https://www.jwz.org/xscreensaver/\n\
317 Bool in_signal_handler_p = 0; /* I hate C so much... */
320 timestring (time_t when)
322 if (in_signal_handler_p)
324 /* Turns out that ctime() and even localtime_r() call malloc() on Linux!
325 So we can't call them from inside SIGCHLD. WTF.
328 strcpy (buf, "... ... .. signal ....");
334 if (! when) when = time ((time_t *) 0);
335 str = (char *) ctime (&when);
336 nl = (char *) strchr (str, '\n');
337 if (nl) *nl = 0; /* take off that dang newline */
342 static Bool blurb_timestamp_p = True; /* kludge */
347 if (!blurb_timestamp_p)
351 static char buf[255];
352 char *ct = timestring(0);
353 int n = strlen(progname);
355 strncpy(buf, progname, n);
358 strncpy(buf+n, ct+11, 8);
359 strcpy(buf+n+9, ": ");
366 saver_ehandler (Display *dpy, XErrorEvent *error)
368 saver_info *si = global_si_kludge; /* I hate C so much... */
372 if (!real_stderr) real_stderr = stderr;
374 fprintf (real_stderr, "\n"
375 "#######################################"
376 "#######################################\n\n"
377 "%s: X Error! PLEASE REPORT THIS BUG.\n",
380 for (i = 0; i < si->nscreens; i++)
382 saver_screen_info *ssi = &si->screens[i];
383 fprintf (real_stderr, "%s: screen %d/%d: 0x%x, 0x%x, 0x%x\n",
384 blurb(), ssi->real_screen_number, ssi->number,
385 (unsigned int) RootWindowOfScreen (si->screens[i].screen),
386 (unsigned int) si->screens[i].real_vroot,
387 (unsigned int) si->screens[i].screensaver_window);
390 fprintf (real_stderr, "\n"
391 "#######################################"
392 "#######################################\n\n");
394 fatal_p = XmuPrintDefaultErrorMessage (dpy, error, real_stderr);
396 fatal_p = True; /* The only time I've ever seen a supposedly nonfatal error,
397 it has been BadImplementation / Xlib sequence lost, which
398 are in truth pretty damned fatal.
401 fprintf (real_stderr, "\n");
404 fprintf (real_stderr, "%s: nonfatal error.\n\n", blurb());
407 if (si->prefs.xsync_p)
409 saver_exit (si, -1, "because of synchronous X Error");
414 __extension__ /* don't warn about "string length is greater than the
415 length ISO C89 compilers are required to support". */
417 fprintf (real_stderr,
418 "#######################################################################\n"
420 " If at all possible, please re-run xscreensaver with the command\n"
421 " line arguments `-sync -verbose -log log.txt', and reproduce this\n"
422 " bug. That will cause xscreensaver to dump a `core' file to the\n"
423 " current directory. Please include the stack trace from that core\n"
424 " file in your bug report. *DO NOT* mail the core file itself! That\n"
425 " won't work. A \"log.txt\" file will also be written. Please *do*\n"
426 " include the complete \"log.txt\" file with your bug report.\n"
428 " https://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
429 " the most useful bug reports, and how to examine core files.\n"
431 " The more information you can provide, the better. But please\n"
432 " report this bug, regardless!\n"
434 "#######################################################################\n"
438 saver_exit (si, -1, 0);
446 /* This error handler is used only while the X connection is being set up;
447 after we've got a connection, we don't use this handler again. The only
448 reason for having this is so that we can present a more idiot-proof error
449 message than "cannot open display."
452 startup_ehandler (String name, String type, String class,
453 String defalt, /* one can't even spel properly
454 in this joke of a language */
455 String *av, Cardinal *ac)
459 saver_info *si = global_si_kludge; /* I hate C so much... */
460 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
462 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
463 fmt, sizeof(fmt)-1, *db);
465 fprintf (stderr, "%s: ", blurb());
467 memset (p, 0, sizeof(p));
468 if (*ac > countof (p)) *ac = countof (p);
469 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
470 fprintf (stderr, fmt, /* Did I mention that I hate C? */
471 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
472 fprintf (stderr, "\n");
474 describe_uids (si, stderr);
476 if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
478 fprintf (stderr, "\n"
479 "%s: This is probably because you're logging in as root. You\n"
480 " shouldn't log in as root: you should log in as a normal user,\n"
481 " and then `su' as needed. If you insist on logging in as\n"
482 " root, you will have to turn off X's security features before\n"
483 " xscreensaver will work.\n"
485 " Please read the manual and FAQ for more information:\n",
490 fprintf (stderr, "\n"
491 "%s: Errors at startup are usually authorization problems.\n"
492 " But you're not logging in as root (good!) so something\n"
493 " else must be wrong. Did you read the manual and the FAQ?\n",
497 fprintf (stderr, "\n"
498 " https://www.jwz.org/xscreensaver/faq.html\n"
499 " https://www.jwz.org/xscreensaver/man.html\n"
508 /* The zillions of initializations.
511 /* Set progname, version, etc. This is done very early.
514 set_version_string (saver_info *si, int *argc, char **argv)
516 progclass = "XScreenSaver";
518 /* progname is reset later, after we connect to X. */
519 progname = strrchr(argv[0], '/');
520 if (progname) progname++;
521 else progname = argv[0];
523 if (strlen(progname) > 100) /* keep it short. */
526 /* The X resource database blows up if argv[0] has a "." in it. */
529 while ((s = strchr (s, '.')))
533 si->version = (char *) malloc (5);
534 memcpy (si->version, screensaver_id + 17, 4);
539 /* Initializations that potentially take place as a priveleged user:
540 If the xscreensaver executable is setuid root, then these initializations
541 are run as root, before discarding privileges.
544 privileged_initialization (saver_info *si, int *argc, char **argv)
547 /* before hack_uid() for proper permissions */
548 lock_priv_init (*argc, argv, si->prefs.verbose_p);
549 #endif /* NO_LOCKING */
555 /* Figure out what locking mechanisms are supported.
558 lock_initialization (saver_info *si, int *argc, char **argv)
561 si->locking_disabled_p = True;
562 si->nolock_reason = "not compiled with locking support";
563 #else /* !NO_LOCKING */
565 /* Finish initializing locking, now that we're out of privileged code. */
566 if (! lock_init (*argc, argv, si->prefs.verbose_p))
568 si->locking_disabled_p = True;
569 si->nolock_reason = "error getting password";
572 /* If locking is currently enabled, but the environment indicates that
573 we have been launched as GDM's "Background" program, then disable
574 locking just in case.
576 if (!si->locking_disabled_p && getenv ("RUNNING_UNDER_GDM"))
578 si->locking_disabled_p = True;
579 si->nolock_reason = "running under GDM";
582 /* If the server is XDarwin (MacOS X) then disable locking.
583 (X grabs only affect X programs, so you can use Command-Tab
584 to bring any other Mac program to the front, e.g., Terminal.)
586 if (!si->locking_disabled_p)
588 int op = 0, event = 0, error = 0;
589 Bool macos_p = False;
592 /* Disable locking if *running* on Apple hardware, since we have no
593 reliable way to determine whether the server is running on MacOS.
594 Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
595 but I'm not really sure about that.
601 /* This extension exists on the Apple X11 server, but not
602 on earlier versions of the XDarwin server. */
603 macos_p = XQueryExtension (si->dpy, "Apple-DRI", &op, &event, &error);
607 si->locking_disabled_p = True;
608 si->nolock_reason = "Cannot lock securely on MacOS X";
612 /* Like MacOS, locking under Wayland's embedded X11 server does not work.
613 (X11 grabs don't work because the Wayland window manager lives at a
614 higher level than the X11 emulation layer.)
616 if (!si->locking_disabled_p && getenv ("WAYLAND_DISPLAY"))
618 si->locking_disabled_p = True;
619 si->nolock_reason = "Cannot lock securely under Wayland";
622 if (si->prefs.debug_p) /* But allow locking anyway in debug mode. */
623 si->locking_disabled_p = False;
625 #endif /* NO_LOCKING */
629 /* Open the connection to the X server, and intern our Atoms.
632 connect_to_server (saver_info *si, int *argc, char **argv)
634 Widget toplevel_shell;
637 char *d = getenv ("DISPLAY");
640 char *ndpy = strdup("DISPLAY=:0.0");
641 /* if (si->prefs.verbose_p) */ /* sigh, too early to test this... */
643 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
647 /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
648 glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
649 do not. So we must leak it (and/or the previous setting). Yay.
652 #endif /* HAVE_PUTENV */
654 XSetErrorHandler (saver_ehandler);
656 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
657 toplevel_shell = XtAppInitialize (&si->app, progclass,
658 options, XtNumber (options),
659 argc, argv, defaults, 0, 0);
660 XtAppSetErrorMsgHandler (si->app, 0);
662 si->dpy = XtDisplay (toplevel_shell);
663 si->prefs.db = XtDatabase (si->dpy);
664 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
666 if(strlen(progname) > 100) /* keep it short. */
669 db = si->prefs.db; /* resources.c needs this */
671 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
672 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
673 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
674 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
675 XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
676 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
678 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
679 XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False);
680 XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False);
681 XA_NET_WM_USER_TIME = XInternAtom (si->dpy, "_NET_WM_USER_TIME", False);
682 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
683 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
684 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
685 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
686 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
687 XA_PREV = XInternAtom (si->dpy, "PREV", False);
688 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
689 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
690 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
691 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
692 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
693 XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
694 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
695 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
697 return toplevel_shell;
701 /* Handle the command-line arguments that were not handled for us by Xt.
702 Issue an error message and exit if there are unknown options.
705 process_command_line (saver_info *si, int *argc, char **argv)
708 for (i = 1; i < *argc; i++)
710 if (!strcmp (argv[i], "-debug"))
711 /* no resource for this one, out of paranoia. */
712 si->prefs.debug_p = True;
714 else if (!strcmp (argv[i], "-h") ||
715 !strcmp (argv[i], "-help") ||
716 !strcmp (argv[i], "--help"))
721 const char *s = argv[i];
722 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
725 if (s[0] == '-' && s[1] == '-') s++;
726 if (!strcmp (s, "-activate") ||
727 !strcmp (s, "-deactivate") ||
728 !strcmp (s, "-cycle") ||
729 !strcmp (s, "-next") ||
730 !strcmp (s, "-prev") ||
731 !strcmp (s, "-exit") ||
732 !strcmp (s, "-restart") ||
733 !strcmp (s, "-demo") ||
734 !strcmp (s, "-prefs") ||
735 !strcmp (s, "-preferences") ||
736 !strcmp (s, "-lock") ||
737 !strcmp (s, "-version") ||
738 !strcmp (s, "-time"))
741 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
742 fprintf (stderr, "\n\
743 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
745 fprintf (stderr, "\n\
746 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
749 The `xscreensaver' program is a daemon that runs in the background.\n\
750 You control a running xscreensaver process by sending it messages\n\
751 with `xscreensaver-demo' or `xscreensaver-command'.\n\
752 . See the man pages for details, or check the web page:\n\
753 https://www.jwz.org/xscreensaver/\n\n");
762 /* Print out the xscreensaver banner to the tty if applicable;
763 Issue any other warnings that are called for at this point.
766 print_banner (saver_info *si)
768 saver_preferences *p = &si->prefs;
771 s = strchr (screensaver_id, '-');
772 s = strrchr (s, '-');
774 strncpy (year, s, 4);
777 /* This resource gets set some time before the others, so that we know
778 whether to print the banner (and so that the banner gets printed before
779 any resource-database-related error messages.)
781 p->verbose_p = (p->debug_p ||
782 get_boolean_resource (si->dpy, "verbose", "Boolean"));
784 /* Ditto, for the locking_disabled_p message. */
785 p->lock_p = get_boolean_resource (si->dpy, "lock", "Boolean");
789 "%s %s, copyright (c) 1991-%s "
790 "by Jamie Zawinski <jwz@jwz.org>.\n",
791 progname, si->version, year);
794 fprintf (stderr, "\n"
795 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
797 "\tNote that in debug mode, the xscreensaver window will only\n"
798 "\tcover the left half of the screen. (The idea is that you\n"
799 "\tcan still see debugging output in a shell, if you position\n"
800 "\tit on the right side of the screen.)\n"
802 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
803 "\tuntrusted environments.\n"
807 if (p->verbose_p && decrepit_p ())
808 fprintf (stderr, "\n"
809 "*************************************"
810 "**************************************\n"
811 "%s: Warning: this version of xscreensaver is VERY OLD!\n"
812 "%s: Please upgrade! https://www.jwz.org/xscreensaver/\n"
813 "*************************************"
814 "**************************************\n"
820 if (!si->uid_message || !*si->uid_message)
821 describe_uids (si, stderr);
824 if (si->orig_uid && *si->orig_uid)
825 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
826 blurb(), si->orig_uid);
827 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
830 fprintf (stderr, "%s: in process %lu.\n", blurb(),
831 (unsigned long) getpid());
836 print_lock_failure_banner (saver_info *si)
838 saver_preferences *p = &si->prefs;
840 /* If locking was not able to be initalized for some reason, explain why.
841 (This has to be done after we've read the lock_p resource.)
843 if (si->locking_disabled_p)
846 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
848 if (strstr (si->nolock_reason, "passw"))
849 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
850 "consult the manual.\n", blurb());
851 else if (strstr (si->nolock_reason, "running as "))
853 "%s: locking only works when xscreensaver is launched\n"
854 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
855 "\t See the manual for details.\n",
862 /* called from screens.c so that all the Xt crud is here. */
864 initialize_screen_root_widget (saver_screen_info *ssi)
866 saver_info *si = ssi->global;
867 if (ssi->toplevel_shell)
868 XtDestroyWidget (ssi->toplevel_shell);
869 ssi->toplevel_shell =
870 XtVaAppCreateShell (progname, progclass,
871 applicationShellWidgetClass,
873 XtNscreen, ssi->screen,
874 XtNvisual, ssi->current_visual,
875 XtNdepth, visual_depth (ssi->screen,
876 ssi->current_visual),
881 /* Examine all of the display's screens, and populate the `saver_screen_info'
882 structures. Make sure this is called after hack_environment() sets $PATH.
885 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
889 update_screen_layout (si);
891 /* Check to see whether fading is ever possible -- if any of the
892 screens on the display has a PseudoColor visual, then fading can
893 work (on at least some screens.) If no screen has a PseudoColor
894 visual, then don't bother ever trying to fade, because it will
895 just cause a delay without causing any visible effect.
897 for (i = 0; i < si->nscreens; i++)
899 saver_screen_info *ssi = &si->screens[i];
900 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
901 get_visual (ssi->screen, "PseudoColor", True, False) ||
902 get_visual (ssi->screen, "GrayScale", True, False))
904 si->fading_possible_p = True;
909 #ifdef HAVE_XF86VMODE_GAMMA
910 si->fading_possible_p = True; /* if we can gamma fade, go for it */
915 /* If any server extensions have been requested, try and initialize them.
916 Issue warnings if requests can't be honored.
919 initialize_server_extensions (saver_info *si)
921 saver_preferences *p = &si->prefs;
923 Bool server_has_xidle_extension_p = False;
924 Bool server_has_sgi_saver_extension_p = False;
925 Bool server_has_mit_saver_extension_p = False;
926 Bool system_has_proc_interrupts_p = False;
927 Bool server_has_xinput_extension_p = False;
928 const char *piwhy = 0;
930 si->using_xidle_extension = p->use_xidle_extension;
931 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
932 si->using_mit_saver_extension = p->use_mit_saver_extension;
933 si->using_proc_interrupts = p->use_proc_interrupts;
934 si->using_xinput_extension = p->use_xinput_extension;
936 #ifdef HAVE_XIDLE_EXTENSION
939 server_has_xidle_extension_p = XidleQueryExtension (si->dpy, &ev, &er);
942 #ifdef HAVE_SGI_SAVER_EXTENSION
943 server_has_sgi_saver_extension_p =
944 XScreenSaverQueryExtension (si->dpy,
945 &si->sgi_saver_ext_event_number,
946 &si->sgi_saver_ext_error_number);
948 #ifdef HAVE_MIT_SAVER_EXTENSION
949 server_has_mit_saver_extension_p =
950 XScreenSaverQueryExtension (si->dpy,
951 &si->mit_saver_ext_event_number,
952 &si->mit_saver_ext_error_number);
954 #ifdef HAVE_PROC_INTERRUPTS
955 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
959 server_has_xinput_extension_p = query_xinput_extension (si);
962 if (!server_has_xidle_extension_p)
963 si->using_xidle_extension = False;
964 else if (p->verbose_p)
966 if (si->using_xidle_extension)
967 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
969 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
972 if (!server_has_sgi_saver_extension_p)
973 si->using_sgi_saver_extension = False;
974 else if (p->verbose_p)
976 if (si->using_sgi_saver_extension)
977 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
980 "%s: not using server's SGI SCREEN_SAVER extension.\n",
984 if (!server_has_mit_saver_extension_p)
985 si->using_mit_saver_extension = False;
986 else if (p->verbose_p)
988 if (si->using_mit_saver_extension)
989 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
993 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
998 if (XRRQueryExtension (si->dpy,
999 &si->randr_event_number, &si->randr_error_number))
1001 int nscreens = ScreenCount (si->dpy); /* number of *real* screens */
1004 si->using_randr_extension = TRUE;
1007 fprintf (stderr, "%s: selecting RANDR events\n", blurb());
1008 for (i = 0; i < nscreens; i++)
1009 # ifdef RRScreenChangeNotifyMask /* randr.h 1.5, 2002/09/29 */
1010 XRRSelectInput (si->dpy, RootWindow (si->dpy, i),
1011 RRScreenChangeNotifyMask);
1012 # else /* !RRScreenChangeNotifyMask */ /* Xrandr.h 1.4, 2001/06/07 */
1013 XRRScreenChangeSelectInput (si->dpy, RootWindow (si->dpy, i), True);
1014 # endif /* !RRScreenChangeNotifyMask */
1016 # endif /* HAVE_RANDR */
1019 if (!server_has_xinput_extension_p)
1020 si->using_xinput_extension = False;
1023 if (si->using_xinput_extension)
1024 init_xinput_extension(si);
1028 if (si->using_xinput_extension)
1030 "%s: selecting events from %d XInputExtension devices.\n",
1031 blurb(), si->num_xinput_devices);
1034 "%s: not using XInputExtension.\n",
1040 if (!system_has_proc_interrupts_p)
1042 si->using_proc_interrupts = False;
1043 if (p->verbose_p && piwhy)
1044 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
1047 else if (p->verbose_p)
1049 if (si->using_proc_interrupts)
1051 "%s: consulting /proc/interrupts for keyboard activity.\n",
1055 "%s: not consulting /proc/interrupts for keyboard activity.\n",
1061 #ifdef DEBUG_MULTISCREEN
1063 debug_multiscreen_timer (XtPointer closure, XtIntervalId *id)
1065 saver_info *si = (saver_info *) closure;
1066 saver_preferences *p = &si->prefs;
1067 if (update_screen_layout (si))
1071 fprintf (stderr, "%s: new layout:\n", blurb());
1072 describe_monitor_layout (si);
1074 resize_screensaver_window (si);
1076 XtAppAddTimeOut (si->app, 1000*4, debug_multiscreen_timer, (XtPointer) si);
1078 #endif /* DEBUG_MULTISCREEN */
1081 /* For the case where we aren't using an server extensions, select user events
1082 on all the existing windows, and launch timers to select events on
1083 newly-created windows as well.
1085 If a server extension is being used, this does nothing.
1088 select_events (saver_info *si)
1090 saver_preferences *p = &si->prefs;
1093 if (si->using_xidle_extension ||
1094 si->using_mit_saver_extension ||
1095 si->using_sgi_saver_extension)
1098 if (p->initial_delay)
1102 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
1103 (int) p->initial_delay/1000,
1104 (p->initial_delay == 1000 ? "" : "s"));
1108 usleep (p->initial_delay);
1110 fprintf (stderr, " done.\n");
1115 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
1120 /* Select events on the root windows of every screen. This also selects
1121 for window creation events, so that new subwindows will be noticed.
1123 for (i = 0; i < si->nscreens; i++)
1125 saver_screen_info *ssi = &si->screens[i];
1126 if (ssi->real_screen_p)
1127 start_notice_events_timer (si,
1128 RootWindowOfScreen (si->screens[i].screen), False);
1132 fprintf (stderr, " done.\n");
1134 # ifdef DEBUG_MULTISCREEN
1135 if (p->debug_p) debug_multiscreen_timer ((XtPointer) si, 0);
1141 maybe_reload_init_file (saver_info *si)
1143 saver_preferences *p = &si->prefs;
1144 if (init_file_changed_p (p))
1147 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
1148 blurb(), init_file_name());
1150 load_init_file (si->dpy, p);
1152 /* If a server extension is in use, and p->timeout has changed,
1153 we need to inform the server of the new timeout. */
1154 disable_builtin_screensaver (si, False);
1156 /* If the DPMS settings in the init file have changed,
1157 change the settings on the server to match. */
1158 sync_server_dpms_settings (si->dpy,
1159 (p->dpms_enabled_p &&
1160 p->mode != DONT_BLANK),
1162 p->dpms_standby / 1000,
1163 p->dpms_suspend / 1000,
1172 - wait until the user is idle;
1174 - wait until the user is active;
1175 - unblank the screen;
1180 main_loop (saver_info *si)
1182 saver_preferences *p = &si->prefs;
1188 Bool was_locked = False;
1191 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1193 check_for_leaks ("unblanked A");
1194 sleep_until_idle (si, True);
1195 check_for_leaks ("unblanked B");
1200 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
1201 si->selection_mode, timestring(0));
1203 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
1207 maybe_reload_init_file (si);
1209 if (p->mode == DONT_BLANK)
1212 fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
1213 blurb(), timestring(0));
1215 /* Go around the loop and wait for the next bout of idleness,
1216 or for the init file to change, or for a remote command to
1217 come in, or something.
1219 But, if locked_p is true, go ahead. This can only happen
1220 if we're in "disabled" mode but a "lock" clientmessage came
1221 in: in that case, we should go ahead and blank/lock the screen.
1227 /* Since we're about to blank the screen, kill the de-race timer,
1228 if any. It might still be running if we have unblanked and then
1229 re-blanked in a short period (e.g., when using the "next" button
1230 in xscreensaver-demo.)
1235 fprintf (stderr, "%s: stopping de-race timer (%d remaining.)\n",
1236 blurb(), si->de_race_ticks);
1237 XtRemoveTimeOut (si->de_race_id);
1242 /* Now, try to blank.
1245 if (! blank_screen (si))
1247 /* We were unable to grab either the keyboard or mouse.
1248 This means we did not (and must not) blank the screen.
1249 If we were to blank the screen while some other program
1250 is holding both the mouse and keyboard grabbed, then
1251 we would never be able to un-blank it! We would never
1252 see any events, and the display would be wedged.
1254 In particular, without that keyboard grab, we will be
1255 unable to ever read keypresses on the unlock dialog.
1256 You can't unlock if you can't type your password.
1258 So, just go around the loop again and wait for the
1259 next bout of idleness. (If the user remains idle, we
1260 will next try to blank the screen again in no more than
1263 Time retry = 60 * 1000;
1264 if (p->timeout < retry)
1270 "%s: DEBUG MODE: unable to grab -- BLANKING ANYWAY.\n",
1276 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
1279 /* Since we were unable to blank, clearly we're not locked,
1280 but we might have been prematurely marked as locked by
1281 the LOCK ClientMessage. */
1283 set_locked_p (si, False);
1285 schedule_wakeup_event (si, retry, p->debug_p);
1290 for (i = 0; i < si->nscreens; i++)
1291 kill_screenhack (&si->screens[i]);
1293 raise_window (si, True, True, False);
1294 if (si->throttled_p)
1295 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
1297 for (i = 0; i < si->nscreens; i++)
1298 spawn_screenhack (&si->screens[i]);
1300 /* If we are blanking only, optionally power down monitor right now. */
1301 if (p->mode == BLANK_ONLY &&
1302 p->dpms_enabled_p &&
1305 sync_server_dpms_settings (si->dpy, True,
1307 p->dpms_standby / 1000,
1308 p->dpms_suspend / 1000,
1311 monitor_power_on (si, False);
1314 /* Don't start the cycle timer in demo mode. */
1315 if (!si->demoing_p && p->cycle)
1316 si->cycle_id = XtAppAddTimeOut (si->app,
1318 /* see comment in cycle_timer() */
1326 /* Maybe start locking the screen.
1329 Time lock_timeout = p->lock_timeout;
1331 if (si->emergency_lock_p && p->lock_p && lock_timeout)
1333 int secs = p->lock_timeout / 1000;
1336 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1338 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1342 si->emergency_lock_p = False;
1344 if (!si->demoing_p && /* if not going into demo mode */
1345 p->lock_p && /* and locking is enabled */
1346 !si->locking_disabled_p && /* and locking is possible */
1347 lock_timeout == 0) /* and locking is not timer-deferred */
1348 set_locked_p (si, True); /* then lock right now. */
1350 /* locked_p might be true already because of the above, or because of
1351 the LOCK ClientMessage. But if not, and if we're supposed to lock
1352 after some time, set up a timer to do so.
1357 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1358 activate_lock_timer,
1361 #endif /* !NO_LOCKING */
1364 ok_to_unblank = True;
1367 check_for_leaks ("blanked A");
1368 sleep_until_idle (si, False); /* until not idle */
1369 check_for_leaks ("blanked B");
1371 maybe_reload_init_file (si);
1374 /* Maybe unlock the screen.
1378 saver_screen_info *ssi = si->default_screen;
1379 if (si->locking_disabled_p) abort ();
1382 si->dbox_up_p = True;
1383 for (i = 0; i < si->nscreens; i++)
1384 suspend_screenhack (&si->screens[i], True); /* suspend */
1385 XUndefineCursor (si->dpy, ssi->screensaver_window);
1387 ok_to_unblank = unlock_p (si);
1389 si->dbox_up_p = False;
1390 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1391 for (i = 0; i < si->nscreens; i++)
1392 suspend_screenhack (&si->screens[i], False); /* resume */
1394 if (!ok_to_unblank &&
1395 !screenhack_running_p (si))
1397 /* If the lock dialog has been dismissed and we're not about to
1398 unlock the screen, and there is currently no hack running,
1399 then launch one. (There might be no hack running if DPMS
1400 had kicked in. But DPMS is off now, so bring back the hack)
1403 XtRemoveTimeOut (si->cycle_id);
1405 cycle_timer ((XtPointer) si, 0);
1408 #endif /* !NO_LOCKING */
1410 } while (!ok_to_unblank);
1414 fprintf (stderr, "%s: unblanking screen at %s.\n",
1415 blurb(), timestring (0));
1417 /* Kill before unblanking, to stop drawing as soon as possible. */
1418 for (i = 0; i < si->nscreens; i++)
1419 kill_screenhack (&si->screens[i]);
1420 unblank_screen (si);
1422 set_locked_p (si, False);
1423 si->emergency_lock_p = False;
1425 si->selection_mode = 0;
1427 /* If we're throttled, and the user has explicitly unlocked the screen,
1428 then unthrottle. If we weren't locked, then don't unthrottle
1429 automatically, because someone might have just bumped the desk... */
1432 if (si->throttled_p && p->verbose_p)
1433 fprintf (stderr, "%s: unthrottled.\n", blurb());
1434 si->throttled_p = False;
1439 XtRemoveTimeOut (si->cycle_id);
1445 XtRemoveTimeOut (si->lock_id);
1449 /* Since we're unblanked now, break race conditions and make
1450 sure we stay that way (see comment in timers.c.) */
1451 if (! si->de_race_id)
1452 de_race_timer ((XtPointer) si, 0);
1456 static void analyze_display (saver_info *si);
1457 static void fix_fds (void);
1460 main (int argc, char **argv)
1464 saver_info *si = &the_si;
1465 saver_preferences *p = &si->prefs;
1466 struct passwd *spasswd;
1469 /* It turns out that if we do setlocale (LC_ALL, "") here, people
1470 running in Japanese locales get font craziness on the password
1471 dialog, presumably because it is displaying Japanese characters
1472 in a non-Japanese font. However, if we don't call setlocale()
1473 at all, then XLookupString() never returns multi-byte UTF-8
1474 characters when people type non-Latin1 characters on the
1477 The current theory (and at this point, I'm really guessing!) is
1478 that using LC_CTYPE instead of LC_ALL will make XLookupString()
1479 behave usefully, without having the side-effect of screwing up
1480 the fonts on the unlock dialog.
1482 See https://bugs.launchpad.net/ubuntu/+source/xscreensaver/+bug/671923
1483 from comment #20 onward.
1488 if (!setlocale (LC_CTYPE, ""))
1489 fprintf (stderr, "%s: warning: could not set default locale\n",
1492 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1493 textdomain (GETTEXT_PACKAGE);
1494 #endif /* ENABLE_NLS */
1496 memset(si, 0, sizeof(*si));
1497 global_si_kludge = si; /* I hate C so much... */
1501 # undef ya_rand_init
1504 save_argv (argc, argv);
1505 set_version_string (si, &argc, argv);
1506 privileged_initialization (si, &argc, argv);
1507 hack_environment (si);
1509 spasswd = getpwuid(getuid());
1512 fprintf(stderr, "Could not figure out who the current user is!\n");
1516 si->user = strdup(spasswd->pw_name ? spasswd->pw_name : "(unknown)");
1519 si->unlock_cb = gui_auth_conv;
1520 si->auth_finished_cb = auth_finished_cb;
1521 # endif /* !NO_LOCKING */
1523 shell = connect_to_server (si, &argc, argv);
1524 process_command_line (si, &argc, argv);
1525 stderr_log_file (si);
1528 load_init_file(si->dpy, p); /* must be before initialize_per_screen_info() */
1529 blurb_timestamp_p = p->timestamp_p; /* kludge */
1530 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1532 /* We can only issue this warning now. */
1533 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1535 "%s: there are no PseudoColor or GrayScale visuals.\n"
1536 "%s: ignoring the request for fading/unfading.\n",
1539 for (i = 0; i < si->nscreens; i++)
1541 saver_screen_info *ssi = &si->screens[i];
1542 if (ssi->real_screen_p)
1543 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1547 lock_initialization (si, &argc, argv);
1548 print_lock_failure_banner (si);
1550 if (p->xsync_p) XSynchronize (si->dpy, True);
1552 if (p->verbose_p) analyze_display (si);
1553 initialize_server_extensions (si);
1555 si->blank_time = time ((time_t *) 0); /* must be before ..._window */
1556 initialize_screensaver_window (si);
1561 disable_builtin_screensaver (si, True);
1562 sync_server_dpms_settings (si->dpy,
1563 (p->dpms_enabled_p &&
1564 p->mode != DONT_BLANK),
1566 p->dpms_standby / 1000,
1567 p->dpms_suspend / 1000,
1571 initialize_stderr (si);
1572 handle_signals (si);
1574 make_splash_dialog (si);
1576 main_loop (si); /* doesn't return */
1583 /* Bad Things Happen if stdin, stdout, and stderr have been closed
1584 (as by the `sh incantation "xscreensaver >&- 2>&-"). When you do
1585 that, the X connection gets allocated to one of these fds, and
1586 then some random library writes to stderr, and random bits get
1587 stuffed down the X pipe, causing "Xlib: sequence lost" errors.
1588 So, we cause the first three file descriptors to be open to
1589 /dev/null if they aren't open to something else already. This
1590 must be done before any other files are opened (or the closing
1591 of that other file will again free up one of the "magic" first
1594 We do this by opening /dev/null three times, and then closing
1595 those fds, *unless* any of them got allocated as #0, #1, or #2,
1596 in which case we leave them open. Gag.
1598 Really, this crap is technically required of *every* X program,
1599 if you want it to be robust in the face of "2>&-".
1601 int fd0 = open ("/dev/null", O_RDWR);
1602 int fd1 = open ("/dev/null", O_RDWR);
1603 int fd2 = open ("/dev/null", O_RDWR);
1604 if (fd0 > 2) close (fd0);
1605 if (fd1 > 2) close (fd1);
1606 if (fd2 > 2) close (fd2);
1611 /* Processing ClientMessage events.
1615 static Bool error_handler_hit_p = False;
1618 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1620 error_handler_hit_p = True;
1624 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1625 them. We only look up the atom names for printing warning messages,
1626 so don't bomb out when it happens...
1629 XGetAtomName_safe (Display *dpy, Atom atom)
1632 XErrorHandler old_handler;
1633 if (!atom) return 0;
1636 error_handler_hit_p = False;
1637 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1638 result = XGetAtomName (dpy, atom);
1640 XSetErrorHandler (old_handler);
1642 if (error_handler_hit_p) result = 0;
1649 sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
1650 return strdup (buf);
1656 clientmessage_response (saver_info *si, Window w, Bool error,
1657 const char *stderr_msg,
1658 const char *protocol_msg)
1662 saver_preferences *p = &si->prefs;
1663 XErrorHandler old_handler;
1665 if (error || p->verbose_p)
1666 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1668 L = strlen(protocol_msg);
1669 proto = (char *) malloc (L + 2);
1670 proto[0] = (error ? '-' : '+');
1671 strcpy (proto+1, protocol_msg);
1674 /* Ignore all X errors while sending a response to a ClientMessage.
1675 Pretty much the only way we could get an error here is if the
1676 window we're trying to send the reply on has been deleted, in
1677 which case, the sender of the ClientMessage won't see our response
1680 XSync (si->dpy, False);
1681 error_handler_hit_p = False;
1682 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1684 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1685 PropModeReplace, (unsigned char *) proto, L);
1687 XSync (si->dpy, False);
1688 XSetErrorHandler (old_handler);
1689 XSync (si->dpy, False);
1696 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1698 #if 0 /* Oh, fuck it. GNOME likes to spew random ClientMessages at us
1699 all the time. This is presumably indicative of an error in
1700 the sender of that ClientMessage: if we're getting it and
1701 ignoring it, then it's not reaching the intended recipient.
1702 But people complain to me about this all the time ("waaah!
1703 xscreensaver is printing to it's stderr and that gets my
1704 panties all in a bunch!") And I'm sick of hearing about it.
1705 So we'll just ignore these messages and let GNOME go right
1706 ahead and continue to stumble along in its malfunction.
1709 saver_preferences *p = &si->prefs;
1710 char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1711 Window w = event->xclient.window;
1714 Bool root_p = False;
1717 for (screen = 0; screen < si->nscreens; screen++)
1718 if (w == si->screens[screen].screensaver_window)
1720 strcpy (wdesc, "xscreensaver");
1723 else if (w == RootWindow (si->dpy, screen))
1725 strcpy (wdesc, "root");
1730 /* If this ClientMessage was sent to the real root window instead of to the
1731 xscreensaver window, then it might be intended for someone else who is
1732 listening on the root window (e.g., the window manager). So only print
1733 the warning if: we are in debug mode; or if the bogus message was
1734 actually sent to one of the xscreensaver-created windows.
1736 if (root_p && !p->debug_p)
1741 XErrorHandler old_handler;
1743 XWindowAttributes xgwa;
1744 memset (&hint, 0, sizeof(hint));
1745 memset (&xgwa, 0, sizeof(xgwa));
1747 XSync (si->dpy, False);
1748 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1749 XGetClassHint (si->dpy, w, &hint);
1750 XGetWindowAttributes (si->dpy, w, &xgwa);
1751 XSync (si->dpy, False);
1752 XSetErrorHandler (old_handler);
1753 XSync (si->dpy, False);
1755 screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1757 sprintf (wdesc, "%.20s / %.20s",
1758 (hint.res_name ? hint.res_name : "(null)"),
1759 (hint.res_class ? hint.res_class : "(null)"));
1760 if (hint.res_name) XFree (hint.res_name);
1761 if (hint.res_class) XFree (hint.res_class);
1764 fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1765 blurb(), screen, (str ? str : "(null)"));
1766 fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1767 blurb(), screen, (unsigned long) w, wdesc);
1768 if (str) XFree (str);
1775 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1777 saver_preferences *p = &si->prefs;
1779 Window window = event->xclient.window;
1781 /* Preferences might affect our handling of client messages. */
1782 maybe_reload_init_file (si);
1784 if (event->xclient.message_type != XA_SCREENSAVER ||
1785 event->xclient.format != 32)
1787 bogus_clientmessage_warning (si, event);
1791 type = event->xclient.data.l[0];
1792 if (type == XA_ACTIVATE)
1796 if (p->mode == DONT_BLANK)
1798 clientmessage_response(si, window, True,
1799 "ACTIVATE ClientMessage received in DONT_BLANK mode.",
1800 "screen blanking is currently disabled.");
1804 clientmessage_response(si, window, False,
1805 "ACTIVATE ClientMessage received.",
1807 si->selection_mode = 0;
1808 si->demoing_p = False;
1810 if (si->throttled_p && p->verbose_p)
1811 fprintf (stderr, "%s: unthrottled.\n", blurb());
1812 si->throttled_p = False;
1814 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1816 XForceScreenSaver (si->dpy, ScreenSaverActive);
1824 clientmessage_response(si, window, True,
1825 "ClientMessage ACTIVATE received while already active.",
1828 else if (type == XA_DEACTIVATE)
1831 /* When -deactivate is received while locked, pop up the dialog box
1832 instead of just ignoring it. Some people depend on this behavior
1833 to be able to unlock by using e.g. a fingerprint reader without
1834 also having to click the mouse first.
1838 clientmessage_response(si, window, False,
1839 "DEACTIVATE ClientMessage received while locked: ignored.",
1840 "screen is locked.");
1847 if (si->throttled_p && p->verbose_p)
1848 fprintf (stderr, "%s: unthrottled.\n", blurb());
1849 si->throttled_p = False;
1851 clientmessage_response(si, window, False,
1852 "DEACTIVATE ClientMessage received.",
1854 if (si->using_mit_saver_extension ||
1855 si->using_sgi_saver_extension)
1857 XForceScreenSaver (si->dpy, ScreenSaverReset);
1865 clientmessage_response(si, window, False,
1866 "ClientMessage DEACTIVATE received while inactive: "
1867 "resetting idle timer.",
1868 "not active: idle timer reset.");
1872 else if (type == XA_CYCLE)
1876 clientmessage_response(si, window, False,
1877 "CYCLE ClientMessage received.",
1879 si->selection_mode = 0; /* 0 means randomize when its time. */
1880 si->demoing_p = False;
1882 if (si->throttled_p && p->verbose_p)
1883 fprintf (stderr, "%s: unthrottled.\n", blurb());
1884 si->throttled_p = False;
1887 XtRemoveTimeOut (si->cycle_id);
1889 cycle_timer ((XtPointer) si, 0);
1892 clientmessage_response(si, window, True,
1893 "ClientMessage CYCLE received while inactive.",
1896 else if (type == XA_NEXT || type == XA_PREV)
1898 clientmessage_response(si, window, False,
1900 ? "NEXT ClientMessage received."
1901 : "PREV ClientMessage received."),
1903 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1904 si->demoing_p = False;
1906 if (si->throttled_p && p->verbose_p)
1907 fprintf (stderr, "%s: unthrottled.\n", blurb());
1908 si->throttled_p = False;
1913 XtRemoveTimeOut (si->cycle_id);
1915 cycle_timer ((XtPointer) si, 0);
1920 else if (type == XA_SELECT)
1924 long which = event->xclient.data.l[1];
1926 if (p->mode == DONT_BLANK)
1928 clientmessage_response(si, window, True,
1929 "SELECT ClientMessage received in DONT_BLANK mode.",
1930 "screen blanking is currently disabled.");
1934 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1935 sprintf (buf2, "activating (%ld).", which);
1936 clientmessage_response (si, window, False, buf, buf2);
1938 if (which < 0) which = 0; /* 0 == "random" */
1939 si->selection_mode = which;
1940 si->demoing_p = False;
1942 if (si->throttled_p && p->verbose_p)
1943 fprintf (stderr, "%s: unthrottled.\n", blurb());
1944 si->throttled_p = False;
1949 XtRemoveTimeOut (si->cycle_id);
1951 cycle_timer ((XtPointer) si, 0);
1956 else if (type == XA_EXIT)
1958 /* Ignore EXIT message if the screen is locked. */
1959 if (until_idle_p || !si->locked_p)
1961 clientmessage_response (si, window, False,
1962 "EXIT ClientMessage received.",
1967 for (i = 0; i < si->nscreens; i++)
1968 kill_screenhack (&si->screens[i]);
1969 unblank_screen (si);
1970 XSync (si->dpy, False);
1972 saver_exit (si, 0, 0);
1975 clientmessage_response (si, window, True,
1976 "EXIT ClientMessage received while locked.",
1977 "screen is locked.");
1979 else if (type == XA_RESTART)
1981 /* The RESTART message works whether the screensaver is active or not,
1982 unless the screen is locked, in which case it doesn't work.
1984 if (until_idle_p || !si->locked_p)
1986 clientmessage_response (si, window, False,
1987 "RESTART ClientMessage received.",
1992 for (i = 0; i < si->nscreens; i++)
1993 kill_screenhack (&si->screens[i]);
1994 unblank_screen (si);
1995 XSync (si->dpy, False);
1998 restart_process (si); /* does not return */
2002 clientmessage_response (si, window, True,
2003 "RESTART ClientMessage received while locked.",
2004 "screen is locked.");
2006 else if (type == XA_DEMO)
2008 long arg = event->xclient.data.l[1];
2009 Bool demo_one_hack_p = (arg == 5000);
2011 if (demo_one_hack_p)
2015 long which = event->xclient.data.l[2];
2018 sprintf (buf, "DEMO %ld ClientMessage received.", which);
2019 sprintf (buf2, "demoing (%ld).", which);
2020 clientmessage_response (si, window, False, buf, buf2);
2022 if (which < 0) which = 0; /* 0 == "random" */
2023 si->selection_mode = which;
2024 si->demoing_p = True;
2026 if (si->throttled_p && p->verbose_p)
2027 fprintf (stderr, "%s: unthrottled.\n", blurb());
2028 si->throttled_p = False;
2033 clientmessage_response (si, window, True,
2034 "DEMO ClientMessage received while active.",
2039 clientmessage_response (si, window, True,
2040 "obsolete form of DEMO ClientMessage.",
2041 "obsolete form of DEMO ClientMessage.");
2044 else if (type == XA_PREFS)
2046 clientmessage_response (si, window, True,
2047 "the PREFS client-message is obsolete.",
2048 "the PREFS client-message is obsolete.");
2050 else if (type == XA_LOCK)
2053 clientmessage_response (si, window, True,
2054 "not compiled with support for locking.",
2055 "locking not enabled.");
2056 #else /* !NO_LOCKING */
2057 if (si->locking_disabled_p)
2058 clientmessage_response (si, window, True,
2059 "LOCK ClientMessage received, but locking is disabled.",
2060 "locking not enabled.");
2061 else if (si->locked_p)
2062 clientmessage_response (si, window, True,
2063 "LOCK ClientMessage received while already locked.",
2068 char *response = (until_idle_p
2069 ? "activating and locking."
2071 sprintf (buf, "LOCK ClientMessage received; %s", response);
2072 clientmessage_response (si, window, False, buf, response);
2074 /* Note that this leaves things in a slightly inconsistent state:
2075 we are blanked but not locked. And blanking might actually
2076 fail if we can't get the grab. */
2077 set_locked_p (si, True);
2079 /* Have to set the time or xscreensaver-command doesn't
2080 report the LOCK state change. */
2081 si->blank_time = time ((time_t *) 0);
2083 si->selection_mode = 0;
2084 si->demoing_p = False;
2086 if (si->lock_id) /* we're doing it now, so lose the timeout */
2088 XtRemoveTimeOut (si->lock_id);
2094 if (si->using_mit_saver_extension ||
2095 si->using_sgi_saver_extension)
2097 XForceScreenSaver (si->dpy, ScreenSaverActive);
2106 #endif /* !NO_LOCKING */
2108 else if (type == XA_THROTTLE)
2110 /* The THROTTLE command is deprecated -- it predates the XDPMS
2111 extension. Instead of using -throttle, users should instead
2112 just power off the monitor (e.g., "xset dpms force off".)
2113 In a few minutes, xscreensaver will notice that the monitor
2114 is off, and cease running hacks.
2116 if (si->throttled_p)
2117 clientmessage_response (si, window, True,
2118 "THROTTLE ClientMessage received, but "
2119 "already throttled.",
2120 "already throttled.");
2124 char *response = "throttled.";
2125 si->throttled_p = True;
2126 si->selection_mode = 0;
2127 si->demoing_p = False;
2128 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
2129 clientmessage_response (si, window, False, buf, response);
2134 XtRemoveTimeOut (si->cycle_id);
2136 cycle_timer ((XtPointer) si, 0);
2140 else if (type == XA_UNTHROTTLE)
2142 if (! si->throttled_p)
2143 clientmessage_response (si, window, True,
2144 "UNTHROTTLE ClientMessage received, but "
2150 char *response = "unthrottled.";
2151 si->throttled_p = False;
2152 si->selection_mode = 0;
2153 si->demoing_p = False;
2154 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
2155 clientmessage_response (si, window, False, buf, response);
2160 XtRemoveTimeOut (si->cycle_id);
2162 cycle_timer ((XtPointer) si, 0);
2170 str = XGetAtomName_safe (si->dpy, type);
2174 if (strlen (str) > 80)
2175 strcpy (str+70, "...");
2176 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
2183 "unrecognised screensaver ClientMessage 0x%x received.",
2184 (unsigned int) event->xclient.data.l[0]);
2187 clientmessage_response (si, window, True, buf, buf);
2193 /* Some random diagnostics printed in -verbose mode.
2197 analyze_display (saver_info *si)
2201 const char *name; const char *desc;
2203 Status (*version_fn) (Display *, int *majP, int *minP);
2206 { "SCREEN_SAVER", /* underscore */ "SGI Screen-Saver",
2207 # ifdef HAVE_SGI_SAVER_EXTENSION
2212 }, { "SCREEN-SAVER", /* dash */ "SGI Screen-Saver",
2213 # ifdef HAVE_SGI_SAVER_EXTENSION
2218 }, { "MIT-SCREEN-SAVER", "MIT Screen-Saver",
2219 # ifdef HAVE_MIT_SAVER_EXTENSION
2220 True, XScreenSaverQueryVersion
2224 }, { "XIDLE", "XIdle",
2225 # ifdef HAVE_XIDLE_EXTENSION
2230 }, { "SGI-VIDEO-CONTROL", "SGI Video-Control",
2231 # ifdef HAVE_SGI_VC_EXTENSION
2232 True, XSGIvcQueryVersion
2236 }, { "READDISPLAY", "SGI Read-Display",
2237 # ifdef HAVE_READ_DISPLAY_EXTENSION
2238 True, XReadDisplayQueryVersion
2242 }, { "MIT-SHM", "Shared Memory",
2243 # ifdef HAVE_XSHM_EXTENSION
2244 True, (Status (*) (Display*,int*,int*)) XShmQueryVersion /* 4 args */
2248 }, { "DOUBLE-BUFFER", "Double-Buffering",
2249 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
2250 True, XdbeQueryExtension
2254 }, { "DPMS", "Power Management",
2255 # ifdef HAVE_DPMS_EXTENSION
2256 True, DPMSGetVersion
2266 }, { "XFree86-VidModeExtension", "XF86 Video-Mode",
2267 # ifdef HAVE_XF86VMODE
2268 True, XF86VidModeQueryVersion
2272 }, { "XC-VidModeExtension", "XC Video-Mode",
2273 # ifdef HAVE_XF86VMODE
2274 True, XF86VidModeQueryVersion
2278 }, { "XFree86-MISC", "XF86 Misc",
2279 # ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2280 True, XF86MiscQueryVersion
2284 }, { "XC-MISC", "XC Misc",
2285 # ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2286 True, XF86MiscQueryVersion
2290 }, { "XINERAMA", "Xinerama",
2291 # ifdef HAVE_XINERAMA
2292 True, XineramaQueryVersion
2296 }, { "RANDR", "Resize-and-Rotate",
2298 True, XRRQueryVersion
2304 }, { "NV-CONTROL", "NVidia",
2306 }, { "NV-GLX", "NVidia GLX",
2308 }, { "Apple-DRI", "Apple-DRI (XDarwin)",
2310 }, { "XInputExtension", "XInput",
2315 fprintf (stderr, "%s: running on display \"%s\"\n", blurb(),
2316 DisplayString(si->dpy));
2317 fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
2318 ServerVendor(si->dpy), VendorRelease(si->dpy));
2320 fprintf (stderr, "%s: useful extensions:\n", blurb());
2321 for (i = 0; i < countof(exts); i++)
2323 int op = 0, event = 0, error = 0;
2325 int maj = 0, min = 0;
2326 int dummy1, dummy2, dummy3;
2329 /* Most of the extension version functions take 3 args,
2330 writing results into args 2 and 3, but some take more.
2331 We only ever care about the first two results, but we
2332 pass in three extra pointers just in case.
2334 Status (*version_fn_2) (Display*,int*,int*,int*,int*,int*) =
2335 (Status (*) (Display*,int*,int*,int*,int*,int*)) exts[i].version_fn;
2337 if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
2339 sprintf (buf, "%s: ", blurb());
2341 strcat (buf, exts[i].desc);
2345 else if (version_fn_2 (si->dpy, &maj, &min, &dummy1, &dummy2, &dummy3))
2346 sprintf (buf+strlen(buf), " (%d.%d)", maj, min);
2348 strcat (buf, " (unavailable)");
2350 if (!exts[i].useful_p)
2351 strcat (buf, " (disabled at compile time)");
2352 fprintf (stderr, "%s\n", buf);
2355 for (i = 0; i < si->nscreens; i++)
2357 saver_screen_info *ssi = &si->screens[i];
2358 unsigned long colormapped_depths = 0;
2359 unsigned long non_mapped_depths = 0;
2360 XVisualInfo vi_in, *vi_out;
2363 if (!ssi->real_screen_p) continue;
2365 vi_in.screen = ssi->real_screen_number;
2366 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
2367 if (!vi_out) continue;
2368 for (j = 0; j < out_count; j++) {
2369 if (vi_out[j].depth >= 32) continue;
2370 if (vi_out[j].class == PseudoColor)
2371 colormapped_depths |= (1 << vi_out[j].depth);
2373 non_mapped_depths |= (1 << vi_out[j].depth);
2375 XFree ((char *) vi_out);
2377 if (colormapped_depths)
2379 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(),
2380 ssi->real_screen_number);
2381 for (j = 0; j < 32; j++)
2382 if (colormapped_depths & (1 << j))
2383 fprintf (stderr, " %d", j);
2384 fprintf (stderr, ".\n");
2386 if (non_mapped_depths)
2388 fprintf (stderr, "%s: screen %d non-colormapped depths:",
2389 blurb(), ssi->real_screen_number);
2390 for (j = 0; j < 32; j++)
2391 if (non_mapped_depths & (1 << j))
2392 fprintf (stderr, " %d", j);
2393 fprintf (stderr, ".\n");
2397 describe_monitor_layout (si);
2402 display_is_on_console_p (saver_info *si)
2404 Bool not_on_console = True;
2405 char *dpystr = DisplayString (si->dpy);
2406 char *tail = (char *) strchr (dpystr, ':');
2407 if (! tail || strncmp (tail, ":0", 2))
2408 not_on_console = True;
2411 char dpyname[255], localname[255];
2412 strncpy (dpyname, dpystr, tail-dpystr);
2413 dpyname [tail-dpystr] = 0;
2415 !strcmp(dpyname, "unix") ||
2416 !strcmp(dpyname, "localhost"))
2417 not_on_console = False;
2418 else if (gethostname (localname, sizeof (localname)))
2419 not_on_console = True; /* can't find hostname? */
2420 else if (!strncmp (dpyname, "/tmp/launch-", 12)) /* MacOS X launchd */
2421 not_on_console = False;
2424 /* We have to call gethostbyname() on the result of gethostname()
2425 because the two aren't guarenteed to be the same name for the
2426 same host: on some losing systems, one is a FQDN and the other
2427 is not. Here in the wide wonderful world of Unix it's rocket
2428 science to obtain the local hostname in a portable fashion.
2430 And don't forget, gethostbyname() reuses the structure it
2431 returns, so we have to copy the fucker before calling it again.
2432 Thank you master, may I have another.
2434 struct hostent *h = gethostbyname (dpyname);
2436 not_on_console = True;
2441 strcpy (hn, h->h_name);
2442 l = gethostbyname (localname);
2443 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
2447 return !not_on_console;
2451 /* Do a little bit of heap introspection...
2454 check_for_leaks (const char *where)
2456 #if defined(HAVE_SBRK) && defined(LEAK_PARANOIA)
2457 static unsigned long last_brk = 0;
2458 int b = (unsigned long) sbrk(0);
2459 if (last_brk && last_brk < b)
2460 fprintf (stderr, "%s: %s: brk grew by %luK.\n",
2462 (((b - last_brk) + 1023) / 1024));
2464 #endif /* HAVE_SBRK */