1 /* xscreensaver, Copyright (c) 1991-2008 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.
21 * We place a __SWM_VROOT property on this window, so that newly-started
22 * clients will think that this window is a "virtual root" window (as per
23 * the logic in the historical "vroot.h" header.)
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 * On multi-screen systems, we do the above on each screen, and start
42 * multiple programs, each with a different value of $DISPLAY.
44 * On Xinerama systems, we do a similar thing, but instead create multiple
45 * windows on the (only) display, and tell the subprocess which one to use
46 * via the $XSCREENSAVER_WINDOW environment variable -- this trick requires
47 * a recent (Aug 2003) revision of vroot.h.
49 * (See comments in screens.c for more details about Xinerama/RANDR stuff.)
51 * While we are waiting for user activity, we also set up timers so that,
52 * after a certain amount of time has passed, we can start a different
53 * screenhack. We do this by killing the running child process with
54 * SIGTERM, and then starting a new one in the same way.
56 * If there was a real virtual root, meaning that we removed the __SWM_VROOT
57 * property from it, meaning we must (absolutely must) restore it before we
58 * exit, then we set up signal handlers for most signals (SIGINT, SIGTERM,
59 * etc.) that do this. Most Xlib and Xt routines are not reentrant, so it
60 * is not generally safe to call them from signal handlers; however, this
61 * program spends most of its time waiting, so the window of opportunity
62 * when code could be called reentrantly is fairly small; and also, the worst
63 * that could happen is that the call would fail. If we've gotten one of
64 * these signals, then we're on our way out anyway. If we didn't restore the
65 * __SWM_VROOT property, that would be very bad, so it's worth a shot. Note
66 * that this means that, if you're using a virtual-root window manager, you
67 * can really fuck up the world by killing this process with "kill -9".
69 * This program accepts ClientMessages of type SCREENSAVER; these messages
70 * may contain the atoms ACTIVATE, DEACTIVATE, etc, meaning to turn the
71 * screensaver on or off now, regardless of the idleness of the user,
72 * and a few other things. The included "xscreensaver-command" program
73 * sends these messsages.
75 * If we don't have the XIdle, MIT-SCREEN-SAVER, or SGI SCREEN_SAVER
76 * extensions, then we do the XAutoLock trick: notice every window that
77 * gets created, and wait 30 seconds or so until its creating process has
78 * settled down, and then select KeyPress events on those windows which
79 * already select for KeyPress events. It's important that we not select
80 * KeyPress on windows which don't select them, because that would
81 * interfere with event propagation. This will break if any program
82 * changes its event mask to contain KeyRelease or PointerMotion more than
83 * 30 seconds after creating the window, but such programs do not seem to
84 * occur in nature (I've never seen it happen in all these years.)
86 * The reason that we can't select KeyPresses on windows that don't have
87 * them already is that, when dispatching a KeyPress event, X finds the
88 * lowest (leafmost) window in the hierarchy on which *any* client selects
89 * for KeyPress, and sends the event to that window. This means that if a
90 * client had a window with subwindows, and expected to receive KeyPress
91 * events on the parent window instead of the subwindows, then that client
92 * would malfunction if some other client selected KeyPress events on the
93 * subwindows. It is an incredible misdesign that one client can make
94 * another client malfunction in this way.
96 * To detect mouse motion, we periodically wake up and poll the mouse
97 * position and button/modifier state, and notice when something has
98 * changed. We make this check every five seconds by default, and since the
99 * screensaver timeout has a granularity of one minute, this makes the
100 * chance of a false positive very small. We could detect mouse motion in
101 * the same way as keyboard activity, but that would suffer from the same
102 * "client changing event mask" problem that the KeyPress events hack does.
103 * I think polling is more reliable.
105 * On systems with /proc/interrupts (Linux) we poll that file and note when
106 * the interrupt counter numbers on the "keyboard" and "PS/2" lines change.
107 * (There is no reliable way, using /proc/interrupts, to detect non-PS/2
108 * mice, so it doesn't help for serial or USB mice.)
110 * None of this crap happens if we're using one of the extensions. Sadly,
111 * the XIdle extension hasn't been available for many years; the SGI
112 * extension only exists on SGIs; and the MIT extension, while widely
113 * deployed, is garbage in several ways.
115 * A third idle-detection option could be implemented (but is not): when
116 * running on the console display ($DISPLAY is `localhost`:0) and we're on a
117 * machine where /dev/tty and /dev/mouse have reasonable last-modification
118 * times, we could just stat() those. But the incremental benefit of
119 * implementing this is really small, so forget I said anything.
122 * - Have a second terminal handy.
123 * - Be careful where you set your breakpoints, you don't want this to
124 * stop under the debugger with the keyboard grabbed or the blackout
126 * - If you run your debugger under XEmacs, try M-ESC (x-grab-keyboard)
127 * to keep your emacs window alive even when xscreensaver has grabbed.
128 * - Go read the code related to `debug_p'.
129 * - You probably can't set breakpoints in functions that are called on
130 * the other side of a call to fork() -- if your subprocesses are
131 * dying with signal 5, Trace/BPT Trap, you're losing in this way.
132 * - If you aren't using a server extension, don't leave this stopped
133 * under the debugger for very long, or the X input buffer will get
134 * huge because of the keypress events it's selecting for. This can
135 * make your X server wedge with "no more input buffers."
137 * ======================================================================== */
145 #include <X11/Xlib.h>
147 #include <X11/Xlibint.h>
149 #include <X11/Xatom.h>
150 #include <X11/Intrinsic.h>
151 #include <X11/StringDefs.h>
152 #include <X11/Shell.h>
155 #include <sys/time.h>
156 #include <netdb.h> /* for gethostbyname() */
157 #include <sys/types.h>
161 # include <X11/Xmu/Error.h>
163 # include <Xmu/Error.h>
165 #else /* !HAVE_XMU */
167 #endif /* !HAVE_XMU */
169 #ifdef HAVE_XIDLE_EXTENSION
170 # include <X11/extensions/xidle.h>
171 #endif /* HAVE_XIDLE_EXTENSION */
174 # include <X11/extensions/Xinerama.h>
175 #endif /* HAVE_XINERAMA */
177 #include "xscreensaver.h"
179 #include "yarandom.h"
180 #include "resources.h"
185 saver_info *global_si_kludge = 0; /* I hate C so much... */
192 static Atom XA_SCREENSAVER_RESPONSE;
193 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
194 static Atom XA_RESTART, XA_SELECT;
195 static Atom XA_THROTTLE, XA_UNTHROTTLE;
196 Atom XA_DEMO, XA_PREFS, XA_EXIT, XA_LOCK, XA_BLANK;
199 static XrmOptionDescRec options [] = {
201 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
202 { "-silent", ".verbose", XrmoptionNoArg, "off" },
204 /* xscreensaver-demo uses this one */
205 { "-nosplash", ".splash", XrmoptionNoArg, "off" },
206 { "-no-splash", ".splash", XrmoptionNoArg, "off" },
208 /* useful for debugging */
209 { "-no-capture-stderr", ".captureStderr", XrmoptionNoArg, "off" },
210 { "-log", ".logFile", XrmoptionSepArg, 0 },
214 __extension__ /* shut up about "string length is greater than the length
215 ISO C89 compilers are required to support" when including
219 static char *defaults[] = {
220 #include "XScreenSaver_ad.h"
225 ERROR! You must not include vroot.h in this file.
229 do_help (saver_info *si)
234 xscreensaver %s, copyright (c) 1991-2008 by Jamie Zawinski <jwz@jwz.org>\n\
236 All xscreensaver configuration is via the `~/.xscreensaver' file.\n\
237 Rather than editing that file by hand, just run `xscreensaver-demo':\n\
238 that program lets you configure the screen saver graphically,\n\
239 including timeouts, locking, and display modes.\n\
243 Just getting started? Try this:\n\
248 For updates, online manual, and FAQ, please see the web page:\n\
250 http://www.jwz.org/xscreensaver/\n\
262 time_t now = time ((time_t *) 0);
263 char *str = (char *) ctime (&now);
264 char *nl = (char *) strchr (str, '\n');
265 if (nl) *nl = 0; /* take off that dang newline */
269 static Bool blurb_timestamp_p = True; /* kludge */
274 if (!blurb_timestamp_p)
278 static char buf[255];
279 char *ct = timestring();
280 int n = strlen(progname);
282 strncpy(buf, progname, n);
285 strncpy(buf+n, ct+11, 8);
286 strcpy(buf+n+9, ": ");
293 saver_ehandler (Display *dpy, XErrorEvent *error)
295 saver_info *si = global_si_kludge; /* I hate C so much... */
299 if (!real_stderr) real_stderr = stderr;
301 fprintf (real_stderr, "\n"
302 "#######################################"
303 "#######################################\n\n"
304 "%s: X Error! PLEASE REPORT THIS BUG.\n",
307 for (i = 0; i < si->nscreens; i++)
309 saver_screen_info *ssi = &si->screens[i];
310 fprintf (real_stderr, "%s: screen %d/%d: 0x%x, 0x%x, 0x%x\n",
311 blurb(), ssi->real_screen_number, ssi->number,
312 (unsigned int) RootWindowOfScreen (si->screens[i].screen),
313 (unsigned int) si->screens[i].real_vroot,
314 (unsigned int) si->screens[i].screensaver_window);
317 fprintf (real_stderr, "\n"
318 "#######################################"
319 "#######################################\n\n");
321 fatal_p = XmuPrintDefaultErrorMessage (dpy, error, real_stderr);
323 fatal_p = True; /* The only time I've ever seen a supposedly nonfatal error,
324 it has been BadImplementation / Xlib sequence lost, which
325 are in truth pretty damned fatal.
328 fprintf (real_stderr, "\n");
331 fprintf (real_stderr, "%s: nonfatal error.\n\n", blurb());
334 if (si->prefs.xsync_p)
336 saver_exit (si, -1, "because of synchronous X Error");
341 __extension__ /* don't warn about "string length is greater than the
342 length ISO C89 compilers are required to support". */
344 fprintf (real_stderr,
345 "#######################################################################\n"
347 " If at all possible, please re-run xscreensaver with the command\n"
348 " line arguments `-sync -verbose -log log.txt', and reproduce this\n"
349 " bug. That will cause xscreensaver to dump a `core' file to the\n"
350 " current directory. Please include the stack trace from that core\n"
351 " file in your bug report. *DO NOT* mail the core file itself! That\n"
352 " won't work. A \"log.txt\" file will also be written. Please *do*\n"
353 " include the complete \"log.txt\" file with your bug report.\n"
355 " http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
356 " the most useful bug reports, and how to examine core files.\n"
358 " The more information you can provide, the better. But please\n"
359 " report this bug, regardless!\n"
361 "#######################################################################\n"
365 saver_exit (si, -1, 0);
373 /* This error handler is used only while the X connection is being set up;
374 after we've got a connection, we don't use this handler again. The only
375 reason for having this is so that we can present a more idiot-proof error
376 message than "cannot open display."
379 startup_ehandler (String name, String type, String class,
380 String defalt, /* one can't even spel properly
381 in this joke of a language */
382 String *av, Cardinal *ac)
386 saver_info *si = global_si_kludge; /* I hate C so much... */
387 XrmDatabase *db = XtAppGetErrorDatabase(si->app);
389 XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
390 fmt, sizeof(fmt)-1, *db);
392 fprintf (stderr, "%s: ", blurb());
394 memset (p, 0, sizeof(p));
395 if (*ac > countof (p)) *ac = countof (p);
396 memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
397 fprintf (stderr, fmt, /* Did I mention that I hate C? */
398 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
399 fprintf (stderr, "\n");
401 describe_uids (si, stderr);
403 if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
405 fprintf (stderr, "\n"
406 "%s: This is probably because you're logging in as root. You\n"
407 " shouldn't log in as root: you should log in as a normal user,\n"
408 " and then `su' as needed. If you insist on logging in as\n"
409 " root, you will have to turn off X's security features before\n"
410 " xscreensaver will work.\n"
412 " Please read the manual and FAQ for more information:\n",
417 fprintf (stderr, "\n"
418 "%s: Errors at startup are usually authorization problems.\n"
419 " But you're not logging in as root (good!) so something\n"
420 " else must be wrong. Did you read the manual and the FAQ?\n",
424 fprintf (stderr, "\n"
425 " http://www.jwz.org/xscreensaver/faq.html\n"
426 " http://www.jwz.org/xscreensaver/man.html\n"
435 /* The zillions of initializations.
438 /* Set progname, version, etc. This is done very early.
441 set_version_string (saver_info *si, int *argc, char **argv)
443 progclass = "XScreenSaver";
445 /* progname is reset later, after we connect to X. */
446 progname = strrchr(argv[0], '/');
447 if (progname) progname++;
448 else progname = argv[0];
450 if (strlen(progname) > 100) /* keep it short. */
453 /* The X resource database blows up if argv[0] has a "." in it. */
456 while ((s = strchr (s, '.')))
460 si->version = (char *) malloc (5);
461 memcpy (si->version, screensaver_id + 17, 4);
466 /* Initializations that potentially take place as a priveleged user:
467 If the xscreensaver executable is setuid root, then these initializations
468 are run as root, before discarding privileges.
471 privileged_initialization (saver_info *si, int *argc, char **argv)
474 /* before hack_uid() for proper permissions */
475 lock_priv_init (*argc, argv, si->prefs.verbose_p);
476 #endif /* NO_LOCKING */
482 /* Figure out what locking mechanisms are supported.
485 lock_initialization (saver_info *si, int *argc, char **argv)
488 si->locking_disabled_p = True;
489 si->nolock_reason = "not compiled with locking support";
490 #else /* !NO_LOCKING */
492 /* Finish initializing locking, now that we're out of privileged code. */
493 if (! lock_init (*argc, argv, si->prefs.verbose_p))
495 si->locking_disabled_p = True;
496 si->nolock_reason = "error getting password";
499 /* If locking is currently enabled, but the environment indicates that
500 we have been launched as GDM's "Background" program, then disable
501 locking just in case.
503 if (!si->locking_disabled_p && getenv ("RUNNING_UNDER_GDM"))
505 si->locking_disabled_p = True;
506 si->nolock_reason = "running under GDM";
509 /* If the server is XDarwin (MacOS X) then disable locking.
510 (X grabs only affect X programs, so you can use Command-Tab
511 to bring any other Mac program to the front, e.g., Terminal.)
513 if (!si->locking_disabled_p)
515 int op = 0, event = 0, error = 0;
516 Bool macos_p = False;
519 /* Disable locking if *running* on Apple hardware, since we have no
520 reliable way to determine whether the server is running on MacOS.
521 Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
522 but I'm not really sure about that.
528 /* This extension exists on the Apple X11 server, but not
529 on earlier versions of the XDarwin server. */
530 macos_p = XQueryExtension (si->dpy, "Apple-DRI", &op, &event, &error);
534 si->locking_disabled_p = True;
535 si->nolock_reason = "Cannot lock securely on MacOS X";
539 #endif /* NO_LOCKING */
543 /* Open the connection to the X server, and intern our Atoms.
546 connect_to_server (saver_info *si, int *argc, char **argv)
548 Widget toplevel_shell;
551 char *d = getenv ("DISPLAY");
554 char *ndpy = strdup("DISPLAY=:0.0");
555 /* if (si->prefs.verbose_p) */ /* sigh, too early to test this... */
557 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
561 /* don't free (ndpy) -- some implementations of putenv (BSD 4.4,
562 glibc 2.0) copy the argument, but some (libc4,5, glibc 2.1.2)
563 do not. So we must leak it (and/or the previous setting). Yay.
566 #endif /* HAVE_PUTENV */
568 XSetErrorHandler (saver_ehandler);
570 XtAppSetErrorMsgHandler (si->app, startup_ehandler);
571 toplevel_shell = XtAppInitialize (&si->app, progclass,
572 options, XtNumber (options),
573 argc, argv, defaults, 0, 0);
574 XtAppSetErrorMsgHandler (si->app, 0);
576 si->dpy = XtDisplay (toplevel_shell);
577 si->prefs.db = XtDatabase (si->dpy);
578 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
580 if(strlen(progname) > 100) /* keep it short. */
583 db = si->prefs.db; /* resources.c needs this */
585 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
586 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
587 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
588 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
589 XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
590 XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
592 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
593 XA_ESETROOT_PMAP_ID = XInternAtom (si->dpy, "ESETROOT_PMAP_ID", False);
594 XA_XROOTPMAP_ID = XInternAtom (si->dpy, "_XROOTPMAP_ID", False);
595 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
596 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
597 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
598 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
599 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
600 XA_PREV = XInternAtom (si->dpy, "PREV", False);
601 XA_SELECT = XInternAtom (si->dpy, "SELECT", False);
602 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
603 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
604 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
605 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
606 XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
607 XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
608 XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
610 return toplevel_shell;
614 /* Handle the command-line arguments that were not handled for us by Xt.
615 Issue an error message and exit if there are unknown options.
618 process_command_line (saver_info *si, int *argc, char **argv)
621 for (i = 1; i < *argc; i++)
623 if (!strcmp (argv[i], "-debug"))
624 /* no resource for this one, out of paranoia. */
625 si->prefs.debug_p = True;
627 else if (!strcmp (argv[i], "-h") ||
628 !strcmp (argv[i], "-help") ||
629 !strcmp (argv[i], "--help"))
634 const char *s = argv[i];
635 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
638 if (s[0] == '-' && s[1] == '-') s++;
639 if (!strcmp (s, "-activate") ||
640 !strcmp (s, "-deactivate") ||
641 !strcmp (s, "-cycle") ||
642 !strcmp (s, "-next") ||
643 !strcmp (s, "-prev") ||
644 !strcmp (s, "-exit") ||
645 !strcmp (s, "-restart") ||
646 !strcmp (s, "-demo") ||
647 !strcmp (s, "-prefs") ||
648 !strcmp (s, "-preferences") ||
649 !strcmp (s, "-lock") ||
650 !strcmp (s, "-version") ||
651 !strcmp (s, "-time"))
654 if (!strcmp (s, "-demo") || !strcmp (s, "-prefs"))
655 fprintf (stderr, "\n\
656 Perhaps you meant to run the `xscreensaver-demo' program instead?\n");
658 fprintf (stderr, "\n\
659 However, `%s' is an option to the `xscreensaver-command' program.\n", s);
662 The `xscreensaver' program is a daemon that runs in the background.\n\
663 You control a running xscreensaver process by sending it messages\n\
664 with `xscreensaver-demo' or `xscreensaver-command'.\n\
665 . See the man pages for details, or check the web page:\n\
666 http://www.jwz.org/xscreensaver/\n\n");
675 /* Print out the xscreensaver banner to the tty if applicable;
676 Issue any other warnings that are called for at this point.
679 print_banner (saver_info *si)
681 saver_preferences *p = &si->prefs;
683 /* This resource gets set some time before the others, so that we know
684 whether to print the banner (and so that the banner gets printed before
685 any resource-database-related error messages.)
687 p->verbose_p = (p->debug_p ||
688 get_boolean_resource (si->dpy, "verbose", "Boolean"));
690 /* Ditto, for the locking_disabled_p message. */
691 p->lock_p = get_boolean_resource (si->dpy, "lock", "Boolean");
695 "%s %s, copyright (c) 1991-2008 "
696 "by Jamie Zawinski <jwz@jwz.org>.\n",
697 progname, si->version);
700 fprintf (stderr, "\n"
701 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
703 "\tNote that in debug mode, the xscreensaver window will only\n"
704 "\tcover the left half of the screen. (The idea is that you\n"
705 "\tcan still see debugging output in a shell, if you position\n"
706 "\tit on the right side of the screen.)\n"
708 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
709 "\tuntrusted environments.\n"
715 if (!si->uid_message || !*si->uid_message)
716 describe_uids (si, stderr);
719 if (si->orig_uid && *si->orig_uid)
720 fprintf (stderr, "%s: initial effective uid/gid was %s.\n",
721 blurb(), si->orig_uid);
722 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
725 fprintf (stderr, "%s: in process %lu.\n", blurb(),
726 (unsigned long) getpid());
731 print_lock_failure_banner (saver_info *si)
733 saver_preferences *p = &si->prefs;
735 /* If locking was not able to be initalized for some reason, explain why.
736 (This has to be done after we've read the lock_p resource.)
738 if (si->locking_disabled_p)
741 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
743 if (strstr (si->nolock_reason, "passw"))
744 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
745 "consult the manual.\n", blurb());
746 else if (strstr (si->nolock_reason, "running as "))
748 "%s: locking only works when xscreensaver is launched\n"
749 "\t by a normal, non-privileged user (e.g., not \"root\".)\n"
750 "\t See the manual for details.\n",
757 /* called from screens.c so that all the Xt crud is here. */
759 initialize_screen_root_widget (saver_screen_info *ssi)
761 saver_info *si = ssi->global;
762 if (ssi->toplevel_shell)
763 XtDestroyWidget (ssi->toplevel_shell);
764 ssi->toplevel_shell =
765 XtVaAppCreateShell (progname, progclass,
766 applicationShellWidgetClass,
768 XtNscreen, ssi->screen,
769 XtNvisual, ssi->current_visual,
770 XtNdepth, visual_depth (ssi->screen,
771 ssi->current_visual),
776 /* Examine all of the display's screens, and populate the `saver_screen_info'
777 structures. Make sure this is called after hack_environment() sets $PATH.
780 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
784 update_screen_layout (si);
786 /* Check to see whether fading is ever possible -- if any of the
787 screens on the display has a PseudoColor visual, then fading can
788 work (on at least some screens.) If no screen has a PseudoColor
789 visual, then don't bother ever trying to fade, because it will
790 just cause a delay without causing any visible effect.
792 for (i = 0; i < si->nscreens; i++)
794 saver_screen_info *ssi = &si->screens[i];
795 if (has_writable_cells (ssi->screen, ssi->current_visual) ||
796 get_visual (ssi->screen, "PseudoColor", True, False) ||
797 get_visual (ssi->screen, "GrayScale", True, False))
799 si->fading_possible_p = True;
804 #ifdef HAVE_XF86VMODE_GAMMA
805 si->fading_possible_p = True; /* if we can gamma fade, go for it */
810 /* If any server extensions have been requested, try and initialize them.
811 Issue warnings if requests can't be honored.
814 initialize_server_extensions (saver_info *si)
816 saver_preferences *p = &si->prefs;
818 Bool server_has_xidle_extension_p = False;
819 Bool server_has_sgi_saver_extension_p = False;
820 Bool server_has_mit_saver_extension_p = False;
821 Bool system_has_proc_interrupts_p = False;
822 const char *piwhy = 0;
824 si->using_xidle_extension = p->use_xidle_extension;
825 si->using_sgi_saver_extension = p->use_sgi_saver_extension;
826 si->using_mit_saver_extension = p->use_mit_saver_extension;
827 si->using_proc_interrupts = p->use_proc_interrupts;
829 #ifdef HAVE_XIDLE_EXTENSION
830 server_has_xidle_extension_p = query_xidle_extension (si);
832 #ifdef HAVE_SGI_SAVER_EXTENSION
833 server_has_sgi_saver_extension_p = query_sgi_saver_extension (si);
835 #ifdef HAVE_MIT_SAVER_EXTENSION
836 server_has_mit_saver_extension_p = query_mit_saver_extension (si);
838 #ifdef HAVE_PROC_INTERRUPTS
839 system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
842 if (!server_has_xidle_extension_p)
843 si->using_xidle_extension = False;
844 else if (p->verbose_p)
846 if (si->using_xidle_extension)
847 fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
849 fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
852 if (!server_has_sgi_saver_extension_p)
853 si->using_sgi_saver_extension = False;
854 else if (p->verbose_p)
856 if (si->using_sgi_saver_extension)
857 fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
860 "%s: not using server's SGI SCREEN_SAVER extension.\n",
864 if (!server_has_mit_saver_extension_p)
865 si->using_mit_saver_extension = False;
866 else if (p->verbose_p)
868 if (si->using_mit_saver_extension)
869 fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
873 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
878 query_randr_extension (si);
881 if (!system_has_proc_interrupts_p)
883 si->using_proc_interrupts = False;
884 if (p->verbose_p && piwhy)
885 fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
888 else if (p->verbose_p)
890 if (si->using_proc_interrupts)
892 "%s: consulting /proc/interrupts for keyboard activity.\n",
896 "%s: not consulting /proc/interrupts for keyboard activity.\n",
902 #ifdef DEBUG_MULTISCREEN
904 debug_multiscreen_timer (XtPointer closure, XtIntervalId *id)
906 saver_info *si = (saver_info *) closure;
907 saver_preferences *p = &si->prefs;
908 if (update_screen_layout (si))
912 fprintf (stderr, "%s: new layout:\n", blurb());
913 describe_monitor_layout (si);
915 resize_screensaver_window (si);
917 XtAppAddTimeOut (si->app, 1000*4, debug_multiscreen_timer, (XtPointer) si);
919 #endif /* DEBUG_MULTISCREEN */
922 /* For the case where we aren't using an server extensions, select user events
923 on all the existing windows, and launch timers to select events on
924 newly-created windows as well.
926 If a server extension is being used, this does nothing.
929 select_events (saver_info *si)
931 saver_preferences *p = &si->prefs;
934 if (si->using_xidle_extension ||
935 si->using_mit_saver_extension ||
936 si->using_sgi_saver_extension)
939 if (p->initial_delay)
943 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
944 (int) p->initial_delay/1000,
945 (p->initial_delay == 1000 ? "" : "s"));
949 usleep (p->initial_delay);
951 fprintf (stderr, " done.\n");
956 fprintf (stderr, "%s: selecting events on extant windows...", blurb());
961 /* Select events on the root windows of every screen. This also selects
962 for window creation events, so that new subwindows will be noticed.
964 for (i = 0; i < si->nscreens; i++)
966 saver_screen_info *ssi = &si->screens[i];
967 if (ssi->real_screen_p)
968 start_notice_events_timer (si,
969 RootWindowOfScreen (si->screens[i].screen), False);
973 fprintf (stderr, " done.\n");
975 # ifdef DEBUG_MULTISCREEN
976 if (p->debug_p) debug_multiscreen_timer ((XtPointer) si, 0);
982 maybe_reload_init_file (saver_info *si)
984 saver_preferences *p = &si->prefs;
985 if (init_file_changed_p (p))
988 fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
989 blurb(), init_file_name());
991 load_init_file (si->dpy, p);
993 /* If a server extension is in use, and p->timeout has changed,
994 we need to inform the server of the new timeout. */
995 disable_builtin_screensaver (si, False);
997 /* If the DPMS settings in the init file have changed,
998 change the settings on the server to match. */
999 sync_server_dpms_settings (si->dpy,
1000 (p->dpms_enabled_p &&
1001 p->mode != DONT_BLANK),
1002 p->dpms_standby / 1000,
1003 p->dpms_suspend / 1000,
1012 - wait until the user is idle;
1014 - wait until the user is active;
1015 - unblank the screen;
1020 main_loop (saver_info *si)
1022 saver_preferences *p = &si->prefs;
1028 Bool was_locked = False;
1031 fprintf (stderr, "%s: awaiting idleness.\n", blurb());
1033 check_for_leaks ("unblanked A");
1034 sleep_until_idle (si, True);
1035 check_for_leaks ("unblanked B");
1040 fprintf (stderr, "%s: demoing %d at %s.\n", blurb(),
1041 si->selection_mode, timestring());
1043 fprintf (stderr, "%s: blanking screen at %s.\n", blurb(),
1047 maybe_reload_init_file (si);
1049 if (p->mode == DONT_BLANK)
1052 fprintf (stderr, "%s: idle with blanking disabled at %s.\n",
1053 blurb(), timestring());
1055 /* Go around the loop and wait for the next bout of idleness,
1056 or for the init file to change, or for a remote command to
1057 come in, or something.
1059 But, if locked_p is true, go ahead. This can only happen
1060 if we're in "disabled" mode but a "lock" clientmessage came
1061 in: in that case, we should go ahead and blank/lock the screen.
1067 /* Since we're about to blank the screen, kill the de-race timer,
1068 if any. It might still be running if we have unblanked and then
1069 re-blanked in a short period (e.g., when using the "next" button
1070 in xscreensaver-demo.)
1075 fprintf (stderr, "%s: stopping de-race timer (%d remaining.)\n",
1076 blurb(), si->de_race_ticks);
1077 XtRemoveTimeOut (si->de_race_id);
1082 /* Now, try to blank.
1085 if (! blank_screen (si))
1087 /* We were unable to grab either the keyboard or mouse.
1088 This means we did not (and must not) blank the screen.
1089 If we were to blank the screen while some other program
1090 is holding both the mouse and keyboard grabbed, then
1091 we would never be able to un-blank it! We would never
1092 see any events, and the display would be wedged.
1094 So, just go around the loop again and wait for the
1095 next bout of idleness. (If the user remains idle, we
1096 will next try to blank the screen again in no more than
1099 Time retry = 60 * 1000;
1100 if (p->timeout < retry)
1106 "%s: DEBUG MODE: unable to grab -- BLANKING ANYWAY.\n",
1112 "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
1115 schedule_wakeup_event (si, retry, p->debug_p);
1120 for (i = 0; i < si->nscreens; i++)
1121 kill_screenhack (&si->screens[i]);
1123 raise_window (si, True, True, False);
1124 if (si->throttled_p)
1125 fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
1127 for (i = 0; i < si->nscreens; i++)
1128 spawn_screenhack (&si->screens[i]);
1130 /* Don't start the cycle timer in demo mode. */
1131 if (!si->demoing_p && p->cycle)
1132 si->cycle_id = XtAppAddTimeOut (si->app,
1134 /* see comment in cycle_timer() */
1142 /* Maybe start locking the screen.
1145 Time lock_timeout = p->lock_timeout;
1147 if (si->emergency_lock_p && p->lock_p && lock_timeout)
1149 int secs = p->lock_timeout / 1000;
1152 "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
1154 (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
1158 si->emergency_lock_p = False;
1160 if (!si->demoing_p && /* if not going into demo mode */
1161 p->lock_p && /* and locking is enabled */
1162 !si->locking_disabled_p && /* and locking is possible */
1163 lock_timeout == 0) /* and locking is not timer-deferred */
1164 set_locked_p (si, True); /* then lock right now. */
1166 /* locked_p might be true already because of the above, or because of
1167 the LOCK ClientMessage. But if not, and if we're supposed to lock
1168 after some time, set up a timer to do so.
1173 si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
1174 activate_lock_timer,
1177 #endif /* !NO_LOCKING */
1180 ok_to_unblank = True;
1183 check_for_leaks ("blanked A");
1184 sleep_until_idle (si, False); /* until not idle */
1185 check_for_leaks ("blanked B");
1187 maybe_reload_init_file (si);
1190 /* Maybe unlock the screen.
1194 saver_screen_info *ssi = si->default_screen;
1195 if (si->locking_disabled_p) abort ();
1198 si->dbox_up_p = True;
1199 for (i = 0; i < si->nscreens; i++)
1200 suspend_screenhack (&si->screens[i], True); /* suspend */
1201 XUndefineCursor (si->dpy, ssi->screensaver_window);
1203 ok_to_unblank = unlock_p (si);
1205 si->dbox_up_p = False;
1206 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1207 for (i = 0; i < si->nscreens; i++)
1208 suspend_screenhack (&si->screens[i], False); /* resume */
1210 if (!ok_to_unblank &&
1211 !screenhack_running_p (si))
1213 /* If the lock dialog has been dismissed and we're not about to
1214 unlock the screen, and there is currently no hack running,
1215 then launch one. (There might be no hack running if DPMS
1216 had kicked in. But DPMS is off now, so bring back the hack)
1219 XtRemoveTimeOut (si->cycle_id);
1221 cycle_timer ((XtPointer) si, 0);
1224 #endif /* !NO_LOCKING */
1226 } while (!ok_to_unblank);
1230 fprintf (stderr, "%s: unblanking screen at %s.\n",
1231 blurb(), timestring ());
1233 /* Kill before unblanking, to stop drawing as soon as possible. */
1234 for (i = 0; i < si->nscreens; i++)
1235 kill_screenhack (&si->screens[i]);
1236 unblank_screen (si);
1238 set_locked_p (si, False);
1239 si->emergency_lock_p = False;
1241 si->selection_mode = 0;
1243 /* If we're throttled, and the user has explicitly unlocked the screen,
1244 then unthrottle. If we weren't locked, then don't unthrottle
1245 automatically, because someone might have just bumped the desk... */
1248 if (si->throttled_p && p->verbose_p)
1249 fprintf (stderr, "%s: unthrottled.\n", blurb());
1250 si->throttled_p = False;
1255 XtRemoveTimeOut (si->cycle_id);
1261 XtRemoveTimeOut (si->lock_id);
1265 /* Since we're unblanked now, break race conditions and make
1266 sure we stay that way (see comment in timers.c.) */
1267 if (! si->de_race_id)
1268 de_race_timer ((XtPointer) si, 0);
1272 static void analyze_display (saver_info *si);
1273 static void fix_fds (void);
1276 main (int argc, char **argv)
1280 saver_info *si = &the_si;
1281 saver_preferences *p = &si->prefs;
1282 struct passwd *spasswd;
1285 memset(si, 0, sizeof(*si));
1286 global_si_kludge = si; /* I hate C so much... */
1290 # undef ya_rand_init
1293 save_argv (argc, argv);
1294 set_version_string (si, &argc, argv);
1295 privileged_initialization (si, &argc, argv);
1296 hack_environment (si);
1298 spasswd = getpwuid(getuid());
1301 fprintf(stderr, "Could not figure out who the current user is!\n");
1305 si->user = strdup(spasswd->pw_name ? spasswd->pw_name : "(unknown)");
1308 si->unlock_cb = gui_auth_conv;
1309 si->auth_finished_cb = auth_finished_cb;
1310 # endif /* !NO_LOCKING */
1312 shell = connect_to_server (si, &argc, argv);
1313 process_command_line (si, &argc, argv);
1314 stderr_log_file (si);
1317 load_init_file(si->dpy, p); /* must be before initialize_per_screen_info() */
1318 blurb_timestamp_p = p->timestamp_p; /* kludge */
1319 initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
1321 /* We can only issue this warning now. */
1322 if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
1324 "%s: there are no PseudoColor or GrayScale visuals.\n"
1325 "%s: ignoring the request for fading/unfading.\n",
1328 for (i = 0; i < si->nscreens; i++)
1330 saver_screen_info *ssi = &si->screens[i];
1331 if (ssi->real_screen_p)
1332 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
1336 lock_initialization (si, &argc, argv);
1337 print_lock_failure_banner (si);
1339 if (p->xsync_p) XSynchronize (si->dpy, True);
1341 if (p->verbose_p) analyze_display (si);
1342 initialize_server_extensions (si);
1344 si->blank_time = time ((time_t) 0); /* must be before ..._window */
1345 initialize_screensaver_window (si);
1350 disable_builtin_screensaver (si, True);
1351 sync_server_dpms_settings (si->dpy,
1352 (p->dpms_enabled_p &&
1353 p->mode != DONT_BLANK),
1354 p->dpms_standby / 1000,
1355 p->dpms_suspend / 1000,
1359 initialize_stderr (si);
1360 handle_signals (si);
1362 make_splash_dialog (si);
1364 main_loop (si); /* doesn't return */
1371 /* Bad Things Happen if stdin, stdout, and stderr have been closed
1372 (as by the `sh incantation "xscreensaver >&- 2>&-"). When you do
1373 that, the X connection gets allocated to one of these fds, and
1374 then some random library writes to stderr, and random bits get
1375 stuffed down the X pipe, causing "Xlib: sequence lost" errors.
1376 So, we cause the first three file descriptors to be open to
1377 /dev/null if they aren't open to something else already. This
1378 must be done before any other files are opened (or the closing
1379 of that other file will again free up one of the "magic" first
1382 We do this by opening /dev/null three times, and then closing
1383 those fds, *unless* any of them got allocated as #0, #1, or #2,
1384 in which case we leave them open. Gag.
1386 Really, this crap is technically required of *every* X program,
1387 if you want it to be robust in the face of "2>&-".
1389 int fd0 = open ("/dev/null", O_RDWR);
1390 int fd1 = open ("/dev/null", O_RDWR);
1391 int fd2 = open ("/dev/null", O_RDWR);
1392 if (fd0 > 2) close (fd0);
1393 if (fd1 > 2) close (fd1);
1394 if (fd2 > 2) close (fd2);
1399 /* Processing ClientMessage events.
1403 static Bool error_handler_hit_p = False;
1406 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1408 error_handler_hit_p = True;
1412 /* Sometimes some systems send us ClientMessage events with bogus atoms in
1413 them. We only look up the atom names for printing warning messages,
1414 so don't bomb out when it happens...
1417 XGetAtomName_safe (Display *dpy, Atom atom)
1420 XErrorHandler old_handler;
1421 if (!atom) return 0;
1424 error_handler_hit_p = False;
1425 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1426 result = XGetAtomName (dpy, atom);
1428 XSetErrorHandler (old_handler);
1430 if (error_handler_hit_p) result = 0;
1437 sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned int) atom);
1438 return strdup (buf);
1444 clientmessage_response (saver_info *si, Window w, Bool error,
1445 const char *stderr_msg,
1446 const char *protocol_msg)
1450 saver_preferences *p = &si->prefs;
1451 XErrorHandler old_handler;
1453 if (error || p->verbose_p)
1454 fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
1456 L = strlen(protocol_msg);
1457 proto = (char *) malloc (L + 2);
1458 proto[0] = (error ? '-' : '+');
1459 strcpy (proto+1, protocol_msg);
1462 /* Ignore all X errors while sending a response to a ClientMessage.
1463 Pretty much the only way we could get an error here is if the
1464 window we're trying to send the reply on has been deleted, in
1465 which case, the sender of the ClientMessage won't see our response
1468 XSync (si->dpy, False);
1469 error_handler_hit_p = False;
1470 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1472 XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
1473 PropModeReplace, (unsigned char *) proto, L);
1475 XSync (si->dpy, False);
1476 XSetErrorHandler (old_handler);
1477 XSync (si->dpy, False);
1484 bogus_clientmessage_warning (saver_info *si, XEvent *event)
1486 #if 0 /* Oh, fuck it. GNOME likes to spew random ClientMessages at us
1487 all the time. This is presumably indicative of an error in
1488 the sender of that ClientMessage: if we're getting it and
1489 ignoring it, then it's not reaching the intended recipient.
1490 But people complain to me about this all the time ("waaah!
1491 xscreensaver is printing to it's stderr and that gets my
1492 panties all in a bunch!") And I'm sick of hearing about it.
1493 So we'll just ignore these messages and let GNOME go right
1494 ahead and continue to stumble along in its malfunction.
1497 saver_preferences *p = &si->prefs;
1498 char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
1499 Window w = event->xclient.window;
1502 Bool root_p = False;
1505 for (screen = 0; screen < si->nscreens; screen++)
1506 if (w == si->screens[screen].screensaver_window)
1508 strcpy (wdesc, "xscreensaver");
1511 else if (w == RootWindow (si->dpy, screen))
1513 strcpy (wdesc, "root");
1518 /* If this ClientMessage was sent to the real root window instead of to the
1519 xscreensaver window, then it might be intended for someone else who is
1520 listening on the root window (e.g., the window manager). So only print
1521 the warning if: we are in debug mode; or if the bogus message was
1522 actually sent to one of the xscreensaver-created windows.
1524 if (root_p && !p->debug_p)
1529 XErrorHandler old_handler;
1531 XWindowAttributes xgwa;
1532 memset (&hint, 0, sizeof(hint));
1533 memset (&xgwa, 0, sizeof(xgwa));
1535 XSync (si->dpy, False);
1536 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1537 XGetClassHint (si->dpy, w, &hint);
1538 XGetWindowAttributes (si->dpy, w, &xgwa);
1539 XSync (si->dpy, False);
1540 XSetErrorHandler (old_handler);
1541 XSync (si->dpy, False);
1543 screen = (xgwa.screen ? screen_number (xgwa.screen) : -1);
1545 sprintf (wdesc, "%.20s / %.20s",
1546 (hint.res_name ? hint.res_name : "(null)"),
1547 (hint.res_class ? hint.res_class : "(null)"));
1548 if (hint.res_name) XFree (hint.res_name);
1549 if (hint.res_class) XFree (hint.res_class);
1552 fprintf (stderr, "%s: %d: unrecognised ClientMessage \"%s\" received\n",
1553 blurb(), screen, (str ? str : "(null)"));
1554 fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
1555 blurb(), screen, (unsigned long) w, wdesc);
1556 if (str) XFree (str);
1563 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1565 saver_preferences *p = &si->prefs;
1567 Window window = event->xclient.window;
1569 /* Preferences might affect our handling of client messages. */
1570 maybe_reload_init_file (si);
1572 if (event->xclient.message_type != XA_SCREENSAVER ||
1573 event->xclient.format != 32)
1575 bogus_clientmessage_warning (si, event);
1579 type = event->xclient.data.l[0];
1580 if (type == XA_ACTIVATE)
1584 if (p->mode == DONT_BLANK)
1586 clientmessage_response(si, window, True,
1587 "ACTIVATE ClientMessage received in DONT_BLANK mode.",
1588 "screen blanking is currently disabled.");
1592 clientmessage_response(si, window, False,
1593 "ACTIVATE ClientMessage received.",
1595 si->selection_mode = 0;
1596 si->demoing_p = False;
1598 if (si->throttled_p && p->verbose_p)
1599 fprintf (stderr, "%s: unthrottled.\n", blurb());
1600 si->throttled_p = False;
1602 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1604 XForceScreenSaver (si->dpy, ScreenSaverActive);
1612 clientmessage_response(si, window, True,
1613 "ClientMessage ACTIVATE received while already active.",
1616 else if (type == XA_DEACTIVATE)
1620 if (si->throttled_p && p->verbose_p)
1621 fprintf (stderr, "%s: unthrottled.\n", blurb());
1622 si->throttled_p = False;
1624 clientmessage_response(si, window, False,
1625 "DEACTIVATE ClientMessage received.",
1627 if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
1629 XForceScreenSaver (si->dpy, ScreenSaverReset);
1637 clientmessage_response(si, window, False,
1638 "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
1639 "not active: idle timer reset.");
1642 else if (type == XA_CYCLE)
1646 clientmessage_response(si, window, False,
1647 "CYCLE ClientMessage received.",
1649 si->selection_mode = 0; /* 0 means randomize when its time. */
1650 si->demoing_p = False;
1652 if (si->throttled_p && p->verbose_p)
1653 fprintf (stderr, "%s: unthrottled.\n", blurb());
1654 si->throttled_p = False;
1657 XtRemoveTimeOut (si->cycle_id);
1659 cycle_timer ((XtPointer) si, 0);
1662 clientmessage_response(si, window, True,
1663 "ClientMessage CYCLE received while inactive.",
1666 else if (type == XA_NEXT || type == XA_PREV)
1668 clientmessage_response(si, window, False,
1670 ? "NEXT ClientMessage received."
1671 : "PREV ClientMessage received."),
1673 si->selection_mode = (type == XA_NEXT ? -1 : -2);
1674 si->demoing_p = False;
1676 if (si->throttled_p && p->verbose_p)
1677 fprintf (stderr, "%s: unthrottled.\n", blurb());
1678 si->throttled_p = False;
1683 XtRemoveTimeOut (si->cycle_id);
1685 cycle_timer ((XtPointer) si, 0);
1690 else if (type == XA_SELECT)
1694 long which = event->xclient.data.l[1];
1696 if (p->mode == DONT_BLANK)
1698 clientmessage_response(si, window, True,
1699 "SELECT ClientMessage received in DONT_BLANK mode.",
1700 "screen blanking is currently disabled.");
1704 sprintf (buf, "SELECT %ld ClientMessage received.", which);
1705 sprintf (buf2, "activating (%ld).", which);
1706 clientmessage_response (si, window, False, buf, buf2);
1708 if (which < 0) which = 0; /* 0 == "random" */
1709 si->selection_mode = which;
1710 si->demoing_p = False;
1712 if (si->throttled_p && p->verbose_p)
1713 fprintf (stderr, "%s: unthrottled.\n", blurb());
1714 si->throttled_p = False;
1719 XtRemoveTimeOut (si->cycle_id);
1721 cycle_timer ((XtPointer) si, 0);
1726 else if (type == XA_EXIT)
1728 /* Ignore EXIT message if the screen is locked. */
1729 if (until_idle_p || !si->locked_p)
1731 clientmessage_response (si, window, False,
1732 "EXIT ClientMessage received.",
1737 for (i = 0; i < si->nscreens; i++)
1738 kill_screenhack (&si->screens[i]);
1739 unblank_screen (si);
1740 XSync (si->dpy, False);
1742 saver_exit (si, 0, 0);
1745 clientmessage_response (si, window, True,
1746 "EXIT ClientMessage received while locked.",
1747 "screen is locked.");
1749 else if (type == XA_RESTART)
1751 /* The RESTART message works whether the screensaver is active or not,
1752 unless the screen is locked, in which case it doesn't work.
1754 if (until_idle_p || !si->locked_p)
1756 clientmessage_response (si, window, False,
1757 "RESTART ClientMessage received.",
1762 for (i = 0; i < si->nscreens; i++)
1763 kill_screenhack (&si->screens[i]);
1764 unblank_screen (si);
1765 XSync (si->dpy, False);
1768 restart_process (si); /* does not return */
1772 clientmessage_response (si, window, True,
1773 "RESTART ClientMessage received while locked.",
1774 "screen is locked.");
1776 else if (type == XA_DEMO)
1778 long arg = event->xclient.data.l[1];
1779 Bool demo_one_hack_p = (arg == 5000);
1781 if (demo_one_hack_p)
1785 long which = event->xclient.data.l[2];
1788 sprintf (buf, "DEMO %ld ClientMessage received.", which);
1789 sprintf (buf2, "demoing (%ld).", which);
1790 clientmessage_response (si, window, False, buf, buf2);
1792 if (which < 0) which = 0; /* 0 == "random" */
1793 si->selection_mode = which;
1794 si->demoing_p = True;
1796 if (si->throttled_p && p->verbose_p)
1797 fprintf (stderr, "%s: unthrottled.\n", blurb());
1798 si->throttled_p = False;
1803 clientmessage_response (si, window, True,
1804 "DEMO ClientMessage received while active.",
1809 clientmessage_response (si, window, True,
1810 "obsolete form of DEMO ClientMessage.",
1811 "obsolete form of DEMO ClientMessage.");
1814 else if (type == XA_PREFS)
1816 clientmessage_response (si, window, True,
1817 "the PREFS client-message is obsolete.",
1818 "the PREFS client-message is obsolete.");
1820 else if (type == XA_LOCK)
1823 clientmessage_response (si, window, True,
1824 "not compiled with support for locking.",
1825 "locking not enabled.");
1826 #else /* !NO_LOCKING */
1827 if (si->locking_disabled_p)
1828 clientmessage_response (si, window, True,
1829 "LOCK ClientMessage received, but locking is disabled.",
1830 "locking not enabled.");
1831 else if (si->locked_p)
1832 clientmessage_response (si, window, True,
1833 "LOCK ClientMessage received while already locked.",
1838 char *response = (until_idle_p
1839 ? "activating and locking."
1841 sprintf (buf, "LOCK ClientMessage received; %s", response);
1842 clientmessage_response (si, window, False, buf, response);
1843 set_locked_p (si, True);
1844 si->selection_mode = 0;
1845 si->demoing_p = False;
1847 if (si->lock_id) /* we're doing it now, so lose the timeout */
1849 XtRemoveTimeOut (si->lock_id);
1855 if (si->using_mit_saver_extension ||
1856 si->using_sgi_saver_extension)
1858 XForceScreenSaver (si->dpy, ScreenSaverActive);
1867 #endif /* !NO_LOCKING */
1869 else if (type == XA_THROTTLE)
1871 /* The THROTTLE command is deprecated -- it predates the XDPMS
1872 extension. Instead of using -throttle, users should instead
1873 just power off the monitor (e.g., "xset dpms force off".)
1874 In a few minutes, xscreensaver will notice that the monitor
1875 is off, and cease running hacks.
1877 if (si->throttled_p)
1878 clientmessage_response (si, window, True,
1879 "THROTTLE ClientMessage received, but "
1880 "already throttled.",
1881 "already throttled.");
1885 char *response = "throttled.";
1886 si->throttled_p = True;
1887 si->selection_mode = 0;
1888 si->demoing_p = False;
1889 sprintf (buf, "THROTTLE ClientMessage received; %s", response);
1890 clientmessage_response (si, window, False, buf, response);
1895 XtRemoveTimeOut (si->cycle_id);
1897 cycle_timer ((XtPointer) si, 0);
1901 else if (type == XA_UNTHROTTLE)
1903 if (! si->throttled_p)
1904 clientmessage_response (si, window, True,
1905 "UNTHROTTLE ClientMessage received, but "
1911 char *response = "unthrottled.";
1912 si->throttled_p = False;
1913 si->selection_mode = 0;
1914 si->demoing_p = False;
1915 sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
1916 clientmessage_response (si, window, False, buf, response);
1921 XtRemoveTimeOut (si->cycle_id);
1923 cycle_timer ((XtPointer) si, 0);
1931 str = XGetAtomName_safe (si->dpy, type);
1935 if (strlen (str) > 80)
1936 strcpy (str+70, "...");
1937 sprintf (buf, "unrecognised screensaver ClientMessage %s received.",
1944 "unrecognised screensaver ClientMessage 0x%x received.",
1945 (unsigned int) event->xclient.data.l[0]);
1948 clientmessage_response (si, window, True, buf, buf);
1954 /* Some random diagnostics printed in -verbose mode.
1958 analyze_display (saver_info *si)
1962 const char *name; const char *desc; Bool useful_p;
1965 { "SCREEN_SAVER", /* underscore */ "SGI Screen-Saver",
1966 # ifdef HAVE_SGI_SAVER_EXTENSION
1971 }, { "SCREEN-SAVER", /* dash */ "SGI Screen-Saver",
1972 # ifdef HAVE_SGI_SAVER_EXTENSION
1977 }, { "MIT-SCREEN-SAVER", "MIT Screen-Saver",
1978 # ifdef HAVE_MIT_SAVER_EXTENSION
1983 }, { "XIDLE", "XIdle",
1984 # ifdef HAVE_XIDLE_EXTENSION
1989 }, { "SGI-VIDEO-CONTROL", "SGI Video-Control",
1990 # ifdef HAVE_SGI_VC_EXTENSION
1995 }, { "READDISPLAY", "SGI Read-Display",
1996 # ifdef HAVE_READ_DISPLAY_EXTENSION
2001 }, { "MIT-SHM", "Shared Memory",
2002 # ifdef HAVE_XSHM_EXTENSION
2007 }, { "DOUBLE-BUFFER", "Double-Buffering",
2008 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
2013 }, { "DPMS", "Power Management",
2014 # ifdef HAVE_DPMS_EXTENSION
2025 }, { "XFree86-VidModeExtension", "XF86 Video-Mode",
2026 # ifdef HAVE_XF86VMODE
2031 }, { "XINERAMA", "Xinerama",
2032 # ifdef HAVE_XINERAMA
2037 }, { "RANDR", "Resize-and-Rotate",
2045 }, { "Apple-DRI", "Apple-DRI (XDarwin)",
2050 fprintf (stderr, "%s: running on display \"%s\"\n", blurb(),
2051 DisplayString(si->dpy));
2052 fprintf (stderr, "%s: vendor is %s, %d.\n", blurb(),
2053 ServerVendor(si->dpy), VendorRelease(si->dpy));
2055 fprintf (stderr, "%s: useful extensions:\n", blurb());
2056 for (i = 0; i < countof(exts); i++)
2058 int op = 0, event = 0, error = 0;
2061 if (!XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
2063 sprintf (buf, "%s: ", blurb());
2065 strcat (buf, exts[i].desc);
2066 if (!exts[i].useful_p)
2067 strcat (buf, " (disabled at compile time)");
2068 fprintf (stderr, "%s\n", buf);
2071 for (i = 0; i < si->nscreens; i++)
2073 saver_screen_info *ssi = &si->screens[i];
2074 unsigned long colormapped_depths = 0;
2075 unsigned long non_mapped_depths = 0;
2076 XVisualInfo vi_in, *vi_out;
2079 if (!ssi->real_screen_p) continue;
2081 vi_in.screen = ssi->real_screen_number;
2082 vi_out = XGetVisualInfo (si->dpy, VisualScreenMask, &vi_in, &out_count);
2083 if (!vi_out) continue;
2084 for (j = 0; j < out_count; j++)
2085 if (vi_out[j].class == PseudoColor)
2086 colormapped_depths |= (1 << vi_out[j].depth);
2088 non_mapped_depths |= (1 << vi_out[j].depth);
2089 XFree ((char *) vi_out);
2091 if (colormapped_depths)
2093 fprintf (stderr, "%s: screen %d colormapped depths:", blurb(),
2094 ssi->real_screen_number);
2095 for (j = 0; j < 32; j++)
2096 if (colormapped_depths & (1 << j))
2097 fprintf (stderr, " %d", j);
2098 fprintf (stderr, ".\n");
2100 if (non_mapped_depths)
2102 fprintf (stderr, "%s: screen %d non-colormapped depths:",
2103 blurb(), ssi->real_screen_number);
2104 for (j = 0; j < 32; j++)
2105 if (non_mapped_depths & (1 << j))
2106 fprintf (stderr, " %d", j);
2107 fprintf (stderr, ".\n");
2111 describe_monitor_layout (si);
2116 display_is_on_console_p (saver_info *si)
2118 Bool not_on_console = True;
2119 char *dpystr = DisplayString (si->dpy);
2120 char *tail = (char *) strchr (dpystr, ':');
2121 if (! tail || strncmp (tail, ":0", 2))
2122 not_on_console = True;
2125 char dpyname[255], localname[255];
2126 strncpy (dpyname, dpystr, tail-dpystr);
2127 dpyname [tail-dpystr] = 0;
2129 !strcmp(dpyname, "unix") ||
2130 !strcmp(dpyname, "localhost"))
2131 not_on_console = False;
2132 else if (gethostname (localname, sizeof (localname)))
2133 not_on_console = True; /* can't find hostname? */
2134 else if (!strncmp (dpyname, "/tmp/launch-", 12)) /* MacOS X launchd */
2135 not_on_console = False;
2138 /* We have to call gethostbyname() on the result of gethostname()
2139 because the two aren't guarenteed to be the same name for the
2140 same host: on some losing systems, one is a FQDN and the other
2141 is not. Here in the wide wonderful world of Unix it's rocket
2142 science to obtain the local hostname in a portable fashion.
2144 And don't forget, gethostbyname() reuses the structure it
2145 returns, so we have to copy the fucker before calling it again.
2146 Thank you master, may I have another.
2148 struct hostent *h = gethostbyname (dpyname);
2150 not_on_console = True;
2155 strcpy (hn, h->h_name);
2156 l = gethostbyname (localname);
2157 not_on_console = (!l || !!(strcmp (l->h_name, hn)));
2161 return !not_on_console;
2165 /* Do a little bit of heap introspection...
2168 check_for_leaks (const char *where)
2170 #if defined(HAVE_SBRK) && defined(LEAK_PARANOIA)
2171 static unsigned long last_brk = 0;
2172 int b = (unsigned long) sbrk(0);
2173 if (last_brk && last_brk < b)
2174 fprintf (stderr, "%s: %s: brk grew by %luK.\n",
2176 (((b - last_brk) + 1023) / 1024));
2178 #endif /* HAVE_SBRK */