1 /* xscreensaver, Copyright (c) 1991-2003 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 (or, in the case of the
20 * MIT-SCREEN-SAVER extension, use the one it gave us.)
22 * We place a __SWM_VROOT property on this window, so that newly-started
23 * clients will think that this window is a "virtual root" window.
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 * While we are waiting, we also set up timers so that, after a certain
42 * amount of time has passed, we can start a different screenhack. We do
43 * this by killing the running child process with SIGTERM, and then starting
44 * a new one in the same way.
46 * If there was a real virtual root, meaning that we removed the __SWM_VROOT
47 * property from it, meaning we must (absolutely must) restore it before we
48 * exit, then we set up signal handlers for most signals (SIGINT, SIGTERM,
49 * etc.) that do this. Most Xlib and Xt routines are not reentrant, so it
50 * is not generally safe to call them from signal handlers; however, this
51 * program spends most of its time waiting, so the window of opportunity
52 * when code could be called reentrantly is fairly small; and also, the worst
53 * that could happen is that the call would fail. If we've gotten one of
54 * these signals, then we're on our way out anyway. If we didn't restore the
55 * __SWM_VROOT property, that would be very bad, so it's worth a shot. Note
56 * that this means that, if you're using a virtual-root window manager, you
57 * can really fuck up the world by killing this process with "kill -9".
59 * This program accepts ClientMessages of type SCREENSAVER; these messages
60 * may contain the atom ACTIVATE or DEACTIVATE, meaning to turn the
61 * screensaver on or off now, regardless of the idleness of the user,
62 * and a few other things. The included "xscreensaver-command" program
63 * sends these messsages.
65 * If we don't have the XIdle, MIT-SCREEN-SAVER, or SGI SCREEN_SAVER
66 * extensions, then we do the XAutoLock trick: notice every window that
67 * gets created, and wait 30 seconds or so until its creating process has
68 * settled down, and then select KeyPress events on those windows which
69 * already select for KeyPress events. It's important that we not select
70 * KeyPress on windows which don't select them, because that would
71 * interfere with event propagation. This will break if any program
72 * changes its event mask to contain KeyRelease or PointerMotion more than
73 * 30 seconds after creating the window, but such programs do not seem to
74 * occur in nature (I've never seen it happen in all these years.)
76 * The reason that we can't select KeyPresses on windows that don't have
77 * them already is that, when dispatching a KeyPress event, X finds the
78 * lowest (leafmost) window in the hierarchy on which *any* client selects
79 * for KeyPress, and sends the event to that window. This means that if a
80 * client had a window with subwindows, and expected to receive KeyPress
81 * events on the parent window instead of the subwindows, then that client
82 * would malfunction if some other client selected KeyPress events on the
83 * subwindows. It is an incredible misdesign that one client can make
84 * another client malfunction in this way.
86 * To detect mouse motion, we periodically wake up and poll the mouse
87 * position and button/modifier state, and notice when something has
88 * changed. We make this check every five seconds by default, and since the
89 * screensaver timeout has a granularity of one minute, this makes the
90 * chance of a false positive very small. We could detect mouse motion in
91 * the same way as keyboard activity, but that would suffer from the same
92 * "client changing event mask" problem that the KeyPress events hack does.
93 * I think polling is more reliable.
95 * None of this crap happens if we're using one of the extensions, so install
96 * one of them if the description above sounds just too flaky to live. It
97 * is, but those are your choices.
99 * A third idle-detection option could be implemented (but is not): when
100 * running on the console display ($DISPLAY is `localhost`:0) and we're on a
101 * machine where /dev/tty and /dev/mouse have reasonable last-modification
102 * times, we could just stat() those. But the incremental benefit of
103 * implementing this is really small, so forget I said anything.
106 * - Have a second terminal handy.
107 * - Be careful where you set your breakpoints, you don't want this to
108 * stop under the debugger with the keyboard grabbed or the blackout
110 * - If you run your debugger under XEmacs, try M-ESC (x-grab-keyboard)
111 * to keep your emacs window alive even when xscreensaver has grabbed.
112 * - Go read the code related to `debug_p'.
113 * - You probably can't set breakpoints in functions that are called on
114 * the other side of a call to fork() -- if your subprocesses are
115 * dying with signal 5, Trace/BPT Trap, you're losing in this way.
116 * - If you aren't using a server extension, don't leave this stopped
117 * under the debugger for very long, or the X input buffer will get
118 * huge because of the keypress events it's selecting for. This can
119 * make your X server wedge with "no more input buffers."
121 * ======================================================================== */
129 #include <X11/Xlib.h>
131 #include <X11/Xlibint.h>
133 #include <X11/Xatom.h>
134 #include <X11/Intrinsic.h>
135 #include <X11/StringDefs.h>
136 #include <X11/Shell.h>
139 #include <sys/time.h>
140 #include <netdb.h> /* for gethostbyname() */
143 # include <X11/Xmu/Error.h>
145 # include <Xmu/Error.h>
147 #else /* !HAVE_XMU */
149 #endif /* !HAVE_XMU */
151 #ifdef HAVE_XIDLE_EXTENSION
152 # include <X11/extensions/xidle.h>
153 #endif /* HAVE_XIDLE_EXTENSION */
155 #include "xscreensaver.h"
157 #include "yarandom.h"
158 #include "resources.h"
162 saver_info *global_si_kludge = 0; /* I hate C so much... */
169 static Atom XA_SCREENSAVER_RESPONSE;
170 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
171 static Atom XA_RESTART, XA_SELECT;
172 static Atom XA_THROTTLE, XA_UNTHROTTLE;
173 Atom XA_DEMO, XA_PREFS, XA_EXIT, XA_LOCK, XA_BLANK;
176 static XrmOptionDescRec options [] = {
178 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
179 { "-silent", ".verbose", XrmoptionNoArg, "off" },
181 /* xscreensaver-demo uses this one */
182 { "-nosplash", ".splash", XrmoptionNoArg, "off" },
183 { "-no-splash", ".splash", XrmoptionNoArg, "off" },
185 /* useful for debugging */
186 { "-no-capture-stderr", ".captureStderr", XrmoptionNoArg, "off" },
188 /* There's really no reason to have these command-line args; they just
189 lead to confusion when the .xscreensaver file has conflicting values.
192 { "-splash", ".splash", XrmoptionNoArg, "on" },
193 { "-capture-stderr", ".captureStderr", XrmoptionNoArg, "on" },
194 { "-timeout", ".timeout", XrmoptionSepArg, 0 },
195 { "-cycle", ".cycle", XrmoptionSepArg, 0 },
196 { "-lock-mode", ".lock", XrmoptionNoArg, "on" },
197 { "-no-lock-mode", ".lock", XrmoptionNoArg, "off" },
198 { "-no-lock", ".lock", XrmoptionNoArg, "off" },
199 { "-lock-timeout", ".lockTimeout", XrmoptionSepArg, 0 },
200 { "-lock-vts", ".lockVTs", XrmoptionNoArg, "on" },
201 { "-no-lock-vts", ".lockVTs", XrmoptionNoArg, "off" },
202 { "-visual", ".visualID", XrmoptionSepArg, 0 },
203 { "-install", ".installColormap", XrmoptionNoArg, "on" },
204 { "-no-install", ".installColormap", XrmoptionNoArg, "off" },
205 { "-timestamp", ".timestamp", XrmoptionNoArg, "on" },
206 { "-xidle-extension", ".xidleExtension", XrmoptionNoArg, "on" },
207 { "-no-xidle-extension", ".xidleExtension", XrmoptionNoArg, "off" },
208 { "-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "on" },
209 { "-no-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "off" },
210 { "-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "on" },
211 { "-no-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "off" },
212 { "-proc-interrupts", ".procInterrupts", XrmoptionNoArg, "on" },
213 { "-no-proc-interrupts", ".procInterrupts", XrmoptionNoArg, "off" },
214 { "-idelay", ".initialDelay", XrmoptionSepArg, 0 },
215 { "-nice", ".nice", XrmoptionSepArg, 0 },
220 __extension__ /* shut up about "string length is greater than the length
221 ISO C89 compilers are required to support" when including
225 static char *defaults[] = {
226 #include "XScreenSaver_ad.h"
231 ERROR! You must not include vroot.h in this file.
235 do_help (saver_info *si)
240 xscreensaver %s, copyright (c) 1991-2003 by Jamie Zawinski <jwz@jwz.org>\n\
242 All xscreensaver configuration is via the `~/.xscreensaver' file.\n\
243 Rather than editing that file by hand, just run `xscreensaver-demo':\n\
244 that program lets you configure the screen saver graphically,\n\
245 including timeouts, locking, and display modes.\n\
249 Just getting started? Try this:\n\
254 For updates, online manual, and FAQ, please see the web page:\n\
256 http://www.jwz.org/xscreensaver/\n\
268 time_t now = time ((time_t *) 0);
269 char *str = (char *) ctime (&now);
270 char *nl = (char *) strchr (str, '\n');
271 if (nl) *nl = 0; /* take off that dang newline */
275 static Bool blurb_timestamp_p = False; /* kludge */
280 if (!blurb_timestamp_p)
284 static char buf[255];
285 char *ct = timestring();
286 int n = strlen(progname);
288 strncpy(buf, progname, n);
291 strncpy(buf+n, ct+11, 8);
292 strcpy(buf+n+9, ": ");
299 saver_ehandler (Display *dpy, XErrorEvent *error)
301 saver_info *si = global_si_kludge; /* I hate C so much... */
305 if (!real_stderr) real_stderr = stderr;
307 fprintf (real_stderr, "\n"
308 "#######################################"
309 "#######################################\n\n"
310 "%s: X Error! PLEASE REPORT THIS BUG.\n",
313 for (i = 0; i < si->nscreens; i++)
314 fprintf (real_stderr, "%s: screen %d: 0x%x, 0x%x, 0x%x\n",
316 (unsigned int) RootWindowOfScreen (si->screens[i].screen),
317 (unsigned int) si->screens[i].real_vroot,
318 (unsigned int) si->screens[i].screensaver_window);
320 fprintf (real_stderr, "\n"
321 "#######################################"
322 "#######################################\n\n");
324 fatal_p = XmuPrintDefaultErrorMessage (dpy, error, real_stderr);
326 fatal_p = True; /* The only time I've ever seen a supposedly nonfatal error,
327 it has been BadImplementation / Xlib sequence lost, which
328 are in truth pretty damned fatal.
331 fprintf (real_stderr, "\n");
334 fprintf (real_stderr, "%s: nonfatal error.\n\n", blurb());
337 if (si->prefs.xsync_p)
339 saver_exit (si, -1, "because of synchronous X Error");
343 fprintf (real_stderr,
344 "#######################################"
345 "#######################################\n\n");
346 fprintf (real_stderr,
347 " If at all possible, please re-run xscreensaver with the command\n"
348 " line arguments `-sync -verbose -no-capture', and reproduce this\n"
349 " bug. That will cause xscreensaver to dump a `core' file to the\n"
350 " current directory. Please include the stack trace from that core\n"
351 " file in your bug report. *DO NOT* mail the core file itself!\n"
352 " That won't work.\n");
353 fprintf (real_stderr,
355 " http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
356 " the most useful bug reports, and how to examine core files.\n"
358 " The more information you can provide, the better. But please\n"
359 " report this bug, regardless!\n"
361 fprintf (real_stderr,
362 "#######################################"
363 "#######################################\n\n");
365 saver_exit (si, -1, 0);
373 /* This error handler is used only while the X connection is being set up;
374 after we've got a connection, we don't use this handler again. The only
375 reason for having this is so that we can present a more idiot-proof error
376 message than "cannot open display."
379 startup_ehandler (String name, String type, String class,
380 String defalt, /* one can't even spel properly
381 in this joke of a language */
382 String *av, Cardinal *ac)
386 saver_info *si = global_si_kludge; /* I hate C so much... */
387 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
389 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
390 fmt, sizeof(fmt)-1, *db);
392 fprintf (stderr, "%s: ", blurb());
394 memset (p, 0, sizeof(p));
395 if (*ac > countof (p)) *ac = countof (p);
396 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
397 fprintf (stderr, fmt, /* Did I mention that I hate C? */
398 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
399 fprintf (stderr, "\n");
401 describe_uids (si, stderr);
403 if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
405 fprintf (stderr, "\n"
406 "%s: This is probably because you're logging in as root. You\n"
407 " shouldn't log in as root: you should log in as a normal user,\n"
408 " and then `su' as needed. If you insist on logging in as\n"
409 " root, you will have to turn off X's security features before\n"
410 " xscreensaver will work.\n"
412 " Please read the manual and FAQ for more information:\n",
417 fprintf (stderr, "\n"
418 "%s: Errors at startup are usually authorization problems.\n"
419 " But you're not logging in as root (good!) so something\n"
420 " else must be wrong. Did you read the manual and the FAQ?\n",
424 fprintf (stderr, "\n"
425 " http://www.jwz.org/xscreensaver/faq.html\n"
426 " http://www.jwz.org/xscreensaver/man.html\n"
435 /* The zillions of initializations.
438 /* Set progname, version, etc. This is done very early.
441 set_version_string (saver_info *si, int *argc, char **argv)
443 progclass = "XScreenSaver";
445 /* progname is reset later, after we connect to X. */
446 progname = strrchr(argv[0], '/');
447 if (progname) progname++;
448 else progname = argv[0];
450 if (strlen(progname) > 100) /* keep it short. */
453 /* The X resource database blows up if argv[0] has a "." in it. */
456 while ((s = strchr (s, '.')))
460 si->version = (char *) malloc (5);
461 memcpy (si->version, screensaver_id + 17, 4);
466 /* Initializations that potentially take place as a priveleged user:
467 If the xscreensaver executable is setuid root, then these initializations
468 are run as root, before discarding privileges.
471 privileged_initialization (saver_info *si, int *argc, char **argv)
474 /* before hack_uid() for proper permissions */
475 lock_priv_init (*argc, argv, si->prefs.verbose_p);
476 #endif /* NO_LOCKING */
482 /* Figure out what locking mechanisms are supported.
485 lock_initialization (saver_info *si, int *argc, char **argv)
488 si->locking_disabled_p = True;
489 si->nolock_reason = "not compiled with locking support";
490 #else /* !NO_LOCKING */
492 /* Finish initializing locking, now that we're out of privileged code. */
493 if (! lock_init (*argc, argv, si->prefs.verbose_p))
495 si->locking_disabled_p = True;
496 si->nolock_reason = "error getting password";
499 /* If locking is currently enabled, but the environment indicates that
500 we have been launched as GDM's "Background" program, then disable
501 locking just in case.
503 if (!si->locking_disabled_p && getenv ("RUNNING_UNDER_GDM"))
505 si->locking_disabled_p = True;
506 si->nolock_reason = "running under GDM";
509 /* If the server is XDarwin (MacOS X) then disable locking.
510 (X grabs only affect X programs, so you can use Command-Tab
511 to bring any other Mac program to the front, e.g., Terminal.)
513 if (!si->locking_disabled_p)
515 int op = 0, event = 0, error = 0;
516 Bool macos_p = False;
519 /* Disable locking if *running* on Apple hardware, since we have no
520 reliable way to determine whether the server is running on MacOS.
521 Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
522 but I'm not really sure about that.
528 /* This extension exists on the Apple X11 server, but not
529 on earlier versions of the XDarwin server. */
530 macos_p = XQueryExtension (si->dpy, "Apple-DRI", &op, &event, &error);
534 si->locking_disabled_p = True;
535 si->nolock_reason = "Cannot lock securely on MacOS X";
539 #endif /* NO_LOCKING */
543 /* Open the connection to the X server, and intern our Atoms.
546 connect_to_server (saver_info *si, int *argc, char **argv)
548 Widget toplevel_shell;
551 char *d = getenv ("DISPLAY");
554 char *ndpy = strdup("DISPLAY=:0.0");
555 /* if (si->prefs.verbose_p) */ /* sigh, too early to test this... */
557 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
561 /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
562 glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
563 do not. So we must leak it (and/or the previous setting). Yay.
566 #endif /* HAVE_PUTENV */
568 XSetErrorHandler (saver_ehandler);
570 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
571 toplevel_shell = XtAppInitialize (&si->app, progclass,
572 options, XtNumber (options),
573 argc, argv, defaults, 0, 0);
574 XtAppSetErrorMsgHandler (si->app, 0);
576 si->dpy = XtDisplay (toplevel_shell);
577 si->prefs.db = XtDatabase (si->dpy);
578 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
580 if(strlen(progname) > 100) /* keep it short. */
583 db = si->prefs.db; /* resources.c needs this */
585 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
586 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
587 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
588 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
589 XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
590 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
592 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
593 XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False);
594 XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False);
595 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
596 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
597 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
598 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
599 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
600 XA_PREV = XInternAtom (si->dpy, "PREV", False);
601 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
602 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
603 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
604 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
605 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
606 XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
607 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
608 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
610 return toplevel_shell;
614 /* Handle the command-line arguments that were not handled for us by Xt.
615 Issue an error message and exit if there are unknown options.
618 process_command_line (saver_info *si, int *argc, char **argv)
621 for (i = 1; i < *argc; i++)
623 if (!strcmp (argv[i], "-debug"))
624 /* no resource for this one, out of paranoia. */
625 si->prefs.debug_p = True;
627 else if (!strcmp (argv[i], "-h") ||
628 !strcmp (argv[i], "-help") ||
629 !strcmp (argv[i], "--help"))
634 const char *s = argv[i];
635 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
638 if (s[0] == '-' && s[1] == '-') s++;
639 if (!strcmp (s, "-activate") ||
640 !strcmp (s, "-deactivate") ||
641 !strcmp (s, "-cycle") ||
642 !strcmp (s, "-next") ||
643 !strcmp (s, "-prev") ||
644 !strcmp (s, "-exit") ||
645 !strcmp (s, "-restart") ||
646 !strcmp (s, "-demo") ||
647 !strcmp (s, "-prefs") ||
648 !strcmp (s, "-preferences") ||
649 !strcmp (s, "-lock") ||
650 !strcmp (s, "-version") ||
651 !strcmp (s, "-time"))
654 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
655 fprintf (stderr, "\n\
656 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
658 fprintf (stderr, "\n\
659 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
662 The `xscreensaver' program is a daemon that runs in the background.\n\
663 You control a running xscreensaver process by sending it messages\n\
664 with `xscreensaver-demo' or `xscreensaver-command'.\n\
665 . See the man pages for details, or check the web page:\n\
666 http://www.jwz.org/xscreensaver/\n\n");
668 /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
669 suggest that explicitly. */
670 if (!strcmp (s, "-lock"))
672 Or perhaps you meant either the \"-lock-mode\" or the\n\
673 \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
682 /* Print out the xscreensaver banner to the tty if applicable;
683 Issue any other warnings that are called for at this point.
686 print_banner (saver_info *si)
688 saver_preferences *p = &si->prefs;
690 /* This resource gets set some time before the others, so that we know
691 whether to print the banner (and so that the banner gets printed before
692 any resource-database-related error messages.)
694 p->verbose_p = (p->debug_p || get_boolean_resource ("verbose", "Boolean"));
696 /* Ditto, for the locking_disabled_p message. */
697 p->lock_p = get_boolean_resource ("lock", "Boolean");
701 "%s %s, copyright (c) 1991-2003 "
702 "by Jamie Zawinski <jwz@jwz.org>.\n",
703 progname, si->version);
706 fprintf (stderr, "\n"
707 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
709 "\tNote that in debug mode, the xscreensaver window will only\n"
710 "\tcover the left half of the screen. (The idea is that you\n"
711 "\tcan still see debugging output in a shell, if you position\n"
712 "\tit on the right side of the screen.)\n"
714 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
715 "\tuntrusted environments.\n"
721 if (!si->uid_message || !*si->uid_message)
722 describe_uids (si, stderr);
725 if (si->orig_uid && *si->orig_uid)
726 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
727 blurb(), si->orig_uid);
728 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
731 fprintf (stderr, "%s: in process %lu.\n", blurb(),
732 (unsigned long) getpid());
737 print_lock_failure_banner (saver_info *si)
739 saver_preferences *p = &si->prefs;
741 /* If locking was not able to be initalized for some reason, explain why.
742 (This has to be done after we've read the lock_p resource.)
744 if (si->locking_disabled_p)
747 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
749 if (strstr (si->nolock_reason, "passw"))
750 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
751 "consult the manual.\n", blurb());
752 else if (strstr (si->nolock_reason, "running as "))
754 "%s: locking only works when xscreensaver is launched\n"
755 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
756 "\t See the manual for details.\n",
763 /* Examine all of the display's screens, and populate the `saver_screen_info'
764 structures. Make sure this is called after hack_environment() sets $PATH.
767 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
769 Bool found_any_writable_cells = False;
772 si->nscreens = ScreenCount(si->dpy);
773 si->screens = (saver_screen_info *)
774 calloc(sizeof(saver_screen_info), si->nscreens);
776 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
778 for (i = 0; i < si->nscreens; i++)
780 saver_screen_info *ssi = &si->screens[i];
782 ssi->screen = ScreenOfDisplay (si->dpy, i);
785 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
786 ssi->default_visual =
787 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
789 ssi->current_visual = ssi->default_visual;
790 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
792 /* Execute a subprocess to find the GL visual. */
793 ssi->best_gl_visual = get_best_gl_visual (ssi);
795 if (ssi == si->default_screen)
796 /* Since this is the default screen, use the one already created. */
797 ssi->toplevel_shell = toplevel_shell;
799 /* Otherwise, each screen must have its own unmapped root widget. */
800 ssi->toplevel_shell =
801 XtVaAppCreateShell (progname, progclass, applicationShellWidgetClass,
803 XtNscreen, ssi->screen,
804 XtNvisual, ssi->current_visual,
805 XtNdepth, visual_depth (ssi->screen,
806 ssi->current_visual),
809 if (! found_any_writable_cells)
811 /* Check to see whether fading is ever possible -- if any of the
812 screens on the display has a PseudoColor visual, then fading can
813 work (on at least some screens.) If no screen has a PseudoColor
814 visual, then don't bother ever trying to fade, because it will
815 just cause a delay without causing any visible effect.
817 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
818 get_visual (ssi->screen, "PseudoColor", True, False) ||
819 get_visual (ssi->screen, "GrayScale", True, False))
820 found_any_writable_cells = True;
824 si->fading_possible_p = found_any_writable_cells;
826 #ifdef HAVE_XF86VMODE_GAMMA
827 si->fading_possible_p = True; /* if we can gamma fade, go for it */
832 /* If any server extensions have been requested, try and initialize them.
833 Issue warnings if requests can't be honored.
836 initialize_server_extensions (saver_info *si)
838 saver_preferences *p = &si->prefs;
840 Bool server_has_xidle_extension_p = False;
841 Bool server_has_sgi_saver_extension_p = False;
842 Bool server_has_mit_saver_extension_p = False;
843 Bool system_has_proc_interrupts_p = False;
844 const char *piwhy = 0;
846 si->using_xidle_extension = p->use_xidle_extension;
847 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
848 si->using_mit_saver_extension = p->use_mit_saver_extension;
849 si->using_proc_interrupts = p->use_proc_interrupts;
851 #ifdef HAVE_XIDLE_EXTENSION
852 server_has_xidle_extension_p = query_xidle_extension (si);
854 #ifdef HAVE_SGI_SAVER_EXTENSION
855 server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
857 #ifdef HAVE_MIT_SAVER_EXTENSION
858 server_has_mit_saver_extension_p = query_mit_saver_extension (si);
860 #ifdef HAVE_PROC_INTERRUPTS
861 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
864 if (!server_has_xidle_extension_p)
865 si->using_xidle_extension = False;
866 else if (p->verbose_p)
868 if (si->using_xidle_extension)
869 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
871 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
874 if (!server_has_sgi_saver_extension_p)
875 si->using_sgi_saver_extension = False;
876 else if (p->verbose_p)
878 if (si->using_sgi_saver_extension)
879 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
882 "%s: not using server's SGI SCREEN_SAVER extension.\n",
886 if (!server_has_mit_saver_extension_p)
887 si->using_mit_saver_extension = False;
888 else if (p->verbose_p)
890 if (si->using_mit_saver_extension)
891 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
895 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
899 if (!system_has_proc_interrupts_p)
901 si->using_proc_interrupts = False;
902 if (p->verbose_p && piwhy)
903 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
906 else if (p->verbose_p)
908 if (si->using_proc_interrupts)
910 "%s: consulting /proc/interrupts for keyboard activity.\n",
914 "%s: not consulting /proc/interrupts for keyboard activity.\n",
920 /* For the case where we aren't using an server extensions, select user events
921 on all the existing windows, and launch timers to select events on
922 newly-created windows as well.
924 If a server extension is being used, this does nothing.
927 select_events (saver_info *si)
929 saver_preferences *p = &si->prefs;
932 if (si->using_xidle_extension ||
933 si->using_mit_saver_extension ||
934 si->using_sgi_saver_extension)
937 if (p->initial_delay)
941 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
942 (int) p->initial_delay/1000,
943 (p->initial_delay == 1000 ? "" : "s"));
947 usleep (p->initial_delay);
949 fprintf (stderr, " done.\n");
954 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
959 /* Select events on the root windows of every screen. This also selects
960 for window creation events, so that new subwindows will be noticed.
962 for (i = 0; i < si->nscreens; i++)
963 start_notice_events_timer (si, RootWindowOfScreen (si->screens[i].screen),
967 fprintf (stderr, " done.\n");
972 maybe_reload_init_file (saver_info *si)
974 saver_preferences *p = &si->prefs;
975 if (init_file_changed_p (p))
978 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
979 blurb(), init_file_name());
983 /* If a server extension is in use, and p->timeout has changed,
984 we need to inform the server of the new timeout. */
985 disable_builtin_screensaver (si, False);
987 /* If the DPMS settings in the init file have changed,
988 change the settings on the server to match. */
989 sync_server_dpms_settings (si->dpy,
990 (p->dpms_enabled_p &&
991 p->mode != DONT_BLANK),
992 p->dpms_standby / 1000,
993 p->dpms_suspend / 1000,
1002 - wait until the user is idle;
1004 - wait until the user is active;
1005 - unblank the screen;
1010 main_loop (saver_info *si)
1012 saver_preferences *p = &si->prefs;
1017 Bool was_locked = False;
1020 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1022 check_for_leaks ("unblanked A");
1023 sleep_until_idle (si, True);
1024 check_for_leaks ("unblanked B");
1029 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
1030 si->selection_mode, timestring());
1032 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
1036 maybe_reload_init_file (si);
1038 if (p->mode == DONT_BLANK)
1041 fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
1042 blurb(), timestring());
1044 /* Go around the loop and wait for the next bout of idleness,
1045 or for the init file to change, or for a remote command to
1046 come in, or something.
1051 if (! blank_screen (si))
1053 /* We were unable to grab either the keyboard or mouse.
1054 This means we did not (and must not) blank the screen.
1055 If we were to blank the screen while some other program
1056 is holding both the mouse and keyboard grabbed, then
1057 we would never be able to un-blank it! We would never
1058 see any events, and the display would be wedged.
1060 So, just go around the loop again and wait for the
1061 next bout of idleness.
1065 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
1070 kill_screenhack (si);
1072 if (!si->throttled_p)
1073 spawn_screenhack (si, True);
1074 else if (p->verbose_p)
1075 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
1077 /* Don't start the cycle timer in demo mode. */
1078 if (!si->demoing_p && p->cycle)
1079 si->cycle_id = XtAppAddTimeOut (si->app,
1081 /* see comment in cycle_timer() */
1089 /* Maybe start locking the screen.
1092 Time lock_timeout = p->lock_timeout;
1094 if (si->emergency_lock_p && p->lock_p && lock_timeout)
1096 int secs = p->lock_timeout / 1000;
1099 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1101 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1105 si->emergency_lock_p = False;
1107 if (!si->demoing_p && /* if not going into demo mode */
1108 p->lock_p && /* and locking is enabled */
1109 !si->locking_disabled_p && /* and locking is possible */
1110 lock_timeout == 0) /* and locking is not timer-deferred */
1111 set_locked_p (si, True); /* then lock right now. */
1113 /* locked_p might be true already because of the above, or because of
1114 the LOCK ClientMessage. But if not, and if we're supposed to lock
1115 after some time, set up a timer to do so.
1120 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1121 activate_lock_timer,
1124 #endif /* !NO_LOCKING */
1127 ok_to_unblank = True;
1130 check_for_leaks ("blanked A");
1131 sleep_until_idle (si, False); /* until not idle */
1132 check_for_leaks ("blanked B");
1134 maybe_reload_init_file (si);
1137 /* Maybe unlock the screen.
1141 saver_screen_info *ssi = si->default_screen;
1142 if (si->locking_disabled_p) abort ();
1145 si->dbox_up_p = True;
1146 suspend_screenhack (si, True);
1147 XUndefineCursor (si->dpy, ssi->screensaver_window);
1149 ok_to_unblank = unlock_p (si);
1151 si->dbox_up_p = False;
1152 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1153 suspend_screenhack (si, False); /* resume */
1155 if (!ok_to_unblank &&
1156 !screenhack_running_p (si))
1158 /* If the lock dialog has been dismissed and we're not about to
1159 unlock the screen, and there is currently no hack running,
1160 then launch one. (There might be no hack running if DPMS
1161 had kicked in. But DPMS is off now, so bring back the hack)
1164 XtRemoveTimeOut (si->cycle_id);
1166 cycle_timer ((XtPointer) si, 0);
1169 #endif /* !NO_LOCKING */
1171 } while (!ok_to_unblank);
1175 fprintf (stderr, "%s: unblanking screen at %s.\n",
1176 blurb(), timestring ());
1178 /* Kill before unblanking, to stop drawing as soon as possible. */
1179 kill_screenhack (si);
1180 unblank_screen (si);
1182 set_locked_p (si, False);
1183 si->emergency_lock_p = False;
1185 si->selection_mode = 0;
1187 /* If we're throttled, and the user has explicitly unlocked the screen,
1188 then unthrottle. If we weren't locked, then don't unthrottle
1189 automatically, because someone might have just bumped the desk... */
1192 if (si->throttled_p && p->verbose_p)
1193 fprintf (stderr, "%s: unthrottled.\n", blurb());
1194 si->throttled_p = False;
1199 XtRemoveTimeOut (si->cycle_id);
1205 XtRemoveTimeOut (si->lock_id);
1209 /* It's possible that a race condition could have led to the saver
1210 window being unexpectedly still mapped. This can happen like so:
1214 - that hack tries to grab a screen image( it does this by
1215 first unmapping the saver window, then remapping it.)
1216 - hack unmaps window
1218 - user becomes active
1219 - hack re-maps window (*)
1220 - driver kills subprocess
1221 - driver unmaps window (**)
1223 The race is that (*) might have been sent to the server before
1224 the client process was killed, but, due to scheduling randomness,
1225 might not have been received by the server until after (**).
1226 In other words, (*) and (**) might happen out of order, meaning
1227 the driver will unmap the window, and then after that, the
1228 recently-dead client will re-map it. This leaves the user
1229 locked out (it looks like a desktop, but it's not!)
1231 To avoid this: after un-blanking the screen, sleep for a second,
1232 and then really make sure the window is unmapped.
1236 XSync (si->dpy, False);
1238 for (i = 0; i < si->nscreens; i++)
1240 saver_screen_info *ssi = &si->screens[i];
1241 Window w = ssi->screensaver_window;
1242 XWindowAttributes xgwa;
1243 XGetWindowAttributes (si->dpy, w, &xgwa);
1244 if (xgwa.map_state != IsUnmapped)
1248 "%s: %d: client race! emergency unmap 0x%lx.\n",
1249 blurb(), i, (unsigned long) w);
1250 XUnmapWindow (si->dpy, w);
1253 XSync (si->dpy, False);
1258 static void analyze_display (saver_info *si);
1259 static void fix_fds (void);
1262 main (int argc, char **argv)
1266 saver_info *si = &the_si;
1267 saver_preferences *p = &si->prefs;
1270 memset(si, 0, sizeof(*si));
1271 global_si_kludge = si; /* I hate C so much... */
1275 # undef ya_rand_init
1278 save_argv (argc, argv);
1279 set_version_string (si, &argc, argv);
1280 privileged_initialization (si, &argc, argv);
1281 hack_environment (si);
1283 shell = connect_to_server (si, &argc, argv);
1284 process_command_line (si, &argc, argv);
1287 load_init_file (p); /* must be before initialize_per_screen_info() */
1288 blurb_timestamp_p = p->timestamp_p; /* kludge */
1289 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1291 /* We can only issue this warnings now. */
1292 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1294 "%s: there are no PseudoColor or GrayScale visuals.\n"
1295 "%s: ignoring the request for fading/unfading.\n",
1298 for (i = 0; i < si->nscreens; i++)
1299 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1302 lock_initialization (si, &argc, argv);
1303 print_lock_failure_banner (si);
1305 if (p->xsync_p) XSynchronize (si->dpy, True);
1307 if (p->verbose_p) analyze_display (si);
1308 initialize_server_extensions (si);
1310 si->blank_time = time ((time_t) 0); /* must be before ..._window */
1311 initialize_screensaver_window (si);
1316 disable_builtin_screensaver (si, True);
1317 sync_server_dpms_settings (si->dpy,
1318 (p->dpms_enabled_p &&
1319 p->mode != DONT_BLANK),
1320 p->dpms_standby / 1000,
1321 p->dpms_suspend / 1000,
1325 initialize_stderr (si);
1326 handle_signals (si);
1328 make_splash_dialog (si);
1330 main_loop (si); /* doesn't return */
1337 /* Bad Things Happen if stdin, stdout, and stderr have been closed
1338 (as by the `sh incantation "xscreensaver >&- 2>&-"). When you do
1339 that, the X connection gets allocated to one of these fds, and
1340 then some random library writes to stderr, and random bits get
1341 stuffed down the X pipe, causing "Xlib: sequence lost" errors.
1342 So, we cause the first three file descriptors to be open to
1343 /dev/null if they aren't open to something else already. This
1344 must be done before any other files are opened (or the closing
1345 of that other file will again free up one of the "magic" first
1348 We do this by opening /dev/null three times, and then closing
1349 those fds, *unless* any of them got allocated as #0, #1, or #2,
1350 in which case we leave them open. Gag.
1352 Really, this crap is technically required of *every* X program,
1353 if you want it to be robust in the face of "2>&-".
1355 int fd0 = open ("/dev/null", O_RDWR);
1356 int fd1 = open ("/dev/null", O_RDWR);
1357 int fd2 = open ("/dev/null", O_RDWR);
1358 if (fd0 > 2) close (fd0);
1359 if (fd1 > 2) close (fd1);
1360 if (fd2 > 2) close (fd2);
1365 /* Processing ClientMessage events.
1369 static Bool error_handler_hit_p = False;
1372 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1374 error_handler_hit_p = True;
1378 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1379 them. We only look up the atom names for printing warning messages,
1380 so don't bomb out when it happens...
1383 XGetAtomName_safe (Display *dpy, Atom atom)
1386 XErrorHandler old_handler;
1387 if (!atom) return 0;
1390 error_handler_hit_p = False;
1391 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1392 result = XGetAtomName (dpy, atom);
1394 XSetErrorHandler (old_handler);
1396 if (error_handler_hit_p) result = 0;
1403 sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
1404 return strdup (buf);
1410 clientmessage_response (saver_info *si, Window w, Bool error,
1411 const char *stderr_msg,
1412 const char *protocol_msg)
1416 saver_preferences *p = &si->prefs;
1417 XErrorHandler old_handler;
1419 if (error || p->verbose_p)
1420 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1422 L = strlen(protocol_msg);
1423 proto = (char *) malloc (L + 2);
1424 proto[0] = (error ? '-' : '+');
1425 strcpy (proto+1, protocol_msg);
1428 /* Ignore all X errors while sending a response to a ClientMessage.
1429 Pretty much the only way we could get an error here is if the
1430 window we're trying to send the reply on has been deleted, in
1431 which case, the sender of the ClientMessage won't see our response
1434 XSync (si->dpy, False);
1435 error_handler_hit_p = False;
1436 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1438 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1439 PropModeReplace, (unsigned char *) proto, L);
1441 XSync (si->dpy, False);
1442 XSetErrorHandler (old_handler);
1443 XSync (si->dpy, False);
1450 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1452 char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1453 Window w = event->xclient.window;
1458 for (screen = 0; screen < si->nscreens; screen++)
1459 if (w == si->screens[screen].screensaver_window)
1461 strcpy (wdesc, "xscreensaver");
1464 else if (w == RootWindow (si->dpy, screen))
1466 strcpy (wdesc, "root");
1472 XErrorHandler old_handler;
1474 XWindowAttributes xgwa;
1475 memset (&hint, 0, sizeof(hint));
1476 memset (&xgwa, 0, sizeof(xgwa));
1478 XSync (si->dpy, False);
1479 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1480 XGetClassHint (si->dpy, w, &hint);
1481 XGetWindowAttributes (si->dpy, w, &xgwa);
1482 XSync (si->dpy, False);
1483 XSetErrorHandler (old_handler);
1484 XSync (si->dpy, False);
1486 screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1488 sprintf (wdesc, "%.20s / %.20s",
1489 (hint.res_name ? hint.res_name : "(null)"),
1490 (hint.res_class ? hint.res_class : "(null)"));
1491 if (hint.res_name) XFree (hint.res_name);
1492 if (hint.res_class) XFree (hint.res_class);
1495 fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1496 blurb(), screen, (str ? str : "(null)"));
1497 fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1498 blurb(), screen, (unsigned long) w, wdesc);
1499 if (str) XFree (str);
1503 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1505 saver_preferences *p = &si->prefs;
1507 Window window = event->xclient.window;
1509 /* Preferences might affect our handling of client messages. */
1510 maybe_reload_init_file (si);
1512 if (event->xclient.message_type != XA_SCREENSAVER ||
1513 event->xclient.format != 32)
1515 bogus_clientmessage_warning (si, event);
1519 type = event->xclient.data.l[0];
1520 if (type == XA_ACTIVATE)
1524 clientmessage_response(si, window, False,
1525 "ACTIVATE ClientMessage received.",
1527 si->selection_mode = 0;
1528 si->demoing_p = False;
1530 if (si->throttled_p && p->verbose_p)
1531 fprintf (stderr, "%s: unthrottled.\n", blurb());
1532 si->throttled_p = False;
1534 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1536 XForceScreenSaver (si->dpy, ScreenSaverActive);
1544 clientmessage_response(si, window, True,
1545 "ClientMessage ACTIVATE received while already active.",
1548 else if (type == XA_DEACTIVATE)
1552 if (si->throttled_p && p->verbose_p)
1553 fprintf (stderr, "%s: unthrottled.\n", blurb());
1554 si->throttled_p = False;
1556 clientmessage_response(si, window, False,
1557 "DEACTIVATE ClientMessage received.",
1559 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1561 XForceScreenSaver (si->dpy, ScreenSaverReset);
1569 clientmessage_response(si, window, False,
1570 "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
1571 "not active: idle timer reset.");
1574 else if (type == XA_CYCLE)
1578 clientmessage_response(si, window, False,
1579 "CYCLE ClientMessage received.",
1581 si->selection_mode = 0; /* 0 means randomize when its time. */
1582 si->demoing_p = False;
1584 if (si->throttled_p && p->verbose_p)
1585 fprintf (stderr, "%s: unthrottled.\n", blurb());
1586 si->throttled_p = False;
1589 XtRemoveTimeOut (si->cycle_id);
1591 cycle_timer ((XtPointer) si, 0);
1594 clientmessage_response(si, window, True,
1595 "ClientMessage CYCLE received while inactive.",
1598 else if (type == XA_NEXT || type == XA_PREV)
1600 clientmessage_response(si, window, False,
1602 ? "NEXT ClientMessage received."
1603 : "PREV ClientMessage received."),
1605 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1606 si->demoing_p = False;
1608 if (si->throttled_p && p->verbose_p)
1609 fprintf (stderr, "%s: unthrottled.\n", blurb());
1610 si->throttled_p = False;
1615 XtRemoveTimeOut (si->cycle_id);
1617 cycle_timer ((XtPointer) si, 0);
1622 else if (type == XA_SELECT)
1626 long which = event->xclient.data.l[1];
1628 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1629 sprintf (buf2, "activating (%ld).", which);
1630 clientmessage_response (si, window, False, buf, buf2);
1632 if (which < 0) which = 0; /* 0 == "random" */
1633 si->selection_mode = which;
1634 si->demoing_p = False;
1636 if (si->throttled_p && p->verbose_p)
1637 fprintf (stderr, "%s: unthrottled.\n", blurb());
1638 si->throttled_p = False;
1643 XtRemoveTimeOut (si->cycle_id);
1645 cycle_timer ((XtPointer) si, 0);
1650 else if (type == XA_EXIT)
1652 /* Ignore EXIT message if the screen is locked. */
1653 if (until_idle_p || !si->locked_p)
1655 clientmessage_response (si, window, False,
1656 "EXIT ClientMessage received.",
1660 unblank_screen (si);
1661 kill_screenhack (si);
1662 XSync (si->dpy, False);
1664 saver_exit (si, 0, 0);
1667 clientmessage_response (si, window, True,
1668 "EXIT ClientMessage received while locked.",
1669 "screen is locked.");
1671 else if (type == XA_RESTART)
1673 /* The RESTART message works whether the screensaver is active or not,
1674 unless the screen is locked, in which case it doesn't work.
1676 if (until_idle_p || !si->locked_p)
1678 clientmessage_response (si, window, False,
1679 "RESTART ClientMessage received.",
1683 unblank_screen (si);
1684 kill_screenhack (si);
1685 XSync (si->dpy, False);
1688 restart_process (si); /* does not return */
1692 clientmessage_response (si, window, True,
1693 "RESTART ClientMessage received while locked.",
1694 "screen is locked.");
1696 else if (type == XA_DEMO)
1698 long arg = event->xclient.data.l[1];
1699 Bool demo_one_hack_p = (arg == 300);
1701 if (demo_one_hack_p)
1705 long which = event->xclient.data.l[2];
1708 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1709 sprintf (buf2, "demoing (%ld).", which);
1710 clientmessage_response (si, window, False, buf, buf2);
1712 if (which < 0) which = 0; /* 0 == "random" */
1713 si->selection_mode = which;
1714 si->demoing_p = True;
1716 if (si->throttled_p && p->verbose_p)
1717 fprintf (stderr, "%s: unthrottled.\n", blurb());
1718 si->throttled_p = False;
1723 clientmessage_response (si, window, True,
1724 "DEMO ClientMessage received while active.",
1729 clientmessage_response (si, window, True,
1730 "obsolete form of DEMO ClientMessage.",
1731 "obsolete form of DEMO ClientMessage.");
1734 else if (type == XA_PREFS)
1736 clientmessage_response (si, window, True,
1737 "the PREFS client-message is obsolete.",
1738 "the PREFS client-message is obsolete.");
1740 else if (type == XA_LOCK)
1743 clientmessage_response (si, window, True,
1744 "not compiled with support for locking.",
1745 "locking not enabled.");
1746 #else /* !NO_LOCKING */
1747 if (si->locking_disabled_p)
1748 clientmessage_response (si, window, True,
1749 "LOCK ClientMessage received, but locking is disabled.",
1750 "locking not enabled.");
1751 else if (si->locked_p)
1752 clientmessage_response (si, window, True,
1753 "LOCK ClientMessage received while already locked.",
1758 char *response = (until_idle_p
1759 ? "activating and locking."
1761 sprintf (buf, "LOCK ClientMessage received; %s", response);
1762 clientmessage_response (si, window, False, buf, response);
1763 set_locked_p (si, True);
1764 si->selection_mode = 0;
1765 si->demoing_p = False;
1767 if (si->lock_id) /* we're doing it now, so lose the timeout */
1769 XtRemoveTimeOut (si->lock_id);
1775 if (si->using_mit_saver_extension ||
1776 si->using_sgi_saver_extension)
1778 XForceScreenSaver (si->dpy, ScreenSaverActive);
1787 #endif /* !NO_LOCKING */
1789 else if (type == XA_THROTTLE)
1791 if (si->throttled_p)
1792 clientmessage_response (si, window, True,
1793 "THROTTLE ClientMessage received, but "
1794 "already throttled.",
1795 "already throttled.");
1799 char *response = "throttled.";
1800 si->throttled_p = True;
1801 si->selection_mode = 0;
1802 si->demoing_p = False;
1803 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
1804 clientmessage_response (si, window, False, buf, response);
1809 XtRemoveTimeOut (si->cycle_id);
1811 cycle_timer ((XtPointer) si, 0);
1815 else if (type == XA_UNTHROTTLE)
1817 if (! si->throttled_p)
1818 clientmessage_response (si, window, True,
1819 "UNTHROTTLE ClientMessage received, but "
1825 char *response = "unthrottled.";
1826 si->throttled_p = False;
1827 si->selection_mode = 0;
1828 si->demoing_p = False;
1829 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
1830 clientmessage_response (si, window, False, buf, response);
1835 XtRemoveTimeOut (si->cycle_id);
1837 cycle_timer ((XtPointer) si, 0);
1845 str = XGetAtomName_safe (si->dpy, type);
1849 if (strlen (str) > 80)
1850 strcpy (str+70, "...");
1851 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
1858 "unrecognised screensaver ClientMessage 0x%x received.",
1859 (unsigned int) event->xclient.data.l[0]);
1862 clientmessage_response (si, window, True, buf, buf);
1868 /* Some random diagnostics printed in -verbose mode.
1872 analyze_display (saver_info *si)
1876 const char *name; const char *desc; Bool useful_p;
1879 { "SCREEN_SAVER", /* underscore */ "SGI Screen-Saver",
1880 # ifdef HAVE_SGI_SAVER_EXTENSION
1885 }, { "SCREEN-SAVER", /* dash */ "SGI Screen-Saver",
1886 # ifdef HAVE_SGI_SAVER_EXTENSION
1891 }, { "MIT-SCREEN-SAVER", "MIT Screen-Saver",
1892 # ifdef HAVE_MIT_SAVER_EXTENSION
1897 }, { "XIDLE", "XIdle",
1898 # ifdef HAVE_XIDLE_EXTENSION
1903 }, { "SGI-VIDEO-CONTROL", "SGI Video-Control",
1904 # ifdef HAVE_SGI_VC_EXTENSION
1909 }, { "READDISPLAY", "SGI Read-Display",
1910 # ifdef HAVE_READ_DISPLAY_EXTENSION
1915 }, { "MIT-SHM", "Shared Memory",
1916 # ifdef HAVE_XSHM_EXTENSION
1921 }, { "DOUBLE-BUFFER", "Double-Buffering",
1922 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
1927 }, { "DPMS", "Power Management",
1928 # ifdef HAVE_DPMS_EXTENSION
1939 }, { "XFree86-VidModeExtension", "XF86 Video-Mode",
1940 # ifdef HAVE_XF86VMODE
1945 }, { "XINERAMA", "Xinerama",
1947 }, { "Apple-DRI", "Apple-DRI (XDarwin)",
1952 fprintf (stderr, "%s: running on display \"%s\" (%d screen%s).\n",
1954 DisplayString(si->dpy),
1955 si->nscreens, (si->nscreens == 1 ? "" : "s"));
1956 fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
1957 ServerVendor(si->dpy), VendorRelease(si->dpy));
1959 fprintf (stderr, "%s: useful extensions:\n", blurb());
1960 for (i = 0; i < countof(exts); i++)
1962 int op = 0, event = 0, error = 0;
1965 if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
1967 sprintf (buf, "%s: ", blurb());
1969 strcat (buf, exts[i].desc);
1970 if (!exts[i].useful_p)
1973 while (strlen (buf) < k) strcat (buf, " ");
1974 strcat (buf, "<-- not supported at compile time!");
1976 fprintf (stderr, "%s\n", buf);
1979 for (i = 0; i < si->nscreens; i++)
1981 unsigned long colormapped_depths = 0;
1982 unsigned long non_mapped_depths = 0;
1983 XVisualInfo vi_in, *vi_out;
1986 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
1987 if (!vi_out) continue;
1988 for (j = 0; j < out_count; j++)
1989 if (vi_out[j].class == PseudoColor)
1990 colormapped_depths |= (1 << vi_out[j].depth);
1992 non_mapped_depths |= (1 << vi_out[j].depth);
1993 XFree ((char *) vi_out);
1995 if (colormapped_depths)
1997 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(), i);
1998 for (j = 0; j < 32; j++)
1999 if (colormapped_depths & (1 << j))
2000 fprintf (stderr, " %d", j);
2001 fprintf (stderr, ".\n");
2003 if (non_mapped_depths)
2005 fprintf (stderr, "%s: screen %d non-colormapped depths:",
2007 for (j = 0; j < 32; j++)
2008 if (non_mapped_depths & (1 << j))
2009 fprintf (stderr, " %d", j);
2010 fprintf (stderr, ".\n");
2016 display_is_on_console_p (saver_info *si)
2018 Bool not_on_console = True;
2019 char *dpystr = DisplayString (si->dpy);
2020 char *tail = (char *) strchr (dpystr, ':');
2021 if (! tail || strncmp (tail, ":0", 2))
2022 not_on_console = True;
2025 char dpyname[255], localname[255];
2026 strncpy (dpyname, dpystr, tail-dpystr);
2027 dpyname [tail-dpystr] = 0;
2029 !strcmp(dpyname, "unix") ||
2030 !strcmp(dpyname, "localhost"))
2031 not_on_console = False;
2032 else if (gethostname (localname, sizeof (localname)))
2033 not_on_console = True; /* can't find hostname? */
2036 /* We have to call gethostbyname() on the result of gethostname()
2037 because the two aren't guarenteed to be the same name for the
2038 same host: on some losing systems, one is a FQDN and the other
2039 is not. Here in the wide wonderful world of Unix it's rocket
2040 science to obtain the local hostname in a portable fashion.
2042 And don't forget, gethostbyname() reuses the structure it
2043 returns, so we have to copy the fucker before calling it again.
2044 Thank you master, may I have another.
2046 struct hostent *h = gethostbyname (dpyname);
2048 not_on_console = True;
2053 strcpy (hn, h->h_name);
2054 l = gethostbyname (localname);
2055 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
2059 return !not_on_console;
2063 /* Do a little bit of heap introspection...
2066 check_for_leaks (const char *where)
2069 static unsigned long last_brk = 0;
2070 int b = (unsigned long) sbrk(0);
2071 if (last_brk && last_brk < b)
2072 fprintf (stderr, "%s: %s: brk grew by %luK.\n",
2074 (((b - last_brk) + 1023) / 1024));
2076 #endif /* HAVE_SBRK */