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 RootWindowOfScreen (si->screens[i].screen),
306 si->screens[i].real_vroot,
307 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";
496 #endif /* NO_LOCKING */
500 /* Open the connection to the X server, and intern our Atoms.
503 connect_to_server (saver_info *si, int *argc, char **argv)
505 Widget toplevel_shell;
508 char *d = getenv ("DISPLAY");
511 char *ndpy = strdup("DISPLAY=:0.0");
512 /* if (si->prefs.verbose_p) */ /* sigh, too early to test this... */
514 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
518 /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
519 glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
520 do not. So we must leak it (and/or the previous setting). Yay.
523 #endif /* HAVE_PUTENV */
525 XSetErrorHandler (saver_ehandler);
527 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
528 toplevel_shell = XtAppInitialize (&si->app, progclass,
529 options, XtNumber (options),
530 argc, argv, defaults, 0, 0);
531 XtAppSetErrorMsgHandler (si->app, 0);
533 si->dpy = XtDisplay (toplevel_shell);
534 si->prefs.db = XtDatabase (si->dpy);
535 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
537 if(strlen(progname) > 100) /* keep it short. */
540 db = si->prefs.db; /* resources.c needs this */
542 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
543 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
544 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
545 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
546 XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
547 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
549 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
550 XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False);
551 XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False);
552 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
553 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
554 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
555 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
556 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
557 XA_PREV = XInternAtom (si->dpy, "PREV", False);
558 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
559 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
560 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
561 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
562 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
563 XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
564 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
565 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
567 return toplevel_shell;
571 /* Handle the command-line arguments that were not handled for us by Xt.
572 Issue an error message and exit if there are unknown options.
575 process_command_line (saver_info *si, int *argc, char **argv)
578 for (i = 1; i < *argc; i++)
580 if (!strcmp (argv[i], "-debug"))
581 /* no resource for this one, out of paranoia. */
582 si->prefs.debug_p = True;
584 else if (!strcmp (argv[i], "-h") ||
585 !strcmp (argv[i], "-help") ||
586 !strcmp (argv[i], "--help"))
591 const char *s = argv[i];
592 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
595 if (s[0] == '-' && s[1] == '-') s++;
596 if (!strcmp (s, "-activate") ||
597 !strcmp (s, "-deactivate") ||
598 !strcmp (s, "-cycle") ||
599 !strcmp (s, "-next") ||
600 !strcmp (s, "-prev") ||
601 !strcmp (s, "-exit") ||
602 !strcmp (s, "-restart") ||
603 !strcmp (s, "-demo") ||
604 !strcmp (s, "-prefs") ||
605 !strcmp (s, "-preferences") ||
606 !strcmp (s, "-lock") ||
607 !strcmp (s, "-version") ||
608 !strcmp (s, "-time"))
611 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
612 fprintf (stderr, "\n\
613 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
615 fprintf (stderr, "\n\
616 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
619 The `xscreensaver' program is a daemon that runs in the background.\n\
620 You control a running xscreensaver process by sending it messages\n\
621 with `xscreensaver-demo' or `xscreensaver-command'.\n\
622 . See the man pages for details, or check the web page:\n\
623 http://www.jwz.org/xscreensaver/\n\n");
625 /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
626 suggest that explicitly. */
627 if (!strcmp (s, "-lock"))
629 Or perhaps you meant either the \"-lock-mode\" or the\n\
630 \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
639 /* Print out the xscreensaver banner to the tty if applicable;
640 Issue any other warnings that are called for at this point.
643 print_banner (saver_info *si)
645 saver_preferences *p = &si->prefs;
647 /* This resource gets set some time before the others, so that we know
648 whether to print the banner (and so that the banner gets printed before
649 any resource-database-related error messages.)
651 p->verbose_p = (p->debug_p || get_boolean_resource ("verbose", "Boolean"));
653 /* Ditto, for the locking_disabled_p message. */
654 p->lock_p = get_boolean_resource ("lock", "Boolean");
658 "%s %s, copyright (c) 1991-2002 "
659 "by Jamie Zawinski <jwz@jwz.org>.\n",
660 progname, si->version);
663 fprintf (stderr, "\n"
664 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
666 "\tNote that in debug mode, the xscreensaver window will only\n"
667 "\tcover the left half of the screen. (The idea is that you\n"
668 "\tcan still see debugging output in a shell, if you position\n"
669 "\tit on the right side of the screen.)\n"
671 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
672 "\tuntrusted environments.\n"
678 if (!si->uid_message || !*si->uid_message)
679 describe_uids (si, stderr);
682 if (si->orig_uid && *si->orig_uid)
683 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
684 blurb(), si->orig_uid);
685 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
688 fprintf (stderr, "%s: in process %lu.\n", blurb(),
689 (unsigned long) getpid());
692 /* If locking was not able to be initalized for some reason, explain why.
693 (This has to be done after we've read the lock_p resource.)
695 if (p->lock_p && si->locking_disabled_p)
698 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
700 if (strstr (si->nolock_reason, "passw"))
701 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
702 "consult the manual.\n", blurb());
703 else if (strstr (si->nolock_reason, "running as "))
705 "%s: locking only works when xscreensaver is launched\n"
706 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
707 "\t See the manual for details.\n",
713 /* Examine all of the display's screens, and populate the `saver_screen_info'
714 structures. Make sure this is called after hack_environment() sets $PATH.
717 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
719 Bool found_any_writable_cells = False;
722 si->nscreens = ScreenCount(si->dpy);
723 si->screens = (saver_screen_info *)
724 calloc(sizeof(saver_screen_info), si->nscreens);
726 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
728 for (i = 0; i < si->nscreens; i++)
730 saver_screen_info *ssi = &si->screens[i];
732 ssi->screen = ScreenOfDisplay (si->dpy, i);
735 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
736 ssi->default_visual =
737 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
739 ssi->current_visual = ssi->default_visual;
740 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
742 /* Execute a subprocess to find the GL visual. */
743 ssi->best_gl_visual = get_best_gl_visual (ssi);
745 if (ssi == si->default_screen)
746 /* Since this is the default screen, use the one already created. */
747 ssi->toplevel_shell = toplevel_shell;
749 /* Otherwise, each screen must have its own unmapped root widget. */
750 ssi->toplevel_shell =
751 XtVaAppCreateShell (progname, progclass, applicationShellWidgetClass,
753 XtNscreen, ssi->screen,
754 XtNvisual, ssi->current_visual,
755 XtNdepth, visual_depth (ssi->screen,
756 ssi->current_visual),
759 if (! found_any_writable_cells)
761 /* Check to see whether fading is ever possible -- if any of the
762 screens on the display has a PseudoColor visual, then fading can
763 work (on at least some screens.) If no screen has a PseudoColor
764 visual, then don't bother ever trying to fade, because it will
765 just cause a delay without causing any visible effect.
767 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
768 get_visual (ssi->screen, "PseudoColor", True, False) ||
769 get_visual (ssi->screen, "GrayScale", True, False))
770 found_any_writable_cells = True;
774 si->fading_possible_p = found_any_writable_cells;
776 #ifdef HAVE_XF86VMODE_GAMMA
777 si->fading_possible_p = True; /* if we can gamma fade, go for it */
782 /* If any server extensions have been requested, try and initialize them.
783 Issue warnings if requests can't be honored.
786 initialize_server_extensions (saver_info *si)
788 saver_preferences *p = &si->prefs;
790 Bool server_has_xidle_extension_p = False;
791 Bool server_has_sgi_saver_extension_p = False;
792 Bool server_has_mit_saver_extension_p = False;
793 Bool system_has_proc_interrupts_p = False;
794 const char *piwhy = 0;
796 si->using_xidle_extension = p->use_xidle_extension;
797 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
798 si->using_mit_saver_extension = p->use_mit_saver_extension;
799 si->using_proc_interrupts = p->use_proc_interrupts;
801 #ifdef HAVE_XIDLE_EXTENSION
802 server_has_xidle_extension_p = query_xidle_extension (si);
804 #ifdef HAVE_SGI_SAVER_EXTENSION
805 server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
807 #ifdef HAVE_MIT_SAVER_EXTENSION
808 server_has_mit_saver_extension_p = query_mit_saver_extension (si);
810 #ifdef HAVE_PROC_INTERRUPTS
811 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
814 if (!server_has_xidle_extension_p)
815 si->using_xidle_extension = False;
816 else if (p->verbose_p)
818 if (si->using_xidle_extension)
819 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
821 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
824 if (!server_has_sgi_saver_extension_p)
825 si->using_sgi_saver_extension = False;
826 else if (p->verbose_p)
828 if (si->using_sgi_saver_extension)
829 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
832 "%s: not using server's SGI SCREEN_SAVER extension.\n",
836 if (!server_has_mit_saver_extension_p)
837 si->using_mit_saver_extension = False;
838 else if (p->verbose_p)
840 if (si->using_mit_saver_extension)
841 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
845 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
849 if (!system_has_proc_interrupts_p)
851 si->using_proc_interrupts = False;
852 if (p->verbose_p && piwhy)
853 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
856 else if (p->verbose_p)
858 if (si->using_proc_interrupts)
860 "%s: consulting /proc/interrupts for keyboard activity.\n",
864 "%s: not consulting /proc/interrupts for keyboard activity.\n",
870 /* For the case where we aren't using an server extensions, select user events
871 on all the existing windows, and launch timers to select events on
872 newly-created windows as well.
874 If a server extension is being used, this does nothing.
877 select_events (saver_info *si)
879 saver_preferences *p = &si->prefs;
882 if (si->using_xidle_extension ||
883 si->using_mit_saver_extension ||
884 si->using_sgi_saver_extension)
887 if (p->initial_delay)
891 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
892 (int) p->initial_delay/1000,
893 (p->initial_delay == 1000 ? "" : "s"));
897 usleep (p->initial_delay);
899 fprintf (stderr, " done.\n");
904 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
909 /* Select events on the root windows of every screen. This also selects
910 for window creation events, so that new subwindows will be noticed.
912 for (i = 0; i < si->nscreens; i++)
913 start_notice_events_timer (si, RootWindowOfScreen (si->screens[i].screen),
917 fprintf (stderr, " done.\n");
922 maybe_reload_init_file (saver_info *si)
924 saver_preferences *p = &si->prefs;
925 if (init_file_changed_p (p))
928 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
929 blurb(), init_file_name());
933 /* If a server extension is in use, and p->timeout has changed,
934 we need to inform the server of the new timeout. */
935 disable_builtin_screensaver (si, False);
937 /* If the DPMS settings in the init file have changed,
938 change the settings on the server to match. */
939 sync_server_dpms_settings (si->dpy,
940 (p->dpms_enabled_p &&
941 p->mode != DONT_BLANK),
942 p->dpms_standby / 1000,
943 p->dpms_suspend / 1000,
952 - wait until the user is idle;
954 - wait until the user is active;
955 - unblank the screen;
960 main_loop (saver_info *si)
962 saver_preferences *p = &si->prefs;
967 Bool was_locked = False;
970 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
972 check_for_leaks ("unblanked A");
973 sleep_until_idle (si, True);
974 check_for_leaks ("unblanked B");
979 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
980 si->selection_mode, timestring());
982 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
986 maybe_reload_init_file (si);
988 if (p->mode == DONT_BLANK)
991 fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
992 blurb(), timestring());
994 /* Go around the loop and wait for the next bout of idleness,
995 or for the init file to change, or for a remote command to
996 come in, or something.
1001 if (! blank_screen (si))
1003 /* We were unable to grab either the keyboard or mouse.
1004 This means we did not (and must not) blank the screen.
1005 If we were to blank the screen while some other program
1006 is holding both the mouse and keyboard grabbed, then
1007 we would never be able to un-blank it! We would never
1008 see any events, and the display would be wedged.
1010 So, just go around the loop again and wait for the
1011 next bout of idleness.
1015 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
1020 kill_screenhack (si);
1022 if (!si->throttled_p)
1023 spawn_screenhack (si, True);
1024 else if (p->verbose_p)
1025 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
1027 /* Don't start the cycle timer in demo mode. */
1028 if (!si->demoing_p && p->cycle)
1029 si->cycle_id = XtAppAddTimeOut (si->app,
1031 /* see comment in cycle_timer() */
1039 /* Maybe start locking the screen.
1042 Time lock_timeout = p->lock_timeout;
1044 if (si->emergency_lock_p && p->lock_p && lock_timeout)
1046 int secs = p->lock_timeout / 1000;
1049 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1051 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1055 si->emergency_lock_p = False;
1057 if (!si->demoing_p && /* if not going into demo mode */
1058 p->lock_p && /* and locking is enabled */
1059 !si->locking_disabled_p && /* and locking is possible */
1060 lock_timeout == 0) /* and locking is not timer-deferred */
1061 set_locked_p (si, True); /* then lock right now. */
1063 /* locked_p might be true already because of the above, or because of
1064 the LOCK ClientMessage. But if not, and if we're supposed to lock
1065 after some time, set up a timer to do so.
1070 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1071 activate_lock_timer,
1074 #endif /* !NO_LOCKING */
1077 ok_to_unblank = True;
1080 check_for_leaks ("blanked A");
1081 sleep_until_idle (si, False); /* until not idle */
1082 check_for_leaks ("blanked B");
1084 maybe_reload_init_file (si);
1087 /* Maybe unlock the screen.
1091 saver_screen_info *ssi = si->default_screen;
1092 if (si->locking_disabled_p) abort ();
1095 si->dbox_up_p = True;
1096 suspend_screenhack (si, True);
1097 XUndefineCursor (si->dpy, ssi->screensaver_window);
1099 ok_to_unblank = unlock_p (si);
1101 si->dbox_up_p = False;
1102 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1103 suspend_screenhack (si, False); /* resume */
1105 if (!ok_to_unblank &&
1106 !screenhack_running_p (si))
1108 /* If the lock dialog has been dismissed and we're not about to
1109 unlock the screen, and there is currently no hack running,
1110 then launch one. (There might be no hack running if DPMS
1111 had kicked in. But DPMS is off now, so bring back the hack)
1114 XtRemoveTimeOut (si->cycle_id);
1116 cycle_timer ((XtPointer) si, 0);
1119 #endif /* !NO_LOCKING */
1121 } while (!ok_to_unblank);
1125 fprintf (stderr, "%s: unblanking screen at %s.\n",
1126 blurb(), timestring ());
1128 /* Kill before unblanking, to stop drawing as soon as possible. */
1129 kill_screenhack (si);
1130 unblank_screen (si);
1132 set_locked_p (si, False);
1133 si->emergency_lock_p = False;
1135 si->selection_mode = 0;
1137 /* If we're throttled, and the user has explicitly unlocked the screen,
1138 then unthrottle. If we weren't locked, then don't unthrottle
1139 automatically, because someone might have just bumped the desk... */
1142 if (si->throttled_p && p->verbose_p)
1143 fprintf (stderr, "%s: unthrottled.\n", blurb());
1144 si->throttled_p = False;
1149 XtRemoveTimeOut (si->cycle_id);
1155 XtRemoveTimeOut (si->lock_id);
1159 /* It's possible that a race condition could have led to the saver
1160 window being unexpectedly still mapped. This can happen like so:
1164 - that hack tries to grab a screen image( it does this by
1165 first unmapping the saver window, then remapping it.)
1166 - hack unmaps window
1168 - user becomes active
1169 - hack re-maps window (*)
1170 - driver kills subprocess
1171 - driver unmaps window (**)
1173 The race is that (*) might have been sent to the server before
1174 the client process was killed, but, due to scheduling randomness,
1175 might not have been received by the server until after (**).
1176 In other words, (*) and (**) might happen out of order, meaning
1177 the driver will unmap the window, and then after that, the
1178 recently-dead client will re-map it. This leaves the user
1179 locked out (it looks like a desktop, but it's not!)
1181 To avoid this: after un-blanking the screen, sleep for a second,
1182 and then really make sure the window is unmapped.
1186 XSync (si->dpy, False);
1188 for (i = 0; i < si->nscreens; i++)
1190 saver_screen_info *ssi = &si->screens[i];
1191 Window w = ssi->screensaver_window;
1192 XWindowAttributes xgwa;
1193 XGetWindowAttributes (si->dpy, w, &xgwa);
1194 if (xgwa.map_state != IsUnmapped)
1198 "%s: %d: client race! emergency unmap 0x%lx.\n",
1199 blurb(), i, (unsigned long) w);
1200 XUnmapWindow (si->dpy, w);
1203 XSync (si->dpy, False);
1208 static void analyze_display (saver_info *si);
1211 main (int argc, char **argv)
1215 saver_info *si = &the_si;
1216 saver_preferences *p = &si->prefs;
1219 memset(si, 0, sizeof(*si));
1220 global_si_kludge = si; /* I hate C so much... */
1222 # undef ya_rand_init
1225 save_argv (argc, argv);
1226 set_version_string (si, &argc, argv);
1227 privileged_initialization (si, &argc, argv);
1228 hack_environment (si);
1230 shell = connect_to_server (si, &argc, argv);
1231 process_command_line (si, &argc, argv);
1234 load_init_file (p); /* must be before initialize_per_screen_info() */
1235 blurb_timestamp_p = p->timestamp_p; /* kludge */
1236 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1238 /* We can only issue this warnings now. */
1239 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1241 "%s: there are no PseudoColor or GrayScale visuals.\n"
1242 "%s: ignoring the request for fading/unfading.\n",
1245 for (i = 0; i < si->nscreens; i++)
1246 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1249 lock_initialization (si, &argc, argv);
1251 if (p->xsync_p) XSynchronize (si->dpy, True);
1253 if (p->verbose_p) analyze_display (si);
1254 initialize_server_extensions (si);
1256 si->blank_time = time ((time_t) 0); /* must be before ..._window */
1257 initialize_screensaver_window (si);
1262 disable_builtin_screensaver (si, True);
1263 sync_server_dpms_settings (si->dpy,
1264 (p->dpms_enabled_p &&
1265 p->mode != DONT_BLANK),
1266 p->dpms_standby / 1000,
1267 p->dpms_suspend / 1000,
1271 initialize_stderr (si);
1272 handle_signals (si);
1274 make_splash_dialog (si);
1276 main_loop (si); /* doesn't return */
1281 /* Processing ClientMessage events.
1285 static Bool error_handler_hit_p = False;
1288 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1290 error_handler_hit_p = True;
1294 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1295 them. We only look up the atom names for printing warning messages,
1296 so don't bomb out when it happens...
1299 XGetAtomName_safe (Display *dpy, Atom atom)
1302 XErrorHandler old_handler;
1303 if (!atom) return 0;
1306 error_handler_hit_p = False;
1307 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1308 result = XGetAtomName (dpy, atom);
1310 XSetErrorHandler (old_handler);
1312 if (error_handler_hit_p) result = 0;
1319 sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned long) atom);
1320 return strdup (buf);
1326 clientmessage_response (saver_info *si, Window w, Bool error,
1327 const char *stderr_msg,
1328 const char *protocol_msg)
1332 saver_preferences *p = &si->prefs;
1333 XErrorHandler old_handler;
1335 if (error || p->verbose_p)
1336 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1338 L = strlen(protocol_msg);
1339 proto = (char *) malloc (L + 2);
1340 proto[0] = (error ? '-' : '+');
1341 strcpy (proto+1, protocol_msg);
1344 /* Ignore all X errors while sending a response to a ClientMessage.
1345 Pretty much the only way we could get an error here is if the
1346 window we're trying to send the reply on has been deleted, in
1347 which case, the sender of the ClientMessage won't see our response
1350 XSync (si->dpy, False);
1351 error_handler_hit_p = False;
1352 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1354 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1355 PropModeReplace, (unsigned char *) proto, L);
1357 XSync (si->dpy, False);
1358 XSetErrorHandler (old_handler);
1359 XSync (si->dpy, False);
1366 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1368 char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1369 Window w = event->xclient.window;
1374 for (screen = 0; screen < si->nscreens; screen++)
1375 if (w == si->screens[screen].screensaver_window)
1377 strcpy (wdesc, "xscreensaver");
1380 else if (w == RootWindow (si->dpy, screen))
1382 strcpy (wdesc, "root");
1388 XErrorHandler old_handler;
1390 XWindowAttributes xgwa;
1391 memset (&hint, 0, sizeof(hint));
1392 memset (&xgwa, 0, sizeof(xgwa));
1394 XSync (si->dpy, False);
1395 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1396 XGetClassHint (si->dpy, w, &hint);
1397 XGetWindowAttributes (si->dpy, w, &xgwa);
1398 XSync (si->dpy, False);
1399 XSetErrorHandler (old_handler);
1400 XSync (si->dpy, False);
1402 screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1404 sprintf (wdesc, "%.20s / %.20s",
1405 (hint.res_name ? hint.res_name : "(null)"),
1406 (hint.res_class ? hint.res_class : "(null)"));
1407 if (hint.res_name) XFree (hint.res_name);
1408 if (hint.res_class) XFree (hint.res_class);
1411 fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1412 blurb(), screen, (str ? str : "(null)"));
1413 fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1414 blurb(), screen, (unsigned long) w, wdesc);
1415 if (str) XFree (str);
1419 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1421 saver_preferences *p = &si->prefs;
1423 Window window = event->xclient.window;
1425 /* Preferences might affect our handling of client messages. */
1426 maybe_reload_init_file (si);
1428 if (event->xclient.message_type != XA_SCREENSAVER ||
1429 event->xclient.format != 32)
1431 bogus_clientmessage_warning (si, event);
1435 type = event->xclient.data.l[0];
1436 if (type == XA_ACTIVATE)
1440 clientmessage_response(si, window, False,
1441 "ACTIVATE ClientMessage received.",
1443 si->selection_mode = 0;
1444 si->demoing_p = False;
1446 if (si->throttled_p && p->verbose_p)
1447 fprintf (stderr, "%s: unthrottled.\n", blurb());
1448 si->throttled_p = False;
1450 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1452 XForceScreenSaver (si->dpy, ScreenSaverActive);
1460 clientmessage_response(si, window, True,
1461 "ClientMessage ACTIVATE received while already active.",
1464 else if (type == XA_DEACTIVATE)
1468 if (si->throttled_p && p->verbose_p)
1469 fprintf (stderr, "%s: unthrottled.\n", blurb());
1470 si->throttled_p = False;
1472 clientmessage_response(si, window, False,
1473 "DEACTIVATE ClientMessage received.",
1475 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1477 XForceScreenSaver (si->dpy, ScreenSaverReset);
1485 clientmessage_response(si, window, False,
1486 "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
1487 "not active: idle timer reset.");
1490 else if (type == XA_CYCLE)
1494 clientmessage_response(si, window, False,
1495 "CYCLE ClientMessage received.",
1497 si->selection_mode = 0; /* 0 means randomize when its time. */
1498 si->demoing_p = False;
1500 if (si->throttled_p && p->verbose_p)
1501 fprintf (stderr, "%s: unthrottled.\n", blurb());
1502 si->throttled_p = False;
1505 XtRemoveTimeOut (si->cycle_id);
1507 cycle_timer ((XtPointer) si, 0);
1510 clientmessage_response(si, window, True,
1511 "ClientMessage CYCLE received while inactive.",
1514 else if (type == XA_NEXT || type == XA_PREV)
1516 clientmessage_response(si, window, False,
1518 ? "NEXT ClientMessage received."
1519 : "PREV ClientMessage received."),
1521 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1522 si->demoing_p = False;
1524 if (si->throttled_p && p->verbose_p)
1525 fprintf (stderr, "%s: unthrottled.\n", blurb());
1526 si->throttled_p = False;
1531 XtRemoveTimeOut (si->cycle_id);
1533 cycle_timer ((XtPointer) si, 0);
1538 else if (type == XA_SELECT)
1542 long which = event->xclient.data.l[1];
1544 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1545 sprintf (buf2, "activating (%ld).", which);
1546 clientmessage_response (si, window, False, buf, buf2);
1548 if (which < 0) which = 0; /* 0 == "random" */
1549 si->selection_mode = which;
1550 si->demoing_p = False;
1552 if (si->throttled_p && p->verbose_p)
1553 fprintf (stderr, "%s: unthrottled.\n", blurb());
1554 si->throttled_p = False;
1559 XtRemoveTimeOut (si->cycle_id);
1561 cycle_timer ((XtPointer) si, 0);
1566 else if (type == XA_EXIT)
1568 /* Ignore EXIT message if the screen is locked. */
1569 if (until_idle_p || !si->locked_p)
1571 clientmessage_response (si, window, False,
1572 "EXIT ClientMessage received.",
1576 unblank_screen (si);
1577 kill_screenhack (si);
1578 XSync (si->dpy, False);
1580 saver_exit (si, 0, 0);
1583 clientmessage_response (si, window, True,
1584 "EXIT ClientMessage received while locked.",
1585 "screen is locked.");
1587 else if (type == XA_RESTART)
1589 /* The RESTART message works whether the screensaver is active or not,
1590 unless the screen is locked, in which case it doesn't work.
1592 if (until_idle_p || !si->locked_p)
1594 clientmessage_response (si, window, False,
1595 "RESTART ClientMessage received.",
1599 unblank_screen (si);
1600 kill_screenhack (si);
1601 XSync (si->dpy, False);
1604 restart_process (si); /* does not return */
1608 clientmessage_response (si, window, True,
1609 "RESTART ClientMessage received while locked.",
1610 "screen is locked.");
1612 else if (type == XA_DEMO)
1614 long arg = event->xclient.data.l[1];
1615 Bool demo_one_hack_p = (arg == 300);
1617 if (demo_one_hack_p)
1621 long which = event->xclient.data.l[2];
1624 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1625 sprintf (buf2, "demoing (%ld).", which);
1626 clientmessage_response (si, window, False, buf, buf2);
1628 if (which < 0) which = 0; /* 0 == "random" */
1629 si->selection_mode = which;
1630 si->demoing_p = True;
1632 if (si->throttled_p && p->verbose_p)
1633 fprintf (stderr, "%s: unthrottled.\n", blurb());
1634 si->throttled_p = False;
1639 clientmessage_response (si, window, True,
1640 "DEMO ClientMessage received while active.",
1645 clientmessage_response (si, window, True,
1646 "obsolete form of DEMO ClientMessage.",
1647 "obsolete form of DEMO ClientMessage.");
1650 else if (type == XA_PREFS)
1652 clientmessage_response (si, window, True,
1653 "the PREFS client-message is obsolete.",
1654 "the PREFS client-message is obsolete.");
1656 else if (type == XA_LOCK)
1659 clientmessage_response (si, window, True,
1660 "not compiled with support for locking.",
1661 "locking not enabled.");
1662 #else /* !NO_LOCKING */
1663 if (si->locking_disabled_p)
1664 clientmessage_response (si, window, True,
1665 "LOCK ClientMessage received, but locking is disabled.",
1666 "locking not enabled.");
1667 else if (si->locked_p)
1668 clientmessage_response (si, window, True,
1669 "LOCK ClientMessage received while already locked.",
1674 char *response = (until_idle_p
1675 ? "activating and locking."
1677 sprintf (buf, "LOCK ClientMessage received; %s", response);
1678 clientmessage_response (si, window, False, buf, response);
1679 set_locked_p (si, True);
1680 si->selection_mode = 0;
1681 si->demoing_p = False;
1683 if (si->lock_id) /* we're doing it now, so lose the timeout */
1685 XtRemoveTimeOut (si->lock_id);
1691 if (si->using_mit_saver_extension ||
1692 si->using_sgi_saver_extension)
1694 XForceScreenSaver (si->dpy, ScreenSaverActive);
1703 #endif /* !NO_LOCKING */
1705 else if (type == XA_THROTTLE)
1707 if (si->throttled_p)
1708 clientmessage_response (si, window, True,
1709 "THROTTLE ClientMessage received, but "
1710 "already throttled.",
1711 "already throttled.");
1715 char *response = "throttled.";
1716 si->throttled_p = True;
1717 si->selection_mode = 0;
1718 si->demoing_p = False;
1719 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
1720 clientmessage_response (si, window, False, buf, response);
1725 XtRemoveTimeOut (si->cycle_id);
1727 cycle_timer ((XtPointer) si, 0);
1731 else if (type == XA_UNTHROTTLE)
1733 if (! si->throttled_p)
1734 clientmessage_response (si, window, True,
1735 "UNTHROTTLE ClientMessage received, but "
1741 char *response = "unthrottled.";
1742 si->throttled_p = False;
1743 si->selection_mode = 0;
1744 si->demoing_p = False;
1745 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
1746 clientmessage_response (si, window, False, buf, response);
1751 XtRemoveTimeOut (si->cycle_id);
1753 cycle_timer ((XtPointer) si, 0);
1761 str = XGetAtomName_safe (si->dpy, type);
1765 if (strlen (str) > 80)
1766 strcpy (str+70, "...");
1767 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
1774 "unrecognised screensaver ClientMessage 0x%x received.",
1775 (unsigned int) event->xclient.data.l[0]);
1778 clientmessage_response (si, window, True, buf, buf);
1784 /* Some random diagnostics printed in -verbose mode.
1788 analyze_display (saver_info *si)
1792 const char *name; const char *desc; Bool useful_p;
1795 { "SCREEN_SAVER", /* underscore */ "SGI Screen-Saver",
1796 # ifdef HAVE_SGI_SAVER_EXTENSION
1801 }, { "SCREEN-SAVER", /* dash */ "SGI Screen-Saver",
1802 # ifdef HAVE_SGI_SAVER_EXTENSION
1807 }, { "MIT-SCREEN-SAVER", "MIT Screen-Saver",
1808 # ifdef HAVE_MIT_SAVER_EXTENSION
1813 }, { "XIDLE", "XIdle",
1814 # ifdef HAVE_XIDLE_EXTENSION
1819 }, { "SGI-VIDEO-CONTROL", "SGI Video-Control",
1820 # ifdef HAVE_SGI_VC_EXTENSION
1825 }, { "READDISPLAY", "SGI Read-Display",
1826 # ifdef HAVE_READ_DISPLAY_EXTENSION
1831 }, { "MIT-SHM", "Shared Memory",
1832 # ifdef HAVE_XSHM_EXTENSION
1837 }, { "DOUBLE-BUFFER", "Double-Buffering",
1838 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
1843 }, { "DPMS", "Power Management",
1844 # ifdef HAVE_DPMS_EXTENSION
1855 }, { "XFree86-VidModeExtension", "XF86 Video-Mode",
1856 # ifdef HAVE_XF86VMODE
1861 }, { "XINERAMA", "Xinerama",
1866 fprintf (stderr, "%s: running on display \"%s\" (%d screen%s).\n",
1868 DisplayString(si->dpy),
1869 si->nscreens, (si->nscreens == 1 ? "" : "s"));
1870 fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
1871 ServerVendor(si->dpy), VendorRelease(si->dpy));
1873 fprintf (stderr, "%s: useful extensions:\n", blurb());
1874 for (i = 0; i < countof(exts); i++)
1876 int op = 0, event = 0, error = 0;
1879 if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
1881 sprintf (buf, "%s: ", blurb());
1883 strcat (buf, exts[i].desc);
1884 if (!exts[i].useful_p)
1887 while (strlen (buf) < k) strcat (buf, " ");
1888 strcat (buf, "<-- not supported at compile time!");
1890 fprintf (stderr, "%s\n", buf);
1893 for (i = 0; i < si->nscreens; i++)
1895 unsigned long colormapped_depths = 0;
1896 unsigned long non_mapped_depths = 0;
1897 XVisualInfo vi_in, *vi_out;
1900 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
1901 if (!vi_out) continue;
1902 for (j = 0; j < out_count; j++)
1903 if (vi_out[j].class == PseudoColor)
1904 colormapped_depths |= (1 << vi_out[j].depth);
1906 non_mapped_depths |= (1 << vi_out[j].depth);
1907 XFree ((char *) vi_out);
1909 if (colormapped_depths)
1911 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(), i);
1912 for (j = 0; j < 32; j++)
1913 if (colormapped_depths & (1 << j))
1914 fprintf (stderr, " %d", j);
1915 fprintf (stderr, ".\n");
1917 if (non_mapped_depths)
1919 fprintf (stderr, "%s: screen %d non-colormapped depths:",
1921 for (j = 0; j < 32; j++)
1922 if (non_mapped_depths & (1 << j))
1923 fprintf (stderr, " %d", j);
1924 fprintf (stderr, ".\n");
1930 display_is_on_console_p (saver_info *si)
1932 Bool not_on_console = True;
1933 char *dpystr = DisplayString (si->dpy);
1934 char *tail = (char *) strchr (dpystr, ':');
1935 if (! tail || strncmp (tail, ":0", 2))
1936 not_on_console = True;
1939 char dpyname[255], localname[255];
1940 strncpy (dpyname, dpystr, tail-dpystr);
1941 dpyname [tail-dpystr] = 0;
1943 !strcmp(dpyname, "unix") ||
1944 !strcmp(dpyname, "localhost"))
1945 not_on_console = False;
1946 else if (gethostname (localname, sizeof (localname)))
1947 not_on_console = True; /* can't find hostname? */
1950 /* We have to call gethostbyname() on the result of gethostname()
1951 because the two aren't guarenteed to be the same name for the
1952 same host: on some losing systems, one is a FQDN and the other
1953 is not. Here in the wide wonderful world of Unix it's rocket
1954 science to obtain the local hostname in a portable fashion.
1956 And don't forget, gethostbyname() reuses the structure it
1957 returns, so we have to copy the fucker before calling it again.
1958 Thank you master, may I have another.
1960 struct hostent *h = gethostbyname (dpyname);
1962 not_on_console = True;
1967 strcpy (hn, h->h_name);
1968 l = gethostbyname (localname);
1969 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
1973 return !not_on_console;
1977 /* Do a little bit of heap introspection...
1980 check_for_leaks (const char *where)
1983 static unsigned long last_brk = 0;
1984 int b = (unsigned long) sbrk(0);
1985 if (last_brk && last_brk < b)
1986 fprintf (stderr, "%s: %s: brk grew by %luK.\n",
1988 (((b - last_brk) + 1023) / 1024));
1990 #endif /* HAVE_SBRK */