1 /* xscreensaver, Copyright (c) 1991-2006 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 * While we are waiting for user activity, we also set up timers so that,
50 * after a certain amount of time has passed, we can start a different
51 * screenhack. We do this by killing the running child process with
52 * SIGTERM, and then starting a new one in the same way.
54 * If there was a real virtual root, meaning that we removed the __SWM_VROOT
55 * property from it, meaning we must (absolutely must) restore it before we
56 * exit, then we set up signal handlers for most signals (SIGINT, SIGTERM,
57 * etc.) that do this. Most Xlib and Xt routines are not reentrant, so it
58 * is not generally safe to call them from signal handlers; however, this
59 * program spends most of its time waiting, so the window of opportunity
60 * when code could be called reentrantly is fairly small; and also, the worst
61 * that could happen is that the call would fail. If we've gotten one of
62 * these signals, then we're on our way out anyway. If we didn't restore the
63 * __SWM_VROOT property, that would be very bad, so it's worth a shot. Note
64 * that this means that, if you're using a virtual-root window manager, you
65 * can really fuck up the world by killing this process with "kill -9".
67 * This program accepts ClientMessages of type SCREENSAVER; these messages
68 * may contain the atoms ACTIVATE, DEACTIVATE, etc, meaning to turn the
69 * screensaver on or off now, regardless of the idleness of the user,
70 * and a few other things. The included "xscreensaver-command" program
71 * sends these messsages.
73 * If we don't have the XIdle, MIT-SCREEN-SAVER, or SGI SCREEN_SAVER
74 * extensions, then we do the XAutoLock trick: notice every window that
75 * gets created, and wait 30 seconds or so until its creating process has
76 * settled down, and then select KeyPress events on those windows which
77 * already select for KeyPress events. It's important that we not select
78 * KeyPress on windows which don't select them, because that would
79 * interfere with event propagation. This will break if any program
80 * changes its event mask to contain KeyRelease or PointerMotion more than
81 * 30 seconds after creating the window, but such programs do not seem to
82 * occur in nature (I've never seen it happen in all these years.)
84 * The reason that we can't select KeyPresses on windows that don't have
85 * them already is that, when dispatching a KeyPress event, X finds the
86 * lowest (leafmost) window in the hierarchy on which *any* client selects
87 * for KeyPress, and sends the event to that window. This means that if a
88 * client had a window with subwindows, and expected to receive KeyPress
89 * events on the parent window instead of the subwindows, then that client
90 * would malfunction if some other client selected KeyPress events on the
91 * subwindows. It is an incredible misdesign that one client can make
92 * another client malfunction in this way.
94 * To detect mouse motion, we periodically wake up and poll the mouse
95 * position and button/modifier state, and notice when something has
96 * changed. We make this check every five seconds by default, and since the
97 * screensaver timeout has a granularity of one minute, this makes the
98 * chance of a false positive very small. We could detect mouse motion in
99 * the same way as keyboard activity, but that would suffer from the same
100 * "client changing event mask" problem that the KeyPress events hack does.
101 * I think polling is more reliable.
103 * On systems with /proc/interrupts (Linux) we poll that file and note when
104 * the interrupt counter numbers on the "keyboard" and "PS/2" lines change.
105 * (There is no reliable way, using /proc/interrupts, to detect non-PS/2
106 * mice, so it doesn't help for serial or USB mice.)
108 * None of this crap happens if we're using one of the extensions. Sadly,
109 * the XIdle extension hasn't been available for many years; the SGI
110 * extension only exists on SGIs; and the MIT extension, while widely
111 * deployed, is garbage in several ways.
113 * A third idle-detection option could be implemented (but is not): when
114 * running on the console display ($DISPLAY is `localhost`:0) and we're on a
115 * machine where /dev/tty and /dev/mouse have reasonable last-modification
116 * times, we could just stat() those. But the incremental benefit of
117 * implementing this is really small, so forget I said anything.
120 * - Have a second terminal handy.
121 * - Be careful where you set your breakpoints, you don't want this to
122 * stop under the debugger with the keyboard grabbed or the blackout
124 * - If you run your debugger under XEmacs, try M-ESC (x-grab-keyboard)
125 * to keep your emacs window alive even when xscreensaver has grabbed.
126 * - Go read the code related to `debug_p'.
127 * - You probably can't set breakpoints in functions that are called on
128 * the other side of a call to fork() -- if your subprocesses are
129 * dying with signal 5, Trace/BPT Trap, you're losing in this way.
130 * - If you aren't using a server extension, don't leave this stopped
131 * under the debugger for very long, or the X input buffer will get
132 * huge because of the keypress events it's selecting for. This can
133 * make your X server wedge with "no more input buffers."
135 * ======================================================================== */
143 #include <X11/Xlib.h>
145 #include <X11/Xlibint.h>
147 #include <X11/Xatom.h>
148 #include <X11/Intrinsic.h>
149 #include <X11/StringDefs.h>
150 #include <X11/Shell.h>
153 #include <sys/time.h>
154 #include <netdb.h> /* for gethostbyname() */
157 # include <X11/Xmu/Error.h>
159 # include <Xmu/Error.h>
161 #else /* !HAVE_XMU */
163 #endif /* !HAVE_XMU */
165 #ifdef HAVE_XIDLE_EXTENSION
166 # include <X11/extensions/xidle.h>
167 #endif /* HAVE_XIDLE_EXTENSION */
170 # include <X11/extensions/Xinerama.h>
171 #endif /* HAVE_XINERAMA */
173 #include "xscreensaver.h"
175 #include "yarandom.h"
176 #include "resources.h"
180 saver_info *global_si_kludge = 0; /* I hate C so much... */
187 static Atom XA_SCREENSAVER_RESPONSE;
188 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
189 static Atom XA_RESTART, XA_SELECT;
190 static Atom XA_THROTTLE, XA_UNTHROTTLE;
191 Atom XA_DEMO, XA_PREFS, XA_EXIT, XA_LOCK, XA_BLANK;
194 static XrmOptionDescRec options [] = {
196 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
197 { "-silent", ".verbose", XrmoptionNoArg, "off" },
199 /* xscreensaver-demo uses this one */
200 { "-nosplash", ".splash", XrmoptionNoArg, "off" },
201 { "-no-splash", ".splash", XrmoptionNoArg, "off" },
203 /* useful for debugging */
204 { "-no-capture-stderr", ".captureStderr", XrmoptionNoArg, "off" },
208 __extension__ /* shut up about "string length is greater than the length
209 ISO C89 compilers are required to support" when including
213 static char *defaults[] = {
214 #include "XScreenSaver_ad.h"
219 ERROR! You must not include vroot.h in this file.
223 do_help (saver_info *si)
228 xscreensaver %s, copyright (c) 1991-2006 by Jamie Zawinski <jwz@jwz.org>\n\
230 All xscreensaver configuration is via the `~/.xscreensaver' file.\n\
231 Rather than editing that file by hand, just run `xscreensaver-demo':\n\
232 that program lets you configure the screen saver graphically,\n\
233 including timeouts, locking, and display modes.\n\
237 Just getting started? Try this:\n\
242 For updates, online manual, and FAQ, please see the web page:\n\
244 http://www.jwz.org/xscreensaver/\n\
256 time_t now = time ((time_t *) 0);
257 char *str = (char *) ctime (&now);
258 char *nl = (char *) strchr (str, '\n');
259 if (nl) *nl = 0; /* take off that dang newline */
263 static Bool blurb_timestamp_p = False; /* kludge */
268 if (!blurb_timestamp_p)
272 static char buf[255];
273 char *ct = timestring();
274 int n = strlen(progname);
276 strncpy(buf, progname, n);
279 strncpy(buf+n, ct+11, 8);
280 strcpy(buf+n+9, ": ");
287 saver_ehandler (Display *dpy, XErrorEvent *error)
289 saver_info *si = global_si_kludge; /* I hate C so much... */
293 if (!real_stderr) real_stderr = stderr;
295 fprintf (real_stderr, "\n"
296 "#######################################"
297 "#######################################\n\n"
298 "%s: X Error! PLEASE REPORT THIS BUG.\n",
301 for (i = 0; i < si->nscreens; i++)
303 saver_screen_info *ssi = &si->screens[i];
304 fprintf (real_stderr, "%s: screen %d/%d: 0x%x, 0x%x, 0x%x\n",
305 blurb(), ssi->real_screen_number, ssi->number,
306 (unsigned int) RootWindowOfScreen (si->screens[i].screen),
307 (unsigned int) si->screens[i].real_vroot,
308 (unsigned int) si->screens[i].screensaver_window);
311 fprintf (real_stderr, "\n"
312 "#######################################"
313 "#######################################\n\n");
315 fatal_p = XmuPrintDefaultErrorMessage (dpy, error, real_stderr);
317 fatal_p = True; /* The only time I've ever seen a supposedly nonfatal error,
318 it has been BadImplementation / Xlib sequence lost, which
319 are in truth pretty damned fatal.
322 fprintf (real_stderr, "\n");
325 fprintf (real_stderr, "%s: nonfatal error.\n\n", blurb());
328 if (si->prefs.xsync_p)
330 saver_exit (si, -1, "because of synchronous X Error");
334 fprintf (real_stderr,
335 "#######################################"
336 "#######################################\n\n");
337 fprintf (real_stderr,
338 " If at all possible, please re-run xscreensaver with the command\n"
339 " line arguments `-sync -verbose -no-capture', and reproduce this\n"
340 " bug. That will cause xscreensaver to dump a `core' file to the\n"
341 " current directory. Please include the stack trace from that core\n"
342 " file in your bug report. *DO NOT* mail the core file itself!\n"
343 " That won't work.\n");
344 fprintf (real_stderr,
346 " http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
347 " the most useful bug reports, and how to examine core files.\n"
349 " The more information you can provide, the better. But please\n"
350 " report this bug, regardless!\n"
352 fprintf (real_stderr,
353 "#######################################"
354 "#######################################\n\n");
356 saver_exit (si, -1, 0);
364 /* This error handler is used only while the X connection is being set up;
365 after we've got a connection, we don't use this handler again. The only
366 reason for having this is so that we can present a more idiot-proof error
367 message than "cannot open display."
370 startup_ehandler (String name, String type, String class,
371 String defalt, /* one can't even spel properly
372 in this joke of a language */
373 String *av, Cardinal *ac)
377 saver_info *si = global_si_kludge; /* I hate C so much... */
378 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
380 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
381 fmt, sizeof(fmt)-1, *db);
383 fprintf (stderr, "%s: ", blurb());
385 memset (p, 0, sizeof(p));
386 if (*ac > countof (p)) *ac = countof (p);
387 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
388 fprintf (stderr, fmt, /* Did I mention that I hate C? */
389 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
390 fprintf (stderr, "\n");
392 describe_uids (si, stderr);
394 if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
396 fprintf (stderr, "\n"
397 "%s: This is probably because you're logging in as root. You\n"
398 " shouldn't log in as root: you should log in as a normal user,\n"
399 " and then `su' as needed. If you insist on logging in as\n"
400 " root, you will have to turn off X's security features before\n"
401 " xscreensaver will work.\n"
403 " Please read the manual and FAQ for more information:\n",
408 fprintf (stderr, "\n"
409 "%s: Errors at startup are usually authorization problems.\n"
410 " But you're not logging in as root (good!) so something\n"
411 " else must be wrong. Did you read the manual and the FAQ?\n",
415 fprintf (stderr, "\n"
416 " http://www.jwz.org/xscreensaver/faq.html\n"
417 " http://www.jwz.org/xscreensaver/man.html\n"
426 /* The zillions of initializations.
429 /* Set progname, version, etc. This is done very early.
432 set_version_string (saver_info *si, int *argc, char **argv)
434 progclass = "XScreenSaver";
436 /* progname is reset later, after we connect to X. */
437 progname = strrchr(argv[0], '/');
438 if (progname) progname++;
439 else progname = argv[0];
441 if (strlen(progname) > 100) /* keep it short. */
444 /* The X resource database blows up if argv[0] has a "." in it. */
447 while ((s = strchr (s, '.')))
451 si->version = (char *) malloc (5);
452 memcpy (si->version, screensaver_id + 17, 4);
457 /* Initializations that potentially take place as a priveleged user:
458 If the xscreensaver executable is setuid root, then these initializations
459 are run as root, before discarding privileges.
462 privileged_initialization (saver_info *si, int *argc, char **argv)
465 /* before hack_uid() for proper permissions */
466 lock_priv_init (*argc, argv, si->prefs.verbose_p);
467 #endif /* NO_LOCKING */
473 /* Figure out what locking mechanisms are supported.
476 lock_initialization (saver_info *si, int *argc, char **argv)
479 si->locking_disabled_p = True;
480 si->nolock_reason = "not compiled with locking support";
481 #else /* !NO_LOCKING */
483 /* Finish initializing locking, now that we're out of privileged code. */
484 if (! lock_init (*argc, argv, si->prefs.verbose_p))
486 si->locking_disabled_p = True;
487 si->nolock_reason = "error getting password";
490 /* If locking is currently enabled, but the environment indicates that
491 we have been launched as GDM's "Background" program, then disable
492 locking just in case.
494 if (!si->locking_disabled_p && getenv ("RUNNING_UNDER_GDM"))
496 si->locking_disabled_p = True;
497 si->nolock_reason = "running under GDM";
500 /* If the server is XDarwin (MacOS X) then disable locking.
501 (X grabs only affect X programs, so you can use Command-Tab
502 to bring any other Mac program to the front, e.g., Terminal.)
504 if (!si->locking_disabled_p)
506 int op = 0, event = 0, error = 0;
507 Bool macos_p = False;
510 /* Disable locking if *running* on Apple hardware, since we have no
511 reliable way to determine whether the server is running on MacOS.
512 Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
513 but I'm not really sure about that.
519 /* This extension exists on the Apple X11 server, but not
520 on earlier versions of the XDarwin server. */
521 macos_p = XQueryExtension (si->dpy, "Apple-DRI", &op, &event, &error);
525 si->locking_disabled_p = True;
526 si->nolock_reason = "Cannot lock securely on MacOS X";
530 #endif /* NO_LOCKING */
534 /* Open the connection to the X server, and intern our Atoms.
537 connect_to_server (saver_info *si, int *argc, char **argv)
539 Widget toplevel_shell;
542 char *d = getenv ("DISPLAY");
545 char *ndpy = strdup("DISPLAY=:0.0");
546 /* if (si->prefs.verbose_p) */ /* sigh, too early to test this... */
548 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
552 /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
553 glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
554 do not. So we must leak it (and/or the previous setting). Yay.
557 #endif /* HAVE_PUTENV */
559 XSetErrorHandler (saver_ehandler);
561 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
562 toplevel_shell = XtAppInitialize (&si->app, progclass,
563 options, XtNumber (options),
564 argc, argv, defaults, 0, 0);
565 XtAppSetErrorMsgHandler (si->app, 0);
567 si->dpy = XtDisplay (toplevel_shell);
568 si->prefs.db = XtDatabase (si->dpy);
569 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
571 if(strlen(progname) > 100) /* keep it short. */
574 db = si->prefs.db; /* resources.c needs this */
576 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
577 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
578 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
579 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
580 XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
581 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
583 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
584 XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False);
585 XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False);
586 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
587 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
588 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
589 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
590 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
591 XA_PREV = XInternAtom (si->dpy, "PREV", False);
592 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
593 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
594 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
595 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
596 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
597 XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
598 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
599 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
601 return toplevel_shell;
605 /* Handle the command-line arguments that were not handled for us by Xt.
606 Issue an error message and exit if there are unknown options.
609 process_command_line (saver_info *si, int *argc, char **argv)
612 for (i = 1; i < *argc; i++)
614 if (!strcmp (argv[i], "-debug"))
615 /* no resource for this one, out of paranoia. */
616 si->prefs.debug_p = True;
618 else if (!strcmp (argv[i], "-h") ||
619 !strcmp (argv[i], "-help") ||
620 !strcmp (argv[i], "--help"))
625 const char *s = argv[i];
626 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
629 if (s[0] == '-' && s[1] == '-') s++;
630 if (!strcmp (s, "-activate") ||
631 !strcmp (s, "-deactivate") ||
632 !strcmp (s, "-cycle") ||
633 !strcmp (s, "-next") ||
634 !strcmp (s, "-prev") ||
635 !strcmp (s, "-exit") ||
636 !strcmp (s, "-restart") ||
637 !strcmp (s, "-demo") ||
638 !strcmp (s, "-prefs") ||
639 !strcmp (s, "-preferences") ||
640 !strcmp (s, "-lock") ||
641 !strcmp (s, "-version") ||
642 !strcmp (s, "-time"))
645 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
646 fprintf (stderr, "\n\
647 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
649 fprintf (stderr, "\n\
650 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
653 The `xscreensaver' program is a daemon that runs in the background.\n\
654 You control a running xscreensaver process by sending it messages\n\
655 with `xscreensaver-demo' or `xscreensaver-command'.\n\
656 . See the man pages for details, or check the web page:\n\
657 http://www.jwz.org/xscreensaver/\n\n");
659 /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
660 suggest that explicitly. */
661 if (!strcmp (s, "-lock"))
663 Or perhaps you meant either the \"-lock-mode\" or the\n\
664 \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
673 /* Print out the xscreensaver banner to the tty if applicable;
674 Issue any other warnings that are called for at this point.
677 print_banner (saver_info *si)
679 saver_preferences *p = &si->prefs;
681 /* This resource gets set some time before the others, so that we know
682 whether to print the banner (and so that the banner gets printed before
683 any resource-database-related error messages.)
685 p->verbose_p = (p->debug_p ||
686 get_boolean_resource (si->dpy, "verbose", "Boolean"));
688 /* Ditto, for the locking_disabled_p message. */
689 p->lock_p = get_boolean_resource (si->dpy, "lock", "Boolean");
693 "%s %s, copyright (c) 1991-2006 "
694 "by Jamie Zawinski <jwz@jwz.org>.\n",
695 progname, si->version);
698 fprintf (stderr, "\n"
699 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
701 "\tNote that in debug mode, the xscreensaver window will only\n"
702 "\tcover the left half of the screen. (The idea is that you\n"
703 "\tcan still see debugging output in a shell, if you position\n"
704 "\tit on the right side of the screen.)\n"
706 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
707 "\tuntrusted environments.\n"
713 if (!si->uid_message || !*si->uid_message)
714 describe_uids (si, stderr);
717 if (si->orig_uid && *si->orig_uid)
718 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
719 blurb(), si->orig_uid);
720 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
723 fprintf (stderr, "%s: in process %lu.\n", blurb(),
724 (unsigned long) getpid());
729 print_lock_failure_banner (saver_info *si)
731 saver_preferences *p = &si->prefs;
733 /* If locking was not able to be initalized for some reason, explain why.
734 (This has to be done after we've read the lock_p resource.)
736 if (si->locking_disabled_p)
739 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
741 if (strstr (si->nolock_reason, "passw"))
742 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
743 "consult the manual.\n", blurb());
744 else if (strstr (si->nolock_reason, "running as "))
746 "%s: locking only works when xscreensaver is launched\n"
747 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
748 "\t See the manual for details.\n",
755 /* Examine all of the display's screens, and populate the `saver_screen_info'
756 structures. Make sure this is called after hack_environment() sets $PATH.
759 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
761 Bool found_any_writable_cells = False;
764 # ifdef HAVE_XINERAMA
767 si->xinerama_p = (XineramaQueryExtension (si->dpy, &event, &error) &&
768 XineramaIsActive (si->dpy));
771 if (si->xinerama_p && ScreenCount (si->dpy) != 1)
773 si->xinerama_p = False;
774 if (si->prefs.verbose_p)
776 "%s: Xinerama AND %d screens? Disabling Xinerama support!\n",
777 blurb(), ScreenCount(si->dpy));
782 XineramaScreenInfo *xsi = XineramaQueryScreens (si->dpy, &si->nscreens);
784 si->xinerama_p = False;
787 si->screens = (saver_screen_info *)
788 calloc(sizeof(saver_screen_info), si->nscreens);
789 for (i = 0; i < si->nscreens; i++)
791 si->screens[i].x = xsi[i].x_org;
792 si->screens[i].y = xsi[i].y_org;
793 si->screens[i].width = xsi[i].width;
794 si->screens[i].height = xsi[i].height;
798 si->default_screen = &si->screens[0];
799 si->default_screen->real_screen_p = True;
801 # endif /* !HAVE_XINERAMA */
805 si->nscreens = ScreenCount(si->dpy);
806 si->screens = (saver_screen_info *)
807 calloc(sizeof(saver_screen_info), si->nscreens);
808 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
810 for (i = 0; i < si->nscreens; i++)
812 saver_screen_info *ssi = &si->screens[i];
813 ssi->width = DisplayWidth (si->dpy, i);
814 ssi->height = DisplayHeight (si->dpy, i);
815 ssi->real_screen_p = True;
816 ssi->real_screen_number = i;
822 /* In "quad mode", we use the Xinerama code to pretend that there are 4
823 screens for every physical screen, and run four times as many hacks...
825 if (si->prefs.quad_p)
827 int ns2 = si->nscreens * 4;
828 saver_screen_info *ssi2 = (saver_screen_info *)
829 calloc(sizeof(saver_screen_info), ns2);
831 for (i = 0; i < si->nscreens; i++)
833 saver_screen_info *old = &si->screens[i];
835 if (si->prefs.debug_p) old->width = old->width / 2;
842 ssi2[i*4 ].width /= 2;
843 ssi2[i*4 ].height /= 2;
845 ssi2[i*4+1].x += ssi2[i*4 ].width;
846 ssi2[i*4+1].width -= ssi2[i*4 ].width;
847 ssi2[i*4+1].height /= 2;
849 ssi2[i*4+2].y += ssi2[i*4 ].height;
850 ssi2[i*4+2].width /= 2;
851 ssi2[i*4+2].height -= ssi2[i*4 ].height;
853 ssi2[i*4+3].x += ssi2[i*4+2].width;
854 ssi2[i*4+3].y += ssi2[i*4+2].height;
855 ssi2[i*4+3].width -= ssi2[i*4+2].width;
856 ssi2[i*4+3].height -= ssi2[i*4+2].height;
858 ssi2[i*4+1].real_screen_p = False;
859 ssi2[i*4+2].real_screen_p = False;
860 ssi2[i*4+3].real_screen_p = False;
866 si->default_screen = &si->screens[DefaultScreen(si->dpy) * 4];
867 si->xinerama_p = True;
869 # endif /* QUAD_MODE */
871 /* finish initializing the screens.
873 for (i = 0; i < si->nscreens; i++)
875 saver_screen_info *ssi = &si->screens[i];
879 ssi->screen = ScreenOfDisplay (si->dpy, ssi->real_screen_number);
880 ssi->poll_mouse_last_root_x = -1;
881 ssi->poll_mouse_last_root_y = -1;
885 ssi->width = WidthOfScreen (ssi->screen);
886 ssi->height = HeightOfScreen (ssi->screen);
889 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
890 ssi->default_visual =
891 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
893 ssi->current_visual = ssi->default_visual;
894 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
896 /* Execute a subprocess to find the GL visual. */
897 ssi->best_gl_visual = get_best_gl_visual (ssi);
899 if (ssi == si->default_screen)
900 /* Since this is the default screen, use the one already created. */
901 ssi->toplevel_shell = toplevel_shell;
903 /* Otherwise, each screen must have its own unmapped root widget. */
904 ssi->toplevel_shell =
905 XtVaAppCreateShell (progname, progclass, applicationShellWidgetClass,
907 XtNscreen, ssi->screen,
908 XtNvisual, ssi->current_visual,
909 XtNdepth, visual_depth (ssi->screen,
910 ssi->current_visual),
913 if (! found_any_writable_cells)
915 /* Check to see whether fading is ever possible -- if any of the
916 screens on the display has a PseudoColor visual, then fading can
917 work (on at least some screens.) If no screen has a PseudoColor
918 visual, then don't bother ever trying to fade, because it will
919 just cause a delay without causing any visible effect.
921 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
922 get_visual (ssi->screen, "PseudoColor", True, False) ||
923 get_visual (ssi->screen, "GrayScale", True, False))
924 found_any_writable_cells = True;
928 si->fading_possible_p = found_any_writable_cells;
930 #ifdef HAVE_XF86VMODE_GAMMA
931 si->fading_possible_p = True; /* if we can gamma fade, go for it */
936 /* If any server extensions have been requested, try and initialize them.
937 Issue warnings if requests can't be honored.
940 initialize_server_extensions (saver_info *si)
942 saver_preferences *p = &si->prefs;
944 Bool server_has_xidle_extension_p = False;
945 Bool server_has_sgi_saver_extension_p = False;
946 Bool server_has_mit_saver_extension_p = False;
947 Bool system_has_proc_interrupts_p = False;
948 const char *piwhy = 0;
950 si->using_xidle_extension = p->use_xidle_extension;
951 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
952 si->using_mit_saver_extension = p->use_mit_saver_extension;
953 si->using_proc_interrupts = p->use_proc_interrupts;
955 #ifdef HAVE_XIDLE_EXTENSION
956 server_has_xidle_extension_p = query_xidle_extension (si);
958 #ifdef HAVE_SGI_SAVER_EXTENSION
959 server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
961 #ifdef HAVE_MIT_SAVER_EXTENSION
962 server_has_mit_saver_extension_p = query_mit_saver_extension (si);
964 #ifdef HAVE_PROC_INTERRUPTS
965 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
968 if (!server_has_xidle_extension_p)
969 si->using_xidle_extension = False;
970 else if (p->verbose_p)
972 if (si->using_xidle_extension)
973 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
975 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
978 if (!server_has_sgi_saver_extension_p)
979 si->using_sgi_saver_extension = False;
980 else if (p->verbose_p)
982 if (si->using_sgi_saver_extension)
983 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
986 "%s: not using server's SGI SCREEN_SAVER extension.\n",
990 if (!server_has_mit_saver_extension_p)
991 si->using_mit_saver_extension = False;
992 else if (p->verbose_p)
994 if (si->using_mit_saver_extension)
995 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
999 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
1003 /* These are incompatible (or at least, our support for them is...) */
1004 if (si->xinerama_p && si->using_mit_saver_extension)
1006 si->using_mit_saver_extension = False;
1008 fprintf (stderr, "%s: Xinerama in use: disabling MIT-SCREEN-SAVER.\n",
1013 query_randr_extension (si);
1016 if (!system_has_proc_interrupts_p)
1018 si->using_proc_interrupts = False;
1019 if (p->verbose_p && piwhy)
1020 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
1023 else if (p->verbose_p)
1025 if (si->using_proc_interrupts)
1027 "%s: consulting /proc/interrupts for keyboard activity.\n",
1031 "%s: not consulting /proc/interrupts for keyboard activity.\n",
1037 /* For the case where we aren't using an server extensions, select user events
1038 on all the existing windows, and launch timers to select events on
1039 newly-created windows as well.
1041 If a server extension is being used, this does nothing.
1044 select_events (saver_info *si)
1046 saver_preferences *p = &si->prefs;
1049 if (si->using_xidle_extension ||
1050 si->using_mit_saver_extension ||
1051 si->using_sgi_saver_extension)
1054 if (p->initial_delay)
1058 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
1059 (int) p->initial_delay/1000,
1060 (p->initial_delay == 1000 ? "" : "s"));
1064 usleep (p->initial_delay);
1066 fprintf (stderr, " done.\n");
1071 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
1076 /* Select events on the root windows of every screen. This also selects
1077 for window creation events, so that new subwindows will be noticed.
1079 for (i = 0; i < si->nscreens; i++)
1081 saver_screen_info *ssi = &si->screens[i];
1082 if (ssi->real_screen_p)
1083 start_notice_events_timer (si,
1084 RootWindowOfScreen (si->screens[i].screen), False);
1088 fprintf (stderr, " done.\n");
1093 maybe_reload_init_file (saver_info *si)
1095 saver_preferences *p = &si->prefs;
1096 if (init_file_changed_p (p))
1099 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
1100 blurb(), init_file_name());
1102 load_init_file (si->dpy, p);
1104 /* If a server extension is in use, and p->timeout has changed,
1105 we need to inform the server of the new timeout. */
1106 disable_builtin_screensaver (si, False);
1108 /* If the DPMS settings in the init file have changed,
1109 change the settings on the server to match. */
1110 sync_server_dpms_settings (si->dpy,
1111 (p->dpms_enabled_p &&
1112 p->mode != DONT_BLANK),
1113 p->dpms_standby / 1000,
1114 p->dpms_suspend / 1000,
1123 - wait until the user is idle;
1125 - wait until the user is active;
1126 - unblank the screen;
1131 main_loop (saver_info *si)
1133 saver_preferences *p = &si->prefs;
1138 Bool was_locked = False;
1141 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1143 check_for_leaks ("unblanked A");
1144 sleep_until_idle (si, True);
1145 check_for_leaks ("unblanked B");
1150 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
1151 si->selection_mode, timestring());
1153 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
1157 maybe_reload_init_file (si);
1159 if (p->mode == DONT_BLANK)
1162 fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
1163 blurb(), timestring());
1165 /* Go around the loop and wait for the next bout of idleness,
1166 or for the init file to change, or for a remote command to
1167 come in, or something.
1169 But, if locked_p is true, go ahead. This can only happen
1170 if we're in "disabled" mode but a "lock" clientmessage came
1171 in: in that case, we should go ahead and blank/lock the screen.
1177 /* Since we're about to blank the screen, kill the de-race timer,
1178 if any. It might still be running if we have unblanked and then
1179 re-blanked in a short period (e.g., when using the "next" button
1180 in xscreensaver-demo.)
1185 fprintf (stderr, "%s: stopping de-race timer (%d remaining.)\n",
1186 blurb(), si->de_race_ticks);
1187 XtRemoveTimeOut (si->de_race_id);
1192 /* Now, try to blank.
1195 if (! blank_screen (si))
1197 /* We were unable to grab either the keyboard or mouse.
1198 This means we did not (and must not) blank the screen.
1199 If we were to blank the screen while some other program
1200 is holding both the mouse and keyboard grabbed, then
1201 we would never be able to un-blank it! We would never
1202 see any events, and the display would be wedged.
1204 So, just go around the loop again and wait for the
1205 next bout of idleness. (If the user remains idle, we
1206 will next try to blank the screen again in no more than
1209 Time retry = 60 * 1000;
1210 if (p->timeout < retry)
1216 "%s: DEBUG MODE: unable to grab -- BLANKING ANYWAY.\n",
1222 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
1225 schedule_wakeup_event (si, retry, p->debug_p);
1230 kill_screenhack (si);
1232 if (!si->throttled_p)
1233 spawn_screenhack (si, True);
1234 else if (p->verbose_p)
1235 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
1237 /* Don't start the cycle timer in demo mode. */
1238 if (!si->demoing_p && p->cycle)
1239 si->cycle_id = XtAppAddTimeOut (si->app,
1241 /* see comment in cycle_timer() */
1249 /* Maybe start locking the screen.
1252 Time lock_timeout = p->lock_timeout;
1254 if (si->emergency_lock_p && p->lock_p && lock_timeout)
1256 int secs = p->lock_timeout / 1000;
1259 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1261 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1265 si->emergency_lock_p = False;
1267 if (!si->demoing_p && /* if not going into demo mode */
1268 p->lock_p && /* and locking is enabled */
1269 !si->locking_disabled_p && /* and locking is possible */
1270 lock_timeout == 0) /* and locking is not timer-deferred */
1271 set_locked_p (si, True); /* then lock right now. */
1273 /* locked_p might be true already because of the above, or because of
1274 the LOCK ClientMessage. But if not, and if we're supposed to lock
1275 after some time, set up a timer to do so.
1280 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1281 activate_lock_timer,
1284 #endif /* !NO_LOCKING */
1287 ok_to_unblank = True;
1290 check_for_leaks ("blanked A");
1291 sleep_until_idle (si, False); /* until not idle */
1292 check_for_leaks ("blanked B");
1294 maybe_reload_init_file (si);
1297 /* Maybe unlock the screen.
1301 saver_screen_info *ssi = si->default_screen;
1302 if (si->locking_disabled_p) abort ();
1305 si->dbox_up_p = True;
1306 suspend_screenhack (si, True);
1307 XUndefineCursor (si->dpy, ssi->screensaver_window);
1309 ok_to_unblank = unlock_p (si);
1311 si->dbox_up_p = False;
1312 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1313 suspend_screenhack (si, False); /* resume */
1315 if (!ok_to_unblank &&
1316 !screenhack_running_p (si))
1318 /* If the lock dialog has been dismissed and we're not about to
1319 unlock the screen, and there is currently no hack running,
1320 then launch one. (There might be no hack running if DPMS
1321 had kicked in. But DPMS is off now, so bring back the hack)
1324 XtRemoveTimeOut (si->cycle_id);
1326 cycle_timer ((XtPointer) si, 0);
1329 #endif /* !NO_LOCKING */
1331 } while (!ok_to_unblank);
1335 fprintf (stderr, "%s: unblanking screen at %s.\n",
1336 blurb(), timestring ());
1338 /* Kill before unblanking, to stop drawing as soon as possible. */
1339 kill_screenhack (si);
1340 unblank_screen (si);
1342 set_locked_p (si, False);
1343 si->emergency_lock_p = False;
1345 si->selection_mode = 0;
1347 /* If we're throttled, and the user has explicitly unlocked the screen,
1348 then unthrottle. If we weren't locked, then don't unthrottle
1349 automatically, because someone might have just bumped the desk... */
1352 if (si->throttled_p && p->verbose_p)
1353 fprintf (stderr, "%s: unthrottled.\n", blurb());
1354 si->throttled_p = False;
1359 XtRemoveTimeOut (si->cycle_id);
1365 XtRemoveTimeOut (si->lock_id);
1369 /* Since we're unblanked now, break race conditions and make
1370 sure we stay that way (see comment in timers.c.) */
1371 if (! si->de_race_id)
1372 de_race_timer ((XtPointer) si, 0);
1376 static void analyze_display (saver_info *si);
1377 static void fix_fds (void);
1380 main (int argc, char **argv)
1384 saver_info *si = &the_si;
1385 saver_preferences *p = &si->prefs;
1388 memset(si, 0, sizeof(*si));
1389 global_si_kludge = si; /* I hate C so much... */
1393 # undef ya_rand_init
1396 save_argv (argc, argv);
1397 set_version_string (si, &argc, argv);
1398 privileged_initialization (si, &argc, argv);
1399 hack_environment (si);
1401 shell = connect_to_server (si, &argc, argv);
1402 process_command_line (si, &argc, argv);
1405 load_init_file(si->dpy, p); /* must be before initialize_per_screen_info() */
1406 blurb_timestamp_p = p->timestamp_p; /* kludge */
1407 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1409 /* We can only issue this warning now. */
1410 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1412 "%s: there are no PseudoColor or GrayScale visuals.\n"
1413 "%s: ignoring the request for fading/unfading.\n",
1416 for (i = 0; i < si->nscreens; i++)
1418 saver_screen_info *ssi = &si->screens[i];
1419 if (ssi->real_screen_p)
1420 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1424 lock_initialization (si, &argc, argv);
1425 print_lock_failure_banner (si);
1427 if (p->xsync_p) XSynchronize (si->dpy, True);
1429 if (p->verbose_p) analyze_display (si);
1430 initialize_server_extensions (si);
1432 si->blank_time = time ((time_t) 0); /* must be before ..._window */
1433 initialize_screensaver_window (si);
1438 disable_builtin_screensaver (si, True);
1439 sync_server_dpms_settings (si->dpy,
1440 (p->dpms_enabled_p &&
1441 p->mode != DONT_BLANK),
1442 p->dpms_standby / 1000,
1443 p->dpms_suspend / 1000,
1447 initialize_stderr (si);
1448 handle_signals (si);
1450 make_splash_dialog (si);
1452 main_loop (si); /* doesn't return */
1459 /* Bad Things Happen if stdin, stdout, and stderr have been closed
1460 (as by the `sh incantation "xscreensaver >&- 2>&-"). When you do
1461 that, the X connection gets allocated to one of these fds, and
1462 then some random library writes to stderr, and random bits get
1463 stuffed down the X pipe, causing "Xlib: sequence lost" errors.
1464 So, we cause the first three file descriptors to be open to
1465 /dev/null if they aren't open to something else already. This
1466 must be done before any other files are opened (or the closing
1467 of that other file will again free up one of the "magic" first
1470 We do this by opening /dev/null three times, and then closing
1471 those fds, *unless* any of them got allocated as #0, #1, or #2,
1472 in which case we leave them open. Gag.
1474 Really, this crap is technically required of *every* X program,
1475 if you want it to be robust in the face of "2>&-".
1477 int fd0 = open ("/dev/null", O_RDWR);
1478 int fd1 = open ("/dev/null", O_RDWR);
1479 int fd2 = open ("/dev/null", O_RDWR);
1480 if (fd0 > 2) close (fd0);
1481 if (fd1 > 2) close (fd1);
1482 if (fd2 > 2) close (fd2);
1487 /* Processing ClientMessage events.
1491 static Bool error_handler_hit_p = False;
1494 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1496 error_handler_hit_p = True;
1500 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1501 them. We only look up the atom names for printing warning messages,
1502 so don't bomb out when it happens...
1505 XGetAtomName_safe (Display *dpy, Atom atom)
1508 XErrorHandler old_handler;
1509 if (!atom) return 0;
1512 error_handler_hit_p = False;
1513 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1514 result = XGetAtomName (dpy, atom);
1516 XSetErrorHandler (old_handler);
1518 if (error_handler_hit_p) result = 0;
1525 sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
1526 return strdup (buf);
1532 clientmessage_response (saver_info *si, Window w, Bool error,
1533 const char *stderr_msg,
1534 const char *protocol_msg)
1538 saver_preferences *p = &si->prefs;
1539 XErrorHandler old_handler;
1541 if (error || p->verbose_p)
1542 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1544 L = strlen(protocol_msg);
1545 proto = (char *) malloc (L + 2);
1546 proto[0] = (error ? '-' : '+');
1547 strcpy (proto+1, protocol_msg);
1550 /* Ignore all X errors while sending a response to a ClientMessage.
1551 Pretty much the only way we could get an error here is if the
1552 window we're trying to send the reply on has been deleted, in
1553 which case, the sender of the ClientMessage won't see our response
1556 XSync (si->dpy, False);
1557 error_handler_hit_p = False;
1558 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1560 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1561 PropModeReplace, (unsigned char *) proto, L);
1563 XSync (si->dpy, False);
1564 XSetErrorHandler (old_handler);
1565 XSync (si->dpy, False);
1572 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1574 saver_preferences *p = &si->prefs;
1575 char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1576 Window w = event->xclient.window;
1579 Bool root_p = False;
1582 for (screen = 0; screen < si->nscreens; screen++)
1583 if (w == si->screens[screen].screensaver_window)
1585 strcpy (wdesc, "xscreensaver");
1588 else if (w == RootWindow (si->dpy, screen))
1590 strcpy (wdesc, "root");
1595 /* If this ClientMessage was sent to the real root window instead of to the
1596 xscreensaver window, then it might be intended for someone else who is
1597 listening on the root window (e.g., the window manager). So only print
1598 the warning if: we are in debug mode; or if the bogus message was
1599 actually sent to one of the xscreensaver-created windows.
1601 if (root_p && !p->debug_p)
1606 XErrorHandler old_handler;
1608 XWindowAttributes xgwa;
1609 memset (&hint, 0, sizeof(hint));
1610 memset (&xgwa, 0, sizeof(xgwa));
1612 XSync (si->dpy, False);
1613 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1614 XGetClassHint (si->dpy, w, &hint);
1615 XGetWindowAttributes (si->dpy, w, &xgwa);
1616 XSync (si->dpy, False);
1617 XSetErrorHandler (old_handler);
1618 XSync (si->dpy, False);
1620 screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1622 sprintf (wdesc, "%.20s / %.20s",
1623 (hint.res_name ? hint.res_name : "(null)"),
1624 (hint.res_class ? hint.res_class : "(null)"));
1625 if (hint.res_name) XFree (hint.res_name);
1626 if (hint.res_class) XFree (hint.res_class);
1629 fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1630 blurb(), screen, (str ? str : "(null)"));
1631 fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1632 blurb(), screen, (unsigned long) w, wdesc);
1633 if (str) XFree (str);
1637 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1639 saver_preferences *p = &si->prefs;
1641 Window window = event->xclient.window;
1643 /* Preferences might affect our handling of client messages. */
1644 maybe_reload_init_file (si);
1646 if (event->xclient.message_type != XA_SCREENSAVER ||
1647 event->xclient.format != 32)
1649 bogus_clientmessage_warning (si, event);
1653 type = event->xclient.data.l[0];
1654 if (type == XA_ACTIVATE)
1658 if (p->mode == DONT_BLANK)
1660 clientmessage_response(si, window, True,
1661 "ACTIVATE ClientMessage received in DONT_BLANK mode.",
1662 "screen blanking is currently disabled.");
1666 clientmessage_response(si, window, False,
1667 "ACTIVATE ClientMessage received.",
1669 si->selection_mode = 0;
1670 si->demoing_p = False;
1672 if (si->throttled_p && p->verbose_p)
1673 fprintf (stderr, "%s: unthrottled.\n", blurb());
1674 si->throttled_p = False;
1676 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1678 XForceScreenSaver (si->dpy, ScreenSaverActive);
1686 clientmessage_response(si, window, True,
1687 "ClientMessage ACTIVATE received while already active.",
1690 else if (type == XA_DEACTIVATE)
1694 if (si->throttled_p && p->verbose_p)
1695 fprintf (stderr, "%s: unthrottled.\n", blurb());
1696 si->throttled_p = False;
1698 clientmessage_response(si, window, False,
1699 "DEACTIVATE ClientMessage received.",
1701 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1703 XForceScreenSaver (si->dpy, ScreenSaverReset);
1711 clientmessage_response(si, window, False,
1712 "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
1713 "not active: idle timer reset.");
1716 else if (type == XA_CYCLE)
1720 clientmessage_response(si, window, False,
1721 "CYCLE ClientMessage received.",
1723 si->selection_mode = 0; /* 0 means randomize when its time. */
1724 si->demoing_p = False;
1726 if (si->throttled_p && p->verbose_p)
1727 fprintf (stderr, "%s: unthrottled.\n", blurb());
1728 si->throttled_p = False;
1731 XtRemoveTimeOut (si->cycle_id);
1733 cycle_timer ((XtPointer) si, 0);
1736 clientmessage_response(si, window, True,
1737 "ClientMessage CYCLE received while inactive.",
1740 else if (type == XA_NEXT || type == XA_PREV)
1742 clientmessage_response(si, window, False,
1744 ? "NEXT ClientMessage received."
1745 : "PREV ClientMessage received."),
1747 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1748 si->demoing_p = False;
1750 if (si->throttled_p && p->verbose_p)
1751 fprintf (stderr, "%s: unthrottled.\n", blurb());
1752 si->throttled_p = False;
1757 XtRemoveTimeOut (si->cycle_id);
1759 cycle_timer ((XtPointer) si, 0);
1764 else if (type == XA_SELECT)
1768 long which = event->xclient.data.l[1];
1770 if (p->mode == DONT_BLANK)
1772 clientmessage_response(si, window, True,
1773 "SELECT ClientMessage received in DONT_BLANK mode.",
1774 "screen blanking is currently disabled.");
1778 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1779 sprintf (buf2, "activating (%ld).", which);
1780 clientmessage_response (si, window, False, buf, buf2);
1782 if (which < 0) which = 0; /* 0 == "random" */
1783 si->selection_mode = which;
1784 si->demoing_p = False;
1786 if (si->throttled_p && p->verbose_p)
1787 fprintf (stderr, "%s: unthrottled.\n", blurb());
1788 si->throttled_p = False;
1793 XtRemoveTimeOut (si->cycle_id);
1795 cycle_timer ((XtPointer) si, 0);
1800 else if (type == XA_EXIT)
1802 /* Ignore EXIT message if the screen is locked. */
1803 if (until_idle_p || !si->locked_p)
1805 clientmessage_response (si, window, False,
1806 "EXIT ClientMessage received.",
1810 unblank_screen (si);
1811 kill_screenhack (si);
1812 XSync (si->dpy, False);
1814 saver_exit (si, 0, 0);
1817 clientmessage_response (si, window, True,
1818 "EXIT ClientMessage received while locked.",
1819 "screen is locked.");
1821 else if (type == XA_RESTART)
1823 /* The RESTART message works whether the screensaver is active or not,
1824 unless the screen is locked, in which case it doesn't work.
1826 if (until_idle_p || !si->locked_p)
1828 clientmessage_response (si, window, False,
1829 "RESTART ClientMessage received.",
1833 unblank_screen (si);
1834 kill_screenhack (si);
1835 XSync (si->dpy, False);
1838 restart_process (si); /* does not return */
1842 clientmessage_response (si, window, True,
1843 "RESTART ClientMessage received while locked.",
1844 "screen is locked.");
1846 else if (type == XA_DEMO)
1848 long arg = event->xclient.data.l[1];
1849 Bool demo_one_hack_p = (arg == 5000);
1851 if (demo_one_hack_p)
1855 long which = event->xclient.data.l[2];
1858 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1859 sprintf (buf2, "demoing (%ld).", which);
1860 clientmessage_response (si, window, False, buf, buf2);
1862 if (which < 0) which = 0; /* 0 == "random" */
1863 si->selection_mode = which;
1864 si->demoing_p = True;
1866 if (si->throttled_p && p->verbose_p)
1867 fprintf (stderr, "%s: unthrottled.\n", blurb());
1868 si->throttled_p = False;
1873 clientmessage_response (si, window, True,
1874 "DEMO ClientMessage received while active.",
1879 clientmessage_response (si, window, True,
1880 "obsolete form of DEMO ClientMessage.",
1881 "obsolete form of DEMO ClientMessage.");
1884 else if (type == XA_PREFS)
1886 clientmessage_response (si, window, True,
1887 "the PREFS client-message is obsolete.",
1888 "the PREFS client-message is obsolete.");
1890 else if (type == XA_LOCK)
1893 clientmessage_response (si, window, True,
1894 "not compiled with support for locking.",
1895 "locking not enabled.");
1896 #else /* !NO_LOCKING */
1897 if (si->locking_disabled_p)
1898 clientmessage_response (si, window, True,
1899 "LOCK ClientMessage received, but locking is disabled.",
1900 "locking not enabled.");
1901 else if (si->locked_p)
1902 clientmessage_response (si, window, True,
1903 "LOCK ClientMessage received while already locked.",
1908 char *response = (until_idle_p
1909 ? "activating and locking."
1911 sprintf (buf, "LOCK ClientMessage received; %s", response);
1912 clientmessage_response (si, window, False, buf, response);
1913 set_locked_p (si, True);
1914 si->selection_mode = 0;
1915 si->demoing_p = False;
1917 if (si->lock_id) /* we're doing it now, so lose the timeout */
1919 XtRemoveTimeOut (si->lock_id);
1925 if (si->using_mit_saver_extension ||
1926 si->using_sgi_saver_extension)
1928 XForceScreenSaver (si->dpy, ScreenSaverActive);
1937 #endif /* !NO_LOCKING */
1939 else if (type == XA_THROTTLE)
1941 /* The THROTTLE command is deprecated -- it predates the XDPMS
1942 extension. Instead of using -throttle, users should instead
1943 just power off the monitor (e.g., "xset dpms force off".)
1944 In a few minutes, xscreensaver will notice that the monitor
1945 is off, and cease running hacks.
1947 if (si->throttled_p)
1948 clientmessage_response (si, window, True,
1949 "THROTTLE ClientMessage received, but "
1950 "already throttled.",
1951 "already throttled.");
1955 char *response = "throttled.";
1956 si->throttled_p = True;
1957 si->selection_mode = 0;
1958 si->demoing_p = False;
1959 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
1960 clientmessage_response (si, window, False, buf, response);
1965 XtRemoveTimeOut (si->cycle_id);
1967 cycle_timer ((XtPointer) si, 0);
1971 else if (type == XA_UNTHROTTLE)
1973 if (! si->throttled_p)
1974 clientmessage_response (si, window, True,
1975 "UNTHROTTLE ClientMessage received, but "
1981 char *response = "unthrottled.";
1982 si->throttled_p = False;
1983 si->selection_mode = 0;
1984 si->demoing_p = False;
1985 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
1986 clientmessage_response (si, window, False, buf, response);
1991 XtRemoveTimeOut (si->cycle_id);
1993 cycle_timer ((XtPointer) si, 0);
2001 str = XGetAtomName_safe (si->dpy, type);
2005 if (strlen (str) > 80)
2006 strcpy (str+70, "...");
2007 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
2014 "unrecognised screensaver ClientMessage 0x%x received.",
2015 (unsigned int) event->xclient.data.l[0]);
2018 clientmessage_response (si, window, True, buf, buf);
2024 /* Some random diagnostics printed in -verbose mode.
2028 analyze_display (saver_info *si)
2032 const char *name; const char *desc; Bool useful_p;
2035 { "SCREEN_SAVER", /* underscore */ "SGI Screen-Saver",
2036 # ifdef HAVE_SGI_SAVER_EXTENSION
2041 }, { "SCREEN-SAVER", /* dash */ "SGI Screen-Saver",
2042 # ifdef HAVE_SGI_SAVER_EXTENSION
2047 }, { "MIT-SCREEN-SAVER", "MIT Screen-Saver",
2048 # ifdef HAVE_MIT_SAVER_EXTENSION
2053 }, { "XIDLE", "XIdle",
2054 # ifdef HAVE_XIDLE_EXTENSION
2059 }, { "SGI-VIDEO-CONTROL", "SGI Video-Control",
2060 # ifdef HAVE_SGI_VC_EXTENSION
2065 }, { "READDISPLAY", "SGI Read-Display",
2066 # ifdef HAVE_READ_DISPLAY_EXTENSION
2071 }, { "MIT-SHM", "Shared Memory",
2072 # ifdef HAVE_XSHM_EXTENSION
2077 }, { "DOUBLE-BUFFER", "Double-Buffering",
2078 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
2083 }, { "DPMS", "Power Management",
2084 # ifdef HAVE_DPMS_EXTENSION
2095 }, { "XFree86-VidModeExtension", "XF86 Video-Mode",
2096 # ifdef HAVE_XF86VMODE
2101 }, { "XINERAMA", "Xinerama",
2102 # ifdef HAVE_XINERAMA
2107 }, { "RANDR", "Resize-and-Rotate",
2113 }, { "Apple-DRI", "Apple-DRI (XDarwin)",
2118 fprintf (stderr, "%s: running on display \"%s\" (%d %sscreen%s).\n",
2120 DisplayString(si->dpy),
2122 (si->xinerama_p ? "Xinerama " : ""),
2123 (si->nscreens == 1 ? "" : "s"));
2124 fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
2125 ServerVendor(si->dpy), VendorRelease(si->dpy));
2127 fprintf (stderr, "%s: useful extensions:\n", blurb());
2128 for (i = 0; i < countof(exts); i++)
2130 int op = 0, event = 0, error = 0;
2133 if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
2135 sprintf (buf, "%s: ", blurb());
2137 strcat (buf, exts[i].desc);
2138 if (!exts[i].useful_p)
2141 while (strlen (buf) < k) strcat (buf, " ");
2142 strcat (buf, "<-- not supported at compile time!");
2144 fprintf (stderr, "%s\n", buf);
2147 for (i = 0; i < si->nscreens; i++)
2149 saver_screen_info *ssi = &si->screens[i];
2150 unsigned long colormapped_depths = 0;
2151 unsigned long non_mapped_depths = 0;
2152 XVisualInfo vi_in, *vi_out;
2155 if (!ssi->real_screen_p) continue;
2157 vi_in.screen = ssi->real_screen_number;
2158 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
2159 if (!vi_out) continue;
2160 for (j = 0; j < out_count; j++)
2161 if (vi_out[j].class == PseudoColor)
2162 colormapped_depths |= (1 << vi_out[j].depth);
2164 non_mapped_depths |= (1 << vi_out[j].depth);
2165 XFree ((char *) vi_out);
2167 if (colormapped_depths)
2169 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(),
2170 ssi->real_screen_number);
2171 for (j = 0; j < 32; j++)
2172 if (colormapped_depths & (1 << j))
2173 fprintf (stderr, " %d", j);
2174 fprintf (stderr, ".\n");
2176 if (non_mapped_depths)
2178 fprintf (stderr, "%s: screen %d non-colormapped depths:",
2179 blurb(), ssi->real_screen_number);
2180 for (j = 0; j < 32; j++)
2181 if (non_mapped_depths & (1 << j))
2182 fprintf (stderr, " %d", j);
2183 fprintf (stderr, ".\n");
2189 fprintf (stderr, "%s: Xinerama layout:\n", blurb());
2190 for (i = 0; i < si->nscreens; i++)
2192 saver_screen_info *ssi = &si->screens[i];
2193 fprintf (stderr, "%s: %c %d/%d: %dx%d+%d+%d\n",
2195 (ssi->real_screen_p ? '+' : ' '),
2196 ssi->number, ssi->real_screen_number,
2197 ssi->width, ssi->height, ssi->x, ssi->y);
2203 display_is_on_console_p (saver_info *si)
2205 Bool not_on_console = True;
2206 char *dpystr = DisplayString (si->dpy);
2207 char *tail = (char *) strchr (dpystr, ':');
2208 if (! tail || strncmp (tail, ":0", 2))
2209 not_on_console = True;
2212 char dpyname[255], localname[255];
2213 strncpy (dpyname, dpystr, tail-dpystr);
2214 dpyname [tail-dpystr] = 0;
2216 !strcmp(dpyname, "unix") ||
2217 !strcmp(dpyname, "localhost"))
2218 not_on_console = False;
2219 else if (gethostname (localname, sizeof (localname)))
2220 not_on_console = True; /* can't find hostname? */
2223 /* We have to call gethostbyname() on the result of gethostname()
2224 because the two aren't guarenteed to be the same name for the
2225 same host: on some losing systems, one is a FQDN and the other
2226 is not. Here in the wide wonderful world of Unix it's rocket
2227 science to obtain the local hostname in a portable fashion.
2229 And don't forget, gethostbyname() reuses the structure it
2230 returns, so we have to copy the fucker before calling it again.
2231 Thank you master, may I have another.
2233 struct hostent *h = gethostbyname (dpyname);
2235 not_on_console = True;
2240 strcpy (hn, h->h_name);
2241 l = gethostbyname (localname);
2242 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
2246 return !not_on_console;
2250 /* Do a little bit of heap introspection...
2253 check_for_leaks (const char *where)
2255 #if defined(HAVE_SBRK) && defined(LEAK_PARANOIA)
2256 static unsigned long last_brk = 0;
2257 int b = (unsigned long) sbrk(0);
2258 if (last_brk && last_brk < b)
2259 fprintf (stderr, "%s: %s: brk grew by %luK.\n",
2261 (((b - last_brk) + 1023) / 1024));
2263 #endif /* HAVE_SBRK */