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 * 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() */
155 #include <sys/types.h>
159 # include <X11/Xmu/Error.h>
161 # include <Xmu/Error.h>
163 #else /* !HAVE_XMU */
165 #endif /* !HAVE_XMU */
167 #ifdef HAVE_XIDLE_EXTENSION
168 # include <X11/extensions/xidle.h>
169 #endif /* HAVE_XIDLE_EXTENSION */
172 # include <X11/extensions/Xinerama.h>
173 #endif /* HAVE_XINERAMA */
175 #include "xscreensaver.h"
177 #include "yarandom.h"
178 #include "resources.h"
183 saver_info *global_si_kludge = 0; /* I hate C so much... */
190 static Atom XA_SCREENSAVER_RESPONSE;
191 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
192 static Atom XA_RESTART, XA_SELECT;
193 static Atom XA_THROTTLE, XA_UNTHROTTLE;
194 Atom XA_DEMO, XA_PREFS, XA_EXIT, XA_LOCK, XA_BLANK;
197 static XrmOptionDescRec options [] = {
199 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
200 { "-silent", ".verbose", XrmoptionNoArg, "off" },
202 /* xscreensaver-demo uses this one */
203 { "-nosplash", ".splash", XrmoptionNoArg, "off" },
204 { "-no-splash", ".splash", XrmoptionNoArg, "off" },
206 /* useful for debugging */
207 { "-no-capture-stderr", ".captureStderr", XrmoptionNoArg, "off" },
211 __extension__ /* shut up about "string length is greater than the length
212 ISO C89 compilers are required to support" when including
216 static char *defaults[] = {
217 #include "XScreenSaver_ad.h"
222 ERROR! You must not include vroot.h in this file.
226 do_help (saver_info *si)
231 xscreensaver %s, copyright (c) 1991-2008 by Jamie Zawinski <jwz@jwz.org>\n\
233 All xscreensaver configuration is via the `~/.xscreensaver' file.\n\
234 Rather than editing that file by hand, just run `xscreensaver-demo':\n\
235 that program lets you configure the screen saver graphically,\n\
236 including timeouts, locking, and display modes.\n\
240 Just getting started? Try this:\n\
245 For updates, online manual, and FAQ, please see the web page:\n\
247 http://www.jwz.org/xscreensaver/\n\
259 time_t now = time ((time_t *) 0);
260 char *str = (char *) ctime (&now);
261 char *nl = (char *) strchr (str, '\n');
262 if (nl) *nl = 0; /* take off that dang newline */
266 static Bool blurb_timestamp_p = False; /* kludge */
271 if (!blurb_timestamp_p)
275 static char buf[255];
276 char *ct = timestring();
277 int n = strlen(progname);
279 strncpy(buf, progname, n);
282 strncpy(buf+n, ct+11, 8);
283 strcpy(buf+n+9, ": ");
290 saver_ehandler (Display *dpy, XErrorEvent *error)
292 saver_info *si = global_si_kludge; /* I hate C so much... */
296 if (!real_stderr) real_stderr = stderr;
298 fprintf (real_stderr, "\n"
299 "#######################################"
300 "#######################################\n\n"
301 "%s: X Error! PLEASE REPORT THIS BUG.\n",
304 for (i = 0; i < si->nscreens; i++)
306 saver_screen_info *ssi = &si->screens[i];
307 fprintf (real_stderr, "%s: screen %d/%d: 0x%x, 0x%x, 0x%x\n",
308 blurb(), ssi->real_screen_number, ssi->number,
309 (unsigned int) RootWindowOfScreen (si->screens[i].screen),
310 (unsigned int) si->screens[i].real_vroot,
311 (unsigned int) si->screens[i].screensaver_window);
314 fprintf (real_stderr, "\n"
315 "#######################################"
316 "#######################################\n\n");
318 fatal_p = XmuPrintDefaultErrorMessage (dpy, error, real_stderr);
320 fatal_p = True; /* The only time I've ever seen a supposedly nonfatal error,
321 it has been BadImplementation / Xlib sequence lost, which
322 are in truth pretty damned fatal.
325 fprintf (real_stderr, "\n");
328 fprintf (real_stderr, "%s: nonfatal error.\n\n", blurb());
331 if (si->prefs.xsync_p)
333 saver_exit (si, -1, "because of synchronous X Error");
337 fprintf (real_stderr,
338 "#######################################"
339 "#######################################\n\n");
340 fprintf (real_stderr,
341 " If at all possible, please re-run xscreensaver with the command\n"
342 " line arguments `-sync -verbose -no-capture', and reproduce this\n"
343 " bug. That will cause xscreensaver to dump a `core' file to the\n"
344 " current directory. Please include the stack trace from that core\n"
345 " file in your bug report. *DO NOT* mail the core file itself!\n"
346 " That won't work.\n");
347 fprintf (real_stderr,
349 " http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
350 " the most useful bug reports, and how to examine core files.\n"
352 " The more information you can provide, the better. But please\n"
353 " report this bug, regardless!\n"
355 fprintf (real_stderr,
356 "#######################################"
357 "#######################################\n\n");
359 saver_exit (si, -1, 0);
367 /* This error handler is used only while the X connection is being set up;
368 after we've got a connection, we don't use this handler again. The only
369 reason for having this is so that we can present a more idiot-proof error
370 message than "cannot open display."
373 startup_ehandler (String name, String type, String class,
374 String defalt, /* one can't even spel properly
375 in this joke of a language */
376 String *av, Cardinal *ac)
380 saver_info *si = global_si_kludge; /* I hate C so much... */
381 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
383 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
384 fmt, sizeof(fmt)-1, *db);
386 fprintf (stderr, "%s: ", blurb());
388 memset (p, 0, sizeof(p));
389 if (*ac > countof (p)) *ac = countof (p);
390 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
391 fprintf (stderr, fmt, /* Did I mention that I hate C? */
392 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
393 fprintf (stderr, "\n");
395 describe_uids (si, stderr);
397 if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
399 fprintf (stderr, "\n"
400 "%s: This is probably because you're logging in as root. You\n"
401 " shouldn't log in as root: you should log in as a normal user,\n"
402 " and then `su' as needed. If you insist on logging in as\n"
403 " root, you will have to turn off X's security features before\n"
404 " xscreensaver will work.\n"
406 " Please read the manual and FAQ for more information:\n",
411 fprintf (stderr, "\n"
412 "%s: Errors at startup are usually authorization problems.\n"
413 " But you're not logging in as root (good!) so something\n"
414 " else must be wrong. Did you read the manual and the FAQ?\n",
418 fprintf (stderr, "\n"
419 " http://www.jwz.org/xscreensaver/faq.html\n"
420 " http://www.jwz.org/xscreensaver/man.html\n"
429 /* The zillions of initializations.
432 /* Set progname, version, etc. This is done very early.
435 set_version_string (saver_info *si, int *argc, char **argv)
437 progclass = "XScreenSaver";
439 /* progname is reset later, after we connect to X. */
440 progname = strrchr(argv[0], '/');
441 if (progname) progname++;
442 else progname = argv[0];
444 if (strlen(progname) > 100) /* keep it short. */
447 /* The X resource database blows up if argv[0] has a "." in it. */
450 while ((s = strchr (s, '.')))
454 si->version = (char *) malloc (5);
455 memcpy (si->version, screensaver_id + 17, 4);
460 /* Initializations that potentially take place as a priveleged user:
461 If the xscreensaver executable is setuid root, then these initializations
462 are run as root, before discarding privileges.
465 privileged_initialization (saver_info *si, int *argc, char **argv)
468 /* before hack_uid() for proper permissions */
469 lock_priv_init (*argc, argv, si->prefs.verbose_p);
470 #endif /* NO_LOCKING */
476 /* Figure out what locking mechanisms are supported.
479 lock_initialization (saver_info *si, int *argc, char **argv)
482 si->locking_disabled_p = True;
483 si->nolock_reason = "not compiled with locking support";
484 #else /* !NO_LOCKING */
486 /* Finish initializing locking, now that we're out of privileged code. */
487 if (! lock_init (*argc, argv, si->prefs.verbose_p))
489 si->locking_disabled_p = True;
490 si->nolock_reason = "error getting password";
493 /* If locking is currently enabled, but the environment indicates that
494 we have been launched as GDM's "Background" program, then disable
495 locking just in case.
497 if (!si->locking_disabled_p && getenv ("RUNNING_UNDER_GDM"))
499 si->locking_disabled_p = True;
500 si->nolock_reason = "running under GDM";
503 /* If the server is XDarwin (MacOS X) then disable locking.
504 (X grabs only affect X programs, so you can use Command-Tab
505 to bring any other Mac program to the front, e.g., Terminal.)
507 if (!si->locking_disabled_p)
509 int op = 0, event = 0, error = 0;
510 Bool macos_p = False;
513 /* Disable locking if *running* on Apple hardware, since we have no
514 reliable way to determine whether the server is running on MacOS.
515 Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
516 but I'm not really sure about that.
522 /* This extension exists on the Apple X11 server, but not
523 on earlier versions of the XDarwin server. */
524 macos_p = XQueryExtension (si->dpy, "Apple-DRI", &op, &event, &error);
528 si->locking_disabled_p = True;
529 si->nolock_reason = "Cannot lock securely on MacOS X";
533 #endif /* NO_LOCKING */
537 /* Open the connection to the X server, and intern our Atoms.
540 connect_to_server (saver_info *si, int *argc, char **argv)
542 Widget toplevel_shell;
545 char *d = getenv ("DISPLAY");
548 char *ndpy = strdup("DISPLAY=:0.0");
549 /* if (si->prefs.verbose_p) */ /* sigh, too early to test this... */
551 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
555 /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
556 glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
557 do not. So we must leak it (and/or the previous setting). Yay.
560 #endif /* HAVE_PUTENV */
562 XSetErrorHandler (saver_ehandler);
564 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
565 toplevel_shell = XtAppInitialize (&si->app, progclass,
566 options, XtNumber (options),
567 argc, argv, defaults, 0, 0);
568 XtAppSetErrorMsgHandler (si->app, 0);
570 si->dpy = XtDisplay (toplevel_shell);
571 si->prefs.db = XtDatabase (si->dpy);
572 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
574 if(strlen(progname) > 100) /* keep it short. */
577 db = si->prefs.db; /* resources.c needs this */
579 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
580 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
581 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
582 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
583 XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
584 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
586 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
587 XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False);
588 XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False);
589 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
590 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
591 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
592 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
593 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
594 XA_PREV = XInternAtom (si->dpy, "PREV", False);
595 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
596 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
597 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
598 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
599 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
600 XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
601 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
602 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
604 return toplevel_shell;
608 /* Handle the command-line arguments that were not handled for us by Xt.
609 Issue an error message and exit if there are unknown options.
612 process_command_line (saver_info *si, int *argc, char **argv)
615 for (i = 1; i < *argc; i++)
617 if (!strcmp (argv[i], "-debug"))
618 /* no resource for this one, out of paranoia. */
619 si->prefs.debug_p = True;
621 else if (!strcmp (argv[i], "-h") ||
622 !strcmp (argv[i], "-help") ||
623 !strcmp (argv[i], "--help"))
628 const char *s = argv[i];
629 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
632 if (s[0] == '-' && s[1] == '-') s++;
633 if (!strcmp (s, "-activate") ||
634 !strcmp (s, "-deactivate") ||
635 !strcmp (s, "-cycle") ||
636 !strcmp (s, "-next") ||
637 !strcmp (s, "-prev") ||
638 !strcmp (s, "-exit") ||
639 !strcmp (s, "-restart") ||
640 !strcmp (s, "-demo") ||
641 !strcmp (s, "-prefs") ||
642 !strcmp (s, "-preferences") ||
643 !strcmp (s, "-lock") ||
644 !strcmp (s, "-version") ||
645 !strcmp (s, "-time"))
648 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
649 fprintf (stderr, "\n\
650 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
652 fprintf (stderr, "\n\
653 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
656 The `xscreensaver' program is a daemon that runs in the background.\n\
657 You control a running xscreensaver process by sending it messages\n\
658 with `xscreensaver-demo' or `xscreensaver-command'.\n\
659 . See the man pages for details, or check the web page:\n\
660 http://www.jwz.org/xscreensaver/\n\n");
662 /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
663 suggest that explicitly. */
664 if (!strcmp (s, "-lock"))
666 Or perhaps you meant either the \"-lock-mode\" or the\n\
667 \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
676 /* Print out the xscreensaver banner to the tty if applicable;
677 Issue any other warnings that are called for at this point.
680 print_banner (saver_info *si)
682 saver_preferences *p = &si->prefs;
684 /* This resource gets set some time before the others, so that we know
685 whether to print the banner (and so that the banner gets printed before
686 any resource-database-related error messages.)
688 p->verbose_p = (p->debug_p ||
689 get_boolean_resource (si->dpy, "verbose", "Boolean"));
691 /* Ditto, for the locking_disabled_p message. */
692 p->lock_p = get_boolean_resource (si->dpy, "lock", "Boolean");
696 "%s %s, copyright (c) 1991-2008 "
697 "by Jamie Zawinski <jwz@jwz.org>.\n",
698 progname, si->version);
701 fprintf (stderr, "\n"
702 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
704 "\tNote that in debug mode, the xscreensaver window will only\n"
705 "\tcover the left half of the screen. (The idea is that you\n"
706 "\tcan still see debugging output in a shell, if you position\n"
707 "\tit on the right side of the screen.)\n"
709 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
710 "\tuntrusted environments.\n"
716 if (!si->uid_message || !*si->uid_message)
717 describe_uids (si, stderr);
720 if (si->orig_uid && *si->orig_uid)
721 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
722 blurb(), si->orig_uid);
723 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
726 fprintf (stderr, "%s: in process %lu.\n", blurb(),
727 (unsigned long) getpid());
732 print_lock_failure_banner (saver_info *si)
734 saver_preferences *p = &si->prefs;
736 /* If locking was not able to be initalized for some reason, explain why.
737 (This has to be done after we've read the lock_p resource.)
739 if (si->locking_disabled_p)
742 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
744 if (strstr (si->nolock_reason, "passw"))
745 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
746 "consult the manual.\n", blurb());
747 else if (strstr (si->nolock_reason, "running as "))
749 "%s: locking only works when xscreensaver is launched\n"
750 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
751 "\t See the manual for details.\n",
761 screens_overlap_p (XineramaScreenInfo *a, XineramaScreenInfo *b)
763 /* Two rectangles overlap if the max of the tops is less than the
764 min of the bottoms and the max of the lefts is less than the min
769 # define MAX(A,B) ((A)>(B)?(A):(B))
770 # define MIN(A,B) ((A)<(B)?(A):(B))
772 int maxleft = MAX(a->x_org, b->x_org);
773 int maxtop = MAX(a->y_org, b->y_org);
774 int minright = MIN(a->x_org + a->width - 1, b->x_org + b->width);
775 int minbot = MIN(a->y_org + a->height - 1, b->y_org + b->height);
776 return (maxtop < minbot && maxleft < minright);
780 /* Go through the list of Xinerama screen descriptions, and mark the
781 ones that appear to be insane, so that we don't use them.
784 check_xinerama_sanity (int count, Bool verbose_p, XineramaScreenInfo *xsi)
786 static Bool printed_p = False;
791 # define X1 xsi[i].x_org
792 # define X2 xsi[j].x_org
793 # define Y1 xsi[i].y_org
794 # define Y2 xsi[j].y_org
795 # define W1 xsi[i].width
796 # define W2 xsi[j].width
797 # define H1 xsi[i].height
798 # define H2 xsi[j].height
800 # define WHINE() do { \
803 fprintf (stderr, "%s: compensating for Xinerama braindamage:\n", \
807 fprintf (stderr, "%s: %d: %s\n", blurb(), xsi[i].screen_number,err); \
809 xsi[i].screen_number = -1; \
812 /* If a screen is enclosed by any other screen, that's insane.
814 for (i = 0; i < count; i++)
815 for (j = 0; j < count; j++)
817 xsi[i].screen_number >= 0 &&
818 xsi[j].screen_number >= 0 &&
819 X1 >= X2 && Y1 >= Y2 && (X1+W1) <= (X2+W2) && (X1+H1) <= (X2+H2))
821 sprintf (err, "%dx%d+%d+%d enclosed by %dx%d+%d+%d",
828 /* After checking for enclosure, check for other lossage against earlier
829 screens. We do enclosure first so that we make sure to pick the
832 for (i = 0; i < count; i++)
833 for (j = 0; j < i; j++)
835 if (xsi[i].screen_number < 0) continue; /* already marked */
838 if (X1 == X2 && Y1 == Y2 && W1 == W2 && H1 == H2)
839 sprintf (err, "%dx%d+%d+%d duplicated", W1, H1, X1, Y1);
841 else if (screens_overlap_p (&xsi[i], &xsi[j]))
842 sprintf (err, "%dx%d+%d+%d overlaps %dx%d+%d+%d",
859 #endif /* HAVE_XINERAMA */
863 /* Examine all of the display's screens, and populate the `saver_screen_info'
864 structures. Make sure this is called after hack_environment() sets $PATH.
867 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
869 Bool found_any_writable_cells = False;
872 # ifdef HAVE_XINERAMA
875 si->xinerama_p = (XineramaQueryExtension (si->dpy, &event, &error) &&
876 XineramaIsActive (si->dpy));
879 if (si->xinerama_p && ScreenCount (si->dpy) != 1)
881 si->xinerama_p = False;
882 if (si->prefs.verbose_p)
884 "%s: Xinerama AND %d screens? Disabling Xinerama support!\n",
885 blurb(), ScreenCount(si->dpy));
891 XineramaScreenInfo *xsi = XineramaQueryScreens (si->dpy, &nscreens);
893 si->xinerama_p = False;
897 si->screens = (saver_screen_info *)
898 calloc(sizeof(saver_screen_info), nscreens);
899 check_xinerama_sanity (nscreens, si->prefs.verbose_p, xsi);
900 for (i = 0; i < nscreens; i++)
902 if (xsi[i].screen_number < 0) /* deemed insane */
904 si->screens[j].x = xsi[i].x_org;
905 si->screens[j].y = xsi[i].y_org;
906 si->screens[j].width = xsi[i].width;
907 si->screens[j].height = xsi[i].height;
913 si->default_screen = &si->screens[0];
914 si->default_screen->real_screen_p = True;
916 # endif /* !HAVE_XINERAMA */
920 si->nscreens = ScreenCount(si->dpy);
921 si->screens = (saver_screen_info *)
922 calloc(sizeof(saver_screen_info), si->nscreens);
923 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
925 for (i = 0; i < si->nscreens; i++)
927 saver_screen_info *ssi = &si->screens[i];
928 ssi->width = DisplayWidth (si->dpy, i);
929 ssi->height = DisplayHeight (si->dpy, i);
930 ssi->real_screen_p = True;
931 ssi->real_screen_number = i;
937 /* In "quad mode", we use the Xinerama code to pretend that there are 4
938 screens for every physical screen, and run four times as many hacks...
940 if (si->prefs.quad_p)
942 int ns2 = si->nscreens * 4;
943 saver_screen_info *ssi2 = (saver_screen_info *)
944 calloc(sizeof(saver_screen_info), ns2);
946 for (i = 0; i < si->nscreens; i++)
948 saver_screen_info *old = &si->screens[i];
950 if (si->prefs.debug_p) old->width = old->width / 2;
957 ssi2[i*4 ].width /= 2;
958 ssi2[i*4 ].height /= 2;
960 ssi2[i*4+1].x += ssi2[i*4 ].width;
961 ssi2[i*4+1].width -= ssi2[i*4 ].width;
962 ssi2[i*4+1].height /= 2;
964 ssi2[i*4+2].y += ssi2[i*4 ].height;
965 ssi2[i*4+2].width /= 2;
966 ssi2[i*4+2].height -= ssi2[i*4 ].height;
968 ssi2[i*4+3].x += ssi2[i*4+2].width;
969 ssi2[i*4+3].y += ssi2[i*4+2].height;
970 ssi2[i*4+3].width -= ssi2[i*4+2].width;
971 ssi2[i*4+3].height -= ssi2[i*4+2].height;
973 ssi2[i*4+1].real_screen_p = False;
974 ssi2[i*4+2].real_screen_p = False;
975 ssi2[i*4+3].real_screen_p = False;
981 si->default_screen = &si->screens[DefaultScreen(si->dpy) * 4];
982 si->xinerama_p = True;
984 # endif /* QUAD_MODE */
986 /* finish initializing the screens.
988 for (i = 0; i < si->nscreens; i++)
990 saver_screen_info *ssi = &si->screens[i];
994 ssi->screen = ScreenOfDisplay (si->dpy, ssi->real_screen_number);
995 ssi->poll_mouse_last_root_x = -1;
996 ssi->poll_mouse_last_root_y = -1;
1000 ssi->width = WidthOfScreen (ssi->screen);
1001 ssi->height = HeightOfScreen (ssi->screen);
1004 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
1005 ssi->default_visual =
1006 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
1008 ssi->current_visual = ssi->default_visual;
1009 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
1011 /* Execute a subprocess to find the GL visual. */
1012 ssi->best_gl_visual = get_best_gl_visual (ssi);
1014 if (ssi == si->default_screen)
1015 /* Since this is the default screen, use the one already created. */
1016 ssi->toplevel_shell = toplevel_shell;
1018 /* Otherwise, each screen must have its own unmapped root widget. */
1019 ssi->toplevel_shell =
1020 XtVaAppCreateShell (progname, progclass, applicationShellWidgetClass,
1022 XtNscreen, ssi->screen,
1023 XtNvisual, ssi->current_visual,
1024 XtNdepth, visual_depth (ssi->screen,
1025 ssi->current_visual),
1028 if (! found_any_writable_cells)
1030 /* Check to see whether fading is ever possible -- if any of the
1031 screens on the display has a PseudoColor visual, then fading can
1032 work (on at least some screens.) If no screen has a PseudoColor
1033 visual, then don't bother ever trying to fade, because it will
1034 just cause a delay without causing any visible effect.
1036 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
1037 get_visual (ssi->screen, "PseudoColor", True, False) ||
1038 get_visual (ssi->screen, "GrayScale", True, False))
1039 found_any_writable_cells = True;
1043 si->fading_possible_p = found_any_writable_cells;
1045 #ifdef HAVE_XF86VMODE_GAMMA
1046 si->fading_possible_p = True; /* if we can gamma fade, go for it */
1051 /* If any server extensions have been requested, try and initialize them.
1052 Issue warnings if requests can't be honored.
1055 initialize_server_extensions (saver_info *si)
1057 saver_preferences *p = &si->prefs;
1059 Bool server_has_xidle_extension_p = False;
1060 Bool server_has_sgi_saver_extension_p = False;
1061 Bool server_has_mit_saver_extension_p = False;
1062 Bool system_has_proc_interrupts_p = False;
1063 const char *piwhy = 0;
1065 si->using_xidle_extension = p->use_xidle_extension;
1066 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
1067 si->using_mit_saver_extension = p->use_mit_saver_extension;
1068 si->using_proc_interrupts = p->use_proc_interrupts;
1070 #ifdef HAVE_XIDLE_EXTENSION
1071 server_has_xidle_extension_p = query_xidle_extension (si);
1073 #ifdef HAVE_SGI_SAVER_EXTENSION
1074 server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
1076 #ifdef HAVE_MIT_SAVER_EXTENSION
1077 server_has_mit_saver_extension_p = query_mit_saver_extension (si);
1079 #ifdef HAVE_PROC_INTERRUPTS
1080 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
1083 if (!server_has_xidle_extension_p)
1084 si->using_xidle_extension = False;
1085 else if (p->verbose_p)
1087 if (si->using_xidle_extension)
1088 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
1090 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
1093 if (!server_has_sgi_saver_extension_p)
1094 si->using_sgi_saver_extension = False;
1095 else if (p->verbose_p)
1097 if (si->using_sgi_saver_extension)
1098 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
1101 "%s: not using server's SGI SCREEN_SAVER extension.\n",
1105 if (!server_has_mit_saver_extension_p)
1106 si->using_mit_saver_extension = False;
1107 else if (p->verbose_p)
1109 if (si->using_mit_saver_extension)
1110 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
1114 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
1118 /* These are incompatible (or at least, our support for them is...) */
1119 if (si->xinerama_p && si->using_mit_saver_extension)
1121 si->using_mit_saver_extension = False;
1123 fprintf (stderr, "%s: Xinerama in use: disabling MIT-SCREEN-SAVER.\n",
1128 query_randr_extension (si);
1131 if (!system_has_proc_interrupts_p)
1133 si->using_proc_interrupts = False;
1134 if (p->verbose_p && piwhy)
1135 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
1138 else if (p->verbose_p)
1140 if (si->using_proc_interrupts)
1142 "%s: consulting /proc/interrupts for keyboard activity.\n",
1146 "%s: not consulting /proc/interrupts for keyboard activity.\n",
1152 /* For the case where we aren't using an server extensions, select user events
1153 on all the existing windows, and launch timers to select events on
1154 newly-created windows as well.
1156 If a server extension is being used, this does nothing.
1159 select_events (saver_info *si)
1161 saver_preferences *p = &si->prefs;
1164 if (si->using_xidle_extension ||
1165 si->using_mit_saver_extension ||
1166 si->using_sgi_saver_extension)
1169 if (p->initial_delay)
1173 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
1174 (int) p->initial_delay/1000,
1175 (p->initial_delay == 1000 ? "" : "s"));
1179 usleep (p->initial_delay);
1181 fprintf (stderr, " done.\n");
1186 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
1191 /* Select events on the root windows of every screen. This also selects
1192 for window creation events, so that new subwindows will be noticed.
1194 for (i = 0; i < si->nscreens; i++)
1196 saver_screen_info *ssi = &si->screens[i];
1197 if (ssi->real_screen_p)
1198 start_notice_events_timer (si,
1199 RootWindowOfScreen (si->screens[i].screen), False);
1203 fprintf (stderr, " done.\n");
1208 maybe_reload_init_file (saver_info *si)
1210 saver_preferences *p = &si->prefs;
1211 if (init_file_changed_p (p))
1214 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
1215 blurb(), init_file_name());
1217 load_init_file (si->dpy, p);
1219 /* If a server extension is in use, and p->timeout has changed,
1220 we need to inform the server of the new timeout. */
1221 disable_builtin_screensaver (si, False);
1223 /* If the DPMS settings in the init file have changed,
1224 change the settings on the server to match. */
1225 sync_server_dpms_settings (si->dpy,
1226 (p->dpms_enabled_p &&
1227 p->mode != DONT_BLANK),
1228 p->dpms_standby / 1000,
1229 p->dpms_suspend / 1000,
1238 - wait until the user is idle;
1240 - wait until the user is active;
1241 - unblank the screen;
1246 main_loop (saver_info *si)
1248 saver_preferences *p = &si->prefs;
1253 Bool was_locked = False;
1256 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1258 check_for_leaks ("unblanked A");
1259 sleep_until_idle (si, True);
1260 check_for_leaks ("unblanked B");
1265 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
1266 si->selection_mode, timestring());
1268 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
1272 maybe_reload_init_file (si);
1274 if (p->mode == DONT_BLANK)
1277 fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
1278 blurb(), timestring());
1280 /* Go around the loop and wait for the next bout of idleness,
1281 or for the init file to change, or for a remote command to
1282 come in, or something.
1284 But, if locked_p is true, go ahead. This can only happen
1285 if we're in "disabled" mode but a "lock" clientmessage came
1286 in: in that case, we should go ahead and blank/lock the screen.
1292 /* Since we're about to blank the screen, kill the de-race timer,
1293 if any. It might still be running if we have unblanked and then
1294 re-blanked in a short period (e.g., when using the "next" button
1295 in xscreensaver-demo.)
1300 fprintf (stderr, "%s: stopping de-race timer (%d remaining.)\n",
1301 blurb(), si->de_race_ticks);
1302 XtRemoveTimeOut (si->de_race_id);
1307 /* Now, try to blank.
1310 if (! blank_screen (si))
1312 /* We were unable to grab either the keyboard or mouse.
1313 This means we did not (and must not) blank the screen.
1314 If we were to blank the screen while some other program
1315 is holding both the mouse and keyboard grabbed, then
1316 we would never be able to un-blank it! We would never
1317 see any events, and the display would be wedged.
1319 So, just go around the loop again and wait for the
1320 next bout of idleness. (If the user remains idle, we
1321 will next try to blank the screen again in no more than
1324 Time retry = 60 * 1000;
1325 if (p->timeout < retry)
1331 "%s: DEBUG MODE: unable to grab -- BLANKING ANYWAY.\n",
1337 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
1340 schedule_wakeup_event (si, retry, p->debug_p);
1345 kill_screenhack (si);
1347 if (!si->throttled_p)
1348 spawn_screenhack (si, True);
1349 else if (p->verbose_p)
1350 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
1352 /* Don't start the cycle timer in demo mode. */
1353 if (!si->demoing_p && p->cycle)
1354 si->cycle_id = XtAppAddTimeOut (si->app,
1356 /* see comment in cycle_timer() */
1364 /* Maybe start locking the screen.
1367 Time lock_timeout = p->lock_timeout;
1369 if (si->emergency_lock_p && p->lock_p && lock_timeout)
1371 int secs = p->lock_timeout / 1000;
1374 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1376 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1380 si->emergency_lock_p = False;
1382 if (!si->demoing_p && /* if not going into demo mode */
1383 p->lock_p && /* and locking is enabled */
1384 !si->locking_disabled_p && /* and locking is possible */
1385 lock_timeout == 0) /* and locking is not timer-deferred */
1386 set_locked_p (si, True); /* then lock right now. */
1388 /* locked_p might be true already because of the above, or because of
1389 the LOCK ClientMessage. But if not, and if we're supposed to lock
1390 after some time, set up a timer to do so.
1395 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1396 activate_lock_timer,
1399 #endif /* !NO_LOCKING */
1402 ok_to_unblank = True;
1405 check_for_leaks ("blanked A");
1406 sleep_until_idle (si, False); /* until not idle */
1407 check_for_leaks ("blanked B");
1409 maybe_reload_init_file (si);
1412 /* Maybe unlock the screen.
1416 saver_screen_info *ssi = si->default_screen;
1417 if (si->locking_disabled_p) abort ();
1420 si->dbox_up_p = True;
1421 suspend_screenhack (si, True);
1422 XUndefineCursor (si->dpy, ssi->screensaver_window);
1424 ok_to_unblank = unlock_p (si);
1426 si->dbox_up_p = False;
1427 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1428 suspend_screenhack (si, False); /* resume */
1430 if (!ok_to_unblank &&
1431 !screenhack_running_p (si))
1433 /* If the lock dialog has been dismissed and we're not about to
1434 unlock the screen, and there is currently no hack running,
1435 then launch one. (There might be no hack running if DPMS
1436 had kicked in. But DPMS is off now, so bring back the hack)
1439 XtRemoveTimeOut (si->cycle_id);
1441 cycle_timer ((XtPointer) si, 0);
1444 #endif /* !NO_LOCKING */
1446 } while (!ok_to_unblank);
1450 fprintf (stderr, "%s: unblanking screen at %s.\n",
1451 blurb(), timestring ());
1453 /* Kill before unblanking, to stop drawing as soon as possible. */
1454 kill_screenhack (si);
1455 unblank_screen (si);
1457 set_locked_p (si, False);
1458 si->emergency_lock_p = False;
1460 si->selection_mode = 0;
1462 /* If we're throttled, and the user has explicitly unlocked the screen,
1463 then unthrottle. If we weren't locked, then don't unthrottle
1464 automatically, because someone might have just bumped the desk... */
1467 if (si->throttled_p && p->verbose_p)
1468 fprintf (stderr, "%s: unthrottled.\n", blurb());
1469 si->throttled_p = False;
1474 XtRemoveTimeOut (si->cycle_id);
1480 XtRemoveTimeOut (si->lock_id);
1484 /* Since we're unblanked now, break race conditions and make
1485 sure we stay that way (see comment in timers.c.) */
1486 if (! si->de_race_id)
1487 de_race_timer ((XtPointer) si, 0);
1491 static void analyze_display (saver_info *si);
1492 static void fix_fds (void);
1495 main (int argc, char **argv)
1499 saver_info *si = &the_si;
1500 saver_preferences *p = &si->prefs;
1501 struct passwd *spasswd;
1504 memset(si, 0, sizeof(*si));
1505 global_si_kludge = si; /* I hate C so much... */
1509 # undef ya_rand_init
1512 save_argv (argc, argv);
1513 set_version_string (si, &argc, argv);
1514 privileged_initialization (si, &argc, argv);
1515 hack_environment (si);
1517 spasswd = getpwuid(getuid());
1520 fprintf(stderr, "Could not figure out who the current user is!\n");
1524 si->user = strdup(spasswd->pw_name ? spasswd->pw_name : "(unknown)");
1527 si->unlock_cb = gui_auth_conv;
1528 si->auth_finished_cb = auth_finished_cb;
1529 # endif /* !NO_LOCKING */
1531 shell = connect_to_server (si, &argc, argv);
1532 process_command_line (si, &argc, argv);
1535 load_init_file(si->dpy, p); /* must be before initialize_per_screen_info() */
1536 blurb_timestamp_p = p->timestamp_p; /* kludge */
1537 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1539 /* We can only issue this warning now. */
1540 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1542 "%s: there are no PseudoColor or GrayScale visuals.\n"
1543 "%s: ignoring the request for fading/unfading.\n",
1546 for (i = 0; i < si->nscreens; i++)
1548 saver_screen_info *ssi = &si->screens[i];
1549 if (ssi->real_screen_p)
1550 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1554 lock_initialization (si, &argc, argv);
1555 print_lock_failure_banner (si);
1557 if (p->xsync_p) XSynchronize (si->dpy, True);
1559 if (p->verbose_p) analyze_display (si);
1560 initialize_server_extensions (si);
1562 si->blank_time = time ((time_t) 0); /* must be before ..._window */
1563 initialize_screensaver_window (si);
1568 disable_builtin_screensaver (si, True);
1569 sync_server_dpms_settings (si->dpy,
1570 (p->dpms_enabled_p &&
1571 p->mode != DONT_BLANK),
1572 p->dpms_standby / 1000,
1573 p->dpms_suspend / 1000,
1577 initialize_stderr (si);
1578 handle_signals (si);
1580 make_splash_dialog (si);
1582 main_loop (si); /* doesn't return */
1589 /* Bad Things Happen if stdin, stdout, and stderr have been closed
1590 (as by the `sh incantation "xscreensaver >&- 2>&-"). When you do
1591 that, the X connection gets allocated to one of these fds, and
1592 then some random library writes to stderr, and random bits get
1593 stuffed down the X pipe, causing "Xlib: sequence lost" errors.
1594 So, we cause the first three file descriptors to be open to
1595 /dev/null if they aren't open to something else already. This
1596 must be done before any other files are opened (or the closing
1597 of that other file will again free up one of the "magic" first
1600 We do this by opening /dev/null three times, and then closing
1601 those fds, *unless* any of them got allocated as #0, #1, or #2,
1602 in which case we leave them open. Gag.
1604 Really, this crap is technically required of *every* X program,
1605 if you want it to be robust in the face of "2>&-".
1607 int fd0 = open ("/dev/null", O_RDWR);
1608 int fd1 = open ("/dev/null", O_RDWR);
1609 int fd2 = open ("/dev/null", O_RDWR);
1610 if (fd0 > 2) close (fd0);
1611 if (fd1 > 2) close (fd1);
1612 if (fd2 > 2) close (fd2);
1617 /* Processing ClientMessage events.
1621 static Bool error_handler_hit_p = False;
1624 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1626 error_handler_hit_p = True;
1630 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1631 them. We only look up the atom names for printing warning messages,
1632 so don't bomb out when it happens...
1635 XGetAtomName_safe (Display *dpy, Atom atom)
1638 XErrorHandler old_handler;
1639 if (!atom) return 0;
1642 error_handler_hit_p = False;
1643 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1644 result = XGetAtomName (dpy, atom);
1646 XSetErrorHandler (old_handler);
1648 if (error_handler_hit_p) result = 0;
1655 sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
1656 return strdup (buf);
1662 clientmessage_response (saver_info *si, Window w, Bool error,
1663 const char *stderr_msg,
1664 const char *protocol_msg)
1668 saver_preferences *p = &si->prefs;
1669 XErrorHandler old_handler;
1671 if (error || p->verbose_p)
1672 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1674 L = strlen(protocol_msg);
1675 proto = (char *) malloc (L + 2);
1676 proto[0] = (error ? '-' : '+');
1677 strcpy (proto+1, protocol_msg);
1680 /* Ignore all X errors while sending a response to a ClientMessage.
1681 Pretty much the only way we could get an error here is if the
1682 window we're trying to send the reply on has been deleted, in
1683 which case, the sender of the ClientMessage won't see our response
1686 XSync (si->dpy, False);
1687 error_handler_hit_p = False;
1688 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1690 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1691 PropModeReplace, (unsigned char *) proto, L);
1693 XSync (si->dpy, False);
1694 XSetErrorHandler (old_handler);
1695 XSync (si->dpy, False);
1702 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1704 #if 0 /* Oh, fuck it. GNOME likes to spew random ClientMessages at us
1705 all the time. This is presumably indicative of an error in
1706 the sender of that ClientMessage: if we're getting it and
1707 ignoring it, then it's not reaching the intended recipient.
1708 But people complain to me about this all the time ("waaah!
1709 xscreensaver is printing to it's stderr and that gets my
1710 panties all in a bunch!") And I'm sick of hearing about it.
1711 So we'll just ignore these messages and let GNOME go right
1712 ahead and continue to stumble along in its malfunction.
1715 saver_preferences *p = &si->prefs;
1716 char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1717 Window w = event->xclient.window;
1720 Bool root_p = False;
1723 for (screen = 0; screen < si->nscreens; screen++)
1724 if (w == si->screens[screen].screensaver_window)
1726 strcpy (wdesc, "xscreensaver");
1729 else if (w == RootWindow (si->dpy, screen))
1731 strcpy (wdesc, "root");
1736 /* If this ClientMessage was sent to the real root window instead of to the
1737 xscreensaver window, then it might be intended for someone else who is
1738 listening on the root window (e.g., the window manager). So only print
1739 the warning if: we are in debug mode; or if the bogus message was
1740 actually sent to one of the xscreensaver-created windows.
1742 if (root_p && !p->debug_p)
1747 XErrorHandler old_handler;
1749 XWindowAttributes xgwa;
1750 memset (&hint, 0, sizeof(hint));
1751 memset (&xgwa, 0, sizeof(xgwa));
1753 XSync (si->dpy, False);
1754 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1755 XGetClassHint (si->dpy, w, &hint);
1756 XGetWindowAttributes (si->dpy, w, &xgwa);
1757 XSync (si->dpy, False);
1758 XSetErrorHandler (old_handler);
1759 XSync (si->dpy, False);
1761 screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1763 sprintf (wdesc, "%.20s / %.20s",
1764 (hint.res_name ? hint.res_name : "(null)"),
1765 (hint.res_class ? hint.res_class : "(null)"));
1766 if (hint.res_name) XFree (hint.res_name);
1767 if (hint.res_class) XFree (hint.res_class);
1770 fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1771 blurb(), screen, (str ? str : "(null)"));
1772 fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1773 blurb(), screen, (unsigned long) w, wdesc);
1774 if (str) XFree (str);
1781 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1783 saver_preferences *p = &si->prefs;
1785 Window window = event->xclient.window;
1787 /* Preferences might affect our handling of client messages. */
1788 maybe_reload_init_file (si);
1790 if (event->xclient.message_type != XA_SCREENSAVER ||
1791 event->xclient.format != 32)
1793 bogus_clientmessage_warning (si, event);
1797 type = event->xclient.data.l[0];
1798 if (type == XA_ACTIVATE)
1802 if (p->mode == DONT_BLANK)
1804 clientmessage_response(si, window, True,
1805 "ACTIVATE ClientMessage received in DONT_BLANK mode.",
1806 "screen blanking is currently disabled.");
1810 clientmessage_response(si, window, False,
1811 "ACTIVATE ClientMessage received.",
1813 si->selection_mode = 0;
1814 si->demoing_p = False;
1816 if (si->throttled_p && p->verbose_p)
1817 fprintf (stderr, "%s: unthrottled.\n", blurb());
1818 si->throttled_p = False;
1820 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1822 XForceScreenSaver (si->dpy, ScreenSaverActive);
1830 clientmessage_response(si, window, True,
1831 "ClientMessage ACTIVATE received while already active.",
1834 else if (type == XA_DEACTIVATE)
1838 if (si->throttled_p && p->verbose_p)
1839 fprintf (stderr, "%s: unthrottled.\n", blurb());
1840 si->throttled_p = False;
1842 clientmessage_response(si, window, False,
1843 "DEACTIVATE ClientMessage received.",
1845 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1847 XForceScreenSaver (si->dpy, ScreenSaverReset);
1855 clientmessage_response(si, window, False,
1856 "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
1857 "not active: idle timer reset.");
1860 else if (type == XA_CYCLE)
1864 clientmessage_response(si, window, False,
1865 "CYCLE ClientMessage received.",
1867 si->selection_mode = 0; /* 0 means randomize when its time. */
1868 si->demoing_p = False;
1870 if (si->throttled_p && p->verbose_p)
1871 fprintf (stderr, "%s: unthrottled.\n", blurb());
1872 si->throttled_p = False;
1875 XtRemoveTimeOut (si->cycle_id);
1877 cycle_timer ((XtPointer) si, 0);
1880 clientmessage_response(si, window, True,
1881 "ClientMessage CYCLE received while inactive.",
1884 else if (type == XA_NEXT || type == XA_PREV)
1886 clientmessage_response(si, window, False,
1888 ? "NEXT ClientMessage received."
1889 : "PREV ClientMessage received."),
1891 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1892 si->demoing_p = False;
1894 if (si->throttled_p && p->verbose_p)
1895 fprintf (stderr, "%s: unthrottled.\n", blurb());
1896 si->throttled_p = False;
1901 XtRemoveTimeOut (si->cycle_id);
1903 cycle_timer ((XtPointer) si, 0);
1908 else if (type == XA_SELECT)
1912 long which = event->xclient.data.l[1];
1914 if (p->mode == DONT_BLANK)
1916 clientmessage_response(si, window, True,
1917 "SELECT ClientMessage received in DONT_BLANK mode.",
1918 "screen blanking is currently disabled.");
1922 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1923 sprintf (buf2, "activating (%ld).", which);
1924 clientmessage_response (si, window, False, buf, buf2);
1926 if (which < 0) which = 0; /* 0 == "random" */
1927 si->selection_mode = which;
1928 si->demoing_p = False;
1930 if (si->throttled_p && p->verbose_p)
1931 fprintf (stderr, "%s: unthrottled.\n", blurb());
1932 si->throttled_p = False;
1937 XtRemoveTimeOut (si->cycle_id);
1939 cycle_timer ((XtPointer) si, 0);
1944 else if (type == XA_EXIT)
1946 /* Ignore EXIT message if the screen is locked. */
1947 if (until_idle_p || !si->locked_p)
1949 clientmessage_response (si, window, False,
1950 "EXIT ClientMessage received.",
1954 unblank_screen (si);
1955 kill_screenhack (si);
1956 XSync (si->dpy, False);
1958 saver_exit (si, 0, 0);
1961 clientmessage_response (si, window, True,
1962 "EXIT ClientMessage received while locked.",
1963 "screen is locked.");
1965 else if (type == XA_RESTART)
1967 /* The RESTART message works whether the screensaver is active or not,
1968 unless the screen is locked, in which case it doesn't work.
1970 if (until_idle_p || !si->locked_p)
1972 clientmessage_response (si, window, False,
1973 "RESTART ClientMessage received.",
1977 unblank_screen (si);
1978 kill_screenhack (si);
1979 XSync (si->dpy, False);
1982 restart_process (si); /* does not return */
1986 clientmessage_response (si, window, True,
1987 "RESTART ClientMessage received while locked.",
1988 "screen is locked.");
1990 else if (type == XA_DEMO)
1992 long arg = event->xclient.data.l[1];
1993 Bool demo_one_hack_p = (arg == 5000);
1995 if (demo_one_hack_p)
1999 long which = event->xclient.data.l[2];
2002 sprintf (buf, "DEMO %ld ClientMessage received.", which);
2003 sprintf (buf2, "demoing (%ld).", which);
2004 clientmessage_response (si, window, False, buf, buf2);
2006 if (which < 0) which = 0; /* 0 == "random" */
2007 si->selection_mode = which;
2008 si->demoing_p = True;
2010 if (si->throttled_p && p->verbose_p)
2011 fprintf (stderr, "%s: unthrottled.\n", blurb());
2012 si->throttled_p = False;
2017 clientmessage_response (si, window, True,
2018 "DEMO ClientMessage received while active.",
2023 clientmessage_response (si, window, True,
2024 "obsolete form of DEMO ClientMessage.",
2025 "obsolete form of DEMO ClientMessage.");
2028 else if (type == XA_PREFS)
2030 clientmessage_response (si, window, True,
2031 "the PREFS client-message is obsolete.",
2032 "the PREFS client-message is obsolete.");
2034 else if (type == XA_LOCK)
2037 clientmessage_response (si, window, True,
2038 "not compiled with support for locking.",
2039 "locking not enabled.");
2040 #else /* !NO_LOCKING */
2041 if (si->locking_disabled_p)
2042 clientmessage_response (si, window, True,
2043 "LOCK ClientMessage received, but locking is disabled.",
2044 "locking not enabled.");
2045 else if (si->locked_p)
2046 clientmessage_response (si, window, True,
2047 "LOCK ClientMessage received while already locked.",
2052 char *response = (until_idle_p
2053 ? "activating and locking."
2055 sprintf (buf, "LOCK ClientMessage received; %s", response);
2056 clientmessage_response (si, window, False, buf, response);
2057 set_locked_p (si, True);
2058 si->selection_mode = 0;
2059 si->demoing_p = False;
2061 if (si->lock_id) /* we're doing it now, so lose the timeout */
2063 XtRemoveTimeOut (si->lock_id);
2069 if (si->using_mit_saver_extension ||
2070 si->using_sgi_saver_extension)
2072 XForceScreenSaver (si->dpy, ScreenSaverActive);
2081 #endif /* !NO_LOCKING */
2083 else if (type == XA_THROTTLE)
2085 /* The THROTTLE command is deprecated -- it predates the XDPMS
2086 extension. Instead of using -throttle, users should instead
2087 just power off the monitor (e.g., "xset dpms force off".)
2088 In a few minutes, xscreensaver will notice that the monitor
2089 is off, and cease running hacks.
2091 if (si->throttled_p)
2092 clientmessage_response (si, window, True,
2093 "THROTTLE ClientMessage received, but "
2094 "already throttled.",
2095 "already throttled.");
2099 char *response = "throttled.";
2100 si->throttled_p = True;
2101 si->selection_mode = 0;
2102 si->demoing_p = False;
2103 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
2104 clientmessage_response (si, window, False, buf, response);
2109 XtRemoveTimeOut (si->cycle_id);
2111 cycle_timer ((XtPointer) si, 0);
2115 else if (type == XA_UNTHROTTLE)
2117 if (! si->throttled_p)
2118 clientmessage_response (si, window, True,
2119 "UNTHROTTLE ClientMessage received, but "
2125 char *response = "unthrottled.";
2126 si->throttled_p = False;
2127 si->selection_mode = 0;
2128 si->demoing_p = False;
2129 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
2130 clientmessage_response (si, window, False, buf, response);
2135 XtRemoveTimeOut (si->cycle_id);
2137 cycle_timer ((XtPointer) si, 0);
2145 str = XGetAtomName_safe (si->dpy, type);
2149 if (strlen (str) > 80)
2150 strcpy (str+70, "...");
2151 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
2158 "unrecognised screensaver ClientMessage 0x%x received.",
2159 (unsigned int) event->xclient.data.l[0]);
2162 clientmessage_response (si, window, True, buf, buf);
2168 /* Some random diagnostics printed in -verbose mode.
2172 analyze_display (saver_info *si)
2176 const char *name; const char *desc; Bool useful_p;
2179 { "SCREEN_SAVER", /* underscore */ "SGI Screen-Saver",
2180 # ifdef HAVE_SGI_SAVER_EXTENSION
2185 }, { "SCREEN-SAVER", /* dash */ "SGI Screen-Saver",
2186 # ifdef HAVE_SGI_SAVER_EXTENSION
2191 }, { "MIT-SCREEN-SAVER", "MIT Screen-Saver",
2192 # ifdef HAVE_MIT_SAVER_EXTENSION
2197 }, { "XIDLE", "XIdle",
2198 # ifdef HAVE_XIDLE_EXTENSION
2203 }, { "SGI-VIDEO-CONTROL", "SGI Video-Control",
2204 # ifdef HAVE_SGI_VC_EXTENSION
2209 }, { "READDISPLAY", "SGI Read-Display",
2210 # ifdef HAVE_READ_DISPLAY_EXTENSION
2215 }, { "MIT-SHM", "Shared Memory",
2216 # ifdef HAVE_XSHM_EXTENSION
2221 }, { "DOUBLE-BUFFER", "Double-Buffering",
2222 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
2227 }, { "DPMS", "Power Management",
2228 # ifdef HAVE_DPMS_EXTENSION
2239 }, { "XFree86-VidModeExtension", "XF86 Video-Mode",
2240 # ifdef HAVE_XF86VMODE
2245 }, { "XINERAMA", "Xinerama",
2246 # ifdef HAVE_XINERAMA
2251 }, { "RANDR", "Resize-and-Rotate",
2257 }, { "Apple-DRI", "Apple-DRI (XDarwin)",
2262 fprintf (stderr, "%s: running on display \"%s\" (%d %sscreen%s).\n",
2264 DisplayString(si->dpy),
2266 (si->xinerama_p ? "Xinerama " : ""),
2267 (si->nscreens == 1 ? "" : "s"));
2268 fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
2269 ServerVendor(si->dpy), VendorRelease(si->dpy));
2271 fprintf (stderr, "%s: useful extensions:\n", blurb());
2272 for (i = 0; i < countof(exts); i++)
2274 int op = 0, event = 0, error = 0;
2277 if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
2279 sprintf (buf, "%s: ", blurb());
2281 strcat (buf, exts[i].desc);
2282 if (!exts[i].useful_p)
2283 strcat (buf, " (disabled at compile time)");
2284 fprintf (stderr, "%s\n", buf);
2287 for (i = 0; i < si->nscreens; i++)
2289 saver_screen_info *ssi = &si->screens[i];
2290 unsigned long colormapped_depths = 0;
2291 unsigned long non_mapped_depths = 0;
2292 XVisualInfo vi_in, *vi_out;
2295 if (!ssi->real_screen_p) continue;
2297 vi_in.screen = ssi->real_screen_number;
2298 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
2299 if (!vi_out) continue;
2300 for (j = 0; j < out_count; j++)
2301 if (vi_out[j].class == PseudoColor)
2302 colormapped_depths |= (1 << vi_out[j].depth);
2304 non_mapped_depths |= (1 << vi_out[j].depth);
2305 XFree ((char *) vi_out);
2307 if (colormapped_depths)
2309 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(),
2310 ssi->real_screen_number);
2311 for (j = 0; j < 32; j++)
2312 if (colormapped_depths & (1 << j))
2313 fprintf (stderr, " %d", j);
2314 fprintf (stderr, ".\n");
2316 if (non_mapped_depths)
2318 fprintf (stderr, "%s: screen %d non-colormapped depths:",
2319 blurb(), ssi->real_screen_number);
2320 for (j = 0; j < 32; j++)
2321 if (non_mapped_depths & (1 << j))
2322 fprintf (stderr, " %d", j);
2323 fprintf (stderr, ".\n");
2329 fprintf (stderr, "%s: Xinerama layout:\n", blurb());
2330 for (i = 0; i < si->nscreens; i++)
2332 saver_screen_info *ssi = &si->screens[i];
2333 fprintf (stderr, "%s: %c %d/%d: %dx%d+%d+%d\n",
2335 (ssi->real_screen_p ? '+' : ' '),
2336 ssi->number, ssi->real_screen_number,
2337 ssi->width, ssi->height, ssi->x, ssi->y);
2343 display_is_on_console_p (saver_info *si)
2345 Bool not_on_console = True;
2346 char *dpystr = DisplayString (si->dpy);
2347 char *tail = (char *) strchr (dpystr, ':');
2348 if (! tail || strncmp (tail, ":0", 2))
2349 not_on_console = True;
2352 char dpyname[255], localname[255];
2353 strncpy (dpyname, dpystr, tail-dpystr);
2354 dpyname [tail-dpystr] = 0;
2356 !strcmp(dpyname, "unix") ||
2357 !strcmp(dpyname, "localhost"))
2358 not_on_console = False;
2359 else if (gethostname (localname, sizeof (localname)))
2360 not_on_console = True; /* can't find hostname? */
2361 else if (!strncmp (dpyname, "/tmp/launch-", 12)) /* MacOS X launchd */
2362 not_on_console = False;
2365 /* We have to call gethostbyname() on the result of gethostname()
2366 because the two aren't guarenteed to be the same name for the
2367 same host: on some losing systems, one is a FQDN and the other
2368 is not. Here in the wide wonderful world of Unix it's rocket
2369 science to obtain the local hostname in a portable fashion.
2371 And don't forget, gethostbyname() reuses the structure it
2372 returns, so we have to copy the fucker before calling it again.
2373 Thank you master, may I have another.
2375 struct hostent *h = gethostbyname (dpyname);
2377 not_on_console = True;
2382 strcpy (hn, h->h_name);
2383 l = gethostbyname (localname);
2384 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
2388 return !not_on_console;
2392 /* Do a little bit of heap introspection...
2395 check_for_leaks (const char *where)
2397 #if defined(HAVE_SBRK) && defined(LEAK_PARANOIA)
2398 static unsigned long last_brk = 0;
2399 int b = (unsigned long) sbrk(0);
2400 if (last_brk && last_brk < b)
2401 fprintf (stderr, "%s: %s: brk grew by %luK.\n",
2403 (((b - last_brk) + 1023) / 1024));
2405 #endif /* HAVE_SBRK */