1 /* xscreensaver, Copyright (c) 1991-2013 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
12 /* ========================================================================
13 * First we wait until the keyboard and mouse become idle for the specified
14 * amount of time. We do this in one of three different ways: periodically
15 * checking with the XIdle server extension; selecting key and mouse events
16 * on (nearly) all windows; or by waiting for the MIT-SCREEN-SAVER extension
17 * to send us a "you are idle" event.
19 * Then, we map a full screen black window.
21 * We place a __SWM_VROOT property on this window, so that newly-started
22 * clients will think that this window is a "virtual root" window (as per
23 * the logic in the historical "vroot.h" header.)
25 * If there is an existing "virtual root" window (one that already had
26 * an __SWM_VROOT property) then we remove that property from that window.
27 * Otherwise, clients would see that window (the real virtual root) instead
28 * of ours (the impostor.)
30 * Then we pick a random program to run, and start it. Two assumptions
31 * are made about this program: that it has been specified with whatever
32 * command-line options are necessary to make it run on the root window;
33 * and that it has been compiled with vroot.h, so that it is able to find
34 * the root window when a virtual-root window manager (or this program) is
37 * Then, we wait for keyboard or mouse events to be generated on the window.
38 * When they are, we kill the inferior process, unmap the window, and restore
39 * the __SWM_VROOT property to the real virtual root window if there was one.
41 * On multi-screen systems, we do the above on each screen, and start
42 * multiple programs, each with a different value of $DISPLAY.
44 * On Xinerama systems, we do a similar thing, but instead create multiple
45 * windows on the (only) display, and tell the subprocess which one to use
46 * via the $XSCREENSAVER_WINDOW environment variable -- this trick requires
47 * a recent (Aug 2003) revision of vroot.h.
49 * (See comments in screens.c for more details about Xinerama/RANDR stuff.)
51 * While we are waiting for user activity, we also set up timers so that,
52 * after a certain amount of time has passed, we can start a different
53 * screenhack. We do this by killing the running child process with
54 * SIGTERM, and then starting a new one in the same way.
56 * If there was a real virtual root, meaning that we removed the __SWM_VROOT
57 * property from it, meaning we must (absolutely must) restore it before we
58 * exit, then we set up signal handlers for most signals (SIGINT, SIGTERM,
59 * etc.) that do this. Most Xlib and Xt routines are not reentrant, so it
60 * is not generally safe to call them from signal handlers; however, this
61 * program spends most of its time waiting, so the window of opportunity
62 * when code could be called reentrantly is fairly small; and also, the worst
63 * that could happen is that the call would fail. If we've gotten one of
64 * these signals, then we're on our way out anyway. If we didn't restore the
65 * __SWM_VROOT property, that would be very bad, so it's worth a shot. Note
66 * that this means that, if you're using a virtual-root window manager, you
67 * can really fuck up the world by killing this process with "kill -9".
69 * This program accepts ClientMessages of type SCREENSAVER; these messages
70 * may contain the atoms ACTIVATE, DEACTIVATE, etc, meaning to turn the
71 * screensaver on or off now, regardless of the idleness of the user,
72 * and a few other things. The included "xscreensaver-command" program
73 * sends these messsages.
75 * If we don't have the XIdle, MIT-SCREEN-SAVER, or SGI SCREEN_SAVER
76 * extensions, then we do the XAutoLock trick: notice every window that
77 * gets created, and wait 30 seconds or so until its creating process has
78 * settled down, and then select KeyPress events on those windows which
79 * already select for KeyPress events. It's important that we not select
80 * KeyPress on windows which don't select them, because that would
81 * interfere with event propagation. This will break if any program
82 * changes its event mask to contain KeyRelease or PointerMotion more than
83 * 30 seconds after creating the window, but such programs do not seem to
84 * occur in nature (I've never seen it happen in all these years.)
86 * The reason that we can't select KeyPresses on windows that don't have
87 * them already is that, when dispatching a KeyPress event, X finds the
88 * lowest (leafmost) window in the hierarchy on which *any* client selects
89 * for KeyPress, and sends the event to that window. This means that if a
90 * client had a window with subwindows, and expected to receive KeyPress
91 * events on the parent window instead of the subwindows, then that client
92 * would malfunction if some other client selected KeyPress events on the
93 * subwindows. It is an incredible misdesign that one client can make
94 * another client malfunction in this way.
96 * To detect mouse motion, we periodically wake up and poll the mouse
97 * position and button/modifier state, and notice when something has
98 * changed. We make this check every five seconds by default, and since the
99 * screensaver timeout has a granularity of one minute, this makes the
100 * chance of a false positive very small. We could detect mouse motion in
101 * the same way as keyboard activity, but that would suffer from the same
102 * "client changing event mask" problem that the KeyPress events hack does.
103 * I think polling is more reliable.
105 * On systems with /proc/interrupts (Linux) we poll that file and note when
106 * the interrupt counter numbers on the "keyboard" and "PS/2" lines change.
107 * (There is no reliable way, using /proc/interrupts, to detect non-PS/2
108 * mice, so it doesn't help for serial or USB mice.)
110 * None of this crap happens if we're using one of the extensions. Sadly,
111 * the XIdle extension hasn't been available for many years; the SGI
112 * extension only exists on SGIs; and the MIT extension, while widely
113 * deployed, is garbage in several ways.
115 * A third idle-detection option could be implemented (but is not): when
116 * running on the console display ($DISPLAY is `localhost`:0) and we're on a
117 * machine where /dev/tty and /dev/mouse have reasonable last-modification
118 * times, we could just stat() those. But the incremental benefit of
119 * implementing this is really small, so forget I said anything.
122 * - Have a second terminal handy.
123 * - Be careful where you set your breakpoints, you don't want this to
124 * stop under the debugger with the keyboard grabbed or the blackout
126 * - If you run your debugger under XEmacs, try M-ESC (x-grab-keyboard)
127 * to keep your emacs window alive even when xscreensaver has grabbed.
128 * - Go read the code related to `debug_p'.
129 * - You probably can't set breakpoints in functions that are called on
130 * the other side of a call to fork() -- if your subprocesses are
131 * dying with signal 5, Trace/BPT Trap, you're losing in this way.
132 * - If you aren't using a server extension, don't leave this stopped
133 * under the debugger for very long, or the X input buffer will get
134 * huge because of the keypress events it's selecting for. This can
135 * make your X server wedge with "no more input buffers."
137 * ======================================================================== */
145 #include <X11/Xlib.h>
149 # include <libintl.h>
150 #endif /* ENABLE_NLS */
152 #include <X11/Xlibint.h>
154 #include <X11/Xatom.h>
155 #include <X11/Intrinsic.h>
156 #include <X11/StringDefs.h>
157 #include <X11/Shell.h>
160 #include <sys/time.h>
161 #include <netdb.h> /* for gethostbyname() */
162 #include <sys/types.h>
166 # include <X11/Xmu/Error.h>
168 # include <Xmu/Error.h>
170 #else /* !HAVE_XMU */
172 #endif /* !HAVE_XMU */
174 #ifdef HAVE_MIT_SAVER_EXTENSION
175 #include <X11/extensions/scrnsaver.h>
176 #endif /* HAVE_MIT_SAVER_EXTENSION */
178 #ifdef HAVE_XIDLE_EXTENSION
179 # include <X11/extensions/xidle.h>
180 #endif /* HAVE_XIDLE_EXTENSION */
182 #ifdef HAVE_SGI_VC_EXTENSION
183 # include <X11/extensions/XSGIvc.h>
184 #endif /* HAVE_SGI_VC_EXTENSION */
186 #ifdef HAVE_READ_DISPLAY_EXTENSION
187 # include <X11/extensions/readdisplay.h>
188 #endif /* HAVE_READ_DISPLAY_EXTENSION */
190 #ifdef HAVE_XSHM_EXTENSION
191 # include <X11/extensions/XShm.h>
192 #endif /* HAVE_XSHM_EXTENSION */
194 #ifdef HAVE_DPMS_EXTENSION
195 # include <X11/extensions/dpms.h>
196 #endif /* HAVE_DPMS_EXTENSION */
199 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
200 # include <X11/extensions/Xdbe.h>
201 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
203 #ifdef HAVE_XF86VMODE
204 # include <X11/extensions/xf86vmode.h>
205 #endif /* HAVE_XF86VMODE */
207 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
208 # include <X11/extensions/xf86misc.h>
209 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
212 # include <X11/extensions/Xinerama.h>
213 #endif /* HAVE_XINERAMA */
216 # include <X11/extensions/Xrandr.h>
217 #endif /* HAVE_RANDR */
220 #include "xscreensaver.h"
222 #include "yarandom.h"
223 #include "resources.h"
228 saver_info *global_si_kludge = 0; /* I hate C so much... */
235 static Atom XA_SCREENSAVER_RESPONSE;
236 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
237 static Atom XA_RESTART, XA_SELECT;
238 static Atom XA_THROTTLE, XA_UNTHROTTLE;
239 Atom XA_DEMO, XA_PREFS, XA_EXIT, XA_LOCK, XA_BLANK;
242 static XrmOptionDescRec options [] = {
244 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
245 { "-silent", ".verbose", XrmoptionNoArg, "off" },
247 /* xscreensaver-demo uses this one */
248 { "-nosplash", ".splash", XrmoptionNoArg, "off" },
249 { "-no-splash", ".splash", XrmoptionNoArg, "off" },
251 /* useful for debugging */
252 { "-no-capture-stderr", ".captureStderr", XrmoptionNoArg, "off" },
253 { "-log", ".logFile", XrmoptionSepArg, 0 },
257 __extension__ /* shut up about "string length is greater than the length
258 ISO C89 compilers are required to support" when including
262 static char *defaults[] = {
263 #include "XScreenSaver_ad.h"
268 ERROR! You must not include vroot.h in this file.
272 do_help (saver_info *si)
275 s = strchr (screensaver_id, '-');
276 s = strrchr (s, '-');
278 strncpy (year, s, 4);
284 xscreensaver %s, copyright (c) 1991-%s by Jamie Zawinski <jwz@jwz.org>\n\
286 All xscreensaver configuration is via the `~/.xscreensaver' file.\n\
287 Rather than editing that file by hand, just run `xscreensaver-demo':\n\
288 that program lets you configure the screen saver graphically,\n\
289 including timeouts, locking, and display modes.\n\
293 Just getting started? Try this:\n\
298 For updates, online manual, and FAQ, please see the web page:\n\
300 http://www.jwz.org/xscreensaver/\n\
309 Bool in_signal_handler_p = 0; /* I hate C so much... */
314 if (in_signal_handler_p)
316 /* Turns out that ctime() and even localtime_r() call malloc() on Linux!
317 So we can't call them from inside SIGCHLD. WTF.
320 strcpy (buf, "... ... .. signal ....");
325 time_t now = time ((time_t *) 0);
326 char *str = (char *) ctime (&now);
327 char *nl = (char *) strchr (str, '\n');
328 if (nl) *nl = 0; /* take off that dang newline */
333 static Bool blurb_timestamp_p = True; /* kludge */
338 if (!blurb_timestamp_p)
342 static char buf[255];
343 char *ct = timestring();
344 int n = strlen(progname);
346 strncpy(buf, progname, n);
349 strncpy(buf+n, ct+11, 8);
350 strcpy(buf+n+9, ": ");
357 saver_ehandler (Display *dpy, XErrorEvent *error)
359 saver_info *si = global_si_kludge; /* I hate C so much... */
363 if (!real_stderr) real_stderr = stderr;
365 fprintf (real_stderr, "\n"
366 "#######################################"
367 "#######################################\n\n"
368 "%s: X Error! PLEASE REPORT THIS BUG.\n",
371 for (i = 0; i < si->nscreens; i++)
373 saver_screen_info *ssi = &si->screens[i];
374 fprintf (real_stderr, "%s: screen %d/%d: 0x%x, 0x%x, 0x%x\n",
375 blurb(), ssi->real_screen_number, ssi->number,
376 (unsigned int) RootWindowOfScreen (si->screens[i].screen),
377 (unsigned int) si->screens[i].real_vroot,
378 (unsigned int) si->screens[i].screensaver_window);
381 fprintf (real_stderr, "\n"
382 "#######################################"
383 "#######################################\n\n");
385 fatal_p = XmuPrintDefaultErrorMessage (dpy, error, real_stderr);
387 fatal_p = True; /* The only time I've ever seen a supposedly nonfatal error,
388 it has been BadImplementation / Xlib sequence lost, which
389 are in truth pretty damned fatal.
392 fprintf (real_stderr, "\n");
395 fprintf (real_stderr, "%s: nonfatal error.\n\n", blurb());
398 if (si->prefs.xsync_p)
400 saver_exit (si, -1, "because of synchronous X Error");
405 __extension__ /* don't warn about "string length is greater than the
406 length ISO C89 compilers are required to support". */
408 fprintf (real_stderr,
409 "#######################################################################\n"
411 " If at all possible, please re-run xscreensaver with the command\n"
412 " line arguments `-sync -verbose -log log.txt', and reproduce this\n"
413 " bug. That will cause xscreensaver to dump a `core' file to the\n"
414 " current directory. Please include the stack trace from that core\n"
415 " file in your bug report. *DO NOT* mail the core file itself! That\n"
416 " won't work. A \"log.txt\" file will also be written. Please *do*\n"
417 " include the complete \"log.txt\" file with your bug report.\n"
419 " http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
420 " the most useful bug reports, and how to examine core files.\n"
422 " The more information you can provide, the better. But please\n"
423 " report this bug, regardless!\n"
425 "#######################################################################\n"
429 saver_exit (si, -1, 0);
437 /* This error handler is used only while the X connection is being set up;
438 after we've got a connection, we don't use this handler again. The only
439 reason for having this is so that we can present a more idiot-proof error
440 message than "cannot open display."
443 startup_ehandler (String name, String type, String class,
444 String defalt, /* one can't even spel properly
445 in this joke of a language */
446 String *av, Cardinal *ac)
450 saver_info *si = global_si_kludge; /* I hate C so much... */
451 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
453 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
454 fmt, sizeof(fmt)-1, *db);
456 fprintf (stderr, "%s: ", blurb());
458 memset (p, 0, sizeof(p));
459 if (*ac > countof (p)) *ac = countof (p);
460 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
461 fprintf (stderr, fmt, /* Did I mention that I hate C? */
462 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
463 fprintf (stderr, "\n");
465 describe_uids (si, stderr);
467 if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
469 fprintf (stderr, "\n"
470 "%s: This is probably because you're logging in as root. You\n"
471 " shouldn't log in as root: you should log in as a normal user,\n"
472 " and then `su' as needed. If you insist on logging in as\n"
473 " root, you will have to turn off X's security features before\n"
474 " xscreensaver will work.\n"
476 " Please read the manual and FAQ for more information:\n",
481 fprintf (stderr, "\n"
482 "%s: Errors at startup are usually authorization problems.\n"
483 " But you're not logging in as root (good!) so something\n"
484 " else must be wrong. Did you read the manual and the FAQ?\n",
488 fprintf (stderr, "\n"
489 " http://www.jwz.org/xscreensaver/faq.html\n"
490 " http://www.jwz.org/xscreensaver/man.html\n"
499 /* The zillions of initializations.
502 /* Set progname, version, etc. This is done very early.
505 set_version_string (saver_info *si, int *argc, char **argv)
507 progclass = "XScreenSaver";
509 /* progname is reset later, after we connect to X. */
510 progname = strrchr(argv[0], '/');
511 if (progname) progname++;
512 else progname = argv[0];
514 if (strlen(progname) > 100) /* keep it short. */
517 /* The X resource database blows up if argv[0] has a "." in it. */
520 while ((s = strchr (s, '.')))
524 si->version = (char *) malloc (5);
525 memcpy (si->version, screensaver_id + 17, 4);
530 /* Initializations that potentially take place as a priveleged user:
531 If the xscreensaver executable is setuid root, then these initializations
532 are run as root, before discarding privileges.
535 privileged_initialization (saver_info *si, int *argc, char **argv)
538 /* before hack_uid() for proper permissions */
539 lock_priv_init (*argc, argv, si->prefs.verbose_p);
540 #endif /* NO_LOCKING */
546 /* Figure out what locking mechanisms are supported.
549 lock_initialization (saver_info *si, int *argc, char **argv)
552 si->locking_disabled_p = True;
553 si->nolock_reason = "not compiled with locking support";
554 #else /* !NO_LOCKING */
556 /* Finish initializing locking, now that we're out of privileged code. */
557 if (! lock_init (*argc, argv, si->prefs.verbose_p))
559 si->locking_disabled_p = True;
560 si->nolock_reason = "error getting password";
563 /* If locking is currently enabled, but the environment indicates that
564 we have been launched as GDM's "Background" program, then disable
565 locking just in case.
567 if (!si->locking_disabled_p && getenv ("RUNNING_UNDER_GDM"))
569 si->locking_disabled_p = True;
570 si->nolock_reason = "running under GDM";
573 /* If the server is XDarwin (MacOS X) then disable locking.
574 (X grabs only affect X programs, so you can use Command-Tab
575 to bring any other Mac program to the front, e.g., Terminal.)
577 if (!si->locking_disabled_p)
579 int op = 0, event = 0, error = 0;
580 Bool macos_p = False;
583 /* Disable locking if *running* on Apple hardware, since we have no
584 reliable way to determine whether the server is running on MacOS.
585 Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
586 but I'm not really sure about that.
592 /* This extension exists on the Apple X11 server, but not
593 on earlier versions of the XDarwin server. */
594 macos_p = XQueryExtension (si->dpy, "Apple-DRI", &op, &event, &error);
598 si->locking_disabled_p = True;
599 si->nolock_reason = "Cannot lock securely on MacOS X";
603 if (si->prefs.debug_p) /* But allow locking anyway in debug mode. */
604 si->locking_disabled_p = False;
606 #endif /* NO_LOCKING */
610 /* Open the connection to the X server, and intern our Atoms.
613 connect_to_server (saver_info *si, int *argc, char **argv)
615 Widget toplevel_shell;
618 char *d = getenv ("DISPLAY");
621 char *ndpy = strdup("DISPLAY=:0.0");
622 /* if (si->prefs.verbose_p) */ /* sigh, too early to test this... */
624 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
628 /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
629 glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
630 do not. So we must leak it (and/or the previous setting). Yay.
633 #endif /* HAVE_PUTENV */
635 XSetErrorHandler (saver_ehandler);
637 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
638 toplevel_shell = XtAppInitialize (&si->app, progclass,
639 options, XtNumber (options),
640 argc, argv, defaults, 0, 0);
641 XtAppSetErrorMsgHandler (si->app, 0);
643 si->dpy = XtDisplay (toplevel_shell);
644 si->prefs.db = XtDatabase (si->dpy);
645 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
647 if(strlen(progname) > 100) /* keep it short. */
650 db = si->prefs.db; /* resources.c needs this */
652 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
653 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
654 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
655 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
656 XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
657 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
659 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
660 XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False);
661 XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False);
662 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
663 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
664 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
665 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
666 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
667 XA_PREV = XInternAtom (si->dpy, "PREV", False);
668 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
669 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
670 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
671 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
672 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
673 XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
674 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
675 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
677 return toplevel_shell;
681 /* Handle the command-line arguments that were not handled for us by Xt.
682 Issue an error message and exit if there are unknown options.
685 process_command_line (saver_info *si, int *argc, char **argv)
688 for (i = 1; i < *argc; i++)
690 if (!strcmp (argv[i], "-debug"))
691 /* no resource for this one, out of paranoia. */
692 si->prefs.debug_p = True;
694 else if (!strcmp (argv[i], "-h") ||
695 !strcmp (argv[i], "-help") ||
696 !strcmp (argv[i], "--help"))
701 const char *s = argv[i];
702 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
705 if (s[0] == '-' && s[1] == '-') s++;
706 if (!strcmp (s, "-activate") ||
707 !strcmp (s, "-deactivate") ||
708 !strcmp (s, "-cycle") ||
709 !strcmp (s, "-next") ||
710 !strcmp (s, "-prev") ||
711 !strcmp (s, "-exit") ||
712 !strcmp (s, "-restart") ||
713 !strcmp (s, "-demo") ||
714 !strcmp (s, "-prefs") ||
715 !strcmp (s, "-preferences") ||
716 !strcmp (s, "-lock") ||
717 !strcmp (s, "-version") ||
718 !strcmp (s, "-time"))
721 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
722 fprintf (stderr, "\n\
723 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
725 fprintf (stderr, "\n\
726 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
729 The `xscreensaver' program is a daemon that runs in the background.\n\
730 You control a running xscreensaver process by sending it messages\n\
731 with `xscreensaver-demo' or `xscreensaver-command'.\n\
732 . See the man pages for details, or check the web page:\n\
733 http://www.jwz.org/xscreensaver/\n\n");
742 /* Print out the xscreensaver banner to the tty if applicable;
743 Issue any other warnings that are called for at this point.
746 print_banner (saver_info *si)
748 saver_preferences *p = &si->prefs;
751 s = strchr (screensaver_id, '-');
752 s = strrchr (s, '-');
754 strncpy (year, s, 4);
757 /* This resource gets set some time before the others, so that we know
758 whether to print the banner (and so that the banner gets printed before
759 any resource-database-related error messages.)
761 p->verbose_p = (p->debug_p ||
762 get_boolean_resource (si->dpy, "verbose", "Boolean"));
764 /* Ditto, for the locking_disabled_p message. */
765 p->lock_p = get_boolean_resource (si->dpy, "lock", "Boolean");
769 "%s %s, copyright (c) 1991-%s "
770 "by Jamie Zawinski <jwz@jwz.org>.\n",
771 progname, si->version, year);
774 fprintf (stderr, "\n"
775 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
777 "\tNote that in debug mode, the xscreensaver window will only\n"
778 "\tcover the left half of the screen. (The idea is that you\n"
779 "\tcan still see debugging output in a shell, if you position\n"
780 "\tit on the right side of the screen.)\n"
782 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
783 "\tuntrusted environments.\n"
787 if (p->verbose_p && senescent_p ())
788 fprintf (stderr, "\n"
789 "*************************************"
790 "**************************************\n"
791 "%s: Warning: this version of xscreensaver is VERY OLD!\n"
792 "%s: Please upgrade! http://www.jwz.org/xscreensaver/\n"
793 "*************************************"
794 "**************************************\n"
800 if (!si->uid_message || !*si->uid_message)
801 describe_uids (si, stderr);
804 if (si->orig_uid && *si->orig_uid)
805 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
806 blurb(), si->orig_uid);
807 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
810 fprintf (stderr, "%s: in process %lu.\n", blurb(),
811 (unsigned long) getpid());
816 print_lock_failure_banner (saver_info *si)
818 saver_preferences *p = &si->prefs;
820 /* If locking was not able to be initalized for some reason, explain why.
821 (This has to be done after we've read the lock_p resource.)
823 if (si->locking_disabled_p)
826 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
828 if (strstr (si->nolock_reason, "passw"))
829 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
830 "consult the manual.\n", blurb());
831 else if (strstr (si->nolock_reason, "running as "))
833 "%s: locking only works when xscreensaver is launched\n"
834 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
835 "\t See the manual for details.\n",
842 /* called from screens.c so that all the Xt crud is here. */
844 initialize_screen_root_widget (saver_screen_info *ssi)
846 saver_info *si = ssi->global;
847 if (ssi->toplevel_shell)
848 XtDestroyWidget (ssi->toplevel_shell);
849 ssi->toplevel_shell =
850 XtVaAppCreateShell (progname, progclass,
851 applicationShellWidgetClass,
853 XtNscreen, ssi->screen,
854 XtNvisual, ssi->current_visual,
855 XtNdepth, visual_depth (ssi->screen,
856 ssi->current_visual),
861 /* Examine all of the display's screens, and populate the `saver_screen_info'
862 structures. Make sure this is called after hack_environment() sets $PATH.
865 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
869 update_screen_layout (si);
871 /* Check to see whether fading is ever possible -- if any of the
872 screens on the display has a PseudoColor visual, then fading can
873 work (on at least some screens.) If no screen has a PseudoColor
874 visual, then don't bother ever trying to fade, because it will
875 just cause a delay without causing any visible effect.
877 for (i = 0; i < si->nscreens; i++)
879 saver_screen_info *ssi = &si->screens[i];
880 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
881 get_visual (ssi->screen, "PseudoColor", True, False) ||
882 get_visual (ssi->screen, "GrayScale", True, False))
884 si->fading_possible_p = True;
889 #ifdef HAVE_XF86VMODE_GAMMA
890 si->fading_possible_p = True; /* if we can gamma fade, go for it */
895 /* If any server extensions have been requested, try and initialize them.
896 Issue warnings if requests can't be honored.
899 initialize_server_extensions (saver_info *si)
901 saver_preferences *p = &si->prefs;
903 Bool server_has_xidle_extension_p = False;
904 Bool server_has_sgi_saver_extension_p = False;
905 Bool server_has_mit_saver_extension_p = False;
906 Bool system_has_proc_interrupts_p = False;
907 Bool server_has_xinput_extension_p = False;
908 const char *piwhy = 0;
910 si->using_xidle_extension = p->use_xidle_extension;
911 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
912 si->using_mit_saver_extension = p->use_mit_saver_extension;
913 si->using_proc_interrupts = p->use_proc_interrupts;
914 si->using_xinput_extension = p->use_xinput_extension;
916 #ifdef HAVE_XIDLE_EXTENSION
919 server_has_xidle_extension_p = XidleQueryExtension (si->dpy, &ev, &er);
922 #ifdef HAVE_SGI_SAVER_EXTENSION
923 server_has_sgi_saver_extension_p =
924 XScreenSaverQueryExtension (si->dpy,
925 &si->sgi_saver_ext_event_number,
926 &si->sgi_saver_ext_error_number);
928 #ifdef HAVE_MIT_SAVER_EXTENSION
929 server_has_mit_saver_extension_p =
930 XScreenSaverQueryExtension (si->dpy,
931 &si->mit_saver_ext_event_number,
932 &si->mit_saver_ext_error_number);
934 #ifdef HAVE_PROC_INTERRUPTS
935 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
939 server_has_xinput_extension_p = query_xinput_extension (si);
942 if (!server_has_xidle_extension_p)
943 si->using_xidle_extension = False;
944 else if (p->verbose_p)
946 if (si->using_xidle_extension)
947 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
949 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
952 if (!server_has_sgi_saver_extension_p)
953 si->using_sgi_saver_extension = False;
954 else if (p->verbose_p)
956 if (si->using_sgi_saver_extension)
957 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
960 "%s: not using server's SGI SCREEN_SAVER extension.\n",
964 if (!server_has_mit_saver_extension_p)
965 si->using_mit_saver_extension = False;
966 else if (p->verbose_p)
968 if (si->using_mit_saver_extension)
969 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
973 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
978 if (XRRQueryExtension (si->dpy,
979 &si->randr_event_number, &si->randr_error_number))
981 int nscreens = ScreenCount (si->dpy); /* number of *real* screens */
984 si->using_randr_extension = TRUE;
987 fprintf (stderr, "%s: selecting RANDR events\n", blurb());
988 for (i = 0; i < nscreens; i++)
989 # ifdef RRScreenChangeNotifyMask /* randr.h 1.5, 2002/09/29 */
990 XRRSelectInput (si->dpy, RootWindow (si->dpy, i),
991 RRScreenChangeNotifyMask);
992 # else /* !RRScreenChangeNotifyMask */ /* Xrandr.h 1.4, 2001/06/07 */
993 XRRScreenChangeSelectInput (si->dpy, RootWindow (si->dpy, i), True);
994 # endif /* !RRScreenChangeNotifyMask */
996 # endif /* HAVE_RANDR */
999 if (!server_has_xinput_extension_p)
1000 si->using_xinput_extension = False;
1003 if (si->using_xinput_extension)
1004 init_xinput_extension(si);
1008 if (si->using_xinput_extension)
1010 "%s: selecting events from %d XInputExtension devices.\n",
1011 blurb(), si->num_xinput_devices);
1014 "%s: not using XInputExtension.\n",
1020 if (!system_has_proc_interrupts_p)
1022 si->using_proc_interrupts = False;
1023 if (p->verbose_p && piwhy)
1024 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
1027 else if (p->verbose_p)
1029 if (si->using_proc_interrupts)
1031 "%s: consulting /proc/interrupts for keyboard activity.\n",
1035 "%s: not consulting /proc/interrupts for keyboard activity.\n",
1041 #ifdef DEBUG_MULTISCREEN
1043 debug_multiscreen_timer (XtPointer closure, XtIntervalId *id)
1045 saver_info *si = (saver_info *) closure;
1046 saver_preferences *p = &si->prefs;
1047 if (update_screen_layout (si))
1051 fprintf (stderr, "%s: new layout:\n", blurb());
1052 describe_monitor_layout (si);
1054 resize_screensaver_window (si);
1056 XtAppAddTimeOut (si->app, 1000*4, debug_multiscreen_timer, (XtPointer) si);
1058 #endif /* DEBUG_MULTISCREEN */
1061 /* For the case where we aren't using an server extensions, select user events
1062 on all the existing windows, and launch timers to select events on
1063 newly-created windows as well.
1065 If a server extension is being used, this does nothing.
1068 select_events (saver_info *si)
1070 saver_preferences *p = &si->prefs;
1073 if (si->using_xidle_extension ||
1074 si->using_mit_saver_extension ||
1075 si->using_sgi_saver_extension)
1078 if (p->initial_delay)
1082 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
1083 (int) p->initial_delay/1000,
1084 (p->initial_delay == 1000 ? "" : "s"));
1088 usleep (p->initial_delay);
1090 fprintf (stderr, " done.\n");
1095 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
1100 /* Select events on the root windows of every screen. This also selects
1101 for window creation events, so that new subwindows will be noticed.
1103 for (i = 0; i < si->nscreens; i++)
1105 saver_screen_info *ssi = &si->screens[i];
1106 if (ssi->real_screen_p)
1107 start_notice_events_timer (si,
1108 RootWindowOfScreen (si->screens[i].screen), False);
1112 fprintf (stderr, " done.\n");
1114 # ifdef DEBUG_MULTISCREEN
1115 if (p->debug_p) debug_multiscreen_timer ((XtPointer) si, 0);
1121 maybe_reload_init_file (saver_info *si)
1123 saver_preferences *p = &si->prefs;
1124 if (init_file_changed_p (p))
1127 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
1128 blurb(), init_file_name());
1130 load_init_file (si->dpy, p);
1132 /* If a server extension is in use, and p->timeout has changed,
1133 we need to inform the server of the new timeout. */
1134 disable_builtin_screensaver (si, False);
1136 /* If the DPMS settings in the init file have changed,
1137 change the settings on the server to match. */
1138 sync_server_dpms_settings (si->dpy,
1139 (p->dpms_enabled_p &&
1140 p->mode != DONT_BLANK),
1142 p->dpms_standby / 1000,
1143 p->dpms_suspend / 1000,
1152 - wait until the user is idle;
1154 - wait until the user is active;
1155 - unblank the screen;
1160 main_loop (saver_info *si)
1162 saver_preferences *p = &si->prefs;
1168 Bool was_locked = False;
1171 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1173 check_for_leaks ("unblanked A");
1174 sleep_until_idle (si, True);
1175 check_for_leaks ("unblanked B");
1180 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
1181 si->selection_mode, timestring());
1183 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
1187 maybe_reload_init_file (si);
1189 if (p->mode == DONT_BLANK)
1192 fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
1193 blurb(), timestring());
1195 /* Go around the loop and wait for the next bout of idleness,
1196 or for the init file to change, or for a remote command to
1197 come in, or something.
1199 But, if locked_p is true, go ahead. This can only happen
1200 if we're in "disabled" mode but a "lock" clientmessage came
1201 in: in that case, we should go ahead and blank/lock the screen.
1207 /* Since we're about to blank the screen, kill the de-race timer,
1208 if any. It might still be running if we have unblanked and then
1209 re-blanked in a short period (e.g., when using the "next" button
1210 in xscreensaver-demo.)
1215 fprintf (stderr, "%s: stopping de-race timer (%d remaining.)\n",
1216 blurb(), si->de_race_ticks);
1217 XtRemoveTimeOut (si->de_race_id);
1222 /* Now, try to blank.
1225 if (! blank_screen (si))
1227 /* We were unable to grab either the keyboard or mouse.
1228 This means we did not (and must not) blank the screen.
1229 If we were to blank the screen while some other program
1230 is holding both the mouse and keyboard grabbed, then
1231 we would never be able to un-blank it! We would never
1232 see any events, and the display would be wedged.
1234 In particular, without that keyboard grab, we will be
1235 unable to ever read keypresses on the unlock dialog.
1236 You can't unlock if you can't type your password.
1238 So, just go around the loop again and wait for the
1239 next bout of idleness. (If the user remains idle, we
1240 will next try to blank the screen again in no more than
1243 Time retry = 60 * 1000;
1244 if (p->timeout < retry)
1250 "%s: DEBUG MODE: unable to grab -- BLANKING ANYWAY.\n",
1256 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
1259 schedule_wakeup_event (si, retry, p->debug_p);
1264 for (i = 0; i < si->nscreens; i++)
1265 kill_screenhack (&si->screens[i]);
1267 raise_window (si, True, True, False);
1268 if (si->throttled_p)
1269 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
1271 for (i = 0; i < si->nscreens; i++)
1272 spawn_screenhack (&si->screens[i]);
1274 /* If we are blanking only, optionally power down monitor right now. */
1275 if (p->mode == BLANK_ONLY &&
1276 p->dpms_enabled_p &&
1279 sync_server_dpms_settings (si->dpy, True,
1281 p->dpms_standby / 1000,
1282 p->dpms_suspend / 1000,
1285 monitor_power_on (si, False);
1288 /* Don't start the cycle timer in demo mode. */
1289 if (!si->demoing_p && p->cycle)
1290 si->cycle_id = XtAppAddTimeOut (si->app,
1292 /* see comment in cycle_timer() */
1300 /* Maybe start locking the screen.
1303 Time lock_timeout = p->lock_timeout;
1305 if (si->emergency_lock_p && p->lock_p && lock_timeout)
1307 int secs = p->lock_timeout / 1000;
1310 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1312 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1316 si->emergency_lock_p = False;
1318 if (!si->demoing_p && /* if not going into demo mode */
1319 p->lock_p && /* and locking is enabled */
1320 !si->locking_disabled_p && /* and locking is possible */
1321 lock_timeout == 0) /* and locking is not timer-deferred */
1322 set_locked_p (si, True); /* then lock right now. */
1324 /* locked_p might be true already because of the above, or because of
1325 the LOCK ClientMessage. But if not, and if we're supposed to lock
1326 after some time, set up a timer to do so.
1331 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1332 activate_lock_timer,
1335 #endif /* !NO_LOCKING */
1338 ok_to_unblank = True;
1341 check_for_leaks ("blanked A");
1342 sleep_until_idle (si, False); /* until not idle */
1343 check_for_leaks ("blanked B");
1345 maybe_reload_init_file (si);
1348 /* Maybe unlock the screen.
1352 saver_screen_info *ssi = si->default_screen;
1353 if (si->locking_disabled_p) abort ();
1356 si->dbox_up_p = True;
1357 for (i = 0; i < si->nscreens; i++)
1358 suspend_screenhack (&si->screens[i], True); /* suspend */
1359 XUndefineCursor (si->dpy, ssi->screensaver_window);
1361 ok_to_unblank = unlock_p (si);
1363 si->dbox_up_p = False;
1364 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1365 for (i = 0; i < si->nscreens; i++)
1366 suspend_screenhack (&si->screens[i], False); /* resume */
1368 if (!ok_to_unblank &&
1369 !screenhack_running_p (si))
1371 /* If the lock dialog has been dismissed and we're not about to
1372 unlock the screen, and there is currently no hack running,
1373 then launch one. (There might be no hack running if DPMS
1374 had kicked in. But DPMS is off now, so bring back the hack)
1377 XtRemoveTimeOut (si->cycle_id);
1379 cycle_timer ((XtPointer) si, 0);
1382 #endif /* !NO_LOCKING */
1384 } while (!ok_to_unblank);
1388 fprintf (stderr, "%s: unblanking screen at %s.\n",
1389 blurb(), timestring ());
1391 /* Kill before unblanking, to stop drawing as soon as possible. */
1392 for (i = 0; i < si->nscreens; i++)
1393 kill_screenhack (&si->screens[i]);
1394 unblank_screen (si);
1396 set_locked_p (si, False);
1397 si->emergency_lock_p = False;
1399 si->selection_mode = 0;
1401 /* If we're throttled, and the user has explicitly unlocked the screen,
1402 then unthrottle. If we weren't locked, then don't unthrottle
1403 automatically, because someone might have just bumped the desk... */
1406 if (si->throttled_p && p->verbose_p)
1407 fprintf (stderr, "%s: unthrottled.\n", blurb());
1408 si->throttled_p = False;
1413 XtRemoveTimeOut (si->cycle_id);
1419 XtRemoveTimeOut (si->lock_id);
1423 /* Since we're unblanked now, break race conditions and make
1424 sure we stay that way (see comment in timers.c.) */
1425 if (! si->de_race_id)
1426 de_race_timer ((XtPointer) si, 0);
1430 static void analyze_display (saver_info *si);
1431 static void fix_fds (void);
1434 main (int argc, char **argv)
1438 saver_info *si = &the_si;
1439 saver_preferences *p = &si->prefs;
1440 struct passwd *spasswd;
1443 /* It turns out that if we do setlocale (LC_ALL, "") here, people
1444 running in Japanese locales get font craziness on the password
1445 dialog, presumably because it is displaying Japanese characters
1446 in a non-Japanese font. However, if we don't call setlocale()
1447 at all, then XLookupString() never returns multi-byte UTF-8
1448 characters when people type non-Latin1 characters on the
1451 The current theory (and at this point, I'm really guessing!) is
1452 that using LC_CTYPE instead of LC_ALL will make XLookupString()
1453 behave usefully, without having the side-effect of screwing up
1454 the fonts on the unlock dialog.
1456 See https://bugs.launchpad.net/ubuntu/+source/xscreensaver/+bug/671923
1457 from comment #20 onward.
1462 if (!setlocale (LC_CTYPE, ""))
1463 fprintf (stderr, "%s: warning: could not set default locale\n",
1466 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1467 textdomain (GETTEXT_PACKAGE);
1468 #endif /* ENABLE_NLS */
1470 memset(si, 0, sizeof(*si));
1471 global_si_kludge = si; /* I hate C so much... */
1475 # undef ya_rand_init
1478 save_argv (argc, argv);
1479 set_version_string (si, &argc, argv);
1480 privileged_initialization (si, &argc, argv);
1481 hack_environment (si);
1483 spasswd = getpwuid(getuid());
1486 fprintf(stderr, "Could not figure out who the current user is!\n");
1490 si->user = strdup(spasswd->pw_name ? spasswd->pw_name : "(unknown)");
1493 si->unlock_cb = gui_auth_conv;
1494 si->auth_finished_cb = auth_finished_cb;
1495 # endif /* !NO_LOCKING */
1497 shell = connect_to_server (si, &argc, argv);
1498 process_command_line (si, &argc, argv);
1499 stderr_log_file (si);
1502 load_init_file(si->dpy, p); /* must be before initialize_per_screen_info() */
1503 blurb_timestamp_p = p->timestamp_p; /* kludge */
1504 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1506 /* We can only issue this warning now. */
1507 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1509 "%s: there are no PseudoColor or GrayScale visuals.\n"
1510 "%s: ignoring the request for fading/unfading.\n",
1513 for (i = 0; i < si->nscreens; i++)
1515 saver_screen_info *ssi = &si->screens[i];
1516 if (ssi->real_screen_p)
1517 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1521 lock_initialization (si, &argc, argv);
1522 print_lock_failure_banner (si);
1524 if (p->xsync_p) XSynchronize (si->dpy, True);
1526 if (p->verbose_p) analyze_display (si);
1527 initialize_server_extensions (si);
1529 si->blank_time = time ((time_t) 0); /* must be before ..._window */
1530 initialize_screensaver_window (si);
1535 disable_builtin_screensaver (si, True);
1536 sync_server_dpms_settings (si->dpy,
1537 (p->dpms_enabled_p &&
1538 p->mode != DONT_BLANK),
1540 p->dpms_standby / 1000,
1541 p->dpms_suspend / 1000,
1545 initialize_stderr (si);
1546 handle_signals (si);
1548 make_splash_dialog (si);
1550 main_loop (si); /* doesn't return */
1557 /* Bad Things Happen if stdin, stdout, and stderr have been closed
1558 (as by the `sh incantation "xscreensaver >&- 2>&-"). When you do
1559 that, the X connection gets allocated to one of these fds, and
1560 then some random library writes to stderr, and random bits get
1561 stuffed down the X pipe, causing "Xlib: sequence lost" errors.
1562 So, we cause the first three file descriptors to be open to
1563 /dev/null if they aren't open to something else already. This
1564 must be done before any other files are opened (or the closing
1565 of that other file will again free up one of the "magic" first
1568 We do this by opening /dev/null three times, and then closing
1569 those fds, *unless* any of them got allocated as #0, #1, or #2,
1570 in which case we leave them open. Gag.
1572 Really, this crap is technically required of *every* X program,
1573 if you want it to be robust in the face of "2>&-".
1575 int fd0 = open ("/dev/null", O_RDWR);
1576 int fd1 = open ("/dev/null", O_RDWR);
1577 int fd2 = open ("/dev/null", O_RDWR);
1578 if (fd0 > 2) close (fd0);
1579 if (fd1 > 2) close (fd1);
1580 if (fd2 > 2) close (fd2);
1585 /* Processing ClientMessage events.
1589 static Bool error_handler_hit_p = False;
1592 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1594 error_handler_hit_p = True;
1598 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1599 them. We only look up the atom names for printing warning messages,
1600 so don't bomb out when it happens...
1603 XGetAtomName_safe (Display *dpy, Atom atom)
1606 XErrorHandler old_handler;
1607 if (!atom) return 0;
1610 error_handler_hit_p = False;
1611 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1612 result = XGetAtomName (dpy, atom);
1614 XSetErrorHandler (old_handler);
1616 if (error_handler_hit_p) result = 0;
1623 sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
1624 return strdup (buf);
1630 clientmessage_response (saver_info *si, Window w, Bool error,
1631 const char *stderr_msg,
1632 const char *protocol_msg)
1636 saver_preferences *p = &si->prefs;
1637 XErrorHandler old_handler;
1639 if (error || p->verbose_p)
1640 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1642 L = strlen(protocol_msg);
1643 proto = (char *) malloc (L + 2);
1644 proto[0] = (error ? '-' : '+');
1645 strcpy (proto+1, protocol_msg);
1648 /* Ignore all X errors while sending a response to a ClientMessage.
1649 Pretty much the only way we could get an error here is if the
1650 window we're trying to send the reply on has been deleted, in
1651 which case, the sender of the ClientMessage won't see our response
1654 XSync (si->dpy, False);
1655 error_handler_hit_p = False;
1656 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1658 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1659 PropModeReplace, (unsigned char *) proto, L);
1661 XSync (si->dpy, False);
1662 XSetErrorHandler (old_handler);
1663 XSync (si->dpy, False);
1670 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1672 #if 0 /* Oh, fuck it. GNOME likes to spew random ClientMessages at us
1673 all the time. This is presumably indicative of an error in
1674 the sender of that ClientMessage: if we're getting it and
1675 ignoring it, then it's not reaching the intended recipient.
1676 But people complain to me about this all the time ("waaah!
1677 xscreensaver is printing to it's stderr and that gets my
1678 panties all in a bunch!") And I'm sick of hearing about it.
1679 So we'll just ignore these messages and let GNOME go right
1680 ahead and continue to stumble along in its malfunction.
1683 saver_preferences *p = &si->prefs;
1684 char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1685 Window w = event->xclient.window;
1688 Bool root_p = False;
1691 for (screen = 0; screen < si->nscreens; screen++)
1692 if (w == si->screens[screen].screensaver_window)
1694 strcpy (wdesc, "xscreensaver");
1697 else if (w == RootWindow (si->dpy, screen))
1699 strcpy (wdesc, "root");
1704 /* If this ClientMessage was sent to the real root window instead of to the
1705 xscreensaver window, then it might be intended for someone else who is
1706 listening on the root window (e.g., the window manager). So only print
1707 the warning if: we are in debug mode; or if the bogus message was
1708 actually sent to one of the xscreensaver-created windows.
1710 if (root_p && !p->debug_p)
1715 XErrorHandler old_handler;
1717 XWindowAttributes xgwa;
1718 memset (&hint, 0, sizeof(hint));
1719 memset (&xgwa, 0, sizeof(xgwa));
1721 XSync (si->dpy, False);
1722 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1723 XGetClassHint (si->dpy, w, &hint);
1724 XGetWindowAttributes (si->dpy, w, &xgwa);
1725 XSync (si->dpy, False);
1726 XSetErrorHandler (old_handler);
1727 XSync (si->dpy, False);
1729 screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1731 sprintf (wdesc, "%.20s / %.20s",
1732 (hint.res_name ? hint.res_name : "(null)"),
1733 (hint.res_class ? hint.res_class : "(null)"));
1734 if (hint.res_name) XFree (hint.res_name);
1735 if (hint.res_class) XFree (hint.res_class);
1738 fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1739 blurb(), screen, (str ? str : "(null)"));
1740 fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1741 blurb(), screen, (unsigned long) w, wdesc);
1742 if (str) XFree (str);
1749 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1751 saver_preferences *p = &si->prefs;
1753 Window window = event->xclient.window;
1755 /* Preferences might affect our handling of client messages. */
1756 maybe_reload_init_file (si);
1758 if (event->xclient.message_type != XA_SCREENSAVER ||
1759 event->xclient.format != 32)
1761 bogus_clientmessage_warning (si, event);
1765 type = event->xclient.data.l[0];
1766 if (type == XA_ACTIVATE)
1770 if (p->mode == DONT_BLANK)
1772 clientmessage_response(si, window, True,
1773 "ACTIVATE ClientMessage received in DONT_BLANK mode.",
1774 "screen blanking is currently disabled.");
1778 clientmessage_response(si, window, False,
1779 "ACTIVATE ClientMessage received.",
1781 si->selection_mode = 0;
1782 si->demoing_p = False;
1784 if (si->throttled_p && p->verbose_p)
1785 fprintf (stderr, "%s: unthrottled.\n", blurb());
1786 si->throttled_p = False;
1788 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1790 XForceScreenSaver (si->dpy, ScreenSaverActive);
1798 clientmessage_response(si, window, True,
1799 "ClientMessage ACTIVATE received while already active.",
1802 else if (type == XA_DEACTIVATE)
1805 /* When -deactivate is received while locked, pop up the dialog box
1806 instead of just ignoring it. Some people depend on this behavior
1807 to be able to unlock by using e.g. a fingerprint reader without
1808 also having to click the mouse first.
1812 clientmessage_response(si, window, False,
1813 "DEACTIVATE ClientMessage received while locked: ignored.",
1814 "screen is locked.");
1821 if (si->throttled_p && p->verbose_p)
1822 fprintf (stderr, "%s: unthrottled.\n", blurb());
1823 si->throttled_p = False;
1825 clientmessage_response(si, window, False,
1826 "DEACTIVATE ClientMessage received.",
1828 if (si->using_mit_saver_extension ||
1829 si->using_sgi_saver_extension)
1831 XForceScreenSaver (si->dpy, ScreenSaverReset);
1839 clientmessage_response(si, window, False,
1840 "ClientMessage DEACTIVATE received while inactive: "
1841 "resetting idle timer.",
1842 "not active: idle timer reset.");
1846 else if (type == XA_CYCLE)
1850 clientmessage_response(si, window, False,
1851 "CYCLE ClientMessage received.",
1853 si->selection_mode = 0; /* 0 means randomize when its time. */
1854 si->demoing_p = False;
1856 if (si->throttled_p && p->verbose_p)
1857 fprintf (stderr, "%s: unthrottled.\n", blurb());
1858 si->throttled_p = False;
1861 XtRemoveTimeOut (si->cycle_id);
1863 cycle_timer ((XtPointer) si, 0);
1866 clientmessage_response(si, window, True,
1867 "ClientMessage CYCLE received while inactive.",
1870 else if (type == XA_NEXT || type == XA_PREV)
1872 clientmessage_response(si, window, False,
1874 ? "NEXT ClientMessage received."
1875 : "PREV ClientMessage received."),
1877 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1878 si->demoing_p = False;
1880 if (si->throttled_p && p->verbose_p)
1881 fprintf (stderr, "%s: unthrottled.\n", blurb());
1882 si->throttled_p = False;
1887 XtRemoveTimeOut (si->cycle_id);
1889 cycle_timer ((XtPointer) si, 0);
1894 else if (type == XA_SELECT)
1898 long which = event->xclient.data.l[1];
1900 if (p->mode == DONT_BLANK)
1902 clientmessage_response(si, window, True,
1903 "SELECT ClientMessage received in DONT_BLANK mode.",
1904 "screen blanking is currently disabled.");
1908 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1909 sprintf (buf2, "activating (%ld).", which);
1910 clientmessage_response (si, window, False, buf, buf2);
1912 if (which < 0) which = 0; /* 0 == "random" */
1913 si->selection_mode = which;
1914 si->demoing_p = False;
1916 if (si->throttled_p && p->verbose_p)
1917 fprintf (stderr, "%s: unthrottled.\n", blurb());
1918 si->throttled_p = False;
1923 XtRemoveTimeOut (si->cycle_id);
1925 cycle_timer ((XtPointer) si, 0);
1930 else if (type == XA_EXIT)
1932 /* Ignore EXIT message if the screen is locked. */
1933 if (until_idle_p || !si->locked_p)
1935 clientmessage_response (si, window, False,
1936 "EXIT ClientMessage received.",
1941 for (i = 0; i < si->nscreens; i++)
1942 kill_screenhack (&si->screens[i]);
1943 unblank_screen (si);
1944 XSync (si->dpy, False);
1946 saver_exit (si, 0, 0);
1949 clientmessage_response (si, window, True,
1950 "EXIT ClientMessage received while locked.",
1951 "screen is locked.");
1953 else if (type == XA_RESTART)
1955 /* The RESTART message works whether the screensaver is active or not,
1956 unless the screen is locked, in which case it doesn't work.
1958 if (until_idle_p || !si->locked_p)
1960 clientmessage_response (si, window, False,
1961 "RESTART 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);
1972 restart_process (si); /* does not return */
1976 clientmessage_response (si, window, True,
1977 "RESTART ClientMessage received while locked.",
1978 "screen is locked.");
1980 else if (type == XA_DEMO)
1982 long arg = event->xclient.data.l[1];
1983 Bool demo_one_hack_p = (arg == 5000);
1985 if (demo_one_hack_p)
1989 long which = event->xclient.data.l[2];
1992 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1993 sprintf (buf2, "demoing (%ld).", which);
1994 clientmessage_response (si, window, False, buf, buf2);
1996 if (which < 0) which = 0; /* 0 == "random" */
1997 si->selection_mode = which;
1998 si->demoing_p = True;
2000 if (si->throttled_p && p->verbose_p)
2001 fprintf (stderr, "%s: unthrottled.\n", blurb());
2002 si->throttled_p = False;
2007 clientmessage_response (si, window, True,
2008 "DEMO ClientMessage received while active.",
2013 clientmessage_response (si, window, True,
2014 "obsolete form of DEMO ClientMessage.",
2015 "obsolete form of DEMO ClientMessage.");
2018 else if (type == XA_PREFS)
2020 clientmessage_response (si, window, True,
2021 "the PREFS client-message is obsolete.",
2022 "the PREFS client-message is obsolete.");
2024 else if (type == XA_LOCK)
2027 clientmessage_response (si, window, True,
2028 "not compiled with support for locking.",
2029 "locking not enabled.");
2030 #else /* !NO_LOCKING */
2031 if (si->locking_disabled_p)
2032 clientmessage_response (si, window, True,
2033 "LOCK ClientMessage received, but locking is disabled.",
2034 "locking not enabled.");
2035 else if (si->locked_p)
2036 clientmessage_response (si, window, True,
2037 "LOCK ClientMessage received while already locked.",
2042 char *response = (until_idle_p
2043 ? "activating and locking."
2045 sprintf (buf, "LOCK ClientMessage received; %s", response);
2046 clientmessage_response (si, window, False, buf, response);
2047 set_locked_p (si, True);
2048 si->selection_mode = 0;
2049 si->demoing_p = False;
2051 if (si->lock_id) /* we're doing it now, so lose the timeout */
2053 XtRemoveTimeOut (si->lock_id);
2059 if (si->using_mit_saver_extension ||
2060 si->using_sgi_saver_extension)
2062 XForceScreenSaver (si->dpy, ScreenSaverActive);
2071 #endif /* !NO_LOCKING */
2073 else if (type == XA_THROTTLE)
2075 /* The THROTTLE command is deprecated -- it predates the XDPMS
2076 extension. Instead of using -throttle, users should instead
2077 just power off the monitor (e.g., "xset dpms force off".)
2078 In a few minutes, xscreensaver will notice that the monitor
2079 is off, and cease running hacks.
2081 if (si->throttled_p)
2082 clientmessage_response (si, window, True,
2083 "THROTTLE ClientMessage received, but "
2084 "already throttled.",
2085 "already throttled.");
2089 char *response = "throttled.";
2090 si->throttled_p = True;
2091 si->selection_mode = 0;
2092 si->demoing_p = False;
2093 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
2094 clientmessage_response (si, window, False, buf, response);
2099 XtRemoveTimeOut (si->cycle_id);
2101 cycle_timer ((XtPointer) si, 0);
2105 else if (type == XA_UNTHROTTLE)
2107 if (! si->throttled_p)
2108 clientmessage_response (si, window, True,
2109 "UNTHROTTLE ClientMessage received, but "
2115 char *response = "unthrottled.";
2116 si->throttled_p = False;
2117 si->selection_mode = 0;
2118 si->demoing_p = False;
2119 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
2120 clientmessage_response (si, window, False, buf, response);
2125 XtRemoveTimeOut (si->cycle_id);
2127 cycle_timer ((XtPointer) si, 0);
2135 str = XGetAtomName_safe (si->dpy, type);
2139 if (strlen (str) > 80)
2140 strcpy (str+70, "...");
2141 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
2148 "unrecognised screensaver ClientMessage 0x%x received.",
2149 (unsigned int) event->xclient.data.l[0]);
2152 clientmessage_response (si, window, True, buf, buf);
2158 /* Some random diagnostics printed in -verbose mode.
2162 analyze_display (saver_info *si)
2166 const char *name; const char *desc;
2168 Status (*version_fn) (Display *, int *majP, int *minP);
2171 { "SCREEN_SAVER", /* underscore */ "SGI Screen-Saver",
2172 # ifdef HAVE_SGI_SAVER_EXTENSION
2177 }, { "SCREEN-SAVER", /* dash */ "SGI Screen-Saver",
2178 # ifdef HAVE_SGI_SAVER_EXTENSION
2183 }, { "MIT-SCREEN-SAVER", "MIT Screen-Saver",
2184 # ifdef HAVE_MIT_SAVER_EXTENSION
2185 True, XScreenSaverQueryVersion
2189 }, { "XIDLE", "XIdle",
2190 # ifdef HAVE_XIDLE_EXTENSION
2195 }, { "SGI-VIDEO-CONTROL", "SGI Video-Control",
2196 # ifdef HAVE_SGI_VC_EXTENSION
2197 True, XSGIvcQueryVersion
2201 }, { "READDISPLAY", "SGI Read-Display",
2202 # ifdef HAVE_READ_DISPLAY_EXTENSION
2203 True, XReadDisplayQueryVersion
2207 }, { "MIT-SHM", "Shared Memory",
2208 # ifdef HAVE_XSHM_EXTENSION
2209 True, (Status (*) (Display*,int*,int*)) XShmQueryVersion /* 4 args */
2213 }, { "DOUBLE-BUFFER", "Double-Buffering",
2214 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
2215 True, XdbeQueryExtension
2219 }, { "DPMS", "Power Management",
2220 # ifdef HAVE_DPMS_EXTENSION
2221 True, DPMSGetVersion
2231 }, { "XFree86-VidModeExtension", "XF86 Video-Mode",
2232 # ifdef HAVE_XF86VMODE
2233 True, XF86VidModeQueryVersion
2237 }, { "XC-VidModeExtension", "XC Video-Mode",
2238 # ifdef HAVE_XF86VMODE
2239 True, XF86VidModeQueryVersion
2243 }, { "XFree86-MISC", "XF86 Misc",
2244 # ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2245 True, XF86MiscQueryVersion
2249 }, { "XC-MISC", "XC Misc",
2250 # ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2251 True, XF86MiscQueryVersion
2255 }, { "XINERAMA", "Xinerama",
2256 # ifdef HAVE_XINERAMA
2257 True, XineramaQueryVersion
2261 }, { "RANDR", "Resize-and-Rotate",
2263 True, XRRQueryVersion
2269 }, { "NV-CONTROL", "NVidia",
2271 }, { "NV-GLX", "NVidia GLX",
2273 }, { "Apple-DRI", "Apple-DRI (XDarwin)",
2275 }, { "XInputExtension", "XInput",
2280 fprintf (stderr, "%s: running on display \"%s\"\n", blurb(),
2281 DisplayString(si->dpy));
2282 fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
2283 ServerVendor(si->dpy), VendorRelease(si->dpy));
2285 fprintf (stderr, "%s: useful extensions:\n", blurb());
2286 for (i = 0; i < countof(exts); i++)
2288 int op = 0, event = 0, error = 0;
2290 int maj = 0, min = 0;
2291 int dummy1, dummy2, dummy3;
2294 /* Most of the extension version functions take 3 args,
2295 writing results into args 2 and 3, but some take more.
2296 We only ever care about the first two results, but we
2297 pass in three extra pointers just in case.
2299 Status (*version_fn_2) (Display*,int*,int*,int*,int*,int*) =
2300 (Status (*) (Display*,int*,int*,int*,int*,int*)) exts[i].version_fn;
2302 if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
2304 sprintf (buf, "%s: ", blurb());
2306 strcat (buf, exts[i].desc);
2310 else if (version_fn_2 (si->dpy, &maj, &min, &dummy1, &dummy2, &dummy3))
2311 sprintf (buf+strlen(buf), " (%d.%d)", maj, min);
2313 strcat (buf, " (unavailable)");
2315 if (!exts[i].useful_p)
2316 strcat (buf, " (disabled at compile time)");
2317 fprintf (stderr, "%s\n", buf);
2320 for (i = 0; i < si->nscreens; i++)
2322 saver_screen_info *ssi = &si->screens[i];
2323 unsigned long colormapped_depths = 0;
2324 unsigned long non_mapped_depths = 0;
2325 XVisualInfo vi_in, *vi_out;
2328 if (!ssi->real_screen_p) continue;
2330 vi_in.screen = ssi->real_screen_number;
2331 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
2332 if (!vi_out) continue;
2333 for (j = 0; j < out_count; j++) {
2334 if (vi_out[j].depth >= 32) continue;
2335 if (vi_out[j].class == PseudoColor)
2336 colormapped_depths |= (1 << vi_out[j].depth);
2338 non_mapped_depths |= (1 << vi_out[j].depth);
2340 XFree ((char *) vi_out);
2342 if (colormapped_depths)
2344 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(),
2345 ssi->real_screen_number);
2346 for (j = 0; j < 32; j++)
2347 if (colormapped_depths & (1 << j))
2348 fprintf (stderr, " %d", j);
2349 fprintf (stderr, ".\n");
2351 if (non_mapped_depths)
2353 fprintf (stderr, "%s: screen %d non-colormapped depths:",
2354 blurb(), ssi->real_screen_number);
2355 for (j = 0; j < 32; j++)
2356 if (non_mapped_depths & (1 << j))
2357 fprintf (stderr, " %d", j);
2358 fprintf (stderr, ".\n");
2362 describe_monitor_layout (si);
2367 display_is_on_console_p (saver_info *si)
2369 Bool not_on_console = True;
2370 char *dpystr = DisplayString (si->dpy);
2371 char *tail = (char *) strchr (dpystr, ':');
2372 if (! tail || strncmp (tail, ":0", 2))
2373 not_on_console = True;
2376 char dpyname[255], localname[255];
2377 strncpy (dpyname, dpystr, tail-dpystr);
2378 dpyname [tail-dpystr] = 0;
2380 !strcmp(dpyname, "unix") ||
2381 !strcmp(dpyname, "localhost"))
2382 not_on_console = False;
2383 else if (gethostname (localname, sizeof (localname)))
2384 not_on_console = True; /* can't find hostname? */
2385 else if (!strncmp (dpyname, "/tmp/launch-", 12)) /* MacOS X launchd */
2386 not_on_console = False;
2389 /* We have to call gethostbyname() on the result of gethostname()
2390 because the two aren't guarenteed to be the same name for the
2391 same host: on some losing systems, one is a FQDN and the other
2392 is not. Here in the wide wonderful world of Unix it's rocket
2393 science to obtain the local hostname in a portable fashion.
2395 And don't forget, gethostbyname() reuses the structure it
2396 returns, so we have to copy the fucker before calling it again.
2397 Thank you master, may I have another.
2399 struct hostent *h = gethostbyname (dpyname);
2401 not_on_console = True;
2406 strcpy (hn, h->h_name);
2407 l = gethostbyname (localname);
2408 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
2412 return !not_on_console;
2416 /* Do a little bit of heap introspection...
2419 check_for_leaks (const char *where)
2421 #if defined(HAVE_SBRK) && defined(LEAK_PARANOIA)
2422 static unsigned long last_brk = 0;
2423 int b = (unsigned long) sbrk(0);
2424 if (last_brk && last_brk < b)
2425 fprintf (stderr, "%s: %s: brk grew by %luK.\n",
2427 (((b - last_brk) + 1023) / 1024));
2429 #endif /* HAVE_SBRK */