1 /* xscreensaver, Copyright (c) 1991-2015 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 http://www.jwz.org/xscreensaver/\n\
317 Bool in_signal_handler_p = 0; /* I hate C so much... */
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 ....");
333 time_t now = time ((time_t *) 0);
334 char *str = (char *) ctime (&now);
335 char *nl = (char *) strchr (str, '\n');
336 if (nl) *nl = 0; /* take off that dang newline */
341 static Bool blurb_timestamp_p = True; /* kludge */
346 if (!blurb_timestamp_p)
350 static char buf[255];
351 char *ct = timestring();
352 int n = strlen(progname);
354 strncpy(buf, progname, n);
357 strncpy(buf+n, ct+11, 8);
358 strcpy(buf+n+9, ": ");
365 saver_ehandler (Display *dpy, XErrorEvent *error)
367 saver_info *si = global_si_kludge; /* I hate C so much... */
371 if (!real_stderr) real_stderr = stderr;
373 fprintf (real_stderr, "\n"
374 "#######################################"
375 "#######################################\n\n"
376 "%s: X Error! PLEASE REPORT THIS BUG.\n",
379 for (i = 0; i < si->nscreens; i++)
381 saver_screen_info *ssi = &si->screens[i];
382 fprintf (real_stderr, "%s: screen %d/%d: 0x%x, 0x%x, 0x%x\n",
383 blurb(), ssi->real_screen_number, ssi->number,
384 (unsigned int) RootWindowOfScreen (si->screens[i].screen),
385 (unsigned int) si->screens[i].real_vroot,
386 (unsigned int) si->screens[i].screensaver_window);
389 fprintf (real_stderr, "\n"
390 "#######################################"
391 "#######################################\n\n");
393 fatal_p = XmuPrintDefaultErrorMessage (dpy, error, real_stderr);
395 fatal_p = True; /* The only time I've ever seen a supposedly nonfatal error,
396 it has been BadImplementation / Xlib sequence lost, which
397 are in truth pretty damned fatal.
400 fprintf (real_stderr, "\n");
403 fprintf (real_stderr, "%s: nonfatal error.\n\n", blurb());
406 if (si->prefs.xsync_p)
408 saver_exit (si, -1, "because of synchronous X Error");
413 __extension__ /* don't warn about "string length is greater than the
414 length ISO C89 compilers are required to support". */
416 fprintf (real_stderr,
417 "#######################################################################\n"
419 " If at all possible, please re-run xscreensaver with the command\n"
420 " line arguments `-sync -verbose -log log.txt', and reproduce this\n"
421 " bug. That will cause xscreensaver to dump a `core' file to the\n"
422 " current directory. Please include the stack trace from that core\n"
423 " file in your bug report. *DO NOT* mail the core file itself! That\n"
424 " won't work. A \"log.txt\" file will also be written. Please *do*\n"
425 " include the complete \"log.txt\" file with your bug report.\n"
427 " http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
428 " the most useful bug reports, and how to examine core files.\n"
430 " The more information you can provide, the better. But please\n"
431 " report this bug, regardless!\n"
433 "#######################################################################\n"
437 saver_exit (si, -1, 0);
445 /* This error handler is used only while the X connection is being set up;
446 after we've got a connection, we don't use this handler again. The only
447 reason for having this is so that we can present a more idiot-proof error
448 message than "cannot open display."
451 startup_ehandler (String name, String type, String class,
452 String defalt, /* one can't even spel properly
453 in this joke of a language */
454 String *av, Cardinal *ac)
458 saver_info *si = global_si_kludge; /* I hate C so much... */
459 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
461 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
462 fmt, sizeof(fmt)-1, *db);
464 fprintf (stderr, "%s: ", blurb());
466 memset (p, 0, sizeof(p));
467 if (*ac > countof (p)) *ac = countof (p);
468 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
469 fprintf (stderr, fmt, /* Did I mention that I hate C? */
470 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
471 fprintf (stderr, "\n");
473 describe_uids (si, stderr);
475 if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
477 fprintf (stderr, "\n"
478 "%s: This is probably because you're logging in as root. You\n"
479 " shouldn't log in as root: you should log in as a normal user,\n"
480 " and then `su' as needed. If you insist on logging in as\n"
481 " root, you will have to turn off X's security features before\n"
482 " xscreensaver will work.\n"
484 " Please read the manual and FAQ for more information:\n",
489 fprintf (stderr, "\n"
490 "%s: Errors at startup are usually authorization problems.\n"
491 " But you're not logging in as root (good!) so something\n"
492 " else must be wrong. Did you read the manual and the FAQ?\n",
496 fprintf (stderr, "\n"
497 " http://www.jwz.org/xscreensaver/faq.html\n"
498 " http://www.jwz.org/xscreensaver/man.html\n"
507 /* The zillions of initializations.
510 /* Set progname, version, etc. This is done very early.
513 set_version_string (saver_info *si, int *argc, char **argv)
515 progclass = "XScreenSaver";
517 /* progname is reset later, after we connect to X. */
518 progname = strrchr(argv[0], '/');
519 if (progname) progname++;
520 else progname = argv[0];
522 if (strlen(progname) > 100) /* keep it short. */
525 /* The X resource database blows up if argv[0] has a "." in it. */
528 while ((s = strchr (s, '.')))
532 si->version = (char *) malloc (5);
533 memcpy (si->version, screensaver_id + 17, 4);
538 /* Initializations that potentially take place as a priveleged user:
539 If the xscreensaver executable is setuid root, then these initializations
540 are run as root, before discarding privileges.
543 privileged_initialization (saver_info *si, int *argc, char **argv)
546 /* before hack_uid() for proper permissions */
547 lock_priv_init (*argc, argv, si->prefs.verbose_p);
548 #endif /* NO_LOCKING */
554 /* Figure out what locking mechanisms are supported.
557 lock_initialization (saver_info *si, int *argc, char **argv)
560 si->locking_disabled_p = True;
561 si->nolock_reason = "not compiled with locking support";
562 #else /* !NO_LOCKING */
564 /* Finish initializing locking, now that we're out of privileged code. */
565 if (! lock_init (*argc, argv, si->prefs.verbose_p))
567 si->locking_disabled_p = True;
568 si->nolock_reason = "error getting password";
571 /* If locking is currently enabled, but the environment indicates that
572 we have been launched as GDM's "Background" program, then disable
573 locking just in case.
575 if (!si->locking_disabled_p && getenv ("RUNNING_UNDER_GDM"))
577 si->locking_disabled_p = True;
578 si->nolock_reason = "running under GDM";
581 /* If the server is XDarwin (MacOS X) then disable locking.
582 (X grabs only affect X programs, so you can use Command-Tab
583 to bring any other Mac program to the front, e.g., Terminal.)
585 if (!si->locking_disabled_p)
587 int op = 0, event = 0, error = 0;
588 Bool macos_p = False;
591 /* Disable locking if *running* on Apple hardware, since we have no
592 reliable way to determine whether the server is running on MacOS.
593 Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
594 but I'm not really sure about that.
600 /* This extension exists on the Apple X11 server, but not
601 on earlier versions of the XDarwin server. */
602 macos_p = XQueryExtension (si->dpy, "Apple-DRI", &op, &event, &error);
606 si->locking_disabled_p = True;
607 si->nolock_reason = "Cannot lock securely on MacOS X";
611 /* Like MacOS, locking under Wayland's embedded X11 server does not work.
612 (X11 grabs don't work because the Wayland window manager lives at a
613 higher level than the X11 emulation layer.)
615 if (!si->locking_disabled_p && getenv ("WAYLAND_DISPLAY"))
617 si->locking_disabled_p = True;
618 si->nolock_reason = "Cannot lock securely under Wayland";
621 if (si->prefs.debug_p) /* But allow locking anyway in debug mode. */
622 si->locking_disabled_p = False;
624 #endif /* NO_LOCKING */
628 /* Open the connection to the X server, and intern our Atoms.
631 connect_to_server (saver_info *si, int *argc, char **argv)
633 Widget toplevel_shell;
636 char *d = getenv ("DISPLAY");
639 char *ndpy = strdup("DISPLAY=:0.0");
640 /* if (si->prefs.verbose_p) */ /* sigh, too early to test this... */
642 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
646 /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
647 glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
648 do not. So we must leak it (and/or the previous setting). Yay.
651 #endif /* HAVE_PUTENV */
653 XSetErrorHandler (saver_ehandler);
655 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
656 toplevel_shell = XtAppInitialize (&si->app, progclass,
657 options, XtNumber (options),
658 argc, argv, defaults, 0, 0);
659 XtAppSetErrorMsgHandler (si->app, 0);
661 si->dpy = XtDisplay (toplevel_shell);
662 si->prefs.db = XtDatabase (si->dpy);
663 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
665 if(strlen(progname) > 100) /* keep it short. */
668 db = si->prefs.db; /* resources.c needs this */
670 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
671 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
672 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
673 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
674 XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
675 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
677 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
678 XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False);
679 XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False);
680 XA_NET_WM_USER_TIME = XInternAtom (si->dpy, "_NET_WM_USER_TIME", False);
681 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
682 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
683 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
684 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
685 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
686 XA_PREV = XInternAtom (si->dpy, "PREV", False);
687 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
688 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
689 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
690 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
691 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
692 XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
693 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
694 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
696 return toplevel_shell;
700 /* Handle the command-line arguments that were not handled for us by Xt.
701 Issue an error message and exit if there are unknown options.
704 process_command_line (saver_info *si, int *argc, char **argv)
707 for (i = 1; i < *argc; i++)
709 if (!strcmp (argv[i], "-debug"))
710 /* no resource for this one, out of paranoia. */
711 si->prefs.debug_p = True;
713 else if (!strcmp (argv[i], "-h") ||
714 !strcmp (argv[i], "-help") ||
715 !strcmp (argv[i], "--help"))
720 const char *s = argv[i];
721 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
724 if (s[0] == '-' && s[1] == '-') s++;
725 if (!strcmp (s, "-activate") ||
726 !strcmp (s, "-deactivate") ||
727 !strcmp (s, "-cycle") ||
728 !strcmp (s, "-next") ||
729 !strcmp (s, "-prev") ||
730 !strcmp (s, "-exit") ||
731 !strcmp (s, "-restart") ||
732 !strcmp (s, "-demo") ||
733 !strcmp (s, "-prefs") ||
734 !strcmp (s, "-preferences") ||
735 !strcmp (s, "-lock") ||
736 !strcmp (s, "-version") ||
737 !strcmp (s, "-time"))
740 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
741 fprintf (stderr, "\n\
742 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
744 fprintf (stderr, "\n\
745 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
748 The `xscreensaver' program is a daemon that runs in the background.\n\
749 You control a running xscreensaver process by sending it messages\n\
750 with `xscreensaver-demo' or `xscreensaver-command'.\n\
751 . See the man pages for details, or check the web page:\n\
752 http://www.jwz.org/xscreensaver/\n\n");
761 /* Print out the xscreensaver banner to the tty if applicable;
762 Issue any other warnings that are called for at this point.
765 print_banner (saver_info *si)
767 saver_preferences *p = &si->prefs;
770 s = strchr (screensaver_id, '-');
771 s = strrchr (s, '-');
773 strncpy (year, s, 4);
776 /* This resource gets set some time before the others, so that we know
777 whether to print the banner (and so that the banner gets printed before
778 any resource-database-related error messages.)
780 p->verbose_p = (p->debug_p ||
781 get_boolean_resource (si->dpy, "verbose", "Boolean"));
783 /* Ditto, for the locking_disabled_p message. */
784 p->lock_p = get_boolean_resource (si->dpy, "lock", "Boolean");
788 "%s %s, copyright (c) 1991-%s "
789 "by Jamie Zawinski <jwz@jwz.org>.\n",
790 progname, si->version, year);
793 fprintf (stderr, "\n"
794 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
796 "\tNote that in debug mode, the xscreensaver window will only\n"
797 "\tcover the left half of the screen. (The idea is that you\n"
798 "\tcan still see debugging output in a shell, if you position\n"
799 "\tit on the right side of the screen.)\n"
801 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
802 "\tuntrusted environments.\n"
806 if (p->verbose_p && senescent_p ())
807 fprintf (stderr, "\n"
808 "*************************************"
809 "**************************************\n"
810 "%s: Warning: this version of xscreensaver is VERY OLD!\n"
811 "%s: Please upgrade! http://www.jwz.org/xscreensaver/\n"
812 "*************************************"
813 "**************************************\n"
819 if (!si->uid_message || !*si->uid_message)
820 describe_uids (si, stderr);
823 if (si->orig_uid && *si->orig_uid)
824 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
825 blurb(), si->orig_uid);
826 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
829 fprintf (stderr, "%s: in process %lu.\n", blurb(),
830 (unsigned long) getpid());
835 print_lock_failure_banner (saver_info *si)
837 saver_preferences *p = &si->prefs;
839 /* If locking was not able to be initalized for some reason, explain why.
840 (This has to be done after we've read the lock_p resource.)
842 if (si->locking_disabled_p)
845 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
847 if (strstr (si->nolock_reason, "passw"))
848 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
849 "consult the manual.\n", blurb());
850 else if (strstr (si->nolock_reason, "running as "))
852 "%s: locking only works when xscreensaver is launched\n"
853 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
854 "\t See the manual for details.\n",
861 /* called from screens.c so that all the Xt crud is here. */
863 initialize_screen_root_widget (saver_screen_info *ssi)
865 saver_info *si = ssi->global;
866 if (ssi->toplevel_shell)
867 XtDestroyWidget (ssi->toplevel_shell);
868 ssi->toplevel_shell =
869 XtVaAppCreateShell (progname, progclass,
870 applicationShellWidgetClass,
872 XtNscreen, ssi->screen,
873 XtNvisual, ssi->current_visual,
874 XtNdepth, visual_depth (ssi->screen,
875 ssi->current_visual),
880 /* Examine all of the display's screens, and populate the `saver_screen_info'
881 structures. Make sure this is called after hack_environment() sets $PATH.
884 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
888 update_screen_layout (si);
890 /* Check to see whether fading is ever possible -- if any of the
891 screens on the display has a PseudoColor visual, then fading can
892 work (on at least some screens.) If no screen has a PseudoColor
893 visual, then don't bother ever trying to fade, because it will
894 just cause a delay without causing any visible effect.
896 for (i = 0; i < si->nscreens; i++)
898 saver_screen_info *ssi = &si->screens[i];
899 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
900 get_visual (ssi->screen, "PseudoColor", True, False) ||
901 get_visual (ssi->screen, "GrayScale", True, False))
903 si->fading_possible_p = True;
908 #ifdef HAVE_XF86VMODE_GAMMA
909 si->fading_possible_p = True; /* if we can gamma fade, go for it */
914 /* If any server extensions have been requested, try and initialize them.
915 Issue warnings if requests can't be honored.
918 initialize_server_extensions (saver_info *si)
920 saver_preferences *p = &si->prefs;
922 Bool server_has_xidle_extension_p = False;
923 Bool server_has_sgi_saver_extension_p = False;
924 Bool server_has_mit_saver_extension_p = False;
925 Bool system_has_proc_interrupts_p = False;
926 Bool server_has_xinput_extension_p = False;
927 const char *piwhy = 0;
929 si->using_xidle_extension = p->use_xidle_extension;
930 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
931 si->using_mit_saver_extension = p->use_mit_saver_extension;
932 si->using_proc_interrupts = p->use_proc_interrupts;
933 si->using_xinput_extension = p->use_xinput_extension;
935 #ifdef HAVE_XIDLE_EXTENSION
938 server_has_xidle_extension_p = XidleQueryExtension (si->dpy, &ev, &er);
941 #ifdef HAVE_SGI_SAVER_EXTENSION
942 server_has_sgi_saver_extension_p =
943 XScreenSaverQueryExtension (si->dpy,
944 &si->sgi_saver_ext_event_number,
945 &si->sgi_saver_ext_error_number);
947 #ifdef HAVE_MIT_SAVER_EXTENSION
948 server_has_mit_saver_extension_p =
949 XScreenSaverQueryExtension (si->dpy,
950 &si->mit_saver_ext_event_number,
951 &si->mit_saver_ext_error_number);
953 #ifdef HAVE_PROC_INTERRUPTS
954 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
958 server_has_xinput_extension_p = query_xinput_extension (si);
961 if (!server_has_xidle_extension_p)
962 si->using_xidle_extension = False;
963 else if (p->verbose_p)
965 if (si->using_xidle_extension)
966 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
968 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
971 if (!server_has_sgi_saver_extension_p)
972 si->using_sgi_saver_extension = False;
973 else if (p->verbose_p)
975 if (si->using_sgi_saver_extension)
976 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
979 "%s: not using server's SGI SCREEN_SAVER extension.\n",
983 if (!server_has_mit_saver_extension_p)
984 si->using_mit_saver_extension = False;
985 else if (p->verbose_p)
987 if (si->using_mit_saver_extension)
988 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
992 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
997 if (XRRQueryExtension (si->dpy,
998 &si->randr_event_number, &si->randr_error_number))
1000 int nscreens = ScreenCount (si->dpy); /* number of *real* screens */
1003 si->using_randr_extension = TRUE;
1006 fprintf (stderr, "%s: selecting RANDR events\n", blurb());
1007 for (i = 0; i < nscreens; i++)
1008 # ifdef RRScreenChangeNotifyMask /* randr.h 1.5, 2002/09/29 */
1009 XRRSelectInput (si->dpy, RootWindow (si->dpy, i),
1010 RRScreenChangeNotifyMask);
1011 # else /* !RRScreenChangeNotifyMask */ /* Xrandr.h 1.4, 2001/06/07 */
1012 XRRScreenChangeSelectInput (si->dpy, RootWindow (si->dpy, i), True);
1013 # endif /* !RRScreenChangeNotifyMask */
1015 # endif /* HAVE_RANDR */
1018 if (!server_has_xinput_extension_p)
1019 si->using_xinput_extension = False;
1022 if (si->using_xinput_extension)
1023 init_xinput_extension(si);
1027 if (si->using_xinput_extension)
1029 "%s: selecting events from %d XInputExtension devices.\n",
1030 blurb(), si->num_xinput_devices);
1033 "%s: not using XInputExtension.\n",
1039 if (!system_has_proc_interrupts_p)
1041 si->using_proc_interrupts = False;
1042 if (p->verbose_p && piwhy)
1043 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
1046 else if (p->verbose_p)
1048 if (si->using_proc_interrupts)
1050 "%s: consulting /proc/interrupts for keyboard activity.\n",
1054 "%s: not consulting /proc/interrupts for keyboard activity.\n",
1060 #ifdef DEBUG_MULTISCREEN
1062 debug_multiscreen_timer (XtPointer closure, XtIntervalId *id)
1064 saver_info *si = (saver_info *) closure;
1065 saver_preferences *p = &si->prefs;
1066 if (update_screen_layout (si))
1070 fprintf (stderr, "%s: new layout:\n", blurb());
1071 describe_monitor_layout (si);
1073 resize_screensaver_window (si);
1075 XtAppAddTimeOut (si->app, 1000*4, debug_multiscreen_timer, (XtPointer) si);
1077 #endif /* DEBUG_MULTISCREEN */
1080 /* For the case where we aren't using an server extensions, select user events
1081 on all the existing windows, and launch timers to select events on
1082 newly-created windows as well.
1084 If a server extension is being used, this does nothing.
1087 select_events (saver_info *si)
1089 saver_preferences *p = &si->prefs;
1092 if (si->using_xidle_extension ||
1093 si->using_mit_saver_extension ||
1094 si->using_sgi_saver_extension)
1097 if (p->initial_delay)
1101 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
1102 (int) p->initial_delay/1000,
1103 (p->initial_delay == 1000 ? "" : "s"));
1107 usleep (p->initial_delay);
1109 fprintf (stderr, " done.\n");
1114 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
1119 /* Select events on the root windows of every screen. This also selects
1120 for window creation events, so that new subwindows will be noticed.
1122 for (i = 0; i < si->nscreens; i++)
1124 saver_screen_info *ssi = &si->screens[i];
1125 if (ssi->real_screen_p)
1126 start_notice_events_timer (si,
1127 RootWindowOfScreen (si->screens[i].screen), False);
1131 fprintf (stderr, " done.\n");
1133 # ifdef DEBUG_MULTISCREEN
1134 if (p->debug_p) debug_multiscreen_timer ((XtPointer) si, 0);
1140 maybe_reload_init_file (saver_info *si)
1142 saver_preferences *p = &si->prefs;
1143 if (init_file_changed_p (p))
1146 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
1147 blurb(), init_file_name());
1149 load_init_file (si->dpy, p);
1151 /* If a server extension is in use, and p->timeout has changed,
1152 we need to inform the server of the new timeout. */
1153 disable_builtin_screensaver (si, False);
1155 /* If the DPMS settings in the init file have changed,
1156 change the settings on the server to match. */
1157 sync_server_dpms_settings (si->dpy,
1158 (p->dpms_enabled_p &&
1159 p->mode != DONT_BLANK),
1161 p->dpms_standby / 1000,
1162 p->dpms_suspend / 1000,
1171 - wait until the user is idle;
1173 - wait until the user is active;
1174 - unblank the screen;
1179 main_loop (saver_info *si)
1181 saver_preferences *p = &si->prefs;
1187 Bool was_locked = False;
1190 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1192 check_for_leaks ("unblanked A");
1193 sleep_until_idle (si, True);
1194 check_for_leaks ("unblanked B");
1199 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
1200 si->selection_mode, timestring());
1202 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
1206 maybe_reload_init_file (si);
1208 if (p->mode == DONT_BLANK)
1211 fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
1212 blurb(), timestring());
1214 /* Go around the loop and wait for the next bout of idleness,
1215 or for the init file to change, or for a remote command to
1216 come in, or something.
1218 But, if locked_p is true, go ahead. This can only happen
1219 if we're in "disabled" mode but a "lock" clientmessage came
1220 in: in that case, we should go ahead and blank/lock the screen.
1226 /* Since we're about to blank the screen, kill the de-race timer,
1227 if any. It might still be running if we have unblanked and then
1228 re-blanked in a short period (e.g., when using the "next" button
1229 in xscreensaver-demo.)
1234 fprintf (stderr, "%s: stopping de-race timer (%d remaining.)\n",
1235 blurb(), si->de_race_ticks);
1236 XtRemoveTimeOut (si->de_race_id);
1241 /* Now, try to blank.
1244 if (! blank_screen (si))
1246 /* We were unable to grab either the keyboard or mouse.
1247 This means we did not (and must not) blank the screen.
1248 If we were to blank the screen while some other program
1249 is holding both the mouse and keyboard grabbed, then
1250 we would never be able to un-blank it! We would never
1251 see any events, and the display would be wedged.
1253 In particular, without that keyboard grab, we will be
1254 unable to ever read keypresses on the unlock dialog.
1255 You can't unlock if you can't type your password.
1257 So, just go around the loop again and wait for the
1258 next bout of idleness. (If the user remains idle, we
1259 will next try to blank the screen again in no more than
1262 Time retry = 60 * 1000;
1263 if (p->timeout < retry)
1269 "%s: DEBUG MODE: unable to grab -- BLANKING ANYWAY.\n",
1275 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
1278 /* Since we were unable to blank, clearly we're not locked,
1279 but we might have been prematurely marked as locked by
1280 the LOCK ClientMessage. */
1282 set_locked_p (si, False);
1284 schedule_wakeup_event (si, retry, p->debug_p);
1289 for (i = 0; i < si->nscreens; i++)
1290 kill_screenhack (&si->screens[i]);
1292 raise_window (si, True, True, False);
1293 if (si->throttled_p)
1294 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
1296 for (i = 0; i < si->nscreens; i++)
1297 spawn_screenhack (&si->screens[i]);
1299 /* If we are blanking only, optionally power down monitor right now. */
1300 if (p->mode == BLANK_ONLY &&
1301 p->dpms_enabled_p &&
1304 sync_server_dpms_settings (si->dpy, True,
1306 p->dpms_standby / 1000,
1307 p->dpms_suspend / 1000,
1310 monitor_power_on (si, False);
1313 /* Don't start the cycle timer in demo mode. */
1314 if (!si->demoing_p && p->cycle)
1315 si->cycle_id = XtAppAddTimeOut (si->app,
1317 /* see comment in cycle_timer() */
1325 /* Maybe start locking the screen.
1328 Time lock_timeout = p->lock_timeout;
1330 if (si->emergency_lock_p && p->lock_p && lock_timeout)
1332 int secs = p->lock_timeout / 1000;
1335 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1337 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1341 si->emergency_lock_p = False;
1343 if (!si->demoing_p && /* if not going into demo mode */
1344 p->lock_p && /* and locking is enabled */
1345 !si->locking_disabled_p && /* and locking is possible */
1346 lock_timeout == 0) /* and locking is not timer-deferred */
1347 set_locked_p (si, True); /* then lock right now. */
1349 /* locked_p might be true already because of the above, or because of
1350 the LOCK ClientMessage. But if not, and if we're supposed to lock
1351 after some time, set up a timer to do so.
1356 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1357 activate_lock_timer,
1360 #endif /* !NO_LOCKING */
1363 ok_to_unblank = True;
1366 check_for_leaks ("blanked A");
1367 sleep_until_idle (si, False); /* until not idle */
1368 check_for_leaks ("blanked B");
1370 maybe_reload_init_file (si);
1373 /* Maybe unlock the screen.
1377 saver_screen_info *ssi = si->default_screen;
1378 if (si->locking_disabled_p) abort ();
1381 si->dbox_up_p = True;
1382 for (i = 0; i < si->nscreens; i++)
1383 suspend_screenhack (&si->screens[i], True); /* suspend */
1384 XUndefineCursor (si->dpy, ssi->screensaver_window);
1386 ok_to_unblank = unlock_p (si);
1388 si->dbox_up_p = False;
1389 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1390 for (i = 0; i < si->nscreens; i++)
1391 suspend_screenhack (&si->screens[i], False); /* resume */
1393 if (!ok_to_unblank &&
1394 !screenhack_running_p (si))
1396 /* If the lock dialog has been dismissed and we're not about to
1397 unlock the screen, and there is currently no hack running,
1398 then launch one. (There might be no hack running if DPMS
1399 had kicked in. But DPMS is off now, so bring back the hack)
1402 XtRemoveTimeOut (si->cycle_id);
1404 cycle_timer ((XtPointer) si, 0);
1407 #endif /* !NO_LOCKING */
1409 } while (!ok_to_unblank);
1413 fprintf (stderr, "%s: unblanking screen at %s.\n",
1414 blurb(), timestring ());
1416 /* Kill before unblanking, to stop drawing as soon as possible. */
1417 for (i = 0; i < si->nscreens; i++)
1418 kill_screenhack (&si->screens[i]);
1419 unblank_screen (si);
1421 set_locked_p (si, False);
1422 si->emergency_lock_p = False;
1424 si->selection_mode = 0;
1426 /* If we're throttled, and the user has explicitly unlocked the screen,
1427 then unthrottle. If we weren't locked, then don't unthrottle
1428 automatically, because someone might have just bumped the desk... */
1431 if (si->throttled_p && p->verbose_p)
1432 fprintf (stderr, "%s: unthrottled.\n", blurb());
1433 si->throttled_p = False;
1438 XtRemoveTimeOut (si->cycle_id);
1444 XtRemoveTimeOut (si->lock_id);
1448 /* Since we're unblanked now, break race conditions and make
1449 sure we stay that way (see comment in timers.c.) */
1450 if (! si->de_race_id)
1451 de_race_timer ((XtPointer) si, 0);
1455 static void analyze_display (saver_info *si);
1456 static void fix_fds (void);
1459 main (int argc, char **argv)
1463 saver_info *si = &the_si;
1464 saver_preferences *p = &si->prefs;
1465 struct passwd *spasswd;
1468 /* It turns out that if we do setlocale (LC_ALL, "") here, people
1469 running in Japanese locales get font craziness on the password
1470 dialog, presumably because it is displaying Japanese characters
1471 in a non-Japanese font. However, if we don't call setlocale()
1472 at all, then XLookupString() never returns multi-byte UTF-8
1473 characters when people type non-Latin1 characters on the
1476 The current theory (and at this point, I'm really guessing!) is
1477 that using LC_CTYPE instead of LC_ALL will make XLookupString()
1478 behave usefully, without having the side-effect of screwing up
1479 the fonts on the unlock dialog.
1481 See https://bugs.launchpad.net/ubuntu/+source/xscreensaver/+bug/671923
1482 from comment #20 onward.
1487 if (!setlocale (LC_CTYPE, ""))
1488 fprintf (stderr, "%s: warning: could not set default locale\n",
1491 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1492 textdomain (GETTEXT_PACKAGE);
1493 #endif /* ENABLE_NLS */
1495 memset(si, 0, sizeof(*si));
1496 global_si_kludge = si; /* I hate C so much... */
1500 # undef ya_rand_init
1503 save_argv (argc, argv);
1504 set_version_string (si, &argc, argv);
1505 privileged_initialization (si, &argc, argv);
1506 hack_environment (si);
1508 spasswd = getpwuid(getuid());
1511 fprintf(stderr, "Could not figure out who the current user is!\n");
1515 si->user = strdup(spasswd->pw_name ? spasswd->pw_name : "(unknown)");
1518 si->unlock_cb = gui_auth_conv;
1519 si->auth_finished_cb = auth_finished_cb;
1520 # endif /* !NO_LOCKING */
1522 shell = connect_to_server (si, &argc, argv);
1523 process_command_line (si, &argc, argv);
1524 stderr_log_file (si);
1527 load_init_file(si->dpy, p); /* must be before initialize_per_screen_info() */
1528 blurb_timestamp_p = p->timestamp_p; /* kludge */
1529 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1531 /* We can only issue this warning now. */
1532 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1534 "%s: there are no PseudoColor or GrayScale visuals.\n"
1535 "%s: ignoring the request for fading/unfading.\n",
1538 for (i = 0; i < si->nscreens; i++)
1540 saver_screen_info *ssi = &si->screens[i];
1541 if (ssi->real_screen_p)
1542 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1546 lock_initialization (si, &argc, argv);
1547 print_lock_failure_banner (si);
1549 if (p->xsync_p) XSynchronize (si->dpy, True);
1551 if (p->verbose_p) analyze_display (si);
1552 initialize_server_extensions (si);
1554 si->blank_time = time ((time_t *) 0); /* must be before ..._window */
1555 initialize_screensaver_window (si);
1560 disable_builtin_screensaver (si, True);
1561 sync_server_dpms_settings (si->dpy,
1562 (p->dpms_enabled_p &&
1563 p->mode != DONT_BLANK),
1565 p->dpms_standby / 1000,
1566 p->dpms_suspend / 1000,
1570 initialize_stderr (si);
1571 handle_signals (si);
1573 make_splash_dialog (si);
1575 main_loop (si); /* doesn't return */
1582 /* Bad Things Happen if stdin, stdout, and stderr have been closed
1583 (as by the `sh incantation "xscreensaver >&- 2>&-"). When you do
1584 that, the X connection gets allocated to one of these fds, and
1585 then some random library writes to stderr, and random bits get
1586 stuffed down the X pipe, causing "Xlib: sequence lost" errors.
1587 So, we cause the first three file descriptors to be open to
1588 /dev/null if they aren't open to something else already. This
1589 must be done before any other files are opened (or the closing
1590 of that other file will again free up one of the "magic" first
1593 We do this by opening /dev/null three times, and then closing
1594 those fds, *unless* any of them got allocated as #0, #1, or #2,
1595 in which case we leave them open. Gag.
1597 Really, this crap is technically required of *every* X program,
1598 if you want it to be robust in the face of "2>&-".
1600 int fd0 = open ("/dev/null", O_RDWR);
1601 int fd1 = open ("/dev/null", O_RDWR);
1602 int fd2 = open ("/dev/null", O_RDWR);
1603 if (fd0 > 2) close (fd0);
1604 if (fd1 > 2) close (fd1);
1605 if (fd2 > 2) close (fd2);
1610 /* Processing ClientMessage events.
1614 static Bool error_handler_hit_p = False;
1617 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1619 error_handler_hit_p = True;
1623 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1624 them. We only look up the atom names for printing warning messages,
1625 so don't bomb out when it happens...
1628 XGetAtomName_safe (Display *dpy, Atom atom)
1631 XErrorHandler old_handler;
1632 if (!atom) return 0;
1635 error_handler_hit_p = False;
1636 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1637 result = XGetAtomName (dpy, atom);
1639 XSetErrorHandler (old_handler);
1641 if (error_handler_hit_p) result = 0;
1648 sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
1649 return strdup (buf);
1655 clientmessage_response (saver_info *si, Window w, Bool error,
1656 const char *stderr_msg,
1657 const char *protocol_msg)
1661 saver_preferences *p = &si->prefs;
1662 XErrorHandler old_handler;
1664 if (error || p->verbose_p)
1665 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1667 L = strlen(protocol_msg);
1668 proto = (char *) malloc (L + 2);
1669 proto[0] = (error ? '-' : '+');
1670 strcpy (proto+1, protocol_msg);
1673 /* Ignore all X errors while sending a response to a ClientMessage.
1674 Pretty much the only way we could get an error here is if the
1675 window we're trying to send the reply on has been deleted, in
1676 which case, the sender of the ClientMessage won't see our response
1679 XSync (si->dpy, False);
1680 error_handler_hit_p = False;
1681 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1683 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1684 PropModeReplace, (unsigned char *) proto, L);
1686 XSync (si->dpy, False);
1687 XSetErrorHandler (old_handler);
1688 XSync (si->dpy, False);
1695 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1697 #if 0 /* Oh, fuck it. GNOME likes to spew random ClientMessages at us
1698 all the time. This is presumably indicative of an error in
1699 the sender of that ClientMessage: if we're getting it and
1700 ignoring it, then it's not reaching the intended recipient.
1701 But people complain to me about this all the time ("waaah!
1702 xscreensaver is printing to it's stderr and that gets my
1703 panties all in a bunch!") And I'm sick of hearing about it.
1704 So we'll just ignore these messages and let GNOME go right
1705 ahead and continue to stumble along in its malfunction.
1708 saver_preferences *p = &si->prefs;
1709 char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1710 Window w = event->xclient.window;
1713 Bool root_p = False;
1716 for (screen = 0; screen < si->nscreens; screen++)
1717 if (w == si->screens[screen].screensaver_window)
1719 strcpy (wdesc, "xscreensaver");
1722 else if (w == RootWindow (si->dpy, screen))
1724 strcpy (wdesc, "root");
1729 /* If this ClientMessage was sent to the real root window instead of to the
1730 xscreensaver window, then it might be intended for someone else who is
1731 listening on the root window (e.g., the window manager). So only print
1732 the warning if: we are in debug mode; or if the bogus message was
1733 actually sent to one of the xscreensaver-created windows.
1735 if (root_p && !p->debug_p)
1740 XErrorHandler old_handler;
1742 XWindowAttributes xgwa;
1743 memset (&hint, 0, sizeof(hint));
1744 memset (&xgwa, 0, sizeof(xgwa));
1746 XSync (si->dpy, False);
1747 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1748 XGetClassHint (si->dpy, w, &hint);
1749 XGetWindowAttributes (si->dpy, w, &xgwa);
1750 XSync (si->dpy, False);
1751 XSetErrorHandler (old_handler);
1752 XSync (si->dpy, False);
1754 screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1756 sprintf (wdesc, "%.20s / %.20s",
1757 (hint.res_name ? hint.res_name : "(null)"),
1758 (hint.res_class ? hint.res_class : "(null)"));
1759 if (hint.res_name) XFree (hint.res_name);
1760 if (hint.res_class) XFree (hint.res_class);
1763 fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1764 blurb(), screen, (str ? str : "(null)"));
1765 fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1766 blurb(), screen, (unsigned long) w, wdesc);
1767 if (str) XFree (str);
1774 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1776 saver_preferences *p = &si->prefs;
1778 Window window = event->xclient.window;
1780 /* Preferences might affect our handling of client messages. */
1781 maybe_reload_init_file (si);
1783 if (event->xclient.message_type != XA_SCREENSAVER ||
1784 event->xclient.format != 32)
1786 bogus_clientmessage_warning (si, event);
1790 type = event->xclient.data.l[0];
1791 if (type == XA_ACTIVATE)
1795 if (p->mode == DONT_BLANK)
1797 clientmessage_response(si, window, True,
1798 "ACTIVATE ClientMessage received in DONT_BLANK mode.",
1799 "screen blanking is currently disabled.");
1803 clientmessage_response(si, window, False,
1804 "ACTIVATE ClientMessage received.",
1806 si->selection_mode = 0;
1807 si->demoing_p = False;
1809 if (si->throttled_p && p->verbose_p)
1810 fprintf (stderr, "%s: unthrottled.\n", blurb());
1811 si->throttled_p = False;
1813 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1815 XForceScreenSaver (si->dpy, ScreenSaverActive);
1823 clientmessage_response(si, window, True,
1824 "ClientMessage ACTIVATE received while already active.",
1827 else if (type == XA_DEACTIVATE)
1830 /* When -deactivate is received while locked, pop up the dialog box
1831 instead of just ignoring it. Some people depend on this behavior
1832 to be able to unlock by using e.g. a fingerprint reader without
1833 also having to click the mouse first.
1837 clientmessage_response(si, window, False,
1838 "DEACTIVATE ClientMessage received while locked: ignored.",
1839 "screen is locked.");
1846 if (si->throttled_p && p->verbose_p)
1847 fprintf (stderr, "%s: unthrottled.\n", blurb());
1848 si->throttled_p = False;
1850 clientmessage_response(si, window, False,
1851 "DEACTIVATE ClientMessage received.",
1853 if (si->using_mit_saver_extension ||
1854 si->using_sgi_saver_extension)
1856 XForceScreenSaver (si->dpy, ScreenSaverReset);
1864 clientmessage_response(si, window, False,
1865 "ClientMessage DEACTIVATE received while inactive: "
1866 "resetting idle timer.",
1867 "not active: idle timer reset.");
1871 else if (type == XA_CYCLE)
1875 clientmessage_response(si, window, False,
1876 "CYCLE ClientMessage received.",
1878 si->selection_mode = 0; /* 0 means randomize when its time. */
1879 si->demoing_p = False;
1881 if (si->throttled_p && p->verbose_p)
1882 fprintf (stderr, "%s: unthrottled.\n", blurb());
1883 si->throttled_p = False;
1886 XtRemoveTimeOut (si->cycle_id);
1888 cycle_timer ((XtPointer) si, 0);
1891 clientmessage_response(si, window, True,
1892 "ClientMessage CYCLE received while inactive.",
1895 else if (type == XA_NEXT || type == XA_PREV)
1897 clientmessage_response(si, window, False,
1899 ? "NEXT ClientMessage received."
1900 : "PREV ClientMessage received."),
1902 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1903 si->demoing_p = False;
1905 if (si->throttled_p && p->verbose_p)
1906 fprintf (stderr, "%s: unthrottled.\n", blurb());
1907 si->throttled_p = False;
1912 XtRemoveTimeOut (si->cycle_id);
1914 cycle_timer ((XtPointer) si, 0);
1919 else if (type == XA_SELECT)
1923 long which = event->xclient.data.l[1];
1925 if (p->mode == DONT_BLANK)
1927 clientmessage_response(si, window, True,
1928 "SELECT ClientMessage received in DONT_BLANK mode.",
1929 "screen blanking is currently disabled.");
1933 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1934 sprintf (buf2, "activating (%ld).", which);
1935 clientmessage_response (si, window, False, buf, buf2);
1937 if (which < 0) which = 0; /* 0 == "random" */
1938 si->selection_mode = which;
1939 si->demoing_p = False;
1941 if (si->throttled_p && p->verbose_p)
1942 fprintf (stderr, "%s: unthrottled.\n", blurb());
1943 si->throttled_p = False;
1948 XtRemoveTimeOut (si->cycle_id);
1950 cycle_timer ((XtPointer) si, 0);
1955 else if (type == XA_EXIT)
1957 /* Ignore EXIT message if the screen is locked. */
1958 if (until_idle_p || !si->locked_p)
1960 clientmessage_response (si, window, False,
1961 "EXIT ClientMessage received.",
1966 for (i = 0; i < si->nscreens; i++)
1967 kill_screenhack (&si->screens[i]);
1968 unblank_screen (si);
1969 XSync (si->dpy, False);
1971 saver_exit (si, 0, 0);
1974 clientmessage_response (si, window, True,
1975 "EXIT ClientMessage received while locked.",
1976 "screen is locked.");
1978 else if (type == XA_RESTART)
1980 /* The RESTART message works whether the screensaver is active or not,
1981 unless the screen is locked, in which case it doesn't work.
1983 if (until_idle_p || !si->locked_p)
1985 clientmessage_response (si, window, False,
1986 "RESTART ClientMessage received.",
1991 for (i = 0; i < si->nscreens; i++)
1992 kill_screenhack (&si->screens[i]);
1993 unblank_screen (si);
1994 XSync (si->dpy, False);
1997 restart_process (si); /* does not return */
2001 clientmessage_response (si, window, True,
2002 "RESTART ClientMessage received while locked.",
2003 "screen is locked.");
2005 else if (type == XA_DEMO)
2007 long arg = event->xclient.data.l[1];
2008 Bool demo_one_hack_p = (arg == 5000);
2010 if (demo_one_hack_p)
2014 long which = event->xclient.data.l[2];
2017 sprintf (buf, "DEMO %ld ClientMessage received.", which);
2018 sprintf (buf2, "demoing (%ld).", which);
2019 clientmessage_response (si, window, False, buf, buf2);
2021 if (which < 0) which = 0; /* 0 == "random" */
2022 si->selection_mode = which;
2023 si->demoing_p = True;
2025 if (si->throttled_p && p->verbose_p)
2026 fprintf (stderr, "%s: unthrottled.\n", blurb());
2027 si->throttled_p = False;
2032 clientmessage_response (si, window, True,
2033 "DEMO ClientMessage received while active.",
2038 clientmessage_response (si, window, True,
2039 "obsolete form of DEMO ClientMessage.",
2040 "obsolete form of DEMO ClientMessage.");
2043 else if (type == XA_PREFS)
2045 clientmessage_response (si, window, True,
2046 "the PREFS client-message is obsolete.",
2047 "the PREFS client-message is obsolete.");
2049 else if (type == XA_LOCK)
2052 clientmessage_response (si, window, True,
2053 "not compiled with support for locking.",
2054 "locking not enabled.");
2055 #else /* !NO_LOCKING */
2056 if (si->locking_disabled_p)
2057 clientmessage_response (si, window, True,
2058 "LOCK ClientMessage received, but locking is disabled.",
2059 "locking not enabled.");
2060 else if (si->locked_p)
2061 clientmessage_response (si, window, True,
2062 "LOCK ClientMessage received while already locked.",
2067 char *response = (until_idle_p
2068 ? "activating and locking."
2070 sprintf (buf, "LOCK ClientMessage received; %s", response);
2071 clientmessage_response (si, window, False, buf, response);
2073 /* Note that this leaves things in a slightly inconsistent state:
2074 we are blanked but not locked. And blanking might actually
2075 fail if we can't get the grab. */
2076 set_locked_p (si, True);
2078 /* Have to set the time or xscreensaver-command doesn't
2079 report the LOCK state change. */
2080 si->blank_time = time ((time_t *) 0);
2082 si->selection_mode = 0;
2083 si->demoing_p = False;
2085 if (si->lock_id) /* we're doing it now, so lose the timeout */
2087 XtRemoveTimeOut (si->lock_id);
2093 if (si->using_mit_saver_extension ||
2094 si->using_sgi_saver_extension)
2096 XForceScreenSaver (si->dpy, ScreenSaverActive);
2105 #endif /* !NO_LOCKING */
2107 else if (type == XA_THROTTLE)
2109 /* The THROTTLE command is deprecated -- it predates the XDPMS
2110 extension. Instead of using -throttle, users should instead
2111 just power off the monitor (e.g., "xset dpms force off".)
2112 In a few minutes, xscreensaver will notice that the monitor
2113 is off, and cease running hacks.
2115 if (si->throttled_p)
2116 clientmessage_response (si, window, True,
2117 "THROTTLE ClientMessage received, but "
2118 "already throttled.",
2119 "already throttled.");
2123 char *response = "throttled.";
2124 si->throttled_p = True;
2125 si->selection_mode = 0;
2126 si->demoing_p = False;
2127 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
2128 clientmessage_response (si, window, False, buf, response);
2133 XtRemoveTimeOut (si->cycle_id);
2135 cycle_timer ((XtPointer) si, 0);
2139 else if (type == XA_UNTHROTTLE)
2141 if (! si->throttled_p)
2142 clientmessage_response (si, window, True,
2143 "UNTHROTTLE ClientMessage received, but "
2149 char *response = "unthrottled.";
2150 si->throttled_p = False;
2151 si->selection_mode = 0;
2152 si->demoing_p = False;
2153 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
2154 clientmessage_response (si, window, False, buf, response);
2159 XtRemoveTimeOut (si->cycle_id);
2161 cycle_timer ((XtPointer) si, 0);
2169 str = XGetAtomName_safe (si->dpy, type);
2173 if (strlen (str) > 80)
2174 strcpy (str+70, "...");
2175 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
2182 "unrecognised screensaver ClientMessage 0x%x received.",
2183 (unsigned int) event->xclient.data.l[0]);
2186 clientmessage_response (si, window, True, buf, buf);
2192 /* Some random diagnostics printed in -verbose mode.
2196 analyze_display (saver_info *si)
2200 const char *name; const char *desc;
2202 Status (*version_fn) (Display *, int *majP, int *minP);
2205 { "SCREEN_SAVER", /* underscore */ "SGI Screen-Saver",
2206 # ifdef HAVE_SGI_SAVER_EXTENSION
2211 }, { "SCREEN-SAVER", /* dash */ "SGI Screen-Saver",
2212 # ifdef HAVE_SGI_SAVER_EXTENSION
2217 }, { "MIT-SCREEN-SAVER", "MIT Screen-Saver",
2218 # ifdef HAVE_MIT_SAVER_EXTENSION
2219 True, XScreenSaverQueryVersion
2223 }, { "XIDLE", "XIdle",
2224 # ifdef HAVE_XIDLE_EXTENSION
2229 }, { "SGI-VIDEO-CONTROL", "SGI Video-Control",
2230 # ifdef HAVE_SGI_VC_EXTENSION
2231 True, XSGIvcQueryVersion
2235 }, { "READDISPLAY", "SGI Read-Display",
2236 # ifdef HAVE_READ_DISPLAY_EXTENSION
2237 True, XReadDisplayQueryVersion
2241 }, { "MIT-SHM", "Shared Memory",
2242 # ifdef HAVE_XSHM_EXTENSION
2243 True, (Status (*) (Display*,int*,int*)) XShmQueryVersion /* 4 args */
2247 }, { "DOUBLE-BUFFER", "Double-Buffering",
2248 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
2249 True, XdbeQueryExtension
2253 }, { "DPMS", "Power Management",
2254 # ifdef HAVE_DPMS_EXTENSION
2255 True, DPMSGetVersion
2265 }, { "XFree86-VidModeExtension", "XF86 Video-Mode",
2266 # ifdef HAVE_XF86VMODE
2267 True, XF86VidModeQueryVersion
2271 }, { "XC-VidModeExtension", "XC Video-Mode",
2272 # ifdef HAVE_XF86VMODE
2273 True, XF86VidModeQueryVersion
2277 }, { "XFree86-MISC", "XF86 Misc",
2278 # ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2279 True, XF86MiscQueryVersion
2283 }, { "XC-MISC", "XC Misc",
2284 # ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2285 True, XF86MiscQueryVersion
2289 }, { "XINERAMA", "Xinerama",
2290 # ifdef HAVE_XINERAMA
2291 True, XineramaQueryVersion
2295 }, { "RANDR", "Resize-and-Rotate",
2297 True, XRRQueryVersion
2303 }, { "NV-CONTROL", "NVidia",
2305 }, { "NV-GLX", "NVidia GLX",
2307 }, { "Apple-DRI", "Apple-DRI (XDarwin)",
2309 }, { "XInputExtension", "XInput",
2314 fprintf (stderr, "%s: running on display \"%s\"\n", blurb(),
2315 DisplayString(si->dpy));
2316 fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
2317 ServerVendor(si->dpy), VendorRelease(si->dpy));
2319 fprintf (stderr, "%s: useful extensions:\n", blurb());
2320 for (i = 0; i < countof(exts); i++)
2322 int op = 0, event = 0, error = 0;
2324 int maj = 0, min = 0;
2325 int dummy1, dummy2, dummy3;
2328 /* Most of the extension version functions take 3 args,
2329 writing results into args 2 and 3, but some take more.
2330 We only ever care about the first two results, but we
2331 pass in three extra pointers just in case.
2333 Status (*version_fn_2) (Display*,int*,int*,int*,int*,int*) =
2334 (Status (*) (Display*,int*,int*,int*,int*,int*)) exts[i].version_fn;
2336 if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
2338 sprintf (buf, "%s: ", blurb());
2340 strcat (buf, exts[i].desc);
2344 else if (version_fn_2 (si->dpy, &maj, &min, &dummy1, &dummy2, &dummy3))
2345 sprintf (buf+strlen(buf), " (%d.%d)", maj, min);
2347 strcat (buf, " (unavailable)");
2349 if (!exts[i].useful_p)
2350 strcat (buf, " (disabled at compile time)");
2351 fprintf (stderr, "%s\n", buf);
2354 for (i = 0; i < si->nscreens; i++)
2356 saver_screen_info *ssi = &si->screens[i];
2357 unsigned long colormapped_depths = 0;
2358 unsigned long non_mapped_depths = 0;
2359 XVisualInfo vi_in, *vi_out;
2362 if (!ssi->real_screen_p) continue;
2364 vi_in.screen = ssi->real_screen_number;
2365 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
2366 if (!vi_out) continue;
2367 for (j = 0; j < out_count; j++) {
2368 if (vi_out[j].depth >= 32) continue;
2369 if (vi_out[j].class == PseudoColor)
2370 colormapped_depths |= (1 << vi_out[j].depth);
2372 non_mapped_depths |= (1 << vi_out[j].depth);
2374 XFree ((char *) vi_out);
2376 if (colormapped_depths)
2378 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(),
2379 ssi->real_screen_number);
2380 for (j = 0; j < 32; j++)
2381 if (colormapped_depths & (1 << j))
2382 fprintf (stderr, " %d", j);
2383 fprintf (stderr, ".\n");
2385 if (non_mapped_depths)
2387 fprintf (stderr, "%s: screen %d non-colormapped depths:",
2388 blurb(), ssi->real_screen_number);
2389 for (j = 0; j < 32; j++)
2390 if (non_mapped_depths & (1 << j))
2391 fprintf (stderr, " %d", j);
2392 fprintf (stderr, ".\n");
2396 describe_monitor_layout (si);
2401 display_is_on_console_p (saver_info *si)
2403 Bool not_on_console = True;
2404 char *dpystr = DisplayString (si->dpy);
2405 char *tail = (char *) strchr (dpystr, ':');
2406 if (! tail || strncmp (tail, ":0", 2))
2407 not_on_console = True;
2410 char dpyname[255], localname[255];
2411 strncpy (dpyname, dpystr, tail-dpystr);
2412 dpyname [tail-dpystr] = 0;
2414 !strcmp(dpyname, "unix") ||
2415 !strcmp(dpyname, "localhost"))
2416 not_on_console = False;
2417 else if (gethostname (localname, sizeof (localname)))
2418 not_on_console = True; /* can't find hostname? */
2419 else if (!strncmp (dpyname, "/tmp/launch-", 12)) /* MacOS X launchd */
2420 not_on_console = False;
2423 /* We have to call gethostbyname() on the result of gethostname()
2424 because the two aren't guarenteed to be the same name for the
2425 same host: on some losing systems, one is a FQDN and the other
2426 is not. Here in the wide wonderful world of Unix it's rocket
2427 science to obtain the local hostname in a portable fashion.
2429 And don't forget, gethostbyname() reuses the structure it
2430 returns, so we have to copy the fucker before calling it again.
2431 Thank you master, may I have another.
2433 struct hostent *h = gethostbyname (dpyname);
2435 not_on_console = True;
2440 strcpy (hn, h->h_name);
2441 l = gethostbyname (localname);
2442 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
2446 return !not_on_console;
2450 /* Do a little bit of heap introspection...
2453 check_for_leaks (const char *where)
2455 #if defined(HAVE_SBRK) && defined(LEAK_PARANOIA)
2456 static unsigned long last_brk = 0;
2457 int b = (unsigned long) sbrk(0);
2458 if (last_brk && last_brk < b)
2459 fprintf (stderr, "%s: %s: brk grew by %luK.\n",
2461 (((b - last_brk) + 1023) / 1024));
2463 #endif /* HAVE_SBRK */