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... */
293 if (!real_stderr) real_stderr = stderr;
295 fprintf (real_stderr, "\n"
296 "#######################################"
297 "#######################################\n\n"
298 "%s: X Error! PLEASE REPORT THIS BUG.\n",
301 for (i = 0; i < si->nscreens; i++)
302 fprintf (real_stderr, "%s: screen %d: 0x%x, 0x%x, 0x%x\n",
304 RootWindowOfScreen (si->screens[i].screen),
305 si->screens[i].real_vroot,
306 si->screens[i].screensaver_window);
308 fprintf (real_stderr, "\n"
309 "#######################################"
310 "#######################################\n\n");
312 if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
314 fprintf (real_stderr, "\n");
315 if (si->prefs.xsync_p)
317 saver_exit (si, -1, "because of synchronous X Error");
321 fprintf (real_stderr,
322 "#######################################"
323 "#######################################\n\n");
324 fprintf (real_stderr,
325 " If at all possible, please re-run xscreensaver with the command\n"
326 " line arguments `-sync -verbose -no-capture', and reproduce this\n"
327 " bug. That will cause xscreensaver to dump a `core' file to the\n"
328 " current directory. Please include the stack trace from that core\n"
329 " file in your bug report. *DO NOT* mail the core file itself!\n"
330 " That won't work.\n"
332 " http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
333 " the most useful bug reports, and how to examine core files.\n"
335 " The more information you can provide, the better. But please\n"
336 " report this bug, regardless!\n"
338 fprintf (real_stderr,
339 "#######################################"
340 "#######################################\n\n");
342 saver_exit (si, -1, 0);
346 fprintf (real_stderr, " (nonfatal.)\n");
351 /* This error handler is used only while the X connection is being set up;
352 after we've got a connection, we don't use this handler again. The only
353 reason for having this is so that we can present a more idiot-proof error
354 message than "cannot open display."
357 startup_ehandler (String name, String type, String class,
358 String defalt, /* one can't even spel properly
359 in this joke of a language */
360 String *av, Cardinal *ac)
364 saver_info *si = global_si_kludge; /* I hate C so much... */
365 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
367 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
368 fmt, sizeof(fmt)-1, *db);
370 fprintf (stderr, "%s: ", blurb());
372 memset (p, 0, sizeof(p));
373 if (*ac > countof (p)) *ac = countof (p);
374 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
375 fprintf (stderr, fmt, /* Did I mention that I hate C? */
376 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
377 fprintf (stderr, "\n");
379 describe_uids (si, stderr);
381 if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
383 fprintf (stderr, "\n"
384 "%s: This is probably because you're logging in as root. You\n"
385 " shouldn't log in as root: you should log in as a normal user,\n"
386 " and then `su' as needed. If you insist on logging in as\n"
387 " root, you will have to turn off X's security features before\n"
388 " xscreensaver will work.\n"
390 " Please read the manual and FAQ for more information:\n",
395 fprintf (stderr, "\n"
396 "%s: Errors at startup are usually authorization problems.\n"
397 " But you're not logging in as root (good!) so something\n"
398 " else must be wrong. Did you read the manual and the FAQ?\n",
402 fprintf (stderr, "\n"
403 " http://www.jwz.org/xscreensaver/faq.html\n"
404 " http://www.jwz.org/xscreensaver/man.html\n"
413 /* The zillions of initializations.
416 /* Set progname, version, etc. This is done very early.
419 set_version_string (saver_info *si, int *argc, char **argv)
421 progclass = "XScreenSaver";
423 /* progname is reset later, after we connect to X. */
424 progname = strrchr(argv[0], '/');
425 if (progname) progname++;
426 else progname = argv[0];
428 if (strlen(progname) > 100) /* keep it short. */
431 /* The X resource database blows up if argv[0] has a "." in it. */
434 while ((s = strchr (s, '.')))
438 si->version = (char *) malloc (5);
439 memcpy (si->version, screensaver_id + 17, 4);
444 /* Initializations that potentially take place as a priveleged user:
445 If the xscreensaver executable is setuid root, then these initializations
446 are run as root, before discarding privileges.
449 privileged_initialization (saver_info *si, int *argc, char **argv)
452 /* before hack_uid() for proper permissions */
453 lock_priv_init (*argc, argv, si->prefs.verbose_p);
454 #endif /* NO_LOCKING */
460 /* Figure out what locking mechanisms are supported.
463 lock_initialization (saver_info *si, int *argc, char **argv)
466 si->locking_disabled_p = True;
467 si->nolock_reason = "not compiled with locking support";
468 #else /* !NO_LOCKING */
470 /* Finish initializing locking, now that we're out of privileged code. */
471 if (! lock_init (*argc, argv, si->prefs.verbose_p))
473 si->locking_disabled_p = True;
474 si->nolock_reason = "error getting password";
477 /* If locking is currently enabled, but the environment indicates that
478 we have been launched as GDM's "Background" program, then disable
479 locking just in case.
481 if (!si->locking_disabled_p && getenv ("RUNNING_UNDER_GDM"))
483 si->locking_disabled_p = True;
484 si->nolock_reason = "running under GDM";
486 #endif /* NO_LOCKING */
490 /* Open the connection to the X server, and intern our Atoms.
493 connect_to_server (saver_info *si, int *argc, char **argv)
495 Widget toplevel_shell;
498 char *d = getenv ("DISPLAY");
501 char *ndpy = strdup("DISPLAY=:0.0");
502 /* if (si->prefs.verbose_p) */ /* sigh, too early to test this... */
504 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
508 /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
509 glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
510 do not. So we must leak it (and/or the previous setting). Yay.
513 #endif /* HAVE_PUTENV */
515 XSetErrorHandler (saver_ehandler);
517 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
518 toplevel_shell = XtAppInitialize (&si->app, progclass,
519 options, XtNumber (options),
520 argc, argv, defaults, 0, 0);
521 XtAppSetErrorMsgHandler (si->app, 0);
523 si->dpy = XtDisplay (toplevel_shell);
524 si->prefs.db = XtDatabase (si->dpy);
525 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
527 if(strlen(progname) > 100) /* keep it short. */
530 db = si->prefs.db; /* resources.c needs this */
532 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
533 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
534 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
535 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
536 XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
537 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
539 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
540 XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False);
541 XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False);
542 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
543 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
544 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
545 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
546 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
547 XA_PREV = XInternAtom (si->dpy, "PREV", False);
548 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
549 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
550 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
551 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
552 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
553 XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
554 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
555 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
557 return toplevel_shell;
561 /* Handle the command-line arguments that were not handled for us by Xt.
562 Issue an error message and exit if there are unknown options.
565 process_command_line (saver_info *si, int *argc, char **argv)
568 for (i = 1; i < *argc; i++)
570 if (!strcmp (argv[i], "-debug"))
571 /* no resource for this one, out of paranoia. */
572 si->prefs.debug_p = True;
574 else if (!strcmp (argv[i], "-h") ||
575 !strcmp (argv[i], "-help") ||
576 !strcmp (argv[i], "--help"))
581 const char *s = argv[i];
582 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
585 if (s[0] == '-' && s[1] == '-') s++;
586 if (!strcmp (s, "-activate") ||
587 !strcmp (s, "-deactivate") ||
588 !strcmp (s, "-cycle") ||
589 !strcmp (s, "-next") ||
590 !strcmp (s, "-prev") ||
591 !strcmp (s, "-exit") ||
592 !strcmp (s, "-restart") ||
593 !strcmp (s, "-demo") ||
594 !strcmp (s, "-prefs") ||
595 !strcmp (s, "-preferences") ||
596 !strcmp (s, "-lock") ||
597 !strcmp (s, "-version") ||
598 !strcmp (s, "-time"))
601 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
602 fprintf (stderr, "\n\
603 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
605 fprintf (stderr, "\n\
606 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
609 The `xscreensaver' program is a daemon that runs in the background.\n\
610 You control a running xscreensaver process by sending it messages\n\
611 with `xscreensaver-demo' or `xscreensaver-command'.\n\
612 . See the man pages for details, or check the web page:\n\
613 http://www.jwz.org/xscreensaver/\n\n");
615 /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
616 suggest that explicitly. */
617 if (!strcmp (s, "-lock"))
619 Or perhaps you meant either the \"-lock-mode\" or the\n\
620 \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
629 /* Print out the xscreensaver banner to the tty if applicable;
630 Issue any other warnings that are called for at this point.
633 print_banner (saver_info *si)
635 saver_preferences *p = &si->prefs;
637 /* This resource gets set some time before the others, so that we know
638 whether to print the banner (and so that the banner gets printed before
639 any resource-database-related error messages.)
641 p->verbose_p = (p->debug_p || get_boolean_resource ("verbose", "Boolean"));
643 /* Ditto, for the locking_disabled_p message. */
644 p->lock_p = get_boolean_resource ("lock", "Boolean");
648 "%s %s, copyright (c) 1991-2002 "
649 "by Jamie Zawinski <jwz@jwz.org>.\n",
650 progname, si->version);
653 fprintf (stderr, "\n"
654 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
656 "\tNote that in debug mode, the xscreensaver window will only\n"
657 "\tcover the left half of the screen. (The idea is that you\n"
658 "\tcan still see debugging output in a shell, if you position\n"
659 "\tit on the right side of the screen.)\n"
661 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
662 "\tuntrusted environments.\n"
668 if (!si->uid_message || !*si->uid_message)
669 describe_uids (si, stderr);
672 if (si->orig_uid && *si->orig_uid)
673 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
674 blurb(), si->orig_uid);
675 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
678 fprintf (stderr, "%s: in process %lu.\n", blurb(),
679 (unsigned long) getpid());
682 /* If locking was not able to be initalized for some reason, explain why.
683 (This has to be done after we've read the lock_p resource.)
685 if (p->lock_p && si->locking_disabled_p)
688 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
690 if (strstr (si->nolock_reason, "passw"))
691 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
692 "consult the manual.\n", blurb());
693 else if (strstr (si->nolock_reason, "running as "))
695 "%s: locking only works when xscreensaver is launched\n"
696 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
697 "\t See the manual for details.\n",
703 /* Examine all of the display's screens, and populate the `saver_screen_info'
704 structures. Make sure this is called after hack_environment() sets $PATH.
707 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
709 Bool found_any_writable_cells = False;
712 si->nscreens = ScreenCount(si->dpy);
713 si->screens = (saver_screen_info *)
714 calloc(sizeof(saver_screen_info), si->nscreens);
716 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
718 for (i = 0; i < si->nscreens; i++)
720 saver_screen_info *ssi = &si->screens[i];
722 ssi->screen = ScreenOfDisplay (si->dpy, i);
725 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
726 ssi->default_visual =
727 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
729 ssi->current_visual = ssi->default_visual;
730 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
732 /* Execute a subprocess to find the GL visual. */
733 ssi->best_gl_visual = get_best_gl_visual (ssi);
735 if (ssi == si->default_screen)
736 /* Since this is the default screen, use the one already created. */
737 ssi->toplevel_shell = toplevel_shell;
739 /* Otherwise, each screen must have its own unmapped root widget. */
740 ssi->toplevel_shell =
741 XtVaAppCreateShell (progname, progclass, applicationShellWidgetClass,
743 XtNscreen, ssi->screen,
744 XtNvisual, ssi->current_visual,
745 XtNdepth, visual_depth (ssi->screen,
746 ssi->current_visual),
749 if (! found_any_writable_cells)
751 /* Check to see whether fading is ever possible -- if any of the
752 screens on the display has a PseudoColor visual, then fading can
753 work (on at least some screens.) If no screen has a PseudoColor
754 visual, then don't bother ever trying to fade, because it will
755 just cause a delay without causing any visible effect.
757 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
758 get_visual (ssi->screen, "PseudoColor", True, False) ||
759 get_visual (ssi->screen, "GrayScale", True, False))
760 found_any_writable_cells = True;
764 si->fading_possible_p = found_any_writable_cells;
766 #ifdef HAVE_XF86VMODE_GAMMA
767 si->fading_possible_p = True; /* if we can gamma fade, go for it */
772 /* If any server extensions have been requested, try and initialize them.
773 Issue warnings if requests can't be honored.
776 initialize_server_extensions (saver_info *si)
778 saver_preferences *p = &si->prefs;
780 Bool server_has_xidle_extension_p = False;
781 Bool server_has_sgi_saver_extension_p = False;
782 Bool server_has_mit_saver_extension_p = False;
783 Bool system_has_proc_interrupts_p = False;
784 const char *piwhy = 0;
786 si->using_xidle_extension = p->use_xidle_extension;
787 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
788 si->using_mit_saver_extension = p->use_mit_saver_extension;
789 si->using_proc_interrupts = p->use_proc_interrupts;
791 #ifdef HAVE_XIDLE_EXTENSION
792 server_has_xidle_extension_p = query_xidle_extension (si);
794 #ifdef HAVE_SGI_SAVER_EXTENSION
795 server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
797 #ifdef HAVE_MIT_SAVER_EXTENSION
798 server_has_mit_saver_extension_p = query_mit_saver_extension (si);
800 #ifdef HAVE_PROC_INTERRUPTS
801 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
804 if (!server_has_xidle_extension_p)
805 si->using_xidle_extension = False;
806 else if (p->verbose_p)
808 if (si->using_xidle_extension)
809 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
811 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
814 if (!server_has_sgi_saver_extension_p)
815 si->using_sgi_saver_extension = False;
816 else if (p->verbose_p)
818 if (si->using_sgi_saver_extension)
819 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
822 "%s: not using server's SGI SCREEN_SAVER extension.\n",
826 if (!server_has_mit_saver_extension_p)
827 si->using_mit_saver_extension = False;
828 else if (p->verbose_p)
830 if (si->using_mit_saver_extension)
831 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
835 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
839 if (!system_has_proc_interrupts_p)
841 si->using_proc_interrupts = False;
842 if (p->verbose_p && piwhy)
843 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
846 else if (p->verbose_p)
848 if (si->using_proc_interrupts)
850 "%s: consulting /proc/interrupts for keyboard activity.\n",
854 "%s: not consulting /proc/interrupts for keyboard activity.\n",
860 /* For the case where we aren't using an server extensions, select user events
861 on all the existing windows, and launch timers to select events on
862 newly-created windows as well.
864 If a server extension is being used, this does nothing.
867 select_events (saver_info *si)
869 saver_preferences *p = &si->prefs;
872 if (si->using_xidle_extension ||
873 si->using_mit_saver_extension ||
874 si->using_sgi_saver_extension)
877 if (p->initial_delay)
881 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
882 (int) p->initial_delay/1000,
883 (p->initial_delay == 1000 ? "" : "s"));
887 usleep (p->initial_delay);
889 fprintf (stderr, " done.\n");
894 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
899 /* Select events on the root windows of every screen. This also selects
900 for window creation events, so that new subwindows will be noticed.
902 for (i = 0; i < si->nscreens; i++)
903 start_notice_events_timer (si, RootWindowOfScreen (si->screens[i].screen),
907 fprintf (stderr, " done.\n");
912 maybe_reload_init_file (saver_info *si)
914 saver_preferences *p = &si->prefs;
915 if (init_file_changed_p (p))
918 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
919 blurb(), init_file_name());
923 /* If a server extension is in use, and p->timeout has changed,
924 we need to inform the server of the new timeout. */
925 disable_builtin_screensaver (si, False);
927 /* If the DPMS settings in the init file have changed,
928 change the settings on the server to match. */
929 sync_server_dpms_settings (si->dpy,
930 (p->dpms_enabled_p &&
931 p->mode != DONT_BLANK),
932 p->dpms_standby / 1000,
933 p->dpms_suspend / 1000,
942 - wait until the user is idle;
944 - wait until the user is active;
945 - unblank the screen;
950 main_loop (saver_info *si)
952 saver_preferences *p = &si->prefs;
957 Bool was_locked = False;
960 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
962 check_for_leaks ("unblanked A");
963 sleep_until_idle (si, True);
964 check_for_leaks ("unblanked B");
969 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
970 si->selection_mode, timestring());
972 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
976 maybe_reload_init_file (si);
978 if (p->mode == DONT_BLANK)
981 fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
982 blurb(), timestring());
984 /* Go around the loop and wait for the next bout of idleness,
985 or for the init file to change, or for a remote command to
986 come in, or something.
991 if (! blank_screen (si))
993 /* We were unable to grab either the keyboard or mouse.
994 This means we did not (and must not) blank the screen.
995 If we were to blank the screen while some other program
996 is holding both the mouse and keyboard grabbed, then
997 we would never be able to un-blank it! We would never
998 see any events, and the display would be wedged.
1000 So, just go around the loop again and wait for the
1001 next bout of idleness.
1005 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
1010 kill_screenhack (si);
1012 if (!si->throttled_p)
1013 spawn_screenhack (si, True);
1014 else if (p->verbose_p)
1015 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
1017 /* Don't start the cycle timer in demo mode. */
1018 if (!si->demoing_p && p->cycle)
1019 si->cycle_id = XtAppAddTimeOut (si->app,
1021 /* see comment in cycle_timer() */
1029 /* Maybe start locking the screen.
1032 Time lock_timeout = p->lock_timeout;
1034 if (si->emergency_lock_p && p->lock_p && lock_timeout)
1036 int secs = p->lock_timeout / 1000;
1039 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1041 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1045 si->emergency_lock_p = False;
1047 if (!si->demoing_p && /* if not going into demo mode */
1048 p->lock_p && /* and locking is enabled */
1049 !si->locking_disabled_p && /* and locking is possible */
1050 lock_timeout == 0) /* and locking is not timer-deferred */
1051 set_locked_p (si, True); /* then lock right now. */
1053 /* locked_p might be true already because of the above, or because of
1054 the LOCK ClientMessage. But if not, and if we're supposed to lock
1055 after some time, set up a timer to do so.
1060 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1061 activate_lock_timer,
1064 #endif /* !NO_LOCKING */
1067 ok_to_unblank = True;
1070 check_for_leaks ("blanked A");
1071 sleep_until_idle (si, False); /* until not idle */
1072 check_for_leaks ("blanked B");
1074 maybe_reload_init_file (si);
1077 /* Maybe unlock the screen.
1081 saver_screen_info *ssi = si->default_screen;
1082 if (si->locking_disabled_p) abort ();
1085 si->dbox_up_p = True;
1086 suspend_screenhack (si, True);
1087 XUndefineCursor (si->dpy, ssi->screensaver_window);
1089 ok_to_unblank = unlock_p (si);
1091 si->dbox_up_p = False;
1092 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1093 suspend_screenhack (si, False); /* resume */
1095 if (!ok_to_unblank &&
1096 !screenhack_running_p (si))
1098 /* If the lock dialog has been dismissed and we're not about to
1099 unlock the screen, and there is currently no hack running,
1100 then launch one. (There might be no hack running if DPMS
1101 had kicked in. But DPMS is off now, so bring back the hack)
1104 XtRemoveTimeOut (si->cycle_id);
1106 cycle_timer ((XtPointer) si, 0);
1109 #endif /* !NO_LOCKING */
1111 } while (!ok_to_unblank);
1115 fprintf (stderr, "%s: unblanking screen at %s.\n",
1116 blurb(), timestring ());
1118 /* Kill before unblanking, to stop drawing as soon as possible. */
1119 kill_screenhack (si);
1120 unblank_screen (si);
1122 set_locked_p (si, False);
1123 si->emergency_lock_p = False;
1125 si->selection_mode = 0;
1127 /* If we're throttled, and the user has explicitly unlocked the screen,
1128 then unthrottle. If we weren't locked, then don't unthrottle
1129 automatically, because someone might have just bumped the desk... */
1132 if (si->throttled_p && p->verbose_p)
1133 fprintf (stderr, "%s: unthrottled.\n", blurb());
1134 si->throttled_p = False;
1139 XtRemoveTimeOut (si->cycle_id);
1145 XtRemoveTimeOut (si->lock_id);
1149 /* It's possible that a race condition could have led to the saver
1150 window being unexpectedly still mapped. This can happen like so:
1154 - that hack tries to grab a screen image( it does this by
1155 first unmapping the saver window, then remapping it.)
1156 - hack unmaps window
1158 - user becomes active
1159 - hack re-maps window (*)
1160 - driver kills subprocess
1161 - driver unmaps window (**)
1163 The race is that (*) might have been sent to the server before
1164 the client process was killed, but, due to scheduling randomness,
1165 might not have been received by the server until after (**).
1166 In other words, (*) and (**) might happen out of order, meaning
1167 the driver will unmap the window, and then after that, the
1168 recently-dead client will re-map it. This leaves the user
1169 locked out (it looks like a desktop, but it's not!)
1171 To avoid this: after un-blanking the screen, sleep for a second,
1172 and then really make sure the window is unmapped.
1176 XSync (si->dpy, False);
1178 for (i = 0; i < si->nscreens; i++)
1180 saver_screen_info *ssi = &si->screens[i];
1181 Window w = ssi->screensaver_window;
1182 XWindowAttributes xgwa;
1183 XGetWindowAttributes (si->dpy, w, &xgwa);
1184 if (xgwa.map_state != IsUnmapped)
1188 "%s: %d: client race! emergency unmap 0x%lx.\n",
1189 blurb(), i, (unsigned long) w);
1190 XUnmapWindow (si->dpy, w);
1193 XSync (si->dpy, False);
1198 static void analyze_display (saver_info *si);
1201 main (int argc, char **argv)
1205 saver_info *si = &the_si;
1206 saver_preferences *p = &si->prefs;
1209 memset(si, 0, sizeof(*si));
1210 global_si_kludge = si; /* I hate C so much... */
1212 # undef ya_rand_init
1215 save_argv (argc, argv);
1216 set_version_string (si, &argc, argv);
1217 privileged_initialization (si, &argc, argv);
1218 hack_environment (si);
1220 shell = connect_to_server (si, &argc, argv);
1221 process_command_line (si, &argc, argv);
1224 load_init_file (p); /* must be before initialize_per_screen_info() */
1225 blurb_timestamp_p = p->timestamp_p; /* kludge */
1226 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1228 /* We can only issue this warnings now. */
1229 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1231 "%s: there are no PseudoColor or GrayScale visuals.\n"
1232 "%s: ignoring the request for fading/unfading.\n",
1235 for (i = 0; i < si->nscreens; i++)
1236 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1239 lock_initialization (si, &argc, argv);
1241 if (p->xsync_p) XSynchronize (si->dpy, True);
1243 if (p->verbose_p) analyze_display (si);
1244 initialize_server_extensions (si);
1246 si->blank_time = time ((time_t) 0); /* must be before ..._window */
1247 initialize_screensaver_window (si);
1252 disable_builtin_screensaver (si, True);
1253 sync_server_dpms_settings (si->dpy,
1254 (p->dpms_enabled_p &&
1255 p->mode != DONT_BLANK),
1256 p->dpms_standby / 1000,
1257 p->dpms_suspend / 1000,
1261 initialize_stderr (si);
1262 handle_signals (si);
1264 make_splash_dialog (si);
1266 main_loop (si); /* doesn't return */
1271 /* Processing ClientMessage events.
1275 static Bool error_handler_hit_p = False;
1278 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1280 error_handler_hit_p = True;
1284 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1285 them. We only look up the atom names for printing warning messages,
1286 so don't bomb out when it happens...
1289 XGetAtomName_safe (Display *dpy, Atom atom)
1292 XErrorHandler old_handler;
1293 if (!atom) return 0;
1296 error_handler_hit_p = False;
1297 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1298 result = XGetAtomName (dpy, atom);
1300 XSetErrorHandler (old_handler);
1302 if (error_handler_hit_p) result = 0;
1309 sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned long) atom);
1310 return strdup (buf);
1316 clientmessage_response (saver_info *si, Window w, Bool error,
1317 const char *stderr_msg,
1318 const char *protocol_msg)
1322 saver_preferences *p = &si->prefs;
1323 XErrorHandler old_handler;
1325 if (error || p->verbose_p)
1326 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1328 L = strlen(protocol_msg);
1329 proto = (char *) malloc (L + 2);
1330 proto[0] = (error ? '-' : '+');
1331 strcpy (proto+1, protocol_msg);
1334 /* Ignore all X errors while sending a response to a ClientMessage.
1335 Pretty much the only way we could get an error here is if the
1336 window we're trying to send the reply on has been deleted, in
1337 which case, the sender of the ClientMessage won't see our response
1340 XSync (si->dpy, False);
1341 error_handler_hit_p = False;
1342 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1344 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1345 PropModeReplace, (unsigned char *) proto, L);
1347 XSync (si->dpy, False);
1348 XSetErrorHandler (old_handler);
1349 XSync (si->dpy, False);
1356 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1358 char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1359 Window w = event->xclient.window;
1364 for (screen = 0; screen < si->nscreens; screen++)
1365 if (w == si->screens[screen].screensaver_window)
1367 strcpy (wdesc, "xscreensaver");
1370 else if (w == RootWindow (si->dpy, screen))
1372 strcpy (wdesc, "root");
1378 XErrorHandler old_handler;
1380 XWindowAttributes xgwa;
1381 memset (&hint, 0, sizeof(hint));
1382 memset (&xgwa, 0, sizeof(xgwa));
1384 XSync (si->dpy, False);
1385 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1386 XGetClassHint (si->dpy, w, &hint);
1387 XGetWindowAttributes (si->dpy, w, &xgwa);
1388 XSync (si->dpy, False);
1389 XSetErrorHandler (old_handler);
1390 XSync (si->dpy, False);
1392 screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1394 sprintf (wdesc, "%.20s / %.20s",
1395 (hint.res_name ? hint.res_name : "(null)"),
1396 (hint.res_class ? hint.res_class : "(null)"));
1397 if (hint.res_name) XFree (hint.res_name);
1398 if (hint.res_class) XFree (hint.res_class);
1401 fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1402 blurb(), screen, (str ? str : "(null)"));
1403 fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1404 blurb(), screen, (unsigned long) w, wdesc);
1405 if (str) XFree (str);
1409 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1411 saver_preferences *p = &si->prefs;
1413 Window window = event->xclient.window;
1415 /* Preferences might affect our handling of client messages. */
1416 maybe_reload_init_file (si);
1418 if (event->xclient.message_type != XA_SCREENSAVER ||
1419 event->xclient.format != 32)
1421 bogus_clientmessage_warning (si, event);
1425 type = event->xclient.data.l[0];
1426 if (type == XA_ACTIVATE)
1430 clientmessage_response(si, window, False,
1431 "ACTIVATE ClientMessage received.",
1433 si->selection_mode = 0;
1434 si->demoing_p = False;
1436 if (si->throttled_p && p->verbose_p)
1437 fprintf (stderr, "%s: unthrottled.\n", blurb());
1438 si->throttled_p = False;
1440 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1442 XForceScreenSaver (si->dpy, ScreenSaverActive);
1450 clientmessage_response(si, window, True,
1451 "ClientMessage ACTIVATE received while already active.",
1454 else if (type == XA_DEACTIVATE)
1458 if (si->throttled_p && p->verbose_p)
1459 fprintf (stderr, "%s: unthrottled.\n", blurb());
1460 si->throttled_p = False;
1462 clientmessage_response(si, window, False,
1463 "DEACTIVATE ClientMessage received.",
1465 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1467 XForceScreenSaver (si->dpy, ScreenSaverReset);
1475 clientmessage_response(si, window, False,
1476 "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
1477 "not active: idle timer reset.");
1480 else if (type == XA_CYCLE)
1484 clientmessage_response(si, window, False,
1485 "CYCLE ClientMessage received.",
1487 si->selection_mode = 0; /* 0 means randomize when its time. */
1488 si->demoing_p = False;
1490 if (si->throttled_p && p->verbose_p)
1491 fprintf (stderr, "%s: unthrottled.\n", blurb());
1492 si->throttled_p = False;
1495 XtRemoveTimeOut (si->cycle_id);
1497 cycle_timer ((XtPointer) si, 0);
1500 clientmessage_response(si, window, True,
1501 "ClientMessage CYCLE received while inactive.",
1504 else if (type == XA_NEXT || type == XA_PREV)
1506 clientmessage_response(si, window, False,
1508 ? "NEXT ClientMessage received."
1509 : "PREV ClientMessage received."),
1511 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1512 si->demoing_p = False;
1514 if (si->throttled_p && p->verbose_p)
1515 fprintf (stderr, "%s: unthrottled.\n", blurb());
1516 si->throttled_p = False;
1521 XtRemoveTimeOut (si->cycle_id);
1523 cycle_timer ((XtPointer) si, 0);
1528 else if (type == XA_SELECT)
1532 long which = event->xclient.data.l[1];
1534 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1535 sprintf (buf2, "activating (%ld).", which);
1536 clientmessage_response (si, window, False, buf, buf2);
1538 if (which < 0) which = 0; /* 0 == "random" */
1539 si->selection_mode = which;
1540 si->demoing_p = False;
1542 if (si->throttled_p && p->verbose_p)
1543 fprintf (stderr, "%s: unthrottled.\n", blurb());
1544 si->throttled_p = False;
1549 XtRemoveTimeOut (si->cycle_id);
1551 cycle_timer ((XtPointer) si, 0);
1556 else if (type == XA_EXIT)
1558 /* Ignore EXIT message if the screen is locked. */
1559 if (until_idle_p || !si->locked_p)
1561 clientmessage_response (si, window, False,
1562 "EXIT ClientMessage received.",
1566 unblank_screen (si);
1567 kill_screenhack (si);
1568 XSync (si->dpy, False);
1570 saver_exit (si, 0, 0);
1573 clientmessage_response (si, window, True,
1574 "EXIT ClientMessage received while locked.",
1575 "screen is locked.");
1577 else if (type == XA_RESTART)
1579 /* The RESTART message works whether the screensaver is active or not,
1580 unless the screen is locked, in which case it doesn't work.
1582 if (until_idle_p || !si->locked_p)
1584 clientmessage_response (si, window, False,
1585 "RESTART ClientMessage received.",
1589 unblank_screen (si);
1590 kill_screenhack (si);
1591 XSync (si->dpy, False);
1594 restart_process (si); /* does not return */
1598 clientmessage_response (si, window, True,
1599 "RESTART ClientMessage received while locked.",
1600 "screen is locked.");
1602 else if (type == XA_DEMO)
1604 long arg = event->xclient.data.l[1];
1605 Bool demo_one_hack_p = (arg == 300);
1607 if (demo_one_hack_p)
1611 long which = event->xclient.data.l[2];
1614 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1615 sprintf (buf2, "demoing (%ld).", which);
1616 clientmessage_response (si, window, False, buf, buf2);
1618 if (which < 0) which = 0; /* 0 == "random" */
1619 si->selection_mode = which;
1620 si->demoing_p = True;
1622 if (si->throttled_p && p->verbose_p)
1623 fprintf (stderr, "%s: unthrottled.\n", blurb());
1624 si->throttled_p = False;
1629 clientmessage_response (si, window, True,
1630 "DEMO ClientMessage received while active.",
1635 clientmessage_response (si, window, True,
1636 "obsolete form of DEMO ClientMessage.",
1637 "obsolete form of DEMO ClientMessage.");
1640 else if (type == XA_PREFS)
1642 clientmessage_response (si, window, True,
1643 "the PREFS client-message is obsolete.",
1644 "the PREFS client-message is obsolete.");
1646 else if (type == XA_LOCK)
1649 clientmessage_response (si, window, True,
1650 "not compiled with support for locking.",
1651 "locking not enabled.");
1652 #else /* !NO_LOCKING */
1653 if (si->locking_disabled_p)
1654 clientmessage_response (si, window, True,
1655 "LOCK ClientMessage received, but locking is disabled.",
1656 "locking not enabled.");
1657 else if (si->locked_p)
1658 clientmessage_response (si, window, True,
1659 "LOCK ClientMessage received while already locked.",
1664 char *response = (until_idle_p
1665 ? "activating and locking."
1667 sprintf (buf, "LOCK ClientMessage received; %s", response);
1668 clientmessage_response (si, window, False, buf, response);
1669 set_locked_p (si, True);
1670 si->selection_mode = 0;
1671 si->demoing_p = False;
1673 if (si->lock_id) /* we're doing it now, so lose the timeout */
1675 XtRemoveTimeOut (si->lock_id);
1681 if (si->using_mit_saver_extension ||
1682 si->using_sgi_saver_extension)
1684 XForceScreenSaver (si->dpy, ScreenSaverActive);
1693 #endif /* !NO_LOCKING */
1695 else if (type == XA_THROTTLE)
1697 if (si->throttled_p)
1698 clientmessage_response (si, window, True,
1699 "THROTTLE ClientMessage received, but "
1700 "already throttled.",
1701 "already throttled.");
1705 char *response = "throttled.";
1706 si->throttled_p = True;
1707 si->selection_mode = 0;
1708 si->demoing_p = False;
1709 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
1710 clientmessage_response (si, window, False, buf, response);
1715 XtRemoveTimeOut (si->cycle_id);
1717 cycle_timer ((XtPointer) si, 0);
1721 else if (type == XA_UNTHROTTLE)
1723 if (! si->throttled_p)
1724 clientmessage_response (si, window, True,
1725 "UNTHROTTLE ClientMessage received, but "
1731 char *response = "unthrottled.";
1732 si->throttled_p = False;
1733 si->selection_mode = 0;
1734 si->demoing_p = False;
1735 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
1736 clientmessage_response (si, window, False, buf, response);
1741 XtRemoveTimeOut (si->cycle_id);
1743 cycle_timer ((XtPointer) si, 0);
1751 str = XGetAtomName_safe (si->dpy, type);
1755 if (strlen (str) > 80)
1756 strcpy (str+70, "...");
1757 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
1764 "unrecognised screensaver ClientMessage 0x%x received.",
1765 (unsigned int) event->xclient.data.l[0]);
1768 clientmessage_response (si, window, True, buf, buf);
1774 /* Some random diagnostics printed in -verbose mode.
1778 analyze_display (saver_info *si)
1782 const char *name; const char *desc; Bool useful_p;
1785 { "SCREEN_SAVER", /* underscore */ "SGI Screen-Saver",
1786 # ifdef HAVE_SGI_SAVER_EXTENSION
1791 }, { "SCREEN-SAVER", /* dash */ "SGI Screen-Saver",
1792 # ifdef HAVE_SGI_SAVER_EXTENSION
1797 }, { "MIT-SCREEN-SAVER", "MIT Screen-Saver",
1798 # ifdef HAVE_MIT_SAVER_EXTENSION
1803 }, { "XIDLE", "XIdle",
1804 # ifdef HAVE_XIDLE_EXTENSION
1809 }, { "SGI-VIDEO-CONTROL", "SGI Video-Control",
1810 # ifdef HAVE_SGI_VC_EXTENSION
1815 }, { "READDISPLAY", "SGI Read-Display",
1816 # ifdef HAVE_READ_DISPLAY_EXTENSION
1821 }, { "MIT-SHM", "Shared Memory",
1822 # ifdef HAVE_XSHM_EXTENSION
1827 }, { "DOUBLE-BUFFER", "Double-Buffering",
1828 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
1833 }, { "DPMS", "Power Management",
1834 # ifdef HAVE_DPMS_EXTENSION
1845 }, { "XFree86-VidModeExtension", "XF86 Video-Mode",
1846 # ifdef HAVE_XF86VMODE
1851 }, { "XINERAMA", "Xinerama",
1856 fprintf (stderr, "%s: running on display \"%s\" (%d screen%s).\n",
1858 DisplayString(si->dpy),
1859 si->nscreens, (si->nscreens == 1 ? "" : "s"));
1860 fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
1861 ServerVendor(si->dpy), VendorRelease(si->dpy));
1863 fprintf (stderr, "%s: useful extensions:\n", blurb());
1864 for (i = 0; i < countof(exts); i++)
1866 int op = 0, event = 0, error = 0;
1869 if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
1871 sprintf (buf, "%s: ", blurb());
1873 strcat (buf, exts[i].desc);
1874 if (!exts[i].useful_p)
1877 while (strlen (buf) < k) strcat (buf, " ");
1878 strcat (buf, "<-- not supported at compile time!");
1880 fprintf (stderr, "%s\n", buf);
1883 for (i = 0; i < si->nscreens; i++)
1885 unsigned long colormapped_depths = 0;
1886 unsigned long non_mapped_depths = 0;
1887 XVisualInfo vi_in, *vi_out;
1890 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
1891 if (!vi_out) continue;
1892 for (j = 0; j < out_count; j++)
1893 if (vi_out[j].class == PseudoColor)
1894 colormapped_depths |= (1 << vi_out[j].depth);
1896 non_mapped_depths |= (1 << vi_out[j].depth);
1897 XFree ((char *) vi_out);
1899 if (colormapped_depths)
1901 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(), i);
1902 for (j = 0; j < 32; j++)
1903 if (colormapped_depths & (1 << j))
1904 fprintf (stderr, " %d", j);
1905 fprintf (stderr, ".\n");
1907 if (non_mapped_depths)
1909 fprintf (stderr, "%s: screen %d non-colormapped depths:",
1911 for (j = 0; j < 32; j++)
1912 if (non_mapped_depths & (1 << j))
1913 fprintf (stderr, " %d", j);
1914 fprintf (stderr, ".\n");
1920 display_is_on_console_p (saver_info *si)
1922 Bool not_on_console = True;
1923 char *dpystr = DisplayString (si->dpy);
1924 char *tail = (char *) strchr (dpystr, ':');
1925 if (! tail || strncmp (tail, ":0", 2))
1926 not_on_console = True;
1929 char dpyname[255], localname[255];
1930 strncpy (dpyname, dpystr, tail-dpystr);
1931 dpyname [tail-dpystr] = 0;
1933 !strcmp(dpyname, "unix") ||
1934 !strcmp(dpyname, "localhost"))
1935 not_on_console = False;
1936 else if (gethostname (localname, sizeof (localname)))
1937 not_on_console = True; /* can't find hostname? */
1940 /* We have to call gethostbyname() on the result of gethostname()
1941 because the two aren't guarenteed to be the same name for the
1942 same host: on some losing systems, one is a FQDN and the other
1943 is not. Here in the wide wonderful world of Unix it's rocket
1944 science to obtain the local hostname in a portable fashion.
1946 And don't forget, gethostbyname() reuses the structure it
1947 returns, so we have to copy the fucker before calling it again.
1948 Thank you master, may I have another.
1950 struct hostent *h = gethostbyname (dpyname);
1952 not_on_console = True;
1957 strcpy (hn, h->h_name);
1958 l = gethostbyname (localname);
1959 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
1963 return !not_on_console;
1967 /* Do a little bit of heap introspection...
1970 check_for_leaks (const char *where)
1973 static unsigned long last_brk = 0;
1974 int b = (unsigned long) sbrk(0);
1975 if (last_brk && last_brk < b)
1976 fprintf (stderr, "%s: %s: brk grew by %luK.\n",
1978 (((b - last_brk) + 1023) / 1024));
1980 #endif /* HAVE_SBRK */