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 },
219 static char *defaults[] = {
220 #include "XScreenSaver_ad.h"
225 ERROR! You must not include vroot.h in this file.
229 do_help (saver_info *si)
234 xscreensaver %s, copyright (c) 1991-2003 by Jamie Zawinski <jwz@jwz.org>\n\
236 All xscreensaver configuration is via the `~/.xscreensaver' file.\n\
237 Rather than editing that file by hand, just run `xscreensaver-demo':\n\
238 that program lets you configure the screen saver graphically,\n\
239 including timeouts, locking, and display modes.\n\
241 Just getting started? Try this:\n\
246 For updates, online manual, and FAQ, please see the web page:\n\
248 http://www.jwz.org/xscreensaver/\n\
260 time_t now = time ((time_t *) 0);
261 char *str = (char *) ctime (&now);
262 char *nl = (char *) strchr (str, '\n');
263 if (nl) *nl = 0; /* take off that dang newline */
267 static Bool blurb_timestamp_p = False; /* kludge */
272 if (!blurb_timestamp_p)
276 static char buf[255];
277 char *ct = timestring();
278 int n = strlen(progname);
280 strncpy(buf, progname, n);
283 strncpy(buf+n, ct+11, 8);
284 strcpy(buf+n+9, ": ");
291 saver_ehandler (Display *dpy, XErrorEvent *error)
293 saver_info *si = global_si_kludge; /* I hate C so much... */
297 if (!real_stderr) real_stderr = stderr;
299 fprintf (real_stderr, "\n"
300 "#######################################"
301 "#######################################\n\n"
302 "%s: X Error! PLEASE REPORT THIS BUG.\n",
305 for (i = 0; i < si->nscreens; i++)
306 fprintf (real_stderr, "%s: screen %d: 0x%x, 0x%x, 0x%x\n",
308 (unsigned int) RootWindowOfScreen (si->screens[i].screen),
309 (unsigned int) si->screens[i].real_vroot,
310 (unsigned int) si->screens[i].screensaver_window);
312 fprintf (real_stderr, "\n"
313 "#######################################"
314 "#######################################\n\n");
316 fatal_p = XmuPrintDefaultErrorMessage (dpy, error, real_stderr);
318 fatal_p = True; /* The only time I've ever seen a supposedly nonfatal error,
319 it has been BadImplementation / Xlib sequence lost, which
320 are in truth pretty damned fatal.
323 fprintf (real_stderr, "\n");
326 fprintf (real_stderr, "%s: nonfatal error.\n\n", blurb());
329 if (si->prefs.xsync_p)
331 saver_exit (si, -1, "because of synchronous X Error");
335 fprintf (real_stderr,
336 "#######################################"
337 "#######################################\n\n");
338 fprintf (real_stderr,
339 " If at all possible, please re-run xscreensaver with the command\n"
340 " line arguments `-sync -verbose -no-capture', and reproduce this\n"
341 " bug. That will cause xscreensaver to dump a `core' file to the\n"
342 " current directory. Please include the stack trace from that core\n"
343 " file in your bug report. *DO NOT* mail the core file itself!\n"
344 " That won't work.\n"
346 " http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
347 " the most useful bug reports, and how to examine core files.\n"
349 " The more information you can provide, the better. But please\n"
350 " report this bug, regardless!\n"
352 fprintf (real_stderr,
353 "#######################################"
354 "#######################################\n\n");
356 saver_exit (si, -1, 0);
364 /* This error handler is used only while the X connection is being set up;
365 after we've got a connection, we don't use this handler again. The only
366 reason for having this is so that we can present a more idiot-proof error
367 message than "cannot open display."
370 startup_ehandler (String name, String type, String class,
371 String defalt, /* one can't even spel properly
372 in this joke of a language */
373 String *av, Cardinal *ac)
377 saver_info *si = global_si_kludge; /* I hate C so much... */
378 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
380 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
381 fmt, sizeof(fmt)-1, *db);
383 fprintf (stderr, "%s: ", blurb());
385 memset (p, 0, sizeof(p));
386 if (*ac > countof (p)) *ac = countof (p);
387 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
388 fprintf (stderr, fmt, /* Did I mention that I hate C? */
389 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
390 fprintf (stderr, "\n");
392 describe_uids (si, stderr);
394 if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
396 fprintf (stderr, "\n"
397 "%s: This is probably because you're logging in as root. You\n"
398 " shouldn't log in as root: you should log in as a normal user,\n"
399 " and then `su' as needed. If you insist on logging in as\n"
400 " root, you will have to turn off X's security features before\n"
401 " xscreensaver will work.\n"
403 " Please read the manual and FAQ for more information:\n",
408 fprintf (stderr, "\n"
409 "%s: Errors at startup are usually authorization problems.\n"
410 " But you're not logging in as root (good!) so something\n"
411 " else must be wrong. Did you read the manual and the FAQ?\n",
415 fprintf (stderr, "\n"
416 " http://www.jwz.org/xscreensaver/faq.html\n"
417 " http://www.jwz.org/xscreensaver/man.html\n"
426 /* The zillions of initializations.
429 /* Set progname, version, etc. This is done very early.
432 set_version_string (saver_info *si, int *argc, char **argv)
434 progclass = "XScreenSaver";
436 /* progname is reset later, after we connect to X. */
437 progname = strrchr(argv[0], '/');
438 if (progname) progname++;
439 else progname = argv[0];
441 if (strlen(progname) > 100) /* keep it short. */
444 /* The X resource database blows up if argv[0] has a "." in it. */
447 while ((s = strchr (s, '.')))
451 si->version = (char *) malloc (5);
452 memcpy (si->version, screensaver_id + 17, 4);
457 /* Initializations that potentially take place as a priveleged user:
458 If the xscreensaver executable is setuid root, then these initializations
459 are run as root, before discarding privileges.
462 privileged_initialization (saver_info *si, int *argc, char **argv)
465 /* before hack_uid() for proper permissions */
466 lock_priv_init (*argc, argv, si->prefs.verbose_p);
467 #endif /* NO_LOCKING */
473 /* Figure out what locking mechanisms are supported.
476 lock_initialization (saver_info *si, int *argc, char **argv)
479 si->locking_disabled_p = True;
480 si->nolock_reason = "not compiled with locking support";
481 #else /* !NO_LOCKING */
483 /* Finish initializing locking, now that we're out of privileged code. */
484 if (! lock_init (*argc, argv, si->prefs.verbose_p))
486 si->locking_disabled_p = True;
487 si->nolock_reason = "error getting password";
490 /* If locking is currently enabled, but the environment indicates that
491 we have been launched as GDM's "Background" program, then disable
492 locking just in case.
494 if (!si->locking_disabled_p && getenv ("RUNNING_UNDER_GDM"))
496 si->locking_disabled_p = True;
497 si->nolock_reason = "running under GDM";
500 /* If the server is XDarwin (MacOS X) then disable locking.
501 (X grabs only affect X programs, so you can use Command-Tab
502 to bring any other Mac program to the front, e.g., Terminal.)
504 if (!si->locking_disabled_p)
506 int op = 0, event = 0, error = 0;
507 Bool macos_p = False;
510 /* Disable locking if *running* on Apple hardware, since we have no
511 reliable way to determine whether the server is running on MacOS.
512 Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
513 but I'm not really sure about that.
519 /* This extension exists on the Apple X11 server, but not
520 on earlier versions of the XDarwin server. */
521 macos_p = XQueryExtension (si->dpy, "Apple-DRI", &op, &event, &error);
525 si->locking_disabled_p = True;
526 si->nolock_reason = "Cannot lock securely on MacOS X";
530 #endif /* NO_LOCKING */
534 /* Open the connection to the X server, and intern our Atoms.
537 connect_to_server (saver_info *si, int *argc, char **argv)
539 Widget toplevel_shell;
542 char *d = getenv ("DISPLAY");
545 char *ndpy = strdup("DISPLAY=:0.0");
546 /* if (si->prefs.verbose_p) */ /* sigh, too early to test this... */
548 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
552 /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
553 glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
554 do not. So we must leak it (and/or the previous setting). Yay.
557 #endif /* HAVE_PUTENV */
559 XSetErrorHandler (saver_ehandler);
561 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
562 toplevel_shell = XtAppInitialize (&si->app, progclass,
563 options, XtNumber (options),
564 argc, argv, defaults, 0, 0);
565 XtAppSetErrorMsgHandler (si->app, 0);
567 si->dpy = XtDisplay (toplevel_shell);
568 si->prefs.db = XtDatabase (si->dpy);
569 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
571 if(strlen(progname) > 100) /* keep it short. */
574 db = si->prefs.db; /* resources.c needs this */
576 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
577 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
578 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
579 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
580 XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
581 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
583 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
584 XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False);
585 XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False);
586 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
587 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
588 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
589 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
590 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
591 XA_PREV = XInternAtom (si->dpy, "PREV", False);
592 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
593 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
594 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
595 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
596 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
597 XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
598 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
599 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
601 return toplevel_shell;
605 /* Handle the command-line arguments that were not handled for us by Xt.
606 Issue an error message and exit if there are unknown options.
609 process_command_line (saver_info *si, int *argc, char **argv)
612 for (i = 1; i < *argc; i++)
614 if (!strcmp (argv[i], "-debug"))
615 /* no resource for this one, out of paranoia. */
616 si->prefs.debug_p = True;
618 else if (!strcmp (argv[i], "-h") ||
619 !strcmp (argv[i], "-help") ||
620 !strcmp (argv[i], "--help"))
625 const char *s = argv[i];
626 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
629 if (s[0] == '-' && s[1] == '-') s++;
630 if (!strcmp (s, "-activate") ||
631 !strcmp (s, "-deactivate") ||
632 !strcmp (s, "-cycle") ||
633 !strcmp (s, "-next") ||
634 !strcmp (s, "-prev") ||
635 !strcmp (s, "-exit") ||
636 !strcmp (s, "-restart") ||
637 !strcmp (s, "-demo") ||
638 !strcmp (s, "-prefs") ||
639 !strcmp (s, "-preferences") ||
640 !strcmp (s, "-lock") ||
641 !strcmp (s, "-version") ||
642 !strcmp (s, "-time"))
645 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
646 fprintf (stderr, "\n\
647 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
649 fprintf (stderr, "\n\
650 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
653 The `xscreensaver' program is a daemon that runs in the background.\n\
654 You control a running xscreensaver process by sending it messages\n\
655 with `xscreensaver-demo' or `xscreensaver-command'.\n\
656 . See the man pages for details, or check the web page:\n\
657 http://www.jwz.org/xscreensaver/\n\n");
659 /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
660 suggest that explicitly. */
661 if (!strcmp (s, "-lock"))
663 Or perhaps you meant either the \"-lock-mode\" or the\n\
664 \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
673 /* Print out the xscreensaver banner to the tty if applicable;
674 Issue any other warnings that are called for at this point.
677 print_banner (saver_info *si)
679 saver_preferences *p = &si->prefs;
681 /* This resource gets set some time before the others, so that we know
682 whether to print the banner (and so that the banner gets printed before
683 any resource-database-related error messages.)
685 p->verbose_p = (p->debug_p || get_boolean_resource ("verbose", "Boolean"));
687 /* Ditto, for the locking_disabled_p message. */
688 p->lock_p = get_boolean_resource ("lock", "Boolean");
692 "%s %s, copyright (c) 1991-2003 "
693 "by Jamie Zawinski <jwz@jwz.org>.\n",
694 progname, si->version);
697 fprintf (stderr, "\n"
698 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
700 "\tNote that in debug mode, the xscreensaver window will only\n"
701 "\tcover the left half of the screen. (The idea is that you\n"
702 "\tcan still see debugging output in a shell, if you position\n"
703 "\tit on the right side of the screen.)\n"
705 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
706 "\tuntrusted environments.\n"
712 if (!si->uid_message || !*si->uid_message)
713 describe_uids (si, stderr);
716 if (si->orig_uid && *si->orig_uid)
717 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
718 blurb(), si->orig_uid);
719 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
722 fprintf (stderr, "%s: in process %lu.\n", blurb(),
723 (unsigned long) getpid());
728 print_lock_failure_banner (saver_info *si)
730 saver_preferences *p = &si->prefs;
732 /* If locking was not able to be initalized for some reason, explain why.
733 (This has to be done after we've read the lock_p resource.)
735 if (si->locking_disabled_p)
738 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
740 if (strstr (si->nolock_reason, "passw"))
741 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
742 "consult the manual.\n", blurb());
743 else if (strstr (si->nolock_reason, "running as "))
745 "%s: locking only works when xscreensaver is launched\n"
746 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
747 "\t See the manual for details.\n",
754 /* Examine all of the display's screens, and populate the `saver_screen_info'
755 structures. Make sure this is called after hack_environment() sets $PATH.
758 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
760 Bool found_any_writable_cells = False;
763 si->nscreens = ScreenCount(si->dpy);
764 si->screens = (saver_screen_info *)
765 calloc(sizeof(saver_screen_info), si->nscreens);
767 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
769 for (i = 0; i < si->nscreens; i++)
771 saver_screen_info *ssi = &si->screens[i];
773 ssi->screen = ScreenOfDisplay (si->dpy, i);
776 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
777 ssi->default_visual =
778 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
780 ssi->current_visual = ssi->default_visual;
781 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
783 /* Execute a subprocess to find the GL visual. */
784 ssi->best_gl_visual = get_best_gl_visual (ssi);
786 if (ssi == si->default_screen)
787 /* Since this is the default screen, use the one already created. */
788 ssi->toplevel_shell = toplevel_shell;
790 /* Otherwise, each screen must have its own unmapped root widget. */
791 ssi->toplevel_shell =
792 XtVaAppCreateShell (progname, progclass, applicationShellWidgetClass,
794 XtNscreen, ssi->screen,
795 XtNvisual, ssi->current_visual,
796 XtNdepth, visual_depth (ssi->screen,
797 ssi->current_visual),
800 if (! found_any_writable_cells)
802 /* Check to see whether fading is ever possible -- if any of the
803 screens on the display has a PseudoColor visual, then fading can
804 work (on at least some screens.) If no screen has a PseudoColor
805 visual, then don't bother ever trying to fade, because it will
806 just cause a delay without causing any visible effect.
808 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
809 get_visual (ssi->screen, "PseudoColor", True, False) ||
810 get_visual (ssi->screen, "GrayScale", True, False))
811 found_any_writable_cells = True;
815 si->fading_possible_p = found_any_writable_cells;
817 #ifdef HAVE_XF86VMODE_GAMMA
818 si->fading_possible_p = True; /* if we can gamma fade, go for it */
823 /* If any server extensions have been requested, try and initialize them.
824 Issue warnings if requests can't be honored.
827 initialize_server_extensions (saver_info *si)
829 saver_preferences *p = &si->prefs;
831 Bool server_has_xidle_extension_p = False;
832 Bool server_has_sgi_saver_extension_p = False;
833 Bool server_has_mit_saver_extension_p = False;
834 Bool system_has_proc_interrupts_p = False;
835 const char *piwhy = 0;
837 si->using_xidle_extension = p->use_xidle_extension;
838 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
839 si->using_mit_saver_extension = p->use_mit_saver_extension;
840 si->using_proc_interrupts = p->use_proc_interrupts;
842 #ifdef HAVE_XIDLE_EXTENSION
843 server_has_xidle_extension_p = query_xidle_extension (si);
845 #ifdef HAVE_SGI_SAVER_EXTENSION
846 server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
848 #ifdef HAVE_MIT_SAVER_EXTENSION
849 server_has_mit_saver_extension_p = query_mit_saver_extension (si);
851 #ifdef HAVE_PROC_INTERRUPTS
852 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
855 if (!server_has_xidle_extension_p)
856 si->using_xidle_extension = False;
857 else if (p->verbose_p)
859 if (si->using_xidle_extension)
860 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
862 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
865 if (!server_has_sgi_saver_extension_p)
866 si->using_sgi_saver_extension = False;
867 else if (p->verbose_p)
869 if (si->using_sgi_saver_extension)
870 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
873 "%s: not using server's SGI SCREEN_SAVER extension.\n",
877 if (!server_has_mit_saver_extension_p)
878 si->using_mit_saver_extension = False;
879 else if (p->verbose_p)
881 if (si->using_mit_saver_extension)
882 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
886 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
890 if (!system_has_proc_interrupts_p)
892 si->using_proc_interrupts = False;
893 if (p->verbose_p && piwhy)
894 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
897 else if (p->verbose_p)
899 if (si->using_proc_interrupts)
901 "%s: consulting /proc/interrupts for keyboard activity.\n",
905 "%s: not consulting /proc/interrupts for keyboard activity.\n",
911 /* For the case where we aren't using an server extensions, select user events
912 on all the existing windows, and launch timers to select events on
913 newly-created windows as well.
915 If a server extension is being used, this does nothing.
918 select_events (saver_info *si)
920 saver_preferences *p = &si->prefs;
923 if (si->using_xidle_extension ||
924 si->using_mit_saver_extension ||
925 si->using_sgi_saver_extension)
928 if (p->initial_delay)
932 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
933 (int) p->initial_delay/1000,
934 (p->initial_delay == 1000 ? "" : "s"));
938 usleep (p->initial_delay);
940 fprintf (stderr, " done.\n");
945 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
950 /* Select events on the root windows of every screen. This also selects
951 for window creation events, so that new subwindows will be noticed.
953 for (i = 0; i < si->nscreens; i++)
954 start_notice_events_timer (si, RootWindowOfScreen (si->screens[i].screen),
958 fprintf (stderr, " done.\n");
963 maybe_reload_init_file (saver_info *si)
965 saver_preferences *p = &si->prefs;
966 if (init_file_changed_p (p))
969 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
970 blurb(), init_file_name());
974 /* If a server extension is in use, and p->timeout has changed,
975 we need to inform the server of the new timeout. */
976 disable_builtin_screensaver (si, False);
978 /* If the DPMS settings in the init file have changed,
979 change the settings on the server to match. */
980 sync_server_dpms_settings (si->dpy,
981 (p->dpms_enabled_p &&
982 p->mode != DONT_BLANK),
983 p->dpms_standby / 1000,
984 p->dpms_suspend / 1000,
993 - wait until the user is idle;
995 - wait until the user is active;
996 - unblank the screen;
1001 main_loop (saver_info *si)
1003 saver_preferences *p = &si->prefs;
1008 Bool was_locked = False;
1011 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1013 check_for_leaks ("unblanked A");
1014 sleep_until_idle (si, True);
1015 check_for_leaks ("unblanked B");
1020 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
1021 si->selection_mode, timestring());
1023 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
1027 maybe_reload_init_file (si);
1029 if (p->mode == DONT_BLANK)
1032 fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
1033 blurb(), timestring());
1035 /* Go around the loop and wait for the next bout of idleness,
1036 or for the init file to change, or for a remote command to
1037 come in, or something.
1042 if (! blank_screen (si))
1044 /* We were unable to grab either the keyboard or mouse.
1045 This means we did not (and must not) blank the screen.
1046 If we were to blank the screen while some other program
1047 is holding both the mouse and keyboard grabbed, then
1048 we would never be able to un-blank it! We would never
1049 see any events, and the display would be wedged.
1051 So, just go around the loop again and wait for the
1052 next bout of idleness.
1056 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
1061 kill_screenhack (si);
1063 if (!si->throttled_p)
1064 spawn_screenhack (si, True);
1065 else if (p->verbose_p)
1066 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
1068 /* Don't start the cycle timer in demo mode. */
1069 if (!si->demoing_p && p->cycle)
1070 si->cycle_id = XtAppAddTimeOut (si->app,
1072 /* see comment in cycle_timer() */
1080 /* Maybe start locking the screen.
1083 Time lock_timeout = p->lock_timeout;
1085 if (si->emergency_lock_p && p->lock_p && lock_timeout)
1087 int secs = p->lock_timeout / 1000;
1090 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1092 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1096 si->emergency_lock_p = False;
1098 if (!si->demoing_p && /* if not going into demo mode */
1099 p->lock_p && /* and locking is enabled */
1100 !si->locking_disabled_p && /* and locking is possible */
1101 lock_timeout == 0) /* and locking is not timer-deferred */
1102 set_locked_p (si, True); /* then lock right now. */
1104 /* locked_p might be true already because of the above, or because of
1105 the LOCK ClientMessage. But if not, and if we're supposed to lock
1106 after some time, set up a timer to do so.
1111 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1112 activate_lock_timer,
1115 #endif /* !NO_LOCKING */
1118 ok_to_unblank = True;
1121 check_for_leaks ("blanked A");
1122 sleep_until_idle (si, False); /* until not idle */
1123 check_for_leaks ("blanked B");
1125 maybe_reload_init_file (si);
1128 /* Maybe unlock the screen.
1132 saver_screen_info *ssi = si->default_screen;
1133 if (si->locking_disabled_p) abort ();
1136 si->dbox_up_p = True;
1137 suspend_screenhack (si, True);
1138 XUndefineCursor (si->dpy, ssi->screensaver_window);
1140 ok_to_unblank = unlock_p (si);
1142 si->dbox_up_p = False;
1143 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1144 suspend_screenhack (si, False); /* resume */
1146 if (!ok_to_unblank &&
1147 !screenhack_running_p (si))
1149 /* If the lock dialog has been dismissed and we're not about to
1150 unlock the screen, and there is currently no hack running,
1151 then launch one. (There might be no hack running if DPMS
1152 had kicked in. But DPMS is off now, so bring back the hack)
1155 XtRemoveTimeOut (si->cycle_id);
1157 cycle_timer ((XtPointer) si, 0);
1160 #endif /* !NO_LOCKING */
1162 } while (!ok_to_unblank);
1166 fprintf (stderr, "%s: unblanking screen at %s.\n",
1167 blurb(), timestring ());
1169 /* Kill before unblanking, to stop drawing as soon as possible. */
1170 kill_screenhack (si);
1171 unblank_screen (si);
1173 set_locked_p (si, False);
1174 si->emergency_lock_p = False;
1176 si->selection_mode = 0;
1178 /* If we're throttled, and the user has explicitly unlocked the screen,
1179 then unthrottle. If we weren't locked, then don't unthrottle
1180 automatically, because someone might have just bumped the desk... */
1183 if (si->throttled_p && p->verbose_p)
1184 fprintf (stderr, "%s: unthrottled.\n", blurb());
1185 si->throttled_p = False;
1190 XtRemoveTimeOut (si->cycle_id);
1196 XtRemoveTimeOut (si->lock_id);
1200 /* It's possible that a race condition could have led to the saver
1201 window being unexpectedly still mapped. This can happen like so:
1205 - that hack tries to grab a screen image( it does this by
1206 first unmapping the saver window, then remapping it.)
1207 - hack unmaps window
1209 - user becomes active
1210 - hack re-maps window (*)
1211 - driver kills subprocess
1212 - driver unmaps window (**)
1214 The race is that (*) might have been sent to the server before
1215 the client process was killed, but, due to scheduling randomness,
1216 might not have been received by the server until after (**).
1217 In other words, (*) and (**) might happen out of order, meaning
1218 the driver will unmap the window, and then after that, the
1219 recently-dead client will re-map it. This leaves the user
1220 locked out (it looks like a desktop, but it's not!)
1222 To avoid this: after un-blanking the screen, sleep for a second,
1223 and then really make sure the window is unmapped.
1227 XSync (si->dpy, False);
1229 for (i = 0; i < si->nscreens; i++)
1231 saver_screen_info *ssi = &si->screens[i];
1232 Window w = ssi->screensaver_window;
1233 XWindowAttributes xgwa;
1234 XGetWindowAttributes (si->dpy, w, &xgwa);
1235 if (xgwa.map_state != IsUnmapped)
1239 "%s: %d: client race! emergency unmap 0x%lx.\n",
1240 blurb(), i, (unsigned long) w);
1241 XUnmapWindow (si->dpy, w);
1244 XSync (si->dpy, False);
1249 static void analyze_display (saver_info *si);
1250 static void fix_fds (void);
1253 main (int argc, char **argv)
1257 saver_info *si = &the_si;
1258 saver_preferences *p = &si->prefs;
1261 memset(si, 0, sizeof(*si));
1262 global_si_kludge = si; /* I hate C so much... */
1266 # undef ya_rand_init
1269 save_argv (argc, argv);
1270 set_version_string (si, &argc, argv);
1271 privileged_initialization (si, &argc, argv);
1272 hack_environment (si);
1274 shell = connect_to_server (si, &argc, argv);
1275 process_command_line (si, &argc, argv);
1278 load_init_file (p); /* must be before initialize_per_screen_info() */
1279 blurb_timestamp_p = p->timestamp_p; /* kludge */
1280 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1282 /* We can only issue this warnings now. */
1283 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1285 "%s: there are no PseudoColor or GrayScale visuals.\n"
1286 "%s: ignoring the request for fading/unfading.\n",
1289 for (i = 0; i < si->nscreens; i++)
1290 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1293 lock_initialization (si, &argc, argv);
1294 print_lock_failure_banner (si);
1296 if (p->xsync_p) XSynchronize (si->dpy, True);
1298 if (p->verbose_p) analyze_display (si);
1299 initialize_server_extensions (si);
1301 si->blank_time = time ((time_t) 0); /* must be before ..._window */
1302 initialize_screensaver_window (si);
1307 disable_builtin_screensaver (si, True);
1308 sync_server_dpms_settings (si->dpy,
1309 (p->dpms_enabled_p &&
1310 p->mode != DONT_BLANK),
1311 p->dpms_standby / 1000,
1312 p->dpms_suspend / 1000,
1316 initialize_stderr (si);
1317 handle_signals (si);
1319 make_splash_dialog (si);
1321 main_loop (si); /* doesn't return */
1328 /* Bad Things Happen if stdin, stdout, and stderr have been closed
1329 (as by the `sh incantation "xscreensaver >&- 2>&-"). When you do
1330 that, the X connection gets allocated to one of these fds, and
1331 then some random library writes to stderr, and random bits get
1332 stuffed down the X pipe, causing "Xlib: sequence lost" errors.
1333 So, we cause the first three file descriptors to be open to
1334 /dev/null if they aren't open to something else already. This
1335 must be done before any other files are opened (or the closing
1336 of that other file will again free up one of the "magic" first
1339 We do this by opening /dev/null three times, and then closing
1340 those fds, *unless* any of them got allocated as #0, #1, or #2,
1341 in which case we leave them open. Gag.
1343 Really, this crap is technically required of *every* X program,
1344 if you want it to be robust in the face of "2>&-".
1346 int fd0 = open ("/dev/null", O_RDWR);
1347 int fd1 = open ("/dev/null", O_RDWR);
1348 int fd2 = open ("/dev/null", O_RDWR);
1349 if (fd0 > 2) close (fd0);
1350 if (fd1 > 2) close (fd1);
1351 if (fd2 > 2) close (fd2);
1356 /* Processing ClientMessage events.
1360 static Bool error_handler_hit_p = False;
1363 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1365 error_handler_hit_p = True;
1369 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1370 them. We only look up the atom names for printing warning messages,
1371 so don't bomb out when it happens...
1374 XGetAtomName_safe (Display *dpy, Atom atom)
1377 XErrorHandler old_handler;
1378 if (!atom) return 0;
1381 error_handler_hit_p = False;
1382 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1383 result = XGetAtomName (dpy, atom);
1385 XSetErrorHandler (old_handler);
1387 if (error_handler_hit_p) result = 0;
1394 sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
1395 return strdup (buf);
1401 clientmessage_response (saver_info *si, Window w, Bool error,
1402 const char *stderr_msg,
1403 const char *protocol_msg)
1407 saver_preferences *p = &si->prefs;
1408 XErrorHandler old_handler;
1410 if (error || p->verbose_p)
1411 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1413 L = strlen(protocol_msg);
1414 proto = (char *) malloc (L + 2);
1415 proto[0] = (error ? '-' : '+');
1416 strcpy (proto+1, protocol_msg);
1419 /* Ignore all X errors while sending a response to a ClientMessage.
1420 Pretty much the only way we could get an error here is if the
1421 window we're trying to send the reply on has been deleted, in
1422 which case, the sender of the ClientMessage won't see our response
1425 XSync (si->dpy, False);
1426 error_handler_hit_p = False;
1427 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1429 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1430 PropModeReplace, (unsigned char *) proto, L);
1432 XSync (si->dpy, False);
1433 XSetErrorHandler (old_handler);
1434 XSync (si->dpy, False);
1441 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1443 char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1444 Window w = event->xclient.window;
1449 for (screen = 0; screen < si->nscreens; screen++)
1450 if (w == si->screens[screen].screensaver_window)
1452 strcpy (wdesc, "xscreensaver");
1455 else if (w == RootWindow (si->dpy, screen))
1457 strcpy (wdesc, "root");
1463 XErrorHandler old_handler;
1465 XWindowAttributes xgwa;
1466 memset (&hint, 0, sizeof(hint));
1467 memset (&xgwa, 0, sizeof(xgwa));
1469 XSync (si->dpy, False);
1470 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1471 XGetClassHint (si->dpy, w, &hint);
1472 XGetWindowAttributes (si->dpy, w, &xgwa);
1473 XSync (si->dpy, False);
1474 XSetErrorHandler (old_handler);
1475 XSync (si->dpy, False);
1477 screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1479 sprintf (wdesc, "%.20s / %.20s",
1480 (hint.res_name ? hint.res_name : "(null)"),
1481 (hint.res_class ? hint.res_class : "(null)"));
1482 if (hint.res_name) XFree (hint.res_name);
1483 if (hint.res_class) XFree (hint.res_class);
1486 fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1487 blurb(), screen, (str ? str : "(null)"));
1488 fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1489 blurb(), screen, (unsigned long) w, wdesc);
1490 if (str) XFree (str);
1494 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1496 saver_preferences *p = &si->prefs;
1498 Window window = event->xclient.window;
1500 /* Preferences might affect our handling of client messages. */
1501 maybe_reload_init_file (si);
1503 if (event->xclient.message_type != XA_SCREENSAVER ||
1504 event->xclient.format != 32)
1506 bogus_clientmessage_warning (si, event);
1510 type = event->xclient.data.l[0];
1511 if (type == XA_ACTIVATE)
1515 clientmessage_response(si, window, False,
1516 "ACTIVATE ClientMessage received.",
1518 si->selection_mode = 0;
1519 si->demoing_p = False;
1521 if (si->throttled_p && p->verbose_p)
1522 fprintf (stderr, "%s: unthrottled.\n", blurb());
1523 si->throttled_p = False;
1525 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1527 XForceScreenSaver (si->dpy, ScreenSaverActive);
1535 clientmessage_response(si, window, True,
1536 "ClientMessage ACTIVATE received while already active.",
1539 else if (type == XA_DEACTIVATE)
1543 if (si->throttled_p && p->verbose_p)
1544 fprintf (stderr, "%s: unthrottled.\n", blurb());
1545 si->throttled_p = False;
1547 clientmessage_response(si, window, False,
1548 "DEACTIVATE ClientMessage received.",
1550 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1552 XForceScreenSaver (si->dpy, ScreenSaverReset);
1560 clientmessage_response(si, window, False,
1561 "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
1562 "not active: idle timer reset.");
1565 else if (type == XA_CYCLE)
1569 clientmessage_response(si, window, False,
1570 "CYCLE ClientMessage received.",
1572 si->selection_mode = 0; /* 0 means randomize when its time. */
1573 si->demoing_p = False;
1575 if (si->throttled_p && p->verbose_p)
1576 fprintf (stderr, "%s: unthrottled.\n", blurb());
1577 si->throttled_p = False;
1580 XtRemoveTimeOut (si->cycle_id);
1582 cycle_timer ((XtPointer) si, 0);
1585 clientmessage_response(si, window, True,
1586 "ClientMessage CYCLE received while inactive.",
1589 else if (type == XA_NEXT || type == XA_PREV)
1591 clientmessage_response(si, window, False,
1593 ? "NEXT ClientMessage received."
1594 : "PREV ClientMessage received."),
1596 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1597 si->demoing_p = False;
1599 if (si->throttled_p && p->verbose_p)
1600 fprintf (stderr, "%s: unthrottled.\n", blurb());
1601 si->throttled_p = False;
1606 XtRemoveTimeOut (si->cycle_id);
1608 cycle_timer ((XtPointer) si, 0);
1613 else if (type == XA_SELECT)
1617 long which = event->xclient.data.l[1];
1619 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1620 sprintf (buf2, "activating (%ld).", which);
1621 clientmessage_response (si, window, False, buf, buf2);
1623 if (which < 0) which = 0; /* 0 == "random" */
1624 si->selection_mode = which;
1625 si->demoing_p = False;
1627 if (si->throttled_p && p->verbose_p)
1628 fprintf (stderr, "%s: unthrottled.\n", blurb());
1629 si->throttled_p = False;
1634 XtRemoveTimeOut (si->cycle_id);
1636 cycle_timer ((XtPointer) si, 0);
1641 else if (type == XA_EXIT)
1643 /* Ignore EXIT message if the screen is locked. */
1644 if (until_idle_p || !si->locked_p)
1646 clientmessage_response (si, window, False,
1647 "EXIT ClientMessage received.",
1651 unblank_screen (si);
1652 kill_screenhack (si);
1653 XSync (si->dpy, False);
1655 saver_exit (si, 0, 0);
1658 clientmessage_response (si, window, True,
1659 "EXIT ClientMessage received while locked.",
1660 "screen is locked.");
1662 else if (type == XA_RESTART)
1664 /* The RESTART message works whether the screensaver is active or not,
1665 unless the screen is locked, in which case it doesn't work.
1667 if (until_idle_p || !si->locked_p)
1669 clientmessage_response (si, window, False,
1670 "RESTART ClientMessage received.",
1674 unblank_screen (si);
1675 kill_screenhack (si);
1676 XSync (si->dpy, False);
1679 restart_process (si); /* does not return */
1683 clientmessage_response (si, window, True,
1684 "RESTART ClientMessage received while locked.",
1685 "screen is locked.");
1687 else if (type == XA_DEMO)
1689 long arg = event->xclient.data.l[1];
1690 Bool demo_one_hack_p = (arg == 300);
1692 if (demo_one_hack_p)
1696 long which = event->xclient.data.l[2];
1699 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1700 sprintf (buf2, "demoing (%ld).", which);
1701 clientmessage_response (si, window, False, buf, buf2);
1703 if (which < 0) which = 0; /* 0 == "random" */
1704 si->selection_mode = which;
1705 si->demoing_p = True;
1707 if (si->throttled_p && p->verbose_p)
1708 fprintf (stderr, "%s: unthrottled.\n", blurb());
1709 si->throttled_p = False;
1714 clientmessage_response (si, window, True,
1715 "DEMO ClientMessage received while active.",
1720 clientmessage_response (si, window, True,
1721 "obsolete form of DEMO ClientMessage.",
1722 "obsolete form of DEMO ClientMessage.");
1725 else if (type == XA_PREFS)
1727 clientmessage_response (si, window, True,
1728 "the PREFS client-message is obsolete.",
1729 "the PREFS client-message is obsolete.");
1731 else if (type == XA_LOCK)
1734 clientmessage_response (si, window, True,
1735 "not compiled with support for locking.",
1736 "locking not enabled.");
1737 #else /* !NO_LOCKING */
1738 if (si->locking_disabled_p)
1739 clientmessage_response (si, window, True,
1740 "LOCK ClientMessage received, but locking is disabled.",
1741 "locking not enabled.");
1742 else if (si->locked_p)
1743 clientmessage_response (si, window, True,
1744 "LOCK ClientMessage received while already locked.",
1749 char *response = (until_idle_p
1750 ? "activating and locking."
1752 sprintf (buf, "LOCK ClientMessage received; %s", response);
1753 clientmessage_response (si, window, False, buf, response);
1754 set_locked_p (si, True);
1755 si->selection_mode = 0;
1756 si->demoing_p = False;
1758 if (si->lock_id) /* we're doing it now, so lose the timeout */
1760 XtRemoveTimeOut (si->lock_id);
1766 if (si->using_mit_saver_extension ||
1767 si->using_sgi_saver_extension)
1769 XForceScreenSaver (si->dpy, ScreenSaverActive);
1778 #endif /* !NO_LOCKING */
1780 else if (type == XA_THROTTLE)
1782 if (si->throttled_p)
1783 clientmessage_response (si, window, True,
1784 "THROTTLE ClientMessage received, but "
1785 "already throttled.",
1786 "already throttled.");
1790 char *response = "throttled.";
1791 si->throttled_p = True;
1792 si->selection_mode = 0;
1793 si->demoing_p = False;
1794 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
1795 clientmessage_response (si, window, False, buf, response);
1800 XtRemoveTimeOut (si->cycle_id);
1802 cycle_timer ((XtPointer) si, 0);
1806 else if (type == XA_UNTHROTTLE)
1808 if (! si->throttled_p)
1809 clientmessage_response (si, window, True,
1810 "UNTHROTTLE ClientMessage received, but "
1816 char *response = "unthrottled.";
1817 si->throttled_p = False;
1818 si->selection_mode = 0;
1819 si->demoing_p = False;
1820 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
1821 clientmessage_response (si, window, False, buf, response);
1826 XtRemoveTimeOut (si->cycle_id);
1828 cycle_timer ((XtPointer) si, 0);
1836 str = XGetAtomName_safe (si->dpy, type);
1840 if (strlen (str) > 80)
1841 strcpy (str+70, "...");
1842 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
1849 "unrecognised screensaver ClientMessage 0x%x received.",
1850 (unsigned int) event->xclient.data.l[0]);
1853 clientmessage_response (si, window, True, buf, buf);
1859 /* Some random diagnostics printed in -verbose mode.
1863 analyze_display (saver_info *si)
1867 const char *name; const char *desc; Bool useful_p;
1870 { "SCREEN_SAVER", /* underscore */ "SGI Screen-Saver",
1871 # ifdef HAVE_SGI_SAVER_EXTENSION
1876 }, { "SCREEN-SAVER", /* dash */ "SGI Screen-Saver",
1877 # ifdef HAVE_SGI_SAVER_EXTENSION
1882 }, { "MIT-SCREEN-SAVER", "MIT Screen-Saver",
1883 # ifdef HAVE_MIT_SAVER_EXTENSION
1888 }, { "XIDLE", "XIdle",
1889 # ifdef HAVE_XIDLE_EXTENSION
1894 }, { "SGI-VIDEO-CONTROL", "SGI Video-Control",
1895 # ifdef HAVE_SGI_VC_EXTENSION
1900 }, { "READDISPLAY", "SGI Read-Display",
1901 # ifdef HAVE_READ_DISPLAY_EXTENSION
1906 }, { "MIT-SHM", "Shared Memory",
1907 # ifdef HAVE_XSHM_EXTENSION
1912 }, { "DOUBLE-BUFFER", "Double-Buffering",
1913 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
1918 }, { "DPMS", "Power Management",
1919 # ifdef HAVE_DPMS_EXTENSION
1930 }, { "XFree86-VidModeExtension", "XF86 Video-Mode",
1931 # ifdef HAVE_XF86VMODE
1936 }, { "XINERAMA", "Xinerama",
1938 }, { "Apple-DRI", "Apple-DRI (XDarwin)",
1943 fprintf (stderr, "%s: running on display \"%s\" (%d screen%s).\n",
1945 DisplayString(si->dpy),
1946 si->nscreens, (si->nscreens == 1 ? "" : "s"));
1947 fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
1948 ServerVendor(si->dpy), VendorRelease(si->dpy));
1950 fprintf (stderr, "%s: useful extensions:\n", blurb());
1951 for (i = 0; i < countof(exts); i++)
1953 int op = 0, event = 0, error = 0;
1956 if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
1958 sprintf (buf, "%s: ", blurb());
1960 strcat (buf, exts[i].desc);
1961 if (!exts[i].useful_p)
1964 while (strlen (buf) < k) strcat (buf, " ");
1965 strcat (buf, "<-- not supported at compile time!");
1967 fprintf (stderr, "%s\n", buf);
1970 for (i = 0; i < si->nscreens; i++)
1972 unsigned long colormapped_depths = 0;
1973 unsigned long non_mapped_depths = 0;
1974 XVisualInfo vi_in, *vi_out;
1977 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
1978 if (!vi_out) continue;
1979 for (j = 0; j < out_count; j++)
1980 if (vi_out[j].class == PseudoColor)
1981 colormapped_depths |= (1 << vi_out[j].depth);
1983 non_mapped_depths |= (1 << vi_out[j].depth);
1984 XFree ((char *) vi_out);
1986 if (colormapped_depths)
1988 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(), i);
1989 for (j = 0; j < 32; j++)
1990 if (colormapped_depths & (1 << j))
1991 fprintf (stderr, " %d", j);
1992 fprintf (stderr, ".\n");
1994 if (non_mapped_depths)
1996 fprintf (stderr, "%s: screen %d non-colormapped depths:",
1998 for (j = 0; j < 32; j++)
1999 if (non_mapped_depths & (1 << j))
2000 fprintf (stderr, " %d", j);
2001 fprintf (stderr, ".\n");
2007 display_is_on_console_p (saver_info *si)
2009 Bool not_on_console = True;
2010 char *dpystr = DisplayString (si->dpy);
2011 char *tail = (char *) strchr (dpystr, ':');
2012 if (! tail || strncmp (tail, ":0", 2))
2013 not_on_console = True;
2016 char dpyname[255], localname[255];
2017 strncpy (dpyname, dpystr, tail-dpystr);
2018 dpyname [tail-dpystr] = 0;
2020 !strcmp(dpyname, "unix") ||
2021 !strcmp(dpyname, "localhost"))
2022 not_on_console = False;
2023 else if (gethostname (localname, sizeof (localname)))
2024 not_on_console = True; /* can't find hostname? */
2027 /* We have to call gethostbyname() on the result of gethostname()
2028 because the two aren't guarenteed to be the same name for the
2029 same host: on some losing systems, one is a FQDN and the other
2030 is not. Here in the wide wonderful world of Unix it's rocket
2031 science to obtain the local hostname in a portable fashion.
2033 And don't forget, gethostbyname() reuses the structure it
2034 returns, so we have to copy the fucker before calling it again.
2035 Thank you master, may I have another.
2037 struct hostent *h = gethostbyname (dpyname);
2039 not_on_console = True;
2044 strcpy (hn, h->h_name);
2045 l = gethostbyname (localname);
2046 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
2050 return !not_on_console;
2054 /* Do a little bit of heap introspection...
2057 check_for_leaks (const char *where)
2060 static unsigned long last_brk = 0;
2061 int b = (unsigned long) sbrk(0);
2062 if (last_brk && last_brk < b)
2063 fprintf (stderr, "%s: %s: brk grew by %luK.\n",
2065 (((b - last_brk) + 1023) / 1024));
2067 #endif /* HAVE_SBRK */