1 /* xscreensaver, Copyright (c) 1991-2008 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>
147 #include <X11/Xlibint.h>
149 #include <X11/Xatom.h>
150 #include <X11/Intrinsic.h>
151 #include <X11/StringDefs.h>
152 #include <X11/Shell.h>
155 #include <sys/time.h>
156 #include <netdb.h> /* for gethostbyname() */
157 #include <sys/types.h>
161 # include <X11/Xmu/Error.h>
163 # include <Xmu/Error.h>
165 #else /* !HAVE_XMU */
167 #endif /* !HAVE_XMU */
169 #ifdef HAVE_MIT_SAVER_EXTENSION
170 #include <X11/extensions/scrnsaver.h>
171 #endif /* HAVE_MIT_SAVER_EXTENSION */
173 #ifdef HAVE_XIDLE_EXTENSION
174 # include <X11/extensions/xidle.h>
175 #endif /* HAVE_XIDLE_EXTENSION */
177 #ifdef HAVE_SGI_VC_EXTENSION
178 # include <X11/extensions/XSGIvc.h>
179 #endif /* HAVE_SGI_VC_EXTENSION */
181 #ifdef HAVE_READ_DISPLAY_EXTENSION
182 # include <X11/extensions/readdisplay.h>
183 #endif /* HAVE_READ_DISPLAY_EXTENSION */
185 #ifdef HAVE_XSHM_EXTENSION
186 # include <X11/extensions/XShm.h>
187 #endif /* HAVE_XSHM_EXTENSION */
189 #ifdef HAVE_DPMS_EXTENSION
190 # include <X11/extensions/dpms.h>
191 #endif /* HAVE_DPMS_EXTENSION */
194 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
195 # include <X11/extensions/Xdbe.h>
196 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
198 #ifdef HAVE_XF86VMODE
199 # include <X11/extensions/xf86vmode.h>
200 #endif /* HAVE_XF86VMODE */
202 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
203 # include <X11/extensions/xf86misc.h>
204 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
207 # include <X11/extensions/Xinerama.h>
208 #endif /* HAVE_XINERAMA */
211 # include <X11/extensions/Xrandr.h>
212 #endif /* HAVE_RANDR */
215 #include "xscreensaver.h"
217 #include "yarandom.h"
218 #include "resources.h"
223 saver_info *global_si_kludge = 0; /* I hate C so much... */
230 static Atom XA_SCREENSAVER_RESPONSE;
231 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
232 static Atom XA_RESTART, XA_SELECT;
233 static Atom XA_THROTTLE, XA_UNTHROTTLE;
234 Atom XA_DEMO, XA_PREFS, XA_EXIT, XA_LOCK, XA_BLANK;
237 static XrmOptionDescRec options [] = {
239 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
240 { "-silent", ".verbose", XrmoptionNoArg, "off" },
242 /* xscreensaver-demo uses this one */
243 { "-nosplash", ".splash", XrmoptionNoArg, "off" },
244 { "-no-splash", ".splash", XrmoptionNoArg, "off" },
246 /* useful for debugging */
247 { "-no-capture-stderr", ".captureStderr", XrmoptionNoArg, "off" },
248 { "-log", ".logFile", XrmoptionSepArg, 0 },
252 __extension__ /* shut up about "string length is greater than the length
253 ISO C89 compilers are required to support" when including
257 static char *defaults[] = {
258 #include "XScreenSaver_ad.h"
263 ERROR! You must not include vroot.h in this file.
267 do_help (saver_info *si)
272 xscreensaver %s, copyright (c) 1991-2008 by Jamie Zawinski <jwz@jwz.org>\n\
274 All xscreensaver configuration is via the `~/.xscreensaver' file.\n\
275 Rather than editing that file by hand, just run `xscreensaver-demo':\n\
276 that program lets you configure the screen saver graphically,\n\
277 including timeouts, locking, and display modes.\n\
281 Just getting started? Try this:\n\
286 For updates, online manual, and FAQ, please see the web page:\n\
288 http://www.jwz.org/xscreensaver/\n\
300 time_t now = time ((time_t *) 0);
301 char *str = (char *) ctime (&now);
302 char *nl = (char *) strchr (str, '\n');
303 if (nl) *nl = 0; /* take off that dang newline */
307 static Bool blurb_timestamp_p = True; /* kludge */
312 if (!blurb_timestamp_p)
316 static char buf[255];
317 char *ct = timestring();
318 int n = strlen(progname);
320 strncpy(buf, progname, n);
323 strncpy(buf+n, ct+11, 8);
324 strcpy(buf+n+9, ": ");
331 saver_ehandler (Display *dpy, XErrorEvent *error)
333 saver_info *si = global_si_kludge; /* I hate C so much... */
337 if (!real_stderr) real_stderr = stderr;
339 fprintf (real_stderr, "\n"
340 "#######################################"
341 "#######################################\n\n"
342 "%s: X Error! PLEASE REPORT THIS BUG.\n",
345 for (i = 0; i < si->nscreens; i++)
347 saver_screen_info *ssi = &si->screens[i];
348 fprintf (real_stderr, "%s: screen %d/%d: 0x%x, 0x%x, 0x%x\n",
349 blurb(), ssi->real_screen_number, ssi->number,
350 (unsigned int) RootWindowOfScreen (si->screens[i].screen),
351 (unsigned int) si->screens[i].real_vroot,
352 (unsigned int) si->screens[i].screensaver_window);
355 fprintf (real_stderr, "\n"
356 "#######################################"
357 "#######################################\n\n");
359 fatal_p = XmuPrintDefaultErrorMessage (dpy, error, real_stderr);
361 fatal_p = True; /* The only time I've ever seen a supposedly nonfatal error,
362 it has been BadImplementation / Xlib sequence lost, which
363 are in truth pretty damned fatal.
366 fprintf (real_stderr, "\n");
369 fprintf (real_stderr, "%s: nonfatal error.\n\n", blurb());
372 if (si->prefs.xsync_p)
374 saver_exit (si, -1, "because of synchronous X Error");
379 __extension__ /* don't warn about "string length is greater than the
380 length ISO C89 compilers are required to support". */
382 fprintf (real_stderr,
383 "#######################################################################\n"
385 " If at all possible, please re-run xscreensaver with the command\n"
386 " line arguments `-sync -verbose -log log.txt', and reproduce this\n"
387 " bug. That will cause xscreensaver to dump a `core' file to the\n"
388 " current directory. Please include the stack trace from that core\n"
389 " file in your bug report. *DO NOT* mail the core file itself! That\n"
390 " won't work. A \"log.txt\" file will also be written. Please *do*\n"
391 " include the complete \"log.txt\" file with your bug report.\n"
393 " http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
394 " the most useful bug reports, and how to examine core files.\n"
396 " The more information you can provide, the better. But please\n"
397 " report this bug, regardless!\n"
399 "#######################################################################\n"
403 saver_exit (si, -1, 0);
411 /* This error handler is used only while the X connection is being set up;
412 after we've got a connection, we don't use this handler again. The only
413 reason for having this is so that we can present a more idiot-proof error
414 message than "cannot open display."
417 startup_ehandler (String name, String type, String class,
418 String defalt, /* one can't even spel properly
419 in this joke of a language */
420 String *av, Cardinal *ac)
424 saver_info *si = global_si_kludge; /* I hate C so much... */
425 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
427 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
428 fmt, sizeof(fmt)-1, *db);
430 fprintf (stderr, "%s: ", blurb());
432 memset (p, 0, sizeof(p));
433 if (*ac > countof (p)) *ac = countof (p);
434 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
435 fprintf (stderr, fmt, /* Did I mention that I hate C? */
436 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
437 fprintf (stderr, "\n");
439 describe_uids (si, stderr);
441 if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
443 fprintf (stderr, "\n"
444 "%s: This is probably because you're logging in as root. You\n"
445 " shouldn't log in as root: you should log in as a normal user,\n"
446 " and then `su' as needed. If you insist on logging in as\n"
447 " root, you will have to turn off X's security features before\n"
448 " xscreensaver will work.\n"
450 " Please read the manual and FAQ for more information:\n",
455 fprintf (stderr, "\n"
456 "%s: Errors at startup are usually authorization problems.\n"
457 " But you're not logging in as root (good!) so something\n"
458 " else must be wrong. Did you read the manual and the FAQ?\n",
462 fprintf (stderr, "\n"
463 " http://www.jwz.org/xscreensaver/faq.html\n"
464 " http://www.jwz.org/xscreensaver/man.html\n"
473 /* The zillions of initializations.
476 /* Set progname, version, etc. This is done very early.
479 set_version_string (saver_info *si, int *argc, char **argv)
481 progclass = "XScreenSaver";
483 /* progname is reset later, after we connect to X. */
484 progname = strrchr(argv[0], '/');
485 if (progname) progname++;
486 else progname = argv[0];
488 if (strlen(progname) > 100) /* keep it short. */
491 /* The X resource database blows up if argv[0] has a "." in it. */
494 while ((s = strchr (s, '.')))
498 si->version = (char *) malloc (5);
499 memcpy (si->version, screensaver_id + 17, 4);
504 /* Initializations that potentially take place as a priveleged user:
505 If the xscreensaver executable is setuid root, then these initializations
506 are run as root, before discarding privileges.
509 privileged_initialization (saver_info *si, int *argc, char **argv)
512 /* before hack_uid() for proper permissions */
513 lock_priv_init (*argc, argv, si->prefs.verbose_p);
514 #endif /* NO_LOCKING */
520 /* Figure out what locking mechanisms are supported.
523 lock_initialization (saver_info *si, int *argc, char **argv)
526 si->locking_disabled_p = True;
527 si->nolock_reason = "not compiled with locking support";
528 #else /* !NO_LOCKING */
530 /* Finish initializing locking, now that we're out of privileged code. */
531 if (! lock_init (*argc, argv, si->prefs.verbose_p))
533 si->locking_disabled_p = True;
534 si->nolock_reason = "error getting password";
537 /* If locking is currently enabled, but the environment indicates that
538 we have been launched as GDM's "Background" program, then disable
539 locking just in case.
541 if (!si->locking_disabled_p && getenv ("RUNNING_UNDER_GDM"))
543 si->locking_disabled_p = True;
544 si->nolock_reason = "running under GDM";
547 /* If the server is XDarwin (MacOS X) then disable locking.
548 (X grabs only affect X programs, so you can use Command-Tab
549 to bring any other Mac program to the front, e.g., Terminal.)
551 if (!si->locking_disabled_p)
553 int op = 0, event = 0, error = 0;
554 Bool macos_p = False;
557 /* Disable locking if *running* on Apple hardware, since we have no
558 reliable way to determine whether the server is running on MacOS.
559 Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
560 but I'm not really sure about that.
566 /* This extension exists on the Apple X11 server, but not
567 on earlier versions of the XDarwin server. */
568 macos_p = XQueryExtension (si->dpy, "Apple-DRI", &op, &event, &error);
572 si->locking_disabled_p = True;
573 si->nolock_reason = "Cannot lock securely on MacOS X";
577 if (si->prefs.debug_p) /* But allow locking anyway in debug mode. */
578 si->locking_disabled_p = False;
580 #endif /* NO_LOCKING */
584 /* Open the connection to the X server, and intern our Atoms.
587 connect_to_server (saver_info *si, int *argc, char **argv)
589 Widget toplevel_shell;
592 char *d = getenv ("DISPLAY");
595 char *ndpy = strdup("DISPLAY=:0.0");
596 /* if (si->prefs.verbose_p) */ /* sigh, too early to test this... */
598 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
602 /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
603 glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
604 do not. So we must leak it (and/or the previous setting). Yay.
607 #endif /* HAVE_PUTENV */
609 XSetErrorHandler (saver_ehandler);
611 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
612 toplevel_shell = XtAppInitialize (&si->app, progclass,
613 options, XtNumber (options),
614 argc, argv, defaults, 0, 0);
615 XtAppSetErrorMsgHandler (si->app, 0);
617 si->dpy = XtDisplay (toplevel_shell);
618 si->prefs.db = XtDatabase (si->dpy);
619 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
621 if(strlen(progname) > 100) /* keep it short. */
624 db = si->prefs.db; /* resources.c needs this */
626 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
627 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
628 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
629 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
630 XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
631 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
633 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
634 XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False);
635 XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False);
636 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
637 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
638 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
639 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
640 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
641 XA_PREV = XInternAtom (si->dpy, "PREV", False);
642 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
643 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
644 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
645 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
646 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
647 XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
648 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
649 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
651 return toplevel_shell;
655 /* Handle the command-line arguments that were not handled for us by Xt.
656 Issue an error message and exit if there are unknown options.
659 process_command_line (saver_info *si, int *argc, char **argv)
662 for (i = 1; i < *argc; i++)
664 if (!strcmp (argv[i], "-debug"))
665 /* no resource for this one, out of paranoia. */
666 si->prefs.debug_p = True;
668 else if (!strcmp (argv[i], "-h") ||
669 !strcmp (argv[i], "-help") ||
670 !strcmp (argv[i], "--help"))
675 const char *s = argv[i];
676 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
679 if (s[0] == '-' && s[1] == '-') s++;
680 if (!strcmp (s, "-activate") ||
681 !strcmp (s, "-deactivate") ||
682 !strcmp (s, "-cycle") ||
683 !strcmp (s, "-next") ||
684 !strcmp (s, "-prev") ||
685 !strcmp (s, "-exit") ||
686 !strcmp (s, "-restart") ||
687 !strcmp (s, "-demo") ||
688 !strcmp (s, "-prefs") ||
689 !strcmp (s, "-preferences") ||
690 !strcmp (s, "-lock") ||
691 !strcmp (s, "-version") ||
692 !strcmp (s, "-time"))
695 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
696 fprintf (stderr, "\n\
697 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
699 fprintf (stderr, "\n\
700 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
703 The `xscreensaver' program is a daemon that runs in the background.\n\
704 You control a running xscreensaver process by sending it messages\n\
705 with `xscreensaver-demo' or `xscreensaver-command'.\n\
706 . See the man pages for details, or check the web page:\n\
707 http://www.jwz.org/xscreensaver/\n\n");
716 /* Print out the xscreensaver banner to the tty if applicable;
717 Issue any other warnings that are called for at this point.
720 print_banner (saver_info *si)
722 saver_preferences *p = &si->prefs;
724 /* This resource gets set some time before the others, so that we know
725 whether to print the banner (and so that the banner gets printed before
726 any resource-database-related error messages.)
728 p->verbose_p = (p->debug_p ||
729 get_boolean_resource (si->dpy, "verbose", "Boolean"));
731 /* Ditto, for the locking_disabled_p message. */
732 p->lock_p = get_boolean_resource (si->dpy, "lock", "Boolean");
736 "%s %s, copyright (c) 1991-2008 "
737 "by Jamie Zawinski <jwz@jwz.org>.\n",
738 progname, si->version);
741 fprintf (stderr, "\n"
742 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
744 "\tNote that in debug mode, the xscreensaver window will only\n"
745 "\tcover the left half of the screen. (The idea is that you\n"
746 "\tcan still see debugging output in a shell, if you position\n"
747 "\tit on the right side of the screen.)\n"
749 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
750 "\tuntrusted environments.\n"
756 if (!si->uid_message || !*si->uid_message)
757 describe_uids (si, stderr);
760 if (si->orig_uid && *si->orig_uid)
761 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
762 blurb(), si->orig_uid);
763 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
766 fprintf (stderr, "%s: in process %lu.\n", blurb(),
767 (unsigned long) getpid());
772 print_lock_failure_banner (saver_info *si)
774 saver_preferences *p = &si->prefs;
776 /* If locking was not able to be initalized for some reason, explain why.
777 (This has to be done after we've read the lock_p resource.)
779 if (si->locking_disabled_p)
782 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
784 if (strstr (si->nolock_reason, "passw"))
785 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
786 "consult the manual.\n", blurb());
787 else if (strstr (si->nolock_reason, "running as "))
789 "%s: locking only works when xscreensaver is launched\n"
790 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
791 "\t See the manual for details.\n",
798 /* called from screens.c so that all the Xt crud is here. */
800 initialize_screen_root_widget (saver_screen_info *ssi)
802 saver_info *si = ssi->global;
803 if (ssi->toplevel_shell)
804 XtDestroyWidget (ssi->toplevel_shell);
805 ssi->toplevel_shell =
806 XtVaAppCreateShell (progname, progclass,
807 applicationShellWidgetClass,
809 XtNscreen, ssi->screen,
810 XtNvisual, ssi->current_visual,
811 XtNdepth, visual_depth (ssi->screen,
812 ssi->current_visual),
817 /* Examine all of the display's screens, and populate the `saver_screen_info'
818 structures. Make sure this is called after hack_environment() sets $PATH.
821 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
825 update_screen_layout (si);
827 /* Check to see whether fading is ever possible -- if any of the
828 screens on the display has a PseudoColor visual, then fading can
829 work (on at least some screens.) If no screen has a PseudoColor
830 visual, then don't bother ever trying to fade, because it will
831 just cause a delay without causing any visible effect.
833 for (i = 0; i < si->nscreens; i++)
835 saver_screen_info *ssi = &si->screens[i];
836 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
837 get_visual (ssi->screen, "PseudoColor", True, False) ||
838 get_visual (ssi->screen, "GrayScale", True, False))
840 si->fading_possible_p = True;
845 #ifdef HAVE_XF86VMODE_GAMMA
846 si->fading_possible_p = True; /* if we can gamma fade, go for it */
851 /* If any server extensions have been requested, try and initialize them.
852 Issue warnings if requests can't be honored.
855 initialize_server_extensions (saver_info *si)
857 saver_preferences *p = &si->prefs;
859 Bool server_has_xidle_extension_p = False;
860 Bool server_has_sgi_saver_extension_p = False;
861 Bool server_has_mit_saver_extension_p = False;
862 Bool system_has_proc_interrupts_p = False;
863 const char *piwhy = 0;
865 si->using_xidle_extension = p->use_xidle_extension;
866 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
867 si->using_mit_saver_extension = p->use_mit_saver_extension;
868 si->using_proc_interrupts = p->use_proc_interrupts;
870 #ifdef HAVE_XIDLE_EXTENSION
873 server_has_xidle_extension_p = XidleQueryExtension (si->dpy, &ev, &er);
876 #ifdef HAVE_SGI_SAVER_EXTENSION
877 server_has_sgi_saver_extension_p =
878 XScreenSaverQueryExtension (si->dpy,
879 &si->sgi_saver_ext_event_number,
880 &si->sgi_saver_ext_error_number);
882 #ifdef HAVE_MIT_SAVER_EXTENSION
883 server_has_mit_saver_extension_p =
884 XScreenSaverQueryExtension (si->dpy,
885 &si->mit_saver_ext_event_number,
886 &si->mit_saver_ext_error_number);
888 #ifdef HAVE_PROC_INTERRUPTS
889 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
892 if (!server_has_xidle_extension_p)
893 si->using_xidle_extension = False;
894 else if (p->verbose_p)
896 if (si->using_xidle_extension)
897 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
899 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
902 if (!server_has_sgi_saver_extension_p)
903 si->using_sgi_saver_extension = False;
904 else if (p->verbose_p)
906 if (si->using_sgi_saver_extension)
907 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
910 "%s: not using server's SGI SCREEN_SAVER extension.\n",
914 if (!server_has_mit_saver_extension_p)
915 si->using_mit_saver_extension = False;
916 else if (p->verbose_p)
918 if (si->using_mit_saver_extension)
919 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
923 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
928 if (XRRQueryExtension (si->dpy,
929 &si->randr_event_number, &si->randr_error_number))
931 int nscreens = ScreenCount (si->dpy); /* number of *real* screens */
935 fprintf (stderr, "%s: selecting RANDR events\n", blurb());
936 for (i = 0; i < nscreens; i++)
937 # ifdef RRScreenChangeNotifyMask /* randr.h 1.5, 2002/09/29 */
938 XRRSelectInput (si->dpy, RootWindow (si->dpy, i),
939 RRScreenChangeNotifyMask);
940 # else /* !RRScreenChangeNotifyMask */ /* Xrandr.h 1.4, 2001/06/07 */
941 XRRScreenChangeSelectInput (si->dpy, RootWindow (si->dpy, i), True);
942 # endif /* !RRScreenChangeNotifyMask */
944 # endif /* HAVE_RANDR */
946 if (!system_has_proc_interrupts_p)
948 si->using_proc_interrupts = False;
949 if (p->verbose_p && piwhy)
950 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
953 else if (p->verbose_p)
955 if (si->using_proc_interrupts)
957 "%s: consulting /proc/interrupts for keyboard activity.\n",
961 "%s: not consulting /proc/interrupts for keyboard activity.\n",
967 #ifdef DEBUG_MULTISCREEN
969 debug_multiscreen_timer (XtPointer closure, XtIntervalId *id)
971 saver_info *si = (saver_info *) closure;
972 saver_preferences *p = &si->prefs;
973 if (update_screen_layout (si))
977 fprintf (stderr, "%s: new layout:\n", blurb());
978 describe_monitor_layout (si);
980 resize_screensaver_window (si);
982 XtAppAddTimeOut (si->app, 1000*4, debug_multiscreen_timer, (XtPointer) si);
984 #endif /* DEBUG_MULTISCREEN */
987 /* For the case where we aren't using an server extensions, select user events
988 on all the existing windows, and launch timers to select events on
989 newly-created windows as well.
991 If a server extension is being used, this does nothing.
994 select_events (saver_info *si)
996 saver_preferences *p = &si->prefs;
999 if (si->using_xidle_extension ||
1000 si->using_mit_saver_extension ||
1001 si->using_sgi_saver_extension)
1004 if (p->initial_delay)
1008 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
1009 (int) p->initial_delay/1000,
1010 (p->initial_delay == 1000 ? "" : "s"));
1014 usleep (p->initial_delay);
1016 fprintf (stderr, " done.\n");
1021 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
1026 /* Select events on the root windows of every screen. This also selects
1027 for window creation events, so that new subwindows will be noticed.
1029 for (i = 0; i < si->nscreens; i++)
1031 saver_screen_info *ssi = &si->screens[i];
1032 if (ssi->real_screen_p)
1033 start_notice_events_timer (si,
1034 RootWindowOfScreen (si->screens[i].screen), False);
1038 fprintf (stderr, " done.\n");
1040 # ifdef DEBUG_MULTISCREEN
1041 if (p->debug_p) debug_multiscreen_timer ((XtPointer) si, 0);
1047 maybe_reload_init_file (saver_info *si)
1049 saver_preferences *p = &si->prefs;
1050 if (init_file_changed_p (p))
1053 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
1054 blurb(), init_file_name());
1056 load_init_file (si->dpy, p);
1058 /* If a server extension is in use, and p->timeout has changed,
1059 we need to inform the server of the new timeout. */
1060 disable_builtin_screensaver (si, False);
1062 /* If the DPMS settings in the init file have changed,
1063 change the settings on the server to match. */
1064 sync_server_dpms_settings (si->dpy,
1065 (p->dpms_enabled_p &&
1066 p->mode != DONT_BLANK),
1067 p->dpms_standby / 1000,
1068 p->dpms_suspend / 1000,
1077 - wait until the user is idle;
1079 - wait until the user is active;
1080 - unblank the screen;
1085 main_loop (saver_info *si)
1087 saver_preferences *p = &si->prefs;
1093 Bool was_locked = False;
1096 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1098 check_for_leaks ("unblanked A");
1099 sleep_until_idle (si, True);
1100 check_for_leaks ("unblanked B");
1105 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
1106 si->selection_mode, timestring());
1108 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
1112 maybe_reload_init_file (si);
1114 if (p->mode == DONT_BLANK)
1117 fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
1118 blurb(), timestring());
1120 /* Go around the loop and wait for the next bout of idleness,
1121 or for the init file to change, or for a remote command to
1122 come in, or something.
1124 But, if locked_p is true, go ahead. This can only happen
1125 if we're in "disabled" mode but a "lock" clientmessage came
1126 in: in that case, we should go ahead and blank/lock the screen.
1132 /* Since we're about to blank the screen, kill the de-race timer,
1133 if any. It might still be running if we have unblanked and then
1134 re-blanked in a short period (e.g., when using the "next" button
1135 in xscreensaver-demo.)
1140 fprintf (stderr, "%s: stopping de-race timer (%d remaining.)\n",
1141 blurb(), si->de_race_ticks);
1142 XtRemoveTimeOut (si->de_race_id);
1147 /* Now, try to blank.
1150 if (! blank_screen (si))
1152 /* We were unable to grab either the keyboard or mouse.
1153 This means we did not (and must not) blank the screen.
1154 If we were to blank the screen while some other program
1155 is holding both the mouse and keyboard grabbed, then
1156 we would never be able to un-blank it! We would never
1157 see any events, and the display would be wedged.
1159 So, just go around the loop again and wait for the
1160 next bout of idleness. (If the user remains idle, we
1161 will next try to blank the screen again in no more than
1164 Time retry = 60 * 1000;
1165 if (p->timeout < retry)
1171 "%s: DEBUG MODE: unable to grab -- BLANKING ANYWAY.\n",
1177 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
1180 schedule_wakeup_event (si, retry, p->debug_p);
1185 for (i = 0; i < si->nscreens; i++)
1186 kill_screenhack (&si->screens[i]);
1188 raise_window (si, True, True, False);
1189 if (si->throttled_p)
1190 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
1192 for (i = 0; i < si->nscreens; i++)
1193 spawn_screenhack (&si->screens[i]);
1195 /* Don't start the cycle timer in demo mode. */
1196 if (!si->demoing_p && p->cycle)
1197 si->cycle_id = XtAppAddTimeOut (si->app,
1199 /* see comment in cycle_timer() */
1207 /* Maybe start locking the screen.
1210 Time lock_timeout = p->lock_timeout;
1212 if (si->emergency_lock_p && p->lock_p && lock_timeout)
1214 int secs = p->lock_timeout / 1000;
1217 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1219 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1223 si->emergency_lock_p = False;
1225 if (!si->demoing_p && /* if not going into demo mode */
1226 p->lock_p && /* and locking is enabled */
1227 !si->locking_disabled_p && /* and locking is possible */
1228 lock_timeout == 0) /* and locking is not timer-deferred */
1229 set_locked_p (si, True); /* then lock right now. */
1231 /* locked_p might be true already because of the above, or because of
1232 the LOCK ClientMessage. But if not, and if we're supposed to lock
1233 after some time, set up a timer to do so.
1238 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1239 activate_lock_timer,
1242 #endif /* !NO_LOCKING */
1245 ok_to_unblank = True;
1248 check_for_leaks ("blanked A");
1249 sleep_until_idle (si, False); /* until not idle */
1250 check_for_leaks ("blanked B");
1252 maybe_reload_init_file (si);
1255 /* Maybe unlock the screen.
1259 saver_screen_info *ssi = si->default_screen;
1260 if (si->locking_disabled_p) abort ();
1263 si->dbox_up_p = True;
1264 for (i = 0; i < si->nscreens; i++)
1265 suspend_screenhack (&si->screens[i], True); /* suspend */
1266 XUndefineCursor (si->dpy, ssi->screensaver_window);
1268 ok_to_unblank = unlock_p (si);
1270 si->dbox_up_p = False;
1271 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1272 for (i = 0; i < si->nscreens; i++)
1273 suspend_screenhack (&si->screens[i], False); /* resume */
1275 if (!ok_to_unblank &&
1276 !screenhack_running_p (si))
1278 /* If the lock dialog has been dismissed and we're not about to
1279 unlock the screen, and there is currently no hack running,
1280 then launch one. (There might be no hack running if DPMS
1281 had kicked in. But DPMS is off now, so bring back the hack)
1284 XtRemoveTimeOut (si->cycle_id);
1286 cycle_timer ((XtPointer) si, 0);
1289 #endif /* !NO_LOCKING */
1291 } while (!ok_to_unblank);
1295 fprintf (stderr, "%s: unblanking screen at %s.\n",
1296 blurb(), timestring ());
1298 /* Kill before unblanking, to stop drawing as soon as possible. */
1299 for (i = 0; i < si->nscreens; i++)
1300 kill_screenhack (&si->screens[i]);
1301 unblank_screen (si);
1303 set_locked_p (si, False);
1304 si->emergency_lock_p = False;
1306 si->selection_mode = 0;
1308 /* If we're throttled, and the user has explicitly unlocked the screen,
1309 then unthrottle. If we weren't locked, then don't unthrottle
1310 automatically, because someone might have just bumped the desk... */
1313 if (si->throttled_p && p->verbose_p)
1314 fprintf (stderr, "%s: unthrottled.\n", blurb());
1315 si->throttled_p = False;
1320 XtRemoveTimeOut (si->cycle_id);
1326 XtRemoveTimeOut (si->lock_id);
1330 /* Since we're unblanked now, break race conditions and make
1331 sure we stay that way (see comment in timers.c.) */
1332 if (! si->de_race_id)
1333 de_race_timer ((XtPointer) si, 0);
1337 static void analyze_display (saver_info *si);
1338 static void fix_fds (void);
1341 main (int argc, char **argv)
1345 saver_info *si = &the_si;
1346 saver_preferences *p = &si->prefs;
1347 struct passwd *spasswd;
1350 memset(si, 0, sizeof(*si));
1351 global_si_kludge = si; /* I hate C so much... */
1355 # undef ya_rand_init
1358 save_argv (argc, argv);
1359 set_version_string (si, &argc, argv);
1360 privileged_initialization (si, &argc, argv);
1361 hack_environment (si);
1363 spasswd = getpwuid(getuid());
1366 fprintf(stderr, "Could not figure out who the current user is!\n");
1370 si->user = strdup(spasswd->pw_name ? spasswd->pw_name : "(unknown)");
1373 si->unlock_cb = gui_auth_conv;
1374 si->auth_finished_cb = auth_finished_cb;
1375 # endif /* !NO_LOCKING */
1377 shell = connect_to_server (si, &argc, argv);
1378 process_command_line (si, &argc, argv);
1379 stderr_log_file (si);
1382 load_init_file(si->dpy, p); /* must be before initialize_per_screen_info() */
1383 blurb_timestamp_p = p->timestamp_p; /* kludge */
1384 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1386 /* We can only issue this warning now. */
1387 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1389 "%s: there are no PseudoColor or GrayScale visuals.\n"
1390 "%s: ignoring the request for fading/unfading.\n",
1393 for (i = 0; i < si->nscreens; i++)
1395 saver_screen_info *ssi = &si->screens[i];
1396 if (ssi->real_screen_p)
1397 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1401 lock_initialization (si, &argc, argv);
1402 print_lock_failure_banner (si);
1404 if (p->xsync_p) XSynchronize (si->dpy, True);
1406 if (p->verbose_p) analyze_display (si);
1407 initialize_server_extensions (si);
1409 si->blank_time = time ((time_t) 0); /* must be before ..._window */
1410 initialize_screensaver_window (si);
1415 disable_builtin_screensaver (si, True);
1416 sync_server_dpms_settings (si->dpy,
1417 (p->dpms_enabled_p &&
1418 p->mode != DONT_BLANK),
1419 p->dpms_standby / 1000,
1420 p->dpms_suspend / 1000,
1424 initialize_stderr (si);
1425 handle_signals (si);
1427 make_splash_dialog (si);
1429 main_loop (si); /* doesn't return */
1436 /* Bad Things Happen if stdin, stdout, and stderr have been closed
1437 (as by the `sh incantation "xscreensaver >&- 2>&-"). When you do
1438 that, the X connection gets allocated to one of these fds, and
1439 then some random library writes to stderr, and random bits get
1440 stuffed down the X pipe, causing "Xlib: sequence lost" errors.
1441 So, we cause the first three file descriptors to be open to
1442 /dev/null if they aren't open to something else already. This
1443 must be done before any other files are opened (or the closing
1444 of that other file will again free up one of the "magic" first
1447 We do this by opening /dev/null three times, and then closing
1448 those fds, *unless* any of them got allocated as #0, #1, or #2,
1449 in which case we leave them open. Gag.
1451 Really, this crap is technically required of *every* X program,
1452 if you want it to be robust in the face of "2>&-".
1454 int fd0 = open ("/dev/null", O_RDWR);
1455 int fd1 = open ("/dev/null", O_RDWR);
1456 int fd2 = open ("/dev/null", O_RDWR);
1457 if (fd0 > 2) close (fd0);
1458 if (fd1 > 2) close (fd1);
1459 if (fd2 > 2) close (fd2);
1464 /* Processing ClientMessage events.
1468 static Bool error_handler_hit_p = False;
1471 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1473 error_handler_hit_p = True;
1477 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1478 them. We only look up the atom names for printing warning messages,
1479 so don't bomb out when it happens...
1482 XGetAtomName_safe (Display *dpy, Atom atom)
1485 XErrorHandler old_handler;
1486 if (!atom) return 0;
1489 error_handler_hit_p = False;
1490 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1491 result = XGetAtomName (dpy, atom);
1493 XSetErrorHandler (old_handler);
1495 if (error_handler_hit_p) result = 0;
1502 sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
1503 return strdup (buf);
1509 clientmessage_response (saver_info *si, Window w, Bool error,
1510 const char *stderr_msg,
1511 const char *protocol_msg)
1515 saver_preferences *p = &si->prefs;
1516 XErrorHandler old_handler;
1518 if (error || p->verbose_p)
1519 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1521 L = strlen(protocol_msg);
1522 proto = (char *) malloc (L + 2);
1523 proto[0] = (error ? '-' : '+');
1524 strcpy (proto+1, protocol_msg);
1527 /* Ignore all X errors while sending a response to a ClientMessage.
1528 Pretty much the only way we could get an error here is if the
1529 window we're trying to send the reply on has been deleted, in
1530 which case, the sender of the ClientMessage won't see our response
1533 XSync (si->dpy, False);
1534 error_handler_hit_p = False;
1535 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1537 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1538 PropModeReplace, (unsigned char *) proto, L);
1540 XSync (si->dpy, False);
1541 XSetErrorHandler (old_handler);
1542 XSync (si->dpy, False);
1549 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1551 #if 0 /* Oh, fuck it. GNOME likes to spew random ClientMessages at us
1552 all the time. This is presumably indicative of an error in
1553 the sender of that ClientMessage: if we're getting it and
1554 ignoring it, then it's not reaching the intended recipient.
1555 But people complain to me about this all the time ("waaah!
1556 xscreensaver is printing to it's stderr and that gets my
1557 panties all in a bunch!") And I'm sick of hearing about it.
1558 So we'll just ignore these messages and let GNOME go right
1559 ahead and continue to stumble along in its malfunction.
1562 saver_preferences *p = &si->prefs;
1563 char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1564 Window w = event->xclient.window;
1567 Bool root_p = False;
1570 for (screen = 0; screen < si->nscreens; screen++)
1571 if (w == si->screens[screen].screensaver_window)
1573 strcpy (wdesc, "xscreensaver");
1576 else if (w == RootWindow (si->dpy, screen))
1578 strcpy (wdesc, "root");
1583 /* If this ClientMessage was sent to the real root window instead of to the
1584 xscreensaver window, then it might be intended for someone else who is
1585 listening on the root window (e.g., the window manager). So only print
1586 the warning if: we are in debug mode; or if the bogus message was
1587 actually sent to one of the xscreensaver-created windows.
1589 if (root_p && !p->debug_p)
1594 XErrorHandler old_handler;
1596 XWindowAttributes xgwa;
1597 memset (&hint, 0, sizeof(hint));
1598 memset (&xgwa, 0, sizeof(xgwa));
1600 XSync (si->dpy, False);
1601 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1602 XGetClassHint (si->dpy, w, &hint);
1603 XGetWindowAttributes (si->dpy, w, &xgwa);
1604 XSync (si->dpy, False);
1605 XSetErrorHandler (old_handler);
1606 XSync (si->dpy, False);
1608 screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1610 sprintf (wdesc, "%.20s / %.20s",
1611 (hint.res_name ? hint.res_name : "(null)"),
1612 (hint.res_class ? hint.res_class : "(null)"));
1613 if (hint.res_name) XFree (hint.res_name);
1614 if (hint.res_class) XFree (hint.res_class);
1617 fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1618 blurb(), screen, (str ? str : "(null)"));
1619 fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1620 blurb(), screen, (unsigned long) w, wdesc);
1621 if (str) XFree (str);
1628 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1630 saver_preferences *p = &si->prefs;
1632 Window window = event->xclient.window;
1634 /* Preferences might affect our handling of client messages. */
1635 maybe_reload_init_file (si);
1637 if (event->xclient.message_type != XA_SCREENSAVER ||
1638 event->xclient.format != 32)
1640 bogus_clientmessage_warning (si, event);
1644 type = event->xclient.data.l[0];
1645 if (type == XA_ACTIVATE)
1649 if (p->mode == DONT_BLANK)
1651 clientmessage_response(si, window, True,
1652 "ACTIVATE ClientMessage received in DONT_BLANK mode.",
1653 "screen blanking is currently disabled.");
1657 clientmessage_response(si, window, False,
1658 "ACTIVATE ClientMessage received.",
1660 si->selection_mode = 0;
1661 si->demoing_p = False;
1663 if (si->throttled_p && p->verbose_p)
1664 fprintf (stderr, "%s: unthrottled.\n", blurb());
1665 si->throttled_p = False;
1667 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1669 XForceScreenSaver (si->dpy, ScreenSaverActive);
1677 clientmessage_response(si, window, True,
1678 "ClientMessage ACTIVATE received while already active.",
1681 else if (type == XA_DEACTIVATE)
1685 if (si->throttled_p && p->verbose_p)
1686 fprintf (stderr, "%s: unthrottled.\n", blurb());
1687 si->throttled_p = False;
1689 clientmessage_response(si, window, False,
1690 "DEACTIVATE ClientMessage received.",
1692 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1694 XForceScreenSaver (si->dpy, ScreenSaverReset);
1702 clientmessage_response(si, window, False,
1703 "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
1704 "not active: idle timer reset.");
1707 else if (type == XA_CYCLE)
1711 clientmessage_response(si, window, False,
1712 "CYCLE ClientMessage received.",
1714 si->selection_mode = 0; /* 0 means randomize when its time. */
1715 si->demoing_p = False;
1717 if (si->throttled_p && p->verbose_p)
1718 fprintf (stderr, "%s: unthrottled.\n", blurb());
1719 si->throttled_p = False;
1722 XtRemoveTimeOut (si->cycle_id);
1724 cycle_timer ((XtPointer) si, 0);
1727 clientmessage_response(si, window, True,
1728 "ClientMessage CYCLE received while inactive.",
1731 else if (type == XA_NEXT || type == XA_PREV)
1733 clientmessage_response(si, window, False,
1735 ? "NEXT ClientMessage received."
1736 : "PREV ClientMessage received."),
1738 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1739 si->demoing_p = False;
1741 if (si->throttled_p && p->verbose_p)
1742 fprintf (stderr, "%s: unthrottled.\n", blurb());
1743 si->throttled_p = False;
1748 XtRemoveTimeOut (si->cycle_id);
1750 cycle_timer ((XtPointer) si, 0);
1755 else if (type == XA_SELECT)
1759 long which = event->xclient.data.l[1];
1761 if (p->mode == DONT_BLANK)
1763 clientmessage_response(si, window, True,
1764 "SELECT ClientMessage received in DONT_BLANK mode.",
1765 "screen blanking is currently disabled.");
1769 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1770 sprintf (buf2, "activating (%ld).", which);
1771 clientmessage_response (si, window, False, buf, buf2);
1773 if (which < 0) which = 0; /* 0 == "random" */
1774 si->selection_mode = which;
1775 si->demoing_p = False;
1777 if (si->throttled_p && p->verbose_p)
1778 fprintf (stderr, "%s: unthrottled.\n", blurb());
1779 si->throttled_p = False;
1784 XtRemoveTimeOut (si->cycle_id);
1786 cycle_timer ((XtPointer) si, 0);
1791 else if (type == XA_EXIT)
1793 /* Ignore EXIT message if the screen is locked. */
1794 if (until_idle_p || !si->locked_p)
1796 clientmessage_response (si, window, False,
1797 "EXIT ClientMessage received.",
1802 for (i = 0; i < si->nscreens; i++)
1803 kill_screenhack (&si->screens[i]);
1804 unblank_screen (si);
1805 XSync (si->dpy, False);
1807 saver_exit (si, 0, 0);
1810 clientmessage_response (si, window, True,
1811 "EXIT ClientMessage received while locked.",
1812 "screen is locked.");
1814 else if (type == XA_RESTART)
1816 /* The RESTART message works whether the screensaver is active or not,
1817 unless the screen is locked, in which case it doesn't work.
1819 if (until_idle_p || !si->locked_p)
1821 clientmessage_response (si, window, False,
1822 "RESTART ClientMessage received.",
1827 for (i = 0; i < si->nscreens; i++)
1828 kill_screenhack (&si->screens[i]);
1829 unblank_screen (si);
1830 XSync (si->dpy, False);
1833 restart_process (si); /* does not return */
1837 clientmessage_response (si, window, True,
1838 "RESTART ClientMessage received while locked.",
1839 "screen is locked.");
1841 else if (type == XA_DEMO)
1843 long arg = event->xclient.data.l[1];
1844 Bool demo_one_hack_p = (arg == 5000);
1846 if (demo_one_hack_p)
1850 long which = event->xclient.data.l[2];
1853 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1854 sprintf (buf2, "demoing (%ld).", which);
1855 clientmessage_response (si, window, False, buf, buf2);
1857 if (which < 0) which = 0; /* 0 == "random" */
1858 si->selection_mode = which;
1859 si->demoing_p = True;
1861 if (si->throttled_p && p->verbose_p)
1862 fprintf (stderr, "%s: unthrottled.\n", blurb());
1863 si->throttled_p = False;
1868 clientmessage_response (si, window, True,
1869 "DEMO ClientMessage received while active.",
1874 clientmessage_response (si, window, True,
1875 "obsolete form of DEMO ClientMessage.",
1876 "obsolete form of DEMO ClientMessage.");
1879 else if (type == XA_PREFS)
1881 clientmessage_response (si, window, True,
1882 "the PREFS client-message is obsolete.",
1883 "the PREFS client-message is obsolete.");
1885 else if (type == XA_LOCK)
1888 clientmessage_response (si, window, True,
1889 "not compiled with support for locking.",
1890 "locking not enabled.");
1891 #else /* !NO_LOCKING */
1892 if (si->locking_disabled_p)
1893 clientmessage_response (si, window, True,
1894 "LOCK ClientMessage received, but locking is disabled.",
1895 "locking not enabled.");
1896 else if (si->locked_p)
1897 clientmessage_response (si, window, True,
1898 "LOCK ClientMessage received while already locked.",
1903 char *response = (until_idle_p
1904 ? "activating and locking."
1906 sprintf (buf, "LOCK ClientMessage received; %s", response);
1907 clientmessage_response (si, window, False, buf, response);
1908 set_locked_p (si, True);
1909 si->selection_mode = 0;
1910 si->demoing_p = False;
1912 if (si->lock_id) /* we're doing it now, so lose the timeout */
1914 XtRemoveTimeOut (si->lock_id);
1920 if (si->using_mit_saver_extension ||
1921 si->using_sgi_saver_extension)
1923 XForceScreenSaver (si->dpy, ScreenSaverActive);
1932 #endif /* !NO_LOCKING */
1934 else if (type == XA_THROTTLE)
1936 /* The THROTTLE command is deprecated -- it predates the XDPMS
1937 extension. Instead of using -throttle, users should instead
1938 just power off the monitor (e.g., "xset dpms force off".)
1939 In a few minutes, xscreensaver will notice that the monitor
1940 is off, and cease running hacks.
1942 if (si->throttled_p)
1943 clientmessage_response (si, window, True,
1944 "THROTTLE ClientMessage received, but "
1945 "already throttled.",
1946 "already throttled.");
1950 char *response = "throttled.";
1951 si->throttled_p = True;
1952 si->selection_mode = 0;
1953 si->demoing_p = False;
1954 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
1955 clientmessage_response (si, window, False, buf, response);
1960 XtRemoveTimeOut (si->cycle_id);
1962 cycle_timer ((XtPointer) si, 0);
1966 else if (type == XA_UNTHROTTLE)
1968 if (! si->throttled_p)
1969 clientmessage_response (si, window, True,
1970 "UNTHROTTLE ClientMessage received, but "
1976 char *response = "unthrottled.";
1977 si->throttled_p = False;
1978 si->selection_mode = 0;
1979 si->demoing_p = False;
1980 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
1981 clientmessage_response (si, window, False, buf, response);
1986 XtRemoveTimeOut (si->cycle_id);
1988 cycle_timer ((XtPointer) si, 0);
1996 str = XGetAtomName_safe (si->dpy, type);
2000 if (strlen (str) > 80)
2001 strcpy (str+70, "...");
2002 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
2009 "unrecognised screensaver ClientMessage 0x%x received.",
2010 (unsigned int) event->xclient.data.l[0]);
2013 clientmessage_response (si, window, True, buf, buf);
2019 /* Some random diagnostics printed in -verbose mode.
2023 analyze_display (saver_info *si)
2027 const char *name; const char *desc;
2029 Status (*version_fn) (Display *, int *majP, int *minP);
2032 { "SCREEN_SAVER", /* underscore */ "SGI Screen-Saver",
2033 # ifdef HAVE_SGI_SAVER_EXTENSION
2038 }, { "SCREEN-SAVER", /* dash */ "SGI Screen-Saver",
2039 # ifdef HAVE_SGI_SAVER_EXTENSION
2044 }, { "MIT-SCREEN-SAVER", "MIT Screen-Saver",
2045 # ifdef HAVE_MIT_SAVER_EXTENSION
2046 True, XScreenSaverQueryVersion
2050 }, { "XIDLE", "XIdle",
2051 # ifdef HAVE_XIDLE_EXTENSION
2056 }, { "SGI-VIDEO-CONTROL", "SGI Video-Control",
2057 # ifdef HAVE_SGI_VC_EXTENSION
2058 True, XSGIvcQueryVersion
2062 }, { "READDISPLAY", "SGI Read-Display",
2063 # ifdef HAVE_READ_DISPLAY_EXTENSION
2064 True, XReadDisplayQueryVersion
2068 }, { "MIT-SHM", "Shared Memory",
2069 # ifdef HAVE_XSHM_EXTENSION
2070 True, (Status (*) (Display*,int*,int*)) XShmQueryVersion /* 4 args */
2074 }, { "DOUBLE-BUFFER", "Double-Buffering",
2075 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
2076 True, XdbeQueryExtension
2080 }, { "DPMS", "Power Management",
2081 # ifdef HAVE_DPMS_EXTENSION
2082 True, DPMSGetVersion
2092 }, { "XFree86-VidModeExtension", "XF86 Video-Mode",
2093 # ifdef HAVE_XF86VMODE
2094 True, XF86VidModeQueryVersion
2098 }, { "XC-VidModeExtension", "XC Video-Mode",
2099 # ifdef HAVE_XF86VMODE
2100 True, XF86VidModeQueryVersion
2104 }, { "XFree86-MISC", "XF86 Misc",
2105 # ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2106 True, XF86MiscQueryVersion
2110 }, { "XC-MISC", "XC Misc",
2111 # ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2112 True, XF86MiscQueryVersion
2116 }, { "XINERAMA", "Xinerama",
2117 # ifdef HAVE_XINERAMA
2118 True, XineramaQueryVersion
2122 }, { "RANDR", "Resize-and-Rotate",
2124 True, XRRQueryVersion
2130 }, { "NV-CONTROL", "NVidia",
2132 }, { "NV-GLX", "NVidia GLX",
2134 }, { "Apple-DRI", "Apple-DRI (XDarwin)",
2139 fprintf (stderr, "%s: running on display \"%s\"\n", blurb(),
2140 DisplayString(si->dpy));
2141 fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
2142 ServerVendor(si->dpy), VendorRelease(si->dpy));
2144 fprintf (stderr, "%s: useful extensions:\n", blurb());
2145 for (i = 0; i < countof(exts); i++)
2147 int op = 0, event = 0, error = 0;
2149 int maj = 0, min = 0;
2150 int dummy1, dummy2, dummy3;
2153 /* Most of the extension version functions take 3 args,
2154 writing results into args 2 and 3, but some take more.
2155 We only ever care about the first two results, but we
2156 pass in three extra pointers just in case.
2158 Status (*version_fn_2) (Display*,int*,int*,int*,int*,int*) =
2159 (Status (*) (Display*,int*,int*,int*,int*,int*)) exts[i].version_fn;
2161 if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
2163 sprintf (buf, "%s: ", blurb());
2165 strcat (buf, exts[i].desc);
2169 else if (version_fn_2 (si->dpy, &maj, &min, &dummy1, &dummy2, &dummy3))
2170 sprintf (buf+strlen(buf), " (%d.%d)", maj, min);
2172 strcat (buf, " (unavailable)");
2174 if (!exts[i].useful_p)
2175 strcat (buf, " (disabled at compile time)");
2176 fprintf (stderr, "%s\n", buf);
2179 for (i = 0; i < si->nscreens; i++)
2181 saver_screen_info *ssi = &si->screens[i];
2182 unsigned long colormapped_depths = 0;
2183 unsigned long non_mapped_depths = 0;
2184 XVisualInfo vi_in, *vi_out;
2187 if (!ssi->real_screen_p) continue;
2189 vi_in.screen = ssi->real_screen_number;
2190 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
2191 if (!vi_out) continue;
2192 for (j = 0; j < out_count; j++)
2193 if (vi_out[j].class == PseudoColor)
2194 colormapped_depths |= (1 << vi_out[j].depth);
2196 non_mapped_depths |= (1 << vi_out[j].depth);
2197 XFree ((char *) vi_out);
2199 if (colormapped_depths)
2201 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(),
2202 ssi->real_screen_number);
2203 for (j = 0; j < 32; j++)
2204 if (colormapped_depths & (1 << j))
2205 fprintf (stderr, " %d", j);
2206 fprintf (stderr, ".\n");
2208 if (non_mapped_depths)
2210 fprintf (stderr, "%s: screen %d non-colormapped depths:",
2211 blurb(), ssi->real_screen_number);
2212 for (j = 0; j < 32; j++)
2213 if (non_mapped_depths & (1 << j))
2214 fprintf (stderr, " %d", j);
2215 fprintf (stderr, ".\n");
2219 describe_monitor_layout (si);
2224 display_is_on_console_p (saver_info *si)
2226 Bool not_on_console = True;
2227 char *dpystr = DisplayString (si->dpy);
2228 char *tail = (char *) strchr (dpystr, ':');
2229 if (! tail || strncmp (tail, ":0", 2))
2230 not_on_console = True;
2233 char dpyname[255], localname[255];
2234 strncpy (dpyname, dpystr, tail-dpystr);
2235 dpyname [tail-dpystr] = 0;
2237 !strcmp(dpyname, "unix") ||
2238 !strcmp(dpyname, "localhost"))
2239 not_on_console = False;
2240 else if (gethostname (localname, sizeof (localname)))
2241 not_on_console = True; /* can't find hostname? */
2242 else if (!strncmp (dpyname, "/tmp/launch-", 12)) /* MacOS X launchd */
2243 not_on_console = False;
2246 /* We have to call gethostbyname() on the result of gethostname()
2247 because the two aren't guarenteed to be the same name for the
2248 same host: on some losing systems, one is a FQDN and the other
2249 is not. Here in the wide wonderful world of Unix it's rocket
2250 science to obtain the local hostname in a portable fashion.
2252 And don't forget, gethostbyname() reuses the structure it
2253 returns, so we have to copy the fucker before calling it again.
2254 Thank you master, may I have another.
2256 struct hostent *h = gethostbyname (dpyname);
2258 not_on_console = True;
2263 strcpy (hn, h->h_name);
2264 l = gethostbyname (localname);
2265 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
2269 return !not_on_console;
2273 /* Do a little bit of heap introspection...
2276 check_for_leaks (const char *where)
2278 #if defined(HAVE_SBRK) && defined(LEAK_PARANOIA)
2279 static unsigned long last_brk = 0;
2280 int b = (unsigned long) sbrk(0);
2281 if (last_brk && last_brk < b)
2282 fprintf (stderr, "%s: %s: brk grew by %luK.\n",
2284 (((b - last_brk) + 1023) / 1024));
2286 #endif /* HAVE_SBRK */