1 /* xscreensaver, Copyright (c) 1991-2002 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>
130 #include <X11/Xatom.h>
131 #include <X11/Intrinsic.h>
132 #include <X11/StringDefs.h>
133 #include <X11/Shell.h>
136 #include <sys/time.h>
137 #include <netdb.h> /* for gethostbyname() */
140 # include <X11/Xmu/Error.h>
142 # include <Xmu/Error.h>
144 #else /* !HAVE_XMU */
146 #endif /* !HAVE_XMU */
148 #ifdef HAVE_XIDLE_EXTENSION
149 # include <X11/extensions/xidle.h>
150 #endif /* HAVE_XIDLE_EXTENSION */
152 #include "xscreensaver.h"
154 #include "yarandom.h"
155 #include "resources.h"
159 saver_info *global_si_kludge = 0; /* I hate C so much... */
166 static Atom XA_SCREENSAVER_RESPONSE;
167 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
168 static Atom XA_RESTART, XA_SELECT;
169 static Atom XA_THROTTLE, XA_UNTHROTTLE;
170 Atom XA_DEMO, XA_PREFS, XA_EXIT, XA_LOCK, XA_BLANK;
173 static XrmOptionDescRec options [] = {
175 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
176 { "-silent", ".verbose", XrmoptionNoArg, "off" },
178 /* xscreensaver-demo uses this one */
179 { "-nosplash", ".splash", XrmoptionNoArg, "off" },
180 { "-no-splash", ".splash", XrmoptionNoArg, "off" },
182 /* useful for debugging */
183 { "-no-capture-stderr", ".captureStderr", XrmoptionNoArg, "off" },
185 /* There's really no reason to have these command-line args; they just
186 lead to confusion when the .xscreensaver file has conflicting values.
189 { "-splash", ".splash", XrmoptionNoArg, "on" },
190 { "-capture-stderr", ".captureStderr", XrmoptionNoArg, "on" },
191 { "-timeout", ".timeout", XrmoptionSepArg, 0 },
192 { "-cycle", ".cycle", XrmoptionSepArg, 0 },
193 { "-lock-mode", ".lock", XrmoptionNoArg, "on" },
194 { "-no-lock-mode", ".lock", XrmoptionNoArg, "off" },
195 { "-no-lock", ".lock", XrmoptionNoArg, "off" },
196 { "-lock-timeout", ".lockTimeout", XrmoptionSepArg, 0 },
197 { "-lock-vts", ".lockVTs", XrmoptionNoArg, "on" },
198 { "-no-lock-vts", ".lockVTs", XrmoptionNoArg, "off" },
199 { "-visual", ".visualID", XrmoptionSepArg, 0 },
200 { "-install", ".installColormap", XrmoptionNoArg, "on" },
201 { "-no-install", ".installColormap", XrmoptionNoArg, "off" },
202 { "-timestamp", ".timestamp", XrmoptionNoArg, "on" },
203 { "-xidle-extension", ".xidleExtension", XrmoptionNoArg, "on" },
204 { "-no-xidle-extension", ".xidleExtension", XrmoptionNoArg, "off" },
205 { "-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "on" },
206 { "-no-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "off" },
207 { "-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "on" },
208 { "-no-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "off" },
209 { "-proc-interrupts", ".procInterrupts", XrmoptionNoArg, "on" },
210 { "-no-proc-interrupts", ".procInterrupts", XrmoptionNoArg, "off" },
211 { "-idelay", ".initialDelay", XrmoptionSepArg, 0 },
212 { "-nice", ".nice", XrmoptionSepArg, 0 },
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-2002 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\
238 Just getting started? Try this:\n\
243 For updates, online manual, and FAQ, please see the web page:\n\
245 http://www.jwz.org/xscreensaver/\n\
257 time_t now = time ((time_t *) 0);
258 char *str = (char *) ctime (&now);
259 char *nl = (char *) strchr (str, '\n');
260 if (nl) *nl = 0; /* take off that dang newline */
264 static Bool blurb_timestamp_p = False; /* kludge */
269 if (!blurb_timestamp_p)
273 static char buf[255];
274 char *ct = timestring();
275 int n = strlen(progname);
277 strncpy(buf, progname, n);
280 strncpy(buf+n, ct+11, 8);
281 strcpy(buf+n+9, ": ");
288 saver_ehandler (Display *dpy, XErrorEvent *error)
290 saver_info *si = global_si_kludge; /* I hate C so much... */
294 if (!real_stderr) real_stderr = stderr;
296 fprintf (real_stderr, "\n"
297 "#######################################"
298 "#######################################\n\n"
299 "%s: X Error! PLEASE REPORT THIS BUG.\n",
302 for (i = 0; i < si->nscreens; i++)
303 fprintf (real_stderr, "%s: screen %d: 0x%x, 0x%x, 0x%x\n",
305 (unsigned int) RootWindowOfScreen (si->screens[i].screen),
306 (unsigned int) si->screens[i].real_vroot,
307 (unsigned int) si->screens[i].screensaver_window);
309 fprintf (real_stderr, "\n"
310 "#######################################"
311 "#######################################\n\n");
313 fatal_p = XmuPrintDefaultErrorMessage (dpy, error, real_stderr);
315 fatal_p = True; /* The only time I've ever seen a supposedly nonfatal error,
316 it has been BadImplementation / Xlib sequence lost, which
317 are in truth pretty damned fatal.
320 fprintf (real_stderr, "\n");
323 fprintf (real_stderr, "%s: nonfatal error.\n\n", blurb());
326 if (si->prefs.xsync_p)
328 saver_exit (si, -1, "because of synchronous X Error");
332 fprintf (real_stderr,
333 "#######################################"
334 "#######################################\n\n");
335 fprintf (real_stderr,
336 " If at all possible, please re-run xscreensaver with the command\n"
337 " line arguments `-sync -verbose -no-capture', and reproduce this\n"
338 " bug. That will cause xscreensaver to dump a `core' file to the\n"
339 " current directory. Please include the stack trace from that core\n"
340 " file in your bug report. *DO NOT* mail the core file itself!\n"
341 " That won't work.\n"
343 " http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
344 " the most useful bug reports, and how to examine core files.\n"
346 " The more information you can provide, the better. But please\n"
347 " report this bug, regardless!\n"
349 fprintf (real_stderr,
350 "#######################################"
351 "#######################################\n\n");
353 saver_exit (si, -1, 0);
361 /* This error handler is used only while the X connection is being set up;
362 after we've got a connection, we don't use this handler again. The only
363 reason for having this is so that we can present a more idiot-proof error
364 message than "cannot open display."
367 startup_ehandler (String name, String type, String class,
368 String defalt, /* one can't even spel properly
369 in this joke of a language */
370 String *av, Cardinal *ac)
374 saver_info *si = global_si_kludge; /* I hate C so much... */
375 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
377 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
378 fmt, sizeof(fmt)-1, *db);
380 fprintf (stderr, "%s: ", blurb());
382 memset (p, 0, sizeof(p));
383 if (*ac > countof (p)) *ac = countof (p);
384 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
385 fprintf (stderr, fmt, /* Did I mention that I hate C? */
386 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
387 fprintf (stderr, "\n");
389 describe_uids (si, stderr);
391 if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
393 fprintf (stderr, "\n"
394 "%s: This is probably because you're logging in as root. You\n"
395 " shouldn't log in as root: you should log in as a normal user,\n"
396 " and then `su' as needed. If you insist on logging in as\n"
397 " root, you will have to turn off X's security features before\n"
398 " xscreensaver will work.\n"
400 " Please read the manual and FAQ for more information:\n",
405 fprintf (stderr, "\n"
406 "%s: Errors at startup are usually authorization problems.\n"
407 " But you're not logging in as root (good!) so something\n"
408 " else must be wrong. Did you read the manual and the FAQ?\n",
412 fprintf (stderr, "\n"
413 " http://www.jwz.org/xscreensaver/faq.html\n"
414 " http://www.jwz.org/xscreensaver/man.html\n"
423 /* The zillions of initializations.
426 /* Set progname, version, etc. This is done very early.
429 set_version_string (saver_info *si, int *argc, char **argv)
431 progclass = "XScreenSaver";
433 /* progname is reset later, after we connect to X. */
434 progname = strrchr(argv[0], '/');
435 if (progname) progname++;
436 else progname = argv[0];
438 if (strlen(progname) > 100) /* keep it short. */
441 /* The X resource database blows up if argv[0] has a "." in it. */
444 while ((s = strchr (s, '.')))
448 si->version = (char *) malloc (5);
449 memcpy (si->version, screensaver_id + 17, 4);
454 /* Initializations that potentially take place as a priveleged user:
455 If the xscreensaver executable is setuid root, then these initializations
456 are run as root, before discarding privileges.
459 privileged_initialization (saver_info *si, int *argc, char **argv)
462 /* before hack_uid() for proper permissions */
463 lock_priv_init (*argc, argv, si->prefs.verbose_p);
464 #endif /* NO_LOCKING */
470 /* Figure out what locking mechanisms are supported.
473 lock_initialization (saver_info *si, int *argc, char **argv)
476 si->locking_disabled_p = True;
477 si->nolock_reason = "not compiled with locking support";
478 #else /* !NO_LOCKING */
480 /* Finish initializing locking, now that we're out of privileged code. */
481 if (! lock_init (*argc, argv, si->prefs.verbose_p))
483 si->locking_disabled_p = True;
484 si->nolock_reason = "error getting password";
487 /* If locking is currently enabled, but the environment indicates that
488 we have been launched as GDM's "Background" program, then disable
489 locking just in case.
491 if (!si->locking_disabled_p && getenv ("RUNNING_UNDER_GDM"))
493 si->locking_disabled_p = True;
494 si->nolock_reason = "running under GDM";
497 /* If the server is XDarwin (MacOS X) then disable locking.
498 (X grabs only affect X programs, so you can use Command-Tab
499 to bring any other Mac program to the front, e.g., Terminal.)
501 if (!si->locking_disabled_p)
503 int op = 0, event = 0, error = 0;
504 Bool macos_p = False;
507 /* Disable locking if *running* on Apple hardware, since we have no
508 reliable way to determine whether the server is running on MacOS.
509 Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
510 but I'm not really sure about that.
516 /* This extension exists on the Apple X11 server, but not
517 on earlier versions of the XDarwin server. */
518 macos_p = XQueryExtension (si->dpy, "Apple-DRI", &op, &event, &error);
522 si->locking_disabled_p = True;
523 si->nolock_reason = "Cannot lock securely on MacOS X";
527 #endif /* NO_LOCKING */
531 /* Open the connection to the X server, and intern our Atoms.
534 connect_to_server (saver_info *si, int *argc, char **argv)
536 Widget toplevel_shell;
539 char *d = getenv ("DISPLAY");
542 char *ndpy = strdup("DISPLAY=:0.0");
543 /* if (si->prefs.verbose_p) */ /* sigh, too early to test this... */
545 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
549 /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
550 glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
551 do not. So we must leak it (and/or the previous setting). Yay.
554 #endif /* HAVE_PUTENV */
556 XSetErrorHandler (saver_ehandler);
558 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
559 toplevel_shell = XtAppInitialize (&si->app, progclass,
560 options, XtNumber (options),
561 argc, argv, defaults, 0, 0);
562 XtAppSetErrorMsgHandler (si->app, 0);
564 si->dpy = XtDisplay (toplevel_shell);
565 si->prefs.db = XtDatabase (si->dpy);
566 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
568 if(strlen(progname) > 100) /* keep it short. */
571 db = si->prefs.db; /* resources.c needs this */
573 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
574 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
575 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
576 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
577 XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
578 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
580 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
581 XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False);
582 XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False);
583 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
584 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
585 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
586 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
587 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
588 XA_PREV = XInternAtom (si->dpy, "PREV", False);
589 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
590 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
591 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
592 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
593 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
594 XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
595 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
596 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
598 return toplevel_shell;
602 /* Handle the command-line arguments that were not handled for us by Xt.
603 Issue an error message and exit if there are unknown options.
606 process_command_line (saver_info *si, int *argc, char **argv)
609 for (i = 1; i < *argc; i++)
611 if (!strcmp (argv[i], "-debug"))
612 /* no resource for this one, out of paranoia. */
613 si->prefs.debug_p = True;
615 else if (!strcmp (argv[i], "-h") ||
616 !strcmp (argv[i], "-help") ||
617 !strcmp (argv[i], "--help"))
622 const char *s = argv[i];
623 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
626 if (s[0] == '-' && s[1] == '-') s++;
627 if (!strcmp (s, "-activate") ||
628 !strcmp (s, "-deactivate") ||
629 !strcmp (s, "-cycle") ||
630 !strcmp (s, "-next") ||
631 !strcmp (s, "-prev") ||
632 !strcmp (s, "-exit") ||
633 !strcmp (s, "-restart") ||
634 !strcmp (s, "-demo") ||
635 !strcmp (s, "-prefs") ||
636 !strcmp (s, "-preferences") ||
637 !strcmp (s, "-lock") ||
638 !strcmp (s, "-version") ||
639 !strcmp (s, "-time"))
642 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
643 fprintf (stderr, "\n\
644 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
646 fprintf (stderr, "\n\
647 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
650 The `xscreensaver' program is a daemon that runs in the background.\n\
651 You control a running xscreensaver process by sending it messages\n\
652 with `xscreensaver-demo' or `xscreensaver-command'.\n\
653 . See the man pages for details, or check the web page:\n\
654 http://www.jwz.org/xscreensaver/\n\n");
656 /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
657 suggest that explicitly. */
658 if (!strcmp (s, "-lock"))
660 Or perhaps you meant either the \"-lock-mode\" or the\n\
661 \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
670 /* Print out the xscreensaver banner to the tty if applicable;
671 Issue any other warnings that are called for at this point.
674 print_banner (saver_info *si)
676 saver_preferences *p = &si->prefs;
678 /* This resource gets set some time before the others, so that we know
679 whether to print the banner (and so that the banner gets printed before
680 any resource-database-related error messages.)
682 p->verbose_p = (p->debug_p || get_boolean_resource ("verbose", "Boolean"));
684 /* Ditto, for the locking_disabled_p message. */
685 p->lock_p = get_boolean_resource ("lock", "Boolean");
689 "%s %s, copyright (c) 1991-2002 "
690 "by Jamie Zawinski <jwz@jwz.org>.\n",
691 progname, si->version);
694 fprintf (stderr, "\n"
695 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
697 "\tNote that in debug mode, the xscreensaver window will only\n"
698 "\tcover the left half of the screen. (The idea is that you\n"
699 "\tcan still see debugging output in a shell, if you position\n"
700 "\tit on the right side of the screen.)\n"
702 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
703 "\tuntrusted environments.\n"
709 if (!si->uid_message || !*si->uid_message)
710 describe_uids (si, stderr);
713 if (si->orig_uid && *si->orig_uid)
714 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
715 blurb(), si->orig_uid);
716 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
719 fprintf (stderr, "%s: in process %lu.\n", blurb(),
720 (unsigned long) getpid());
725 print_lock_failure_banner (saver_info *si)
727 saver_preferences *p = &si->prefs;
729 /* If locking was not able to be initalized for some reason, explain why.
730 (This has to be done after we've read the lock_p resource.)
732 if (si->locking_disabled_p)
735 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
737 if (strstr (si->nolock_reason, "passw"))
738 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
739 "consult the manual.\n", blurb());
740 else if (strstr (si->nolock_reason, "running as "))
742 "%s: locking only works when xscreensaver is launched\n"
743 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
744 "\t See the manual for details.\n",
751 /* Examine all of the display's screens, and populate the `saver_screen_info'
752 structures. Make sure this is called after hack_environment() sets $PATH.
755 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
757 Bool found_any_writable_cells = False;
760 si->nscreens = ScreenCount(si->dpy);
761 si->screens = (saver_screen_info *)
762 calloc(sizeof(saver_screen_info), si->nscreens);
764 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
766 for (i = 0; i < si->nscreens; i++)
768 saver_screen_info *ssi = &si->screens[i];
770 ssi->screen = ScreenOfDisplay (si->dpy, i);
773 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
774 ssi->default_visual =
775 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
777 ssi->current_visual = ssi->default_visual;
778 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
780 /* Execute a subprocess to find the GL visual. */
781 ssi->best_gl_visual = get_best_gl_visual (ssi);
783 if (ssi == si->default_screen)
784 /* Since this is the default screen, use the one already created. */
785 ssi->toplevel_shell = toplevel_shell;
787 /* Otherwise, each screen must have its own unmapped root widget. */
788 ssi->toplevel_shell =
789 XtVaAppCreateShell (progname, progclass, applicationShellWidgetClass,
791 XtNscreen, ssi->screen,
792 XtNvisual, ssi->current_visual,
793 XtNdepth, visual_depth (ssi->screen,
794 ssi->current_visual),
797 if (! found_any_writable_cells)
799 /* Check to see whether fading is ever possible -- if any of the
800 screens on the display has a PseudoColor visual, then fading can
801 work (on at least some screens.) If no screen has a PseudoColor
802 visual, then don't bother ever trying to fade, because it will
803 just cause a delay without causing any visible effect.
805 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
806 get_visual (ssi->screen, "PseudoColor", True, False) ||
807 get_visual (ssi->screen, "GrayScale", True, False))
808 found_any_writable_cells = True;
812 si->fading_possible_p = found_any_writable_cells;
814 #ifdef HAVE_XF86VMODE_GAMMA
815 si->fading_possible_p = True; /* if we can gamma fade, go for it */
820 /* If any server extensions have been requested, try and initialize them.
821 Issue warnings if requests can't be honored.
824 initialize_server_extensions (saver_info *si)
826 saver_preferences *p = &si->prefs;
828 Bool server_has_xidle_extension_p = False;
829 Bool server_has_sgi_saver_extension_p = False;
830 Bool server_has_mit_saver_extension_p = False;
831 Bool system_has_proc_interrupts_p = False;
832 const char *piwhy = 0;
834 si->using_xidle_extension = p->use_xidle_extension;
835 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
836 si->using_mit_saver_extension = p->use_mit_saver_extension;
837 si->using_proc_interrupts = p->use_proc_interrupts;
839 #ifdef HAVE_XIDLE_EXTENSION
840 server_has_xidle_extension_p = query_xidle_extension (si);
842 #ifdef HAVE_SGI_SAVER_EXTENSION
843 server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
845 #ifdef HAVE_MIT_SAVER_EXTENSION
846 server_has_mit_saver_extension_p = query_mit_saver_extension (si);
848 #ifdef HAVE_PROC_INTERRUPTS
849 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
852 if (!server_has_xidle_extension_p)
853 si->using_xidle_extension = False;
854 else if (p->verbose_p)
856 if (si->using_xidle_extension)
857 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
859 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
862 if (!server_has_sgi_saver_extension_p)
863 si->using_sgi_saver_extension = False;
864 else if (p->verbose_p)
866 if (si->using_sgi_saver_extension)
867 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
870 "%s: not using server's SGI SCREEN_SAVER extension.\n",
874 if (!server_has_mit_saver_extension_p)
875 si->using_mit_saver_extension = False;
876 else if (p->verbose_p)
878 if (si->using_mit_saver_extension)
879 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
883 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
887 if (!system_has_proc_interrupts_p)
889 si->using_proc_interrupts = False;
890 if (p->verbose_p && piwhy)
891 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
894 else if (p->verbose_p)
896 if (si->using_proc_interrupts)
898 "%s: consulting /proc/interrupts for keyboard activity.\n",
902 "%s: not consulting /proc/interrupts for keyboard activity.\n",
908 /* For the case where we aren't using an server extensions, select user events
909 on all the existing windows, and launch timers to select events on
910 newly-created windows as well.
912 If a server extension is being used, this does nothing.
915 select_events (saver_info *si)
917 saver_preferences *p = &si->prefs;
920 if (si->using_xidle_extension ||
921 si->using_mit_saver_extension ||
922 si->using_sgi_saver_extension)
925 if (p->initial_delay)
929 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
930 (int) p->initial_delay/1000,
931 (p->initial_delay == 1000 ? "" : "s"));
935 usleep (p->initial_delay);
937 fprintf (stderr, " done.\n");
942 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
947 /* Select events on the root windows of every screen. This also selects
948 for window creation events, so that new subwindows will be noticed.
950 for (i = 0; i < si->nscreens; i++)
951 start_notice_events_timer (si, RootWindowOfScreen (si->screens[i].screen),
955 fprintf (stderr, " done.\n");
960 maybe_reload_init_file (saver_info *si)
962 saver_preferences *p = &si->prefs;
963 if (init_file_changed_p (p))
966 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
967 blurb(), init_file_name());
971 /* If a server extension is in use, and p->timeout has changed,
972 we need to inform the server of the new timeout. */
973 disable_builtin_screensaver (si, False);
975 /* If the DPMS settings in the init file have changed,
976 change the settings on the server to match. */
977 sync_server_dpms_settings (si->dpy,
978 (p->dpms_enabled_p &&
979 p->mode != DONT_BLANK),
980 p->dpms_standby / 1000,
981 p->dpms_suspend / 1000,
990 - wait until the user is idle;
992 - wait until the user is active;
993 - unblank the screen;
998 main_loop (saver_info *si)
1000 saver_preferences *p = &si->prefs;
1005 Bool was_locked = False;
1008 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1010 check_for_leaks ("unblanked A");
1011 sleep_until_idle (si, True);
1012 check_for_leaks ("unblanked B");
1017 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
1018 si->selection_mode, timestring());
1020 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
1024 maybe_reload_init_file (si);
1026 if (p->mode == DONT_BLANK)
1029 fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
1030 blurb(), timestring());
1032 /* Go around the loop and wait for the next bout of idleness,
1033 or for the init file to change, or for a remote command to
1034 come in, or something.
1039 if (! blank_screen (si))
1041 /* We were unable to grab either the keyboard or mouse.
1042 This means we did not (and must not) blank the screen.
1043 If we were to blank the screen while some other program
1044 is holding both the mouse and keyboard grabbed, then
1045 we would never be able to un-blank it! We would never
1046 see any events, and the display would be wedged.
1048 So, just go around the loop again and wait for the
1049 next bout of idleness.
1053 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
1058 kill_screenhack (si);
1060 if (!si->throttled_p)
1061 spawn_screenhack (si, True);
1062 else if (p->verbose_p)
1063 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
1065 /* Don't start the cycle timer in demo mode. */
1066 if (!si->demoing_p && p->cycle)
1067 si->cycle_id = XtAppAddTimeOut (si->app,
1069 /* see comment in cycle_timer() */
1077 /* Maybe start locking the screen.
1080 Time lock_timeout = p->lock_timeout;
1082 if (si->emergency_lock_p && p->lock_p && lock_timeout)
1084 int secs = p->lock_timeout / 1000;
1087 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1089 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1093 si->emergency_lock_p = False;
1095 if (!si->demoing_p && /* if not going into demo mode */
1096 p->lock_p && /* and locking is enabled */
1097 !si->locking_disabled_p && /* and locking is possible */
1098 lock_timeout == 0) /* and locking is not timer-deferred */
1099 set_locked_p (si, True); /* then lock right now. */
1101 /* locked_p might be true already because of the above, or because of
1102 the LOCK ClientMessage. But if not, and if we're supposed to lock
1103 after some time, set up a timer to do so.
1108 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1109 activate_lock_timer,
1112 #endif /* !NO_LOCKING */
1115 ok_to_unblank = True;
1118 check_for_leaks ("blanked A");
1119 sleep_until_idle (si, False); /* until not idle */
1120 check_for_leaks ("blanked B");
1122 maybe_reload_init_file (si);
1125 /* Maybe unlock the screen.
1129 saver_screen_info *ssi = si->default_screen;
1130 if (si->locking_disabled_p) abort ();
1133 si->dbox_up_p = True;
1134 suspend_screenhack (si, True);
1135 XUndefineCursor (si->dpy, ssi->screensaver_window);
1137 ok_to_unblank = unlock_p (si);
1139 si->dbox_up_p = False;
1140 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1141 suspend_screenhack (si, False); /* resume */
1143 if (!ok_to_unblank &&
1144 !screenhack_running_p (si))
1146 /* If the lock dialog has been dismissed and we're not about to
1147 unlock the screen, and there is currently no hack running,
1148 then launch one. (There might be no hack running if DPMS
1149 had kicked in. But DPMS is off now, so bring back the hack)
1152 XtRemoveTimeOut (si->cycle_id);
1154 cycle_timer ((XtPointer) si, 0);
1157 #endif /* !NO_LOCKING */
1159 } while (!ok_to_unblank);
1163 fprintf (stderr, "%s: unblanking screen at %s.\n",
1164 blurb(), timestring ());
1166 /* Kill before unblanking, to stop drawing as soon as possible. */
1167 kill_screenhack (si);
1168 unblank_screen (si);
1170 set_locked_p (si, False);
1171 si->emergency_lock_p = False;
1173 si->selection_mode = 0;
1175 /* If we're throttled, and the user has explicitly unlocked the screen,
1176 then unthrottle. If we weren't locked, then don't unthrottle
1177 automatically, because someone might have just bumped the desk... */
1180 if (si->throttled_p && p->verbose_p)
1181 fprintf (stderr, "%s: unthrottled.\n", blurb());
1182 si->throttled_p = False;
1187 XtRemoveTimeOut (si->cycle_id);
1193 XtRemoveTimeOut (si->lock_id);
1197 /* It's possible that a race condition could have led to the saver
1198 window being unexpectedly still mapped. This can happen like so:
1202 - that hack tries to grab a screen image( it does this by
1203 first unmapping the saver window, then remapping it.)
1204 - hack unmaps window
1206 - user becomes active
1207 - hack re-maps window (*)
1208 - driver kills subprocess
1209 - driver unmaps window (**)
1211 The race is that (*) might have been sent to the server before
1212 the client process was killed, but, due to scheduling randomness,
1213 might not have been received by the server until after (**).
1214 In other words, (*) and (**) might happen out of order, meaning
1215 the driver will unmap the window, and then after that, the
1216 recently-dead client will re-map it. This leaves the user
1217 locked out (it looks like a desktop, but it's not!)
1219 To avoid this: after un-blanking the screen, sleep for a second,
1220 and then really make sure the window is unmapped.
1224 XSync (si->dpy, False);
1226 for (i = 0; i < si->nscreens; i++)
1228 saver_screen_info *ssi = &si->screens[i];
1229 Window w = ssi->screensaver_window;
1230 XWindowAttributes xgwa;
1231 XGetWindowAttributes (si->dpy, w, &xgwa);
1232 if (xgwa.map_state != IsUnmapped)
1236 "%s: %d: client race! emergency unmap 0x%lx.\n",
1237 blurb(), i, (unsigned long) w);
1238 XUnmapWindow (si->dpy, w);
1241 XSync (si->dpy, False);
1246 static void analyze_display (saver_info *si);
1247 static void fix_fds (void);
1250 main (int argc, char **argv)
1254 saver_info *si = &the_si;
1255 saver_preferences *p = &si->prefs;
1258 memset(si, 0, sizeof(*si));
1259 global_si_kludge = si; /* I hate C so much... */
1263 # undef ya_rand_init
1266 save_argv (argc, argv);
1267 set_version_string (si, &argc, argv);
1268 privileged_initialization (si, &argc, argv);
1269 hack_environment (si);
1271 shell = connect_to_server (si, &argc, argv);
1272 process_command_line (si, &argc, argv);
1275 load_init_file (p); /* must be before initialize_per_screen_info() */
1276 blurb_timestamp_p = p->timestamp_p; /* kludge */
1277 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1279 /* We can only issue this warnings now. */
1280 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1282 "%s: there are no PseudoColor or GrayScale visuals.\n"
1283 "%s: ignoring the request for fading/unfading.\n",
1286 for (i = 0; i < si->nscreens; i++)
1287 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1290 lock_initialization (si, &argc, argv);
1291 print_lock_failure_banner (si);
1293 if (p->xsync_p) XSynchronize (si->dpy, True);
1295 if (p->verbose_p) analyze_display (si);
1296 initialize_server_extensions (si);
1298 si->blank_time = time ((time_t) 0); /* must be before ..._window */
1299 initialize_screensaver_window (si);
1304 disable_builtin_screensaver (si, True);
1305 sync_server_dpms_settings (si->dpy,
1306 (p->dpms_enabled_p &&
1307 p->mode != DONT_BLANK),
1308 p->dpms_standby / 1000,
1309 p->dpms_suspend / 1000,
1313 initialize_stderr (si);
1314 handle_signals (si);
1316 make_splash_dialog (si);
1318 main_loop (si); /* doesn't return */
1325 /* Bad Things Happen if stdin, stdout, and stderr have been closed
1326 (as by the `sh incantation "xscreensaver >&- 2>&-"). When you do
1327 that, the X connection gets allocated to one of these fds, and
1328 then some random library writes to stderr, and random bits get
1329 stuffed down the X pipe, causing "Xlib: sequence lost" errors.
1330 So, we cause the first three file descriptors to be open to
1331 /dev/null if they aren't open to something else already. This
1332 must be done before any other files are opened (or the closing
1333 of that other file will again free up one of the "magic" first
1336 We do this by opening /dev/null three times, and then closing
1337 those fds, *unless* any of them got allocated as #0, #1, or #2,
1338 in which case we leave them open. Gag.
1340 Really, this crap is technically required of *every* X program,
1341 if you want it to be robust in the face of "2>&-".
1343 int fd0 = open ("/dev/null", O_RDWR);
1344 int fd1 = open ("/dev/null", O_RDWR);
1345 int fd2 = open ("/dev/null", O_RDWR);
1346 if (fd0 > 2) close (fd0);
1347 if (fd1 > 2) close (fd1);
1348 if (fd2 > 2) close (fd2);
1353 /* Processing ClientMessage events.
1357 static Bool error_handler_hit_p = False;
1360 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1362 error_handler_hit_p = True;
1366 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1367 them. We only look up the atom names for printing warning messages,
1368 so don't bomb out when it happens...
1371 XGetAtomName_safe (Display *dpy, Atom atom)
1374 XErrorHandler old_handler;
1375 if (!atom) return 0;
1378 error_handler_hit_p = False;
1379 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1380 result = XGetAtomName (dpy, atom);
1382 XSetErrorHandler (old_handler);
1384 if (error_handler_hit_p) result = 0;
1391 sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
1392 return strdup (buf);
1398 clientmessage_response (saver_info *si, Window w, Bool error,
1399 const char *stderr_msg,
1400 const char *protocol_msg)
1404 saver_preferences *p = &si->prefs;
1405 XErrorHandler old_handler;
1407 if (error || p->verbose_p)
1408 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1410 L = strlen(protocol_msg);
1411 proto = (char *) malloc (L + 2);
1412 proto[0] = (error ? '-' : '+');
1413 strcpy (proto+1, protocol_msg);
1416 /* Ignore all X errors while sending a response to a ClientMessage.
1417 Pretty much the only way we could get an error here is if the
1418 window we're trying to send the reply on has been deleted, in
1419 which case, the sender of the ClientMessage won't see our response
1422 XSync (si->dpy, False);
1423 error_handler_hit_p = False;
1424 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1426 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1427 PropModeReplace, (unsigned char *) proto, L);
1429 XSync (si->dpy, False);
1430 XSetErrorHandler (old_handler);
1431 XSync (si->dpy, False);
1438 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1440 char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1441 Window w = event->xclient.window;
1446 for (screen = 0; screen < si->nscreens; screen++)
1447 if (w == si->screens[screen].screensaver_window)
1449 strcpy (wdesc, "xscreensaver");
1452 else if (w == RootWindow (si->dpy, screen))
1454 strcpy (wdesc, "root");
1460 XErrorHandler old_handler;
1462 XWindowAttributes xgwa;
1463 memset (&hint, 0, sizeof(hint));
1464 memset (&xgwa, 0, sizeof(xgwa));
1466 XSync (si->dpy, False);
1467 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1468 XGetClassHint (si->dpy, w, &hint);
1469 XGetWindowAttributes (si->dpy, w, &xgwa);
1470 XSync (si->dpy, False);
1471 XSetErrorHandler (old_handler);
1472 XSync (si->dpy, False);
1474 screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1476 sprintf (wdesc, "%.20s / %.20s",
1477 (hint.res_name ? hint.res_name : "(null)"),
1478 (hint.res_class ? hint.res_class : "(null)"));
1479 if (hint.res_name) XFree (hint.res_name);
1480 if (hint.res_class) XFree (hint.res_class);
1483 fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1484 blurb(), screen, (str ? str : "(null)"));
1485 fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1486 blurb(), screen, (unsigned long) w, wdesc);
1487 if (str) XFree (str);
1491 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1493 saver_preferences *p = &si->prefs;
1495 Window window = event->xclient.window;
1497 /* Preferences might affect our handling of client messages. */
1498 maybe_reload_init_file (si);
1500 if (event->xclient.message_type != XA_SCREENSAVER ||
1501 event->xclient.format != 32)
1503 bogus_clientmessage_warning (si, event);
1507 type = event->xclient.data.l[0];
1508 if (type == XA_ACTIVATE)
1512 clientmessage_response(si, window, False,
1513 "ACTIVATE ClientMessage received.",
1515 si->selection_mode = 0;
1516 si->demoing_p = False;
1518 if (si->throttled_p && p->verbose_p)
1519 fprintf (stderr, "%s: unthrottled.\n", blurb());
1520 si->throttled_p = False;
1522 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1524 XForceScreenSaver (si->dpy, ScreenSaverActive);
1532 clientmessage_response(si, window, True,
1533 "ClientMessage ACTIVATE received while already active.",
1536 else if (type == XA_DEACTIVATE)
1540 if (si->throttled_p && p->verbose_p)
1541 fprintf (stderr, "%s: unthrottled.\n", blurb());
1542 si->throttled_p = False;
1544 clientmessage_response(si, window, False,
1545 "DEACTIVATE ClientMessage received.",
1547 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1549 XForceScreenSaver (si->dpy, ScreenSaverReset);
1557 clientmessage_response(si, window, False,
1558 "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
1559 "not active: idle timer reset.");
1562 else if (type == XA_CYCLE)
1566 clientmessage_response(si, window, False,
1567 "CYCLE ClientMessage received.",
1569 si->selection_mode = 0; /* 0 means randomize when its time. */
1570 si->demoing_p = False;
1572 if (si->throttled_p && p->verbose_p)
1573 fprintf (stderr, "%s: unthrottled.\n", blurb());
1574 si->throttled_p = False;
1577 XtRemoveTimeOut (si->cycle_id);
1579 cycle_timer ((XtPointer) si, 0);
1582 clientmessage_response(si, window, True,
1583 "ClientMessage CYCLE received while inactive.",
1586 else if (type == XA_NEXT || type == XA_PREV)
1588 clientmessage_response(si, window, False,
1590 ? "NEXT ClientMessage received."
1591 : "PREV ClientMessage received."),
1593 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1594 si->demoing_p = False;
1596 if (si->throttled_p && p->verbose_p)
1597 fprintf (stderr, "%s: unthrottled.\n", blurb());
1598 si->throttled_p = False;
1603 XtRemoveTimeOut (si->cycle_id);
1605 cycle_timer ((XtPointer) si, 0);
1610 else if (type == XA_SELECT)
1614 long which = event->xclient.data.l[1];
1616 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1617 sprintf (buf2, "activating (%ld).", which);
1618 clientmessage_response (si, window, False, buf, buf2);
1620 if (which < 0) which = 0; /* 0 == "random" */
1621 si->selection_mode = which;
1622 si->demoing_p = False;
1624 if (si->throttled_p && p->verbose_p)
1625 fprintf (stderr, "%s: unthrottled.\n", blurb());
1626 si->throttled_p = False;
1631 XtRemoveTimeOut (si->cycle_id);
1633 cycle_timer ((XtPointer) si, 0);
1638 else if (type == XA_EXIT)
1640 /* Ignore EXIT message if the screen is locked. */
1641 if (until_idle_p || !si->locked_p)
1643 clientmessage_response (si, window, False,
1644 "EXIT ClientMessage received.",
1648 unblank_screen (si);
1649 kill_screenhack (si);
1650 XSync (si->dpy, False);
1652 saver_exit (si, 0, 0);
1655 clientmessage_response (si, window, True,
1656 "EXIT ClientMessage received while locked.",
1657 "screen is locked.");
1659 else if (type == XA_RESTART)
1661 /* The RESTART message works whether the screensaver is active or not,
1662 unless the screen is locked, in which case it doesn't work.
1664 if (until_idle_p || !si->locked_p)
1666 clientmessage_response (si, window, False,
1667 "RESTART ClientMessage received.",
1671 unblank_screen (si);
1672 kill_screenhack (si);
1673 XSync (si->dpy, False);
1676 restart_process (si); /* does not return */
1680 clientmessage_response (si, window, True,
1681 "RESTART ClientMessage received while locked.",
1682 "screen is locked.");
1684 else if (type == XA_DEMO)
1686 long arg = event->xclient.data.l[1];
1687 Bool demo_one_hack_p = (arg == 300);
1689 if (demo_one_hack_p)
1693 long which = event->xclient.data.l[2];
1696 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1697 sprintf (buf2, "demoing (%ld).", which);
1698 clientmessage_response (si, window, False, buf, buf2);
1700 if (which < 0) which = 0; /* 0 == "random" */
1701 si->selection_mode = which;
1702 si->demoing_p = True;
1704 if (si->throttled_p && p->verbose_p)
1705 fprintf (stderr, "%s: unthrottled.\n", blurb());
1706 si->throttled_p = False;
1711 clientmessage_response (si, window, True,
1712 "DEMO ClientMessage received while active.",
1717 clientmessage_response (si, window, True,
1718 "obsolete form of DEMO ClientMessage.",
1719 "obsolete form of DEMO ClientMessage.");
1722 else if (type == XA_PREFS)
1724 clientmessage_response (si, window, True,
1725 "the PREFS client-message is obsolete.",
1726 "the PREFS client-message is obsolete.");
1728 else if (type == XA_LOCK)
1731 clientmessage_response (si, window, True,
1732 "not compiled with support for locking.",
1733 "locking not enabled.");
1734 #else /* !NO_LOCKING */
1735 if (si->locking_disabled_p)
1736 clientmessage_response (si, window, True,
1737 "LOCK ClientMessage received, but locking is disabled.",
1738 "locking not enabled.");
1739 else if (si->locked_p)
1740 clientmessage_response (si, window, True,
1741 "LOCK ClientMessage received while already locked.",
1746 char *response = (until_idle_p
1747 ? "activating and locking."
1749 sprintf (buf, "LOCK ClientMessage received; %s", response);
1750 clientmessage_response (si, window, False, buf, response);
1751 set_locked_p (si, True);
1752 si->selection_mode = 0;
1753 si->demoing_p = False;
1755 if (si->lock_id) /* we're doing it now, so lose the timeout */
1757 XtRemoveTimeOut (si->lock_id);
1763 if (si->using_mit_saver_extension ||
1764 si->using_sgi_saver_extension)
1766 XForceScreenSaver (si->dpy, ScreenSaverActive);
1775 #endif /* !NO_LOCKING */
1777 else if (type == XA_THROTTLE)
1779 if (si->throttled_p)
1780 clientmessage_response (si, window, True,
1781 "THROTTLE ClientMessage received, but "
1782 "already throttled.",
1783 "already throttled.");
1787 char *response = "throttled.";
1788 si->throttled_p = True;
1789 si->selection_mode = 0;
1790 si->demoing_p = False;
1791 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
1792 clientmessage_response (si, window, False, buf, response);
1797 XtRemoveTimeOut (si->cycle_id);
1799 cycle_timer ((XtPointer) si, 0);
1803 else if (type == XA_UNTHROTTLE)
1805 if (! si->throttled_p)
1806 clientmessage_response (si, window, True,
1807 "UNTHROTTLE ClientMessage received, but "
1813 char *response = "unthrottled.";
1814 si->throttled_p = False;
1815 si->selection_mode = 0;
1816 si->demoing_p = False;
1817 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
1818 clientmessage_response (si, window, False, buf, response);
1823 XtRemoveTimeOut (si->cycle_id);
1825 cycle_timer ((XtPointer) si, 0);
1833 str = XGetAtomName_safe (si->dpy, type);
1837 if (strlen (str) > 80)
1838 strcpy (str+70, "...");
1839 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
1846 "unrecognised screensaver ClientMessage 0x%x received.",
1847 (unsigned int) event->xclient.data.l[0]);
1850 clientmessage_response (si, window, True, buf, buf);
1856 /* Some random diagnostics printed in -verbose mode.
1860 analyze_display (saver_info *si)
1864 const char *name; const char *desc; Bool useful_p;
1867 { "SCREEN_SAVER", /* underscore */ "SGI Screen-Saver",
1868 # ifdef HAVE_SGI_SAVER_EXTENSION
1873 }, { "SCREEN-SAVER", /* dash */ "SGI Screen-Saver",
1874 # ifdef HAVE_SGI_SAVER_EXTENSION
1879 }, { "MIT-SCREEN-SAVER", "MIT Screen-Saver",
1880 # ifdef HAVE_MIT_SAVER_EXTENSION
1885 }, { "XIDLE", "XIdle",
1886 # ifdef HAVE_XIDLE_EXTENSION
1891 }, { "SGI-VIDEO-CONTROL", "SGI Video-Control",
1892 # ifdef HAVE_SGI_VC_EXTENSION
1897 }, { "READDISPLAY", "SGI Read-Display",
1898 # ifdef HAVE_READ_DISPLAY_EXTENSION
1903 }, { "MIT-SHM", "Shared Memory",
1904 # ifdef HAVE_XSHM_EXTENSION
1909 }, { "DOUBLE-BUFFER", "Double-Buffering",
1910 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
1915 }, { "DPMS", "Power Management",
1916 # ifdef HAVE_DPMS_EXTENSION
1927 }, { "XFree86-VidModeExtension", "XF86 Video-Mode",
1928 # ifdef HAVE_XF86VMODE
1933 }, { "XINERAMA", "Xinerama",
1935 }, { "Apple-DRI", "Apple-DRI (XDarwin)",
1940 fprintf (stderr, "%s: running on display \"%s\" (%d screen%s).\n",
1942 DisplayString(si->dpy),
1943 si->nscreens, (si->nscreens == 1 ? "" : "s"));
1944 fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
1945 ServerVendor(si->dpy), VendorRelease(si->dpy));
1947 fprintf (stderr, "%s: useful extensions:\n", blurb());
1948 for (i = 0; i < countof(exts); i++)
1950 int op = 0, event = 0, error = 0;
1953 if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
1955 sprintf (buf, "%s: ", blurb());
1957 strcat (buf, exts[i].desc);
1958 if (!exts[i].useful_p)
1961 while (strlen (buf) < k) strcat (buf, " ");
1962 strcat (buf, "<-- not supported at compile time!");
1964 fprintf (stderr, "%s\n", buf);
1967 for (i = 0; i < si->nscreens; i++)
1969 unsigned long colormapped_depths = 0;
1970 unsigned long non_mapped_depths = 0;
1971 XVisualInfo vi_in, *vi_out;
1974 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
1975 if (!vi_out) continue;
1976 for (j = 0; j < out_count; j++)
1977 if (vi_out[j].class == PseudoColor)
1978 colormapped_depths |= (1 << vi_out[j].depth);
1980 non_mapped_depths |= (1 << vi_out[j].depth);
1981 XFree ((char *) vi_out);
1983 if (colormapped_depths)
1985 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(), i);
1986 for (j = 0; j < 32; j++)
1987 if (colormapped_depths & (1 << j))
1988 fprintf (stderr, " %d", j);
1989 fprintf (stderr, ".\n");
1991 if (non_mapped_depths)
1993 fprintf (stderr, "%s: screen %d non-colormapped depths:",
1995 for (j = 0; j < 32; j++)
1996 if (non_mapped_depths & (1 << j))
1997 fprintf (stderr, " %d", j);
1998 fprintf (stderr, ".\n");
2004 display_is_on_console_p (saver_info *si)
2006 Bool not_on_console = True;
2007 char *dpystr = DisplayString (si->dpy);
2008 char *tail = (char *) strchr (dpystr, ':');
2009 if (! tail || strncmp (tail, ":0", 2))
2010 not_on_console = True;
2013 char dpyname[255], localname[255];
2014 strncpy (dpyname, dpystr, tail-dpystr);
2015 dpyname [tail-dpystr] = 0;
2017 !strcmp(dpyname, "unix") ||
2018 !strcmp(dpyname, "localhost"))
2019 not_on_console = False;
2020 else if (gethostname (localname, sizeof (localname)))
2021 not_on_console = True; /* can't find hostname? */
2024 /* We have to call gethostbyname() on the result of gethostname()
2025 because the two aren't guarenteed to be the same name for the
2026 same host: on some losing systems, one is a FQDN and the other
2027 is not. Here in the wide wonderful world of Unix it's rocket
2028 science to obtain the local hostname in a portable fashion.
2030 And don't forget, gethostbyname() reuses the structure it
2031 returns, so we have to copy the fucker before calling it again.
2032 Thank you master, may I have another.
2034 struct hostent *h = gethostbyname (dpyname);
2036 not_on_console = True;
2041 strcpy (hn, h->h_name);
2042 l = gethostbyname (localname);
2043 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
2047 return !not_on_console;
2051 /* Do a little bit of heap introspection...
2054 check_for_leaks (const char *where)
2057 static unsigned long last_brk = 0;
2058 int b = (unsigned long) sbrk(0);
2059 if (last_brk && last_brk < b)
2060 fprintf (stderr, "%s: %s: brk grew by %luK.\n",
2062 (((b - last_brk) + 1023) / 1024));
2064 #endif /* HAVE_SBRK */