1 /* xscreensaver, Copyright (c) 1991-1998 Jamie Zawinski <jwz@netscape.com>
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 that's probably pretty rare.
75 * The reason that we can't select KeyPresses on windows that don't have
76 * them already is that, when dispatching a KeyPress event, X finds the
77 * lowest (leafmost) window in the hierarchy on which *any* client selects
78 * for KeyPress, and sends the event to that window. This means that if a
79 * client had a window with subwindows, and expected to receive KeyPress
80 * events on the parent window instead of the subwindows, then that client
81 * would malfunction if some other client selected KeyPress events on the
82 * subwindows. It is an incredible misdesign that one client can make
83 * another client malfunction in this way.
85 * To detect mouse motion, we periodically wake up and poll the mouse
86 * position and button/modifier state, and notice when something has
87 * changed. We make this check every five seconds by default, and since the
88 * screensaver timeout has a granularity of one minute, this makes the
89 * chance of a false positive very small. We could detect mouse motion in
90 * the same way as keyboard activity, but that would suffer from the same
91 * "client changing event mask" problem that the KeyPress events hack does.
92 * I think polling is more reliable.
94 * None of this crap happens if we're using one of the extensions, so install
95 * one of them if the description above sounds just too flaky to live. It
96 * is, but those are your choices.
98 * A third idle-detection option could be implemented (but is not): when
99 * running on the console display ($DISPLAY is `localhost`:0) and we're on a
100 * machine where /dev/tty and /dev/mouse have reasonable last-modification
101 * times, we could just stat() those. But the incremental benefit of
102 * implementing this is really small, so forget I said anything.
105 * - Have a second terminal handy.
106 * - Be careful where you set your breakpoints, you don't want this to
107 * stop under the debugger with the keyboard grabbed or the blackout
109 * - If you run your debugger under XEmacs, try M-ESC (x-grab-keyboard)
110 * to keep your emacs window alive even when xscreensaver has grabbed.
111 * - Go read the code related to `debug_p'.
112 * - You probably can't set breakpoints in functions that are called on
113 * the other side of a call to fork() -- if your clients are dying
114 * with signal 5, Trace/BPT Trap, you're losing in this way.
115 * - If you aren't using a server extension, don't leave this stopped
116 * under the debugger for very long, or the X input buffer will get
117 * huge because of the keypress events it's selecting for. This can
118 * make your X server wedge with "no more input buffers."
120 * ======================================================================== */
128 #include <X11/Xlib.h>
129 #include <X11/Xatom.h>
130 #include <X11/Intrinsic.h>
131 #include <X11/StringDefs.h>
132 #include <X11/Shell.h>
136 # include <X11/Xmu/Error.h>
138 # include <Xmu/Error.h>
140 #else /* !HAVE_XMU */
142 #endif /* !HAVE_XMU */
144 #ifdef HAVE_XIDLE_EXTENSION
145 #include <X11/extensions/xidle.h>
146 #endif /* HAVE_XIDLE_EXTENSION */
148 #include "xscreensaver.h"
150 #include "yarandom.h"
151 #include "resources.h"
154 saver_info *global_si_kludge = 0; /* I hate C so much... */
161 static Atom XA_SCREENSAVER;
162 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
163 static Atom XA_EXIT, XA_RESTART, XA_DEMO, XA_LOCK;
166 static XrmOptionDescRec options [] = {
167 { "-timeout", ".timeout", XrmoptionSepArg, 0 },
168 { "-cycle", ".cycle", XrmoptionSepArg, 0 },
169 { "-idelay", ".initialDelay", XrmoptionSepArg, 0 },
170 { "-nice", ".nice", XrmoptionSepArg, 0 },
171 { "-visual", ".visualID", XrmoptionSepArg, 0 },
172 { "-lock-timeout", ".lockTimeout", XrmoptionSepArg, 0 },
173 { "-install", ".installColormap", XrmoptionNoArg, "on" },
174 { "-no-install", ".installColormap", XrmoptionNoArg, "off" },
175 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
176 { "-silent", ".verbose", XrmoptionNoArg, "off" },
177 { "-xidle-extension", ".xidleExtension", XrmoptionNoArg, "on" },
178 { "-no-xidle-extension", ".xidleExtension", XrmoptionNoArg, "off" },
179 { "-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "on" },
180 { "-no-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "off" },
181 { "-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "on" },
182 { "-no-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "off" },
183 { "-lock", ".lock", XrmoptionNoArg, "on" },
184 { "-no-lock", ".lock", XrmoptionNoArg, "off" }
187 static char *defaults[] = {
188 #include "XScreenSaver_ad.h"
193 do_help (saver_info *si)
196 xscreensaver %s, copyright (c) 1991-1998 by Jamie Zawinski <jwz@netscape.com>\n\
197 The standard Xt command-line options are accepted; other options include:\n\
199 -timeout <minutes> When the screensaver should activate.\n\
200 -cycle <minutes> How long to let each hack run.\n\
201 -idelay <seconds> How long to sleep before startup.\n\
202 -visual <id-or-class> Which X visual to run on.\n\
203 -demo Enter interactive demo mode on startup.\n\
204 -install Install a private colormap.\n\
205 -no-install Don't.\n\
208 -xidle-extension Use the R5 XIdle server extension.\n\
209 -no-xidle-extension Don't.\n\
210 -mit-extension Use the R6 MIT_SCREEN_SAVER server extension.\n\
211 -no-mit-extension Don't.\n\
212 -sgi-extension Use the SGI SCREEN-SAVER server extension.\n\
213 -no-sgi-extension Don't.\n\
214 -lock Require a password before deactivating.\n\
216 -lock-timeout <minutes> Grace period before locking; default 0.\n\
217 -help This message.\n\
219 Use the `xscreensaver-command' program to control a running screensaver.\n\
221 The *programs resource controls which graphics demos will be launched by the\n\
222 screensaver. See the man page for more details. For updates, check\n\
223 http://people.netscape.com/jwz/xscreensaver/\n\n",
227 printf ("Support for locking was not enabled at compile-time.\n");
230 printf ("Support for demo mode was not enabled at compile-time.\n");
232 #if !defined(HAVE_XIDLE_EXTENSION) && !defined(HAVE_MIT_SAVER_EXTENSION) && !defined(HAVE_SGI_SAVER_EXTENSION)
233 printf ("Support for the XIDLE, SCREEN_SAVER, and MIT-SCREEN-SAVER server\
234 extensions\nwas not enabled at compile-time.\n");
235 #endif /* !HAVE_XIDLE_EXTENSION && !HAVE_MIT_SAVER_EXTENSION && !HAVE_SGI_SAVER_EXTENSION */
243 reformat_hack(const char *hack)
246 const char *in = hack;
248 char *h2 = (char *) malloc(strlen(in) + indent + 2);
251 while (isspace(*in)) in++; /* skip whitespace */
252 while (*in && !isspace(*in) && *in != ':')
253 *out++ = *in++; /* snarf first token */
254 while (isspace(*in)) in++; /* skip whitespace */
257 *out++ = *in++; /* copy colon */
261 out = h2; /* reset to beginning */
266 while (isspace(*in)) in++; /* skip whitespace */
267 for (i = strlen(h2); i < indent; i++) /* indent */
270 while (*in) *out++ = *in++; /* copy rest of line */
278 get_screenhacks (saver_info *si)
280 saver_preferences *p = &si->prefs;
286 d = get_string_resource ("monoPrograms", "MonoPrograms");
287 if (d && !*d) { free(d); d = 0; }
289 d = get_string_resource ("colorPrograms", "ColorPrograms");
290 if (d && !*d) { free(d); d = 0; }
295 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
296 see the manual for details.\n", progname);
300 d = get_string_resource ("programs", "Programs");
303 p->screenhacks = (char **) malloc (sizeof (char *) * hacks_size);
304 p->screenhacks_count = 0;
309 if (d[i] == ' ' || d[i] == '\t' || d[i] == '\n' || d[i] == 0)
314 if (hacks_size <= p->screenhacks_count)
315 p->screenhacks = (char **) realloc (p->screenhacks,
316 (hacks_size = hacks_size * 2) *
318 p->screenhacks [p->screenhacks_count++] = d + i;
319 while (d[i] != 0 && d[i] != '\n')
322 while (i > start && (d[i-1] == ' ' || d[i-1] == '\t'))
328 /* shrink all whitespace to one space, for the benefit of the "demo"
329 mode display. We only do this when we can easily tell that the
330 whitespace is not significant (no shell metachars).
332 for (i = 0; i < p->screenhacks_count; i++)
334 char *s = p->screenhacks [i];
338 for (j = 0; j < L; j++)
342 case '\'': case '"': case '`': case '\\':
348 for (s2 = s+j+1; *s2 == ' ' || *s2 == '\t'; s2++)
352 for (s2 = s+j+1; s2[k]; s2++)
360 p->screenhacks[i] = reformat_hack(s); /* mallocs */
363 if (p->screenhacks_count)
365 /* Shrink down the screenhacks array to be only as big as it needs to.
366 This doesn't really matter at all. */
367 p->screenhacks = (char **)
368 realloc (p->screenhacks, ((p->screenhacks_count + 1) *
370 p->screenhacks [p->screenhacks_count] = 0;
374 free (p->screenhacks);
381 get_resources (saver_info *si)
384 saver_preferences *p = &si->prefs;
386 p->verbose_p = get_boolean_resource ("verbose", "Boolean");
387 p->lock_p = get_boolean_resource ("lock", "Boolean");
388 p->fade_p = get_boolean_resource ("fade", "Boolean");
389 p->unfade_p = get_boolean_resource ("unfade", "Boolean");
390 p->fade_seconds = get_seconds_resource ("fadeSeconds", "Time");
391 p->fade_ticks = get_integer_resource ("fadeTicks", "Integer");
392 p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
393 p->nice_inferior = get_integer_resource ("nice", "Nice");
395 p->initial_delay = get_seconds_resource ("initialDelay", "Time");
396 p->timeout = 1000 * get_minutes_resource ("timeout", "Time");
397 p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
398 p->cycle = 1000 * get_minutes_resource ("cycle", "Time");
401 p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
404 p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
405 p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
407 p->shell = get_string_resource ("bourneShell", "BourneShell");
410 /* don't set use_xidle_extension unless it is explicitly specified */
411 if ((s = get_string_resource ("xidleExtension", "Boolean")))
412 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
414 #ifdef HAVE_XIDLE_EXTENSION /* pick a default */
415 p->use_xidle_extension = True; /* if we have it, use it */
416 #else /* !HAVE_XIDLE_EXTENSION */
417 p->use_xidle_extension = False;
418 #endif /* !HAVE_XIDLE_EXTENSION */
421 /* don't set use_mit_extension unless it is explicitly specified */
422 if ((s = get_string_resource ("mitSaverExtension", "Boolean")))
423 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
426 #ifdef HAVE_MIT_SAVER_EXTENSION /* pick a default */
427 p->use_mit_saver_extension = False; /* Default false, because it sucks */
428 #else /* !HAVE_MIT_SAVER_EXTENSION */
429 p->use_mit_saver_extension = False;
430 #endif /* !HAVE_MIT_SAVER_EXTENSION */
434 /* don't set use_mit_extension unless it is explicitly specified */
435 if ((s = get_string_resource ("sgiSaverExtension", "Boolean")))
436 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
439 #ifdef HAVE_SGI_SAVER_EXTENSION /* pick a default */
440 p->use_sgi_saver_extension = True; /* if we have it, use it */
441 #else /* !HAVE_SGI_SAVER_EXTENSION */
442 p->use_sgi_saver_extension = False;
443 #endif /* !HAVE_SGI_SAVER_EXTENSION */
447 /* Throttle the various timeouts to reasonable values.
450 if (p->passwd_timeout == 0) p->passwd_timeout = 30000; /* 30 secs */
452 if (p->timeout < 10000) p->timeout = 10000; /* 10 secs */
453 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
454 if (p->pointer_timeout == 0) p->pointer_timeout = 5000; /* 5 secs */
455 if (p->notice_events_timeout == 0)
456 p->notice_events_timeout = 10000; /* 10 secs */
457 if (p->fade_seconds == 0 || p->fade_ticks == 0)
459 if (! p->fade_p) p->unfade_p = False;
461 p->watchdog_timeout = p->cycle;
462 if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */
463 if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */
466 si->locking_disabled_p = True;
467 si->nolock_reason = "not compiled with locking support";
471 fprintf (stderr, "%s: not compiled with support for locking.\n",
474 #else /* ! NO_LOCKING */
475 if (p->lock_p && si->locking_disabled_p)
477 fprintf (stderr, "%s: locking is disabled (%s).\n", progname,
481 #endif /* ! NO_LOCKING */
483 get_screenhacks (si);
487 XSynchronize(si->dpy, True);
489 p->initial_delay = 0;
497 time_t now = time ((time_t *) 0);
498 char *str = (char *) ctime (&now);
499 char *nl = (char *) strchr (str, '\n');
500 if (nl) *nl = 0; /* take off that dang newline */
504 static void initialize (saver_info *si, int argc, char **argv);
505 static void main_loop (saver_info *si);
508 main (int argc, char **argv)
511 memset(&si, 0, sizeof(si));
512 global_si_kludge = &si; /* I hate C so much... */
513 initialize (&si, argc, argv);
514 main_loop (&si); /* doesn't return */
520 saver_ehandler (Display *dpy, XErrorEvent *error)
522 saver_info *si = global_si_kludge; /* I hate C so much... */
524 fprintf (real_stderr, "\nX error in %s:\n", progname);
525 if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
528 fprintf (real_stderr, " (nonfatal.)\n");
533 initialize_connection (saver_info *si, int argc, char **argv)
536 Widget toplevel_shell = XtAppInitialize (&si->app, progclass,
537 options, XtNumber (options),
538 &argc, argv, defaults, 0, 0);
540 si->dpy = XtDisplay (toplevel_shell);
541 si->db = XtDatabase (si->dpy);
542 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
544 db = si->db; /* resources.c needs this */
546 if (argc == 2 && !strcmp (argv[1], "-help"))
549 else if (argc == 2 && !strcmp (argv[1], "-debug"))
550 si->prefs.debug_p = True; /* no resource for this one, out of paranoia. */
554 fprintf (stderr, "%s: unknown option %s\n", progname, argv [1]);
560 #endif /* NO_SETUID */
561 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
562 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
563 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
564 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
565 XA_SCREENSAVER_TIME = XInternAtom (si->dpy, "_SCREENSAVER_TIME", False);
566 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
567 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
568 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
569 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
570 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
571 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
572 XA_PREV = XInternAtom (si->dpy, "PREV", False);
573 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
574 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
575 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
577 si->nscreens = ScreenCount(si->dpy);
578 si->screens = (saver_screen_info *)
579 calloc(sizeof(saver_screen_info), si->nscreens);
581 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
583 for (i = 0; i < si->nscreens; i++)
585 saver_screen_info *ssi = &si->screens[i];
587 ssi->screen = ScreenOfDisplay (si->dpy, i);
589 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
590 ssi->default_visual =
591 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
593 ssi->current_visual = ssi->default_visual;
594 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
596 if (ssi == si->default_screen)
597 /* Since this is the default screen, use the one already created. */
598 ssi->toplevel_shell = toplevel_shell;
600 /* Otherwise, each screen must have its own unmapped root widget. */
601 ssi->toplevel_shell =
602 XtVaAppCreateShell(progname, progclass, applicationShellWidgetClass,
604 XtNscreen, ssi->screen,
605 XtNvisual, ssi->current_visual,
606 XtNdepth, visual_depth(ssi->screen,
607 ssi->current_visual),
614 initialize (saver_info *si, int argc, char **argv)
617 saver_preferences *p = &si->prefs;
618 Bool initial_demo_mode_p = False;
619 si->version = (char *) malloc (5);
620 memcpy (si->version, screensaver_id + 17, 4);
622 progname = argv[0]; /* reset later; this is for the benefit of lock_init() */
625 si->locking_disabled_p = True;
626 si->nolock_reason = "not compiled with locking support";
628 si->locking_disabled_p = False;
631 set_auth_parameters(argc, argv);
634 if (! lock_init (argc, argv)) /* before hack_uid() for proper permissions */
636 si->locking_disabled_p = True;
637 si->nolock_reason = "error getting password";
645 progclass = "XScreenSaver";
647 /* remove -demo switch before saving argv */
648 for (i = 1; i < argc; i++)
649 while (!strcmp ("-demo", argv [i]))
652 initial_demo_mode_p = True;
653 for (j = i; j < argc; j++)
654 argv [j] = argv [j+1];
657 if (argc <= i) break;
659 save_argv (argc, argv);
660 initialize_connection (si, argc, argv);
664 %s %s, copyright (c) 1991-1998 by Jamie Zawinski <jwz@netscape.com>\n\
665 pid = %d.\n", progname, si->version, (int) getpid ());
668 for (i = 0; i < si->nscreens; i++)
669 ensure_no_screensaver_running (si->dpy, si->screens[i].screen);
671 si->demo_mode_p = initial_demo_mode_p;
672 srandom ((int) time ((time_t *) 0));
674 if (p->use_sgi_saver_extension)
676 #ifdef HAVE_SGI_SAVER_EXTENSION
677 if (! query_sgi_saver_extension (si))
680 "%s: display %s does not support the SGI SCREEN_SAVER extension.\n",
681 progname, DisplayString (si->dpy));
682 p->use_sgi_saver_extension = False;
684 else if (p->use_mit_saver_extension)
686 fprintf (stderr, "%s: SGI SCREEN_SAVER extension used instead\
687 of MIT-SCREEN-SAVER extension.\n",
689 p->use_mit_saver_extension = False;
691 else if (p->use_xidle_extension)
694 "%s: SGI SCREEN_SAVER extension used instead of XIDLE extension.\n",
696 p->use_xidle_extension = False;
698 #else /* !HAVE_MIT_SAVER_EXTENSION */
700 "%s: not compiled with support for the SGI SCREEN_SAVER extension.\n",
702 p->use_sgi_saver_extension = False;
703 #endif /* !HAVE_SGI_SAVER_EXTENSION */
706 if (p->use_mit_saver_extension)
708 #ifdef HAVE_MIT_SAVER_EXTENSION
709 if (! query_mit_saver_extension (si))
712 "%s: display %s does not support the MIT-SCREEN-SAVER extension.\n",
713 progname, DisplayString (si->dpy));
714 p->use_mit_saver_extension = False;
716 else if (p->use_xidle_extension)
719 "%s: MIT-SCREEN-SAVER extension used instead of XIDLE extension.\n",
721 p->use_xidle_extension = False;
723 #else /* !HAVE_MIT_SAVER_EXTENSION */
725 "%s: not compiled with support for the MIT-SCREEN-SAVER extension.\n",
727 p->use_mit_saver_extension = False;
728 #endif /* !HAVE_MIT_SAVER_EXTENSION */
731 if (p->use_xidle_extension)
733 #ifdef HAVE_XIDLE_EXTENSION
734 int first_event, first_error;
735 if (! XidleQueryExtension (si->dpy, &first_event, &first_error))
738 "%s: display %s does not support the XIdle extension.\n",
739 progname, DisplayString (si->dpy));
740 p->use_xidle_extension = False;
742 #else /* !HAVE_XIDLE_EXTENSION */
743 fprintf (stderr, "%s: not compiled with support for XIdle.\n",
745 p->use_xidle_extension = False;
746 #endif /* !HAVE_XIDLE_EXTENSION */
749 /* Call this only after having probed for presence of desired extension. */
750 initialize_screensaver_window (si);
754 disable_builtin_screensaver (si, True);
756 if (p->verbose_p && p->use_mit_saver_extension)
757 fprintf (stderr, "%s: using MIT-SCREEN-SAVER server extension.\n",
759 if (p->verbose_p && p->use_sgi_saver_extension)
760 fprintf (stderr, "%s: using SGI SCREEN_SAVER server extension.\n",
762 if (p->verbose_p && p->use_xidle_extension)
763 fprintf (stderr, "%s: using XIdle server extension.\n",
766 initialize_stderr (si);
767 XSetErrorHandler (saver_ehandler);
769 if (initial_demo_mode_p)
770 /* If the user wants demo mode, don't wait around before doing it. */
771 p->initial_delay = 0;
773 if (!p->use_xidle_extension &&
774 !p->use_mit_saver_extension &&
775 !p->use_sgi_saver_extension)
777 if (p->initial_delay)
781 printf ("%s: waiting for %d second%s...", progname,
782 (int) p->initial_delay,
783 (p->initial_delay == 1 ? "" : "s"));
786 sleep (p->initial_delay);
792 printf ("%s: selecting events on extant windows...", progname);
796 /* Select events on the root windows of every screen. This also selects
797 for window creation events, so that new subwindows will be noticed.
799 for (i = 0; i < si->nscreens; i++)
800 start_notice_events_timer (si,
801 RootWindowOfScreen (si->screens[i].screen));
809 main_loop (saver_info *si)
811 saver_preferences *p = &si->prefs;
814 if (! si->demo_mode_p)
815 sleep_until_idle (si, True);
821 #endif /* !NO_DEMO_MODE */
824 printf ("%s: user is idle; waking up at %s.\n", progname,
827 spawn_screenhack (si, True);
829 si->cycle_id = XtAppAddTimeOut (si->app, p->cycle, cycle_timer,
833 if (p->lock_p && p->lock_timeout == 0)
835 if (p->lock_p && !si->locked_p)
836 /* locked_p might be true already because of ClientMessage */
837 si->lock_id = XtAppAddTimeOut (si->app, p->lock_timeout,
840 #endif /* !NO_LOCKING */
844 sleep_until_idle (si, False); /* until not idle */
850 if (si->locking_disabled_p) abort ();
851 si->dbox_up_p = True;
853 /* We used to ungrab the keyboard here, before calling unlock_p()
854 to pop up the dialog box. This left the keyboard ungrabbed
855 for a small window, during an insecure state. Bennett Todd
856 was seeing the bahavior that, when the load was high, he could
857 actually get characters through to a shell under the saver
858 window (he accidentally typed his password there...)
860 So the ungrab has been moved down into pop_passwd_dialog()
861 just after the server is grabbed, closing this window
864 /* ungrab_keyboard_and_mouse (si); */
867 saver_screen_info *ssi = si->default_screen;
868 suspend_screenhack (si, True);
869 XUndefineCursor (si->dpy, ssi->screensaver_window);
871 printf ("%s: prompting for password.\n", progname);
873 if (p->verbose_p && val == False)
874 printf ("%s: password incorrect!\n", progname);
875 si->dbox_up_p = False;
876 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
877 suspend_screenhack (si, False);
879 /* I think this grab is now redundant, but it shouldn't hurt.
881 if (!si->demo_mode_p)
882 grab_keyboard_and_mouse (si, ssi->screensaver_window,
888 si->locked_p = False;
890 #endif /* !NO_LOCKING */
892 /* Let's kill it before unblanking, to get it to stop drawing as
893 soon as possible... */
894 kill_screenhack (si);
899 XtRemoveTimeOut (si->cycle_id);
906 XtRemoveTimeOut (si->lock_id);
909 #endif /* !NO_LOCKING */
912 printf ("%s: user is active; going to sleep at %s.\n", progname,
921 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
923 saver_preferences *p = &si->prefs;
925 if (event->xclient.message_type != XA_SCREENSAVER)
928 str = XGetAtomName (si->dpy, event->xclient.message_type);
929 fprintf (stderr, "%s: unrecognised ClientMessage type %s received\n",
930 progname, (str ? str : "(null)"));
931 if (str) XFree (str);
934 if (event->xclient.format != 32)
936 fprintf (stderr, "%s: ClientMessage of format %d received, not 32\n",
937 progname, event->xclient.format);
941 type = event->xclient.data.l[0];
942 if (type == XA_ACTIVATE)
947 printf ("%s: ACTIVATE ClientMessage received.\n", progname);
948 if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
950 XForceScreenSaver (si->dpy, ScreenSaverActive);
959 "%s: ClientMessage ACTIVATE received while already active.\n",
962 else if (type == XA_DEACTIVATE)
967 printf ("%s: DEACTIVATE ClientMessage received.\n", progname);
968 if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
970 XForceScreenSaver (si->dpy, ScreenSaverReset);
979 "%s: ClientMessage DEACTIVATE received while inactive.\n",
982 else if (type == XA_CYCLE)
987 printf ("%s: CYCLE ClientMessage received.\n", progname);
989 XtRemoveTimeOut (si->cycle_id);
991 cycle_timer ((XtPointer) si, 0);
994 fprintf (stderr, "%s: ClientMessage CYCLE received while inactive.\n",
997 else if (type == XA_NEXT || type == XA_PREV)
1000 printf ("%s: %s ClientMessage received.\n", progname,
1001 (type == XA_NEXT ? "NEXT" : "PREV"));
1002 si->next_mode_p = 1 + (type == XA_PREV);
1007 XtRemoveTimeOut (si->cycle_id);
1009 cycle_timer ((XtPointer) si, 0);
1014 else if (type == XA_EXIT)
1016 /* Ignore EXIT message if the screen is locked. */
1017 if (until_idle_p || !si->locked_p)
1020 printf ("%s: EXIT ClientMessage received.\n", progname);
1023 unblank_screen (si);
1024 kill_screenhack (si);
1025 XSync (si->dpy, False);
1030 fprintf (stderr, "%s: EXIT ClientMessage received while locked.\n",
1033 else if (type == XA_RESTART)
1035 /* The RESTART message works whether the screensaver is active or not,
1036 unless the screen is locked, in which case it doesn't work.
1038 if (until_idle_p || !si->locked_p)
1041 printf ("%s: RESTART ClientMessage received.\n", progname);
1044 unblank_screen (si);
1045 kill_screenhack (si);
1046 XSync (si->dpy, False);
1049 /* make sure error message shows up before exit. */
1050 if (real_stderr && stderr != real_stderr)
1051 dup2 (fileno(real_stderr), fileno(stderr));
1053 restart_process (si);
1054 exit (1); /* shouldn't get here; but if restarting didn't work,
1055 make this command be the same as EXIT. */
1058 fprintf(stderr, "%s: RESTART ClientMessage received while locked.\n",
1061 else if (type == XA_DEMO)
1064 fprintf (stderr, "%s: not compiled with support for DEMO mode\n",
1070 printf ("%s: DEMO ClientMessage received.\n", progname);
1071 si->demo_mode_p = True;
1075 "%s: DEMO ClientMessage received while active.\n", progname);
1078 else if (type == XA_LOCK)
1081 fprintf (stderr, "%s: not compiled with support for LOCK mode\n",
1084 if (si->locking_disabled_p)
1086 "%s: LOCK ClientMessage received, but locking is disabled.\n",
1088 else if (si->locked_p)
1090 "%s: LOCK ClientMessage received while already locked.\n",
1094 si->locked_p = True;
1096 printf ("%s: LOCK ClientMessage received;%s locking.\n",
1097 progname, until_idle_p ? " activating and" : "");
1099 if (si->lock_id) /* we're doing it now, so lose the timeout */
1101 XtRemoveTimeOut (si->lock_id);
1107 if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
1109 XForceScreenSaver (si->dpy, ScreenSaverActive);
1123 str = (type ? XGetAtomName(si->dpy, type) : 0);
1126 "%s: unrecognised screensaver ClientMessage %s received\n",
1130 "%s: unrecognised screensaver ClientMessage 0x%x received\n",
1131 progname, (unsigned int) event->xclient.data.l[0]);
1132 if (str) XFree (str);