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_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
162 static Atom XA_EXIT, XA_RESTART, XA_DEMO, XA_LOCK;
165 static XrmOptionDescRec options [] = {
166 { "-timeout", ".timeout", XrmoptionSepArg, 0 },
167 { "-cycle", ".cycle", XrmoptionSepArg, 0 },
168 { "-lock-mode", ".lock", XrmoptionNoArg, "on" },
169 { "-no-lock-mode", ".lock", XrmoptionNoArg, "off" },
170 { "-lock-timeout", ".lockTimeout", XrmoptionSepArg, 0 },
171 { "-visual", ".visualID", XrmoptionSepArg, 0 },
172 { "-install", ".installColormap", XrmoptionNoArg, "on" },
173 { "-no-install", ".installColormap", XrmoptionNoArg, "off" },
174 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
175 { "-silent", ".verbose", XrmoptionNoArg, "off" },
176 { "-xidle-extension", ".xidleExtension", XrmoptionNoArg, "on" },
177 { "-no-xidle-extension", ".xidleExtension", XrmoptionNoArg, "off" },
178 { "-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "on" },
179 { "-no-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "off" },
180 { "-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "on" },
181 { "-no-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "off" },
182 { "-idelay", ".initialDelay", XrmoptionSepArg, 0 },
183 { "-nice", ".nice", XrmoptionSepArg, 0 }
186 static char *defaults[] = {
187 #include "XScreenSaver_ad.h"
192 do_help (saver_info *si)
195 xscreensaver %s, copyright (c) 1991-1998 by Jamie Zawinski <jwz@netscape.com>\n\
196 The standard Xt command-line options are accepted; other options include:\n\
198 -timeout <minutes> When the screensaver should activate.\n\
199 -cycle <minutes> How long to let each hack run.\n\
200 -lock-mode Require a password before deactivating.\n\
201 -no-lock-mode Don't.\n\
202 -lock-timeout <minutes> Grace period before locking; default 0.\n\
203 -visual <id-or-class> Which X visual to run on.\n\
204 -install Install a private colormap.\n\
205 -no-install Don't.\n\
208 -mit-extension Use the R6 MIT_SCREEN_SAVER server extension.\n\
209 -no-mit-extension Don't.\n\
210 -sgi-extension Use the SGI SCREEN-SAVER server extension.\n\
211 -no-sgi-extension Don't.\n\
212 -xidle-extension Use the R5 XIdle server extension.\n\
213 -no-xidle-extension Don't.\n\
214 -help This message.\n\
216 The `xscreensaver' program should be left running in the background.\n\
217 Use the `xscreensaver-command' program to manipulate a running xscreensaver.\n\
219 The `*programs' resource controls which graphics demos will be launched by\n\
220 the screensaver. See `man xscreensaver' or the web page for more details.\n\
222 For updates, check http://people.netscape.com/jwz/xscreensaver/\n\n",
226 printf ("Support for locking was not enabled at compile-time.\n");
229 printf ("Support for demo mode was not enabled at compile-time.\n");
231 #if !defined(HAVE_XIDLE_EXTENSION) && !defined(HAVE_MIT_SAVER_EXTENSION) && !defined(HAVE_SGI_SAVER_EXTENSION)
232 printf ("Support for the XIDLE, SCREEN_SAVER, and MIT-SCREEN-SAVER server\
233 extensions\nwas not enabled at compile-time.\n");
234 #endif /* !HAVE_XIDLE_EXTENSION && !HAVE_MIT_SAVER_EXTENSION && !HAVE_SGI_SAVER_EXTENSION */
242 reformat_hack(const char *hack)
245 const char *in = hack;
247 char *h2 = (char *) malloc(strlen(in) + indent + 2);
250 while (isspace(*in)) in++; /* skip whitespace */
251 while (*in && !isspace(*in) && *in != ':')
252 *out++ = *in++; /* snarf first token */
253 while (isspace(*in)) in++; /* skip whitespace */
256 *out++ = *in++; /* copy colon */
260 out = h2; /* reset to beginning */
265 while (isspace(*in)) in++; /* skip whitespace */
266 for (i = strlen(h2); i < indent; i++) /* indent */
269 while (*in) *out++ = *in++; /* copy rest of line */
277 get_screenhacks (saver_info *si)
279 saver_preferences *p = &si->prefs;
285 d = get_string_resource ("monoPrograms", "MonoPrograms");
286 if (d && !*d) { free(d); d = 0; }
288 d = get_string_resource ("colorPrograms", "ColorPrograms");
289 if (d && !*d) { free(d); d = 0; }
294 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
295 see the manual for details.\n", progname);
299 d = get_string_resource ("programs", "Programs");
301 size = d ? strlen (d) : 0;
302 p->screenhacks = (char **) malloc (sizeof (char *) * hacks_size);
303 p->screenhacks_count = 0;
308 if (d[i] == ' ' || d[i] == '\t' || d[i] == '\n' || d[i] == 0)
313 if (hacks_size <= p->screenhacks_count)
314 p->screenhacks = (char **) realloc (p->screenhacks,
315 (hacks_size = hacks_size * 2) *
317 p->screenhacks [p->screenhacks_count++] = d + i;
318 while (d[i] != 0 && d[i] != '\n')
321 while (i > start && (d[i-1] == ' ' || d[i-1] == '\t'))
327 /* shrink all whitespace to one space, for the benefit of the "demo"
328 mode display. We only do this when we can easily tell that the
329 whitespace is not significant (no shell metachars).
331 for (i = 0; i < p->screenhacks_count; i++)
333 char *s = p->screenhacks [i];
337 for (j = 0; j < L; j++)
341 case '\'': case '"': case '`': case '\\':
347 for (s2 = s+j+1; *s2 == ' ' || *s2 == '\t'; s2++)
351 for (s2 = s+j+1; s2[k]; s2++)
359 p->screenhacks[i] = reformat_hack(s); /* mallocs */
362 if (p->screenhacks_count)
364 /* Shrink down the screenhacks array to be only as big as it needs to.
365 This doesn't really matter at all. */
366 p->screenhacks = (char **)
367 realloc (p->screenhacks, ((p->screenhacks_count + 1) *
369 p->screenhacks [p->screenhacks_count] = 0;
373 free (p->screenhacks);
380 get_resources (saver_info *si)
383 saver_preferences *p = &si->prefs;
385 p->verbose_p = get_boolean_resource ("verbose", "Boolean");
386 p->lock_p = get_boolean_resource ("lock", "Boolean");
387 p->fade_p = get_boolean_resource ("fade", "Boolean");
388 p->unfade_p = get_boolean_resource ("unfade", "Boolean");
389 p->fade_seconds = get_seconds_resource ("fadeSeconds", "Time");
390 p->fade_ticks = get_integer_resource ("fadeTicks", "Integer");
391 p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
392 p->nice_inferior = get_integer_resource ("nice", "Nice");
394 p->initial_delay = get_seconds_resource ("initialDelay", "Time");
395 p->timeout = 1000 * get_minutes_resource ("timeout", "Time");
396 p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
397 p->cycle = 1000 * get_minutes_resource ("cycle", "Time");
400 p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
403 p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
404 p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
406 p->shell = get_string_resource ("bourneShell", "BourneShell");
409 /* don't set use_xidle_extension unless it is explicitly specified */
410 if ((s = get_string_resource ("xidleExtension", "Boolean")))
411 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
413 #ifdef HAVE_XIDLE_EXTENSION /* pick a default */
414 p->use_xidle_extension = True; /* if we have it, use it */
415 #else /* !HAVE_XIDLE_EXTENSION */
416 p->use_xidle_extension = False;
417 #endif /* !HAVE_XIDLE_EXTENSION */
420 /* don't set use_mit_extension unless it is explicitly specified */
421 if ((s = get_string_resource ("mitSaverExtension", "Boolean")))
422 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
425 #ifdef HAVE_MIT_SAVER_EXTENSION /* pick a default */
426 p->use_mit_saver_extension = False; /* Default false, because it sucks */
427 #else /* !HAVE_MIT_SAVER_EXTENSION */
428 p->use_mit_saver_extension = False;
429 #endif /* !HAVE_MIT_SAVER_EXTENSION */
433 /* don't set use_mit_extension unless it is explicitly specified */
434 if ((s = get_string_resource ("sgiSaverExtension", "Boolean")))
435 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
438 #ifdef HAVE_SGI_SAVER_EXTENSION /* pick a default */
439 p->use_sgi_saver_extension = True; /* if we have it, use it */
440 #else /* !HAVE_SGI_SAVER_EXTENSION */
441 p->use_sgi_saver_extension = False;
442 #endif /* !HAVE_SGI_SAVER_EXTENSION */
446 /* Throttle the various timeouts to reasonable values.
449 if (p->passwd_timeout == 0) p->passwd_timeout = 30000; /* 30 secs */
451 if (p->timeout < 10000) p->timeout = 10000; /* 10 secs */
452 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
453 if (p->pointer_timeout == 0) p->pointer_timeout = 5000; /* 5 secs */
454 if (p->notice_events_timeout == 0)
455 p->notice_events_timeout = 10000; /* 10 secs */
456 if (p->fade_seconds == 0 || p->fade_ticks == 0)
458 if (! p->fade_p) p->unfade_p = False;
460 p->watchdog_timeout = p->cycle;
461 if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */
462 if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */
465 si->locking_disabled_p = True;
466 si->nolock_reason = "not compiled with locking support";
470 fprintf (stderr, "%s: not compiled with support for locking.\n",
473 #else /* ! NO_LOCKING */
474 if (p->lock_p && si->locking_disabled_p)
476 fprintf (stderr, "%s: locking is disabled (%s).\n", progname,
480 #endif /* ! NO_LOCKING */
482 get_screenhacks (si);
486 XSynchronize(si->dpy, True);
488 p->initial_delay = 0;
496 time_t now = time ((time_t *) 0);
497 char *str = (char *) ctime (&now);
498 char *nl = (char *) strchr (str, '\n');
499 if (nl) *nl = 0; /* take off that dang newline */
503 static void initialize (saver_info *si, int argc, char **argv);
504 static void main_loop (saver_info *si);
507 main (int argc, char **argv)
510 memset(&si, 0, sizeof(si));
511 global_si_kludge = &si; /* I hate C so much... */
512 initialize (&si, argc, argv);
513 main_loop (&si); /* doesn't return */
519 saver_ehandler (Display *dpy, XErrorEvent *error)
521 saver_info *si = global_si_kludge; /* I hate C so much... */
523 fprintf (real_stderr, "\nX error in %s:\n", progname);
524 if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
527 fprintf (real_stderr, " (nonfatal.)\n");
532 initialize_connection (saver_info *si, int argc, char **argv)
535 Widget toplevel_shell = XtAppInitialize (&si->app, progclass,
536 options, XtNumber (options),
537 &argc, argv, defaults, 0, 0);
539 si->dpy = XtDisplay (toplevel_shell);
540 si->db = XtDatabase (si->dpy);
541 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
543 if(strlen(progname) > 100) progname [99] = 0; /* keep it short. */
545 db = si->db; /* resources.c needs this */
547 if (argc == 2 && !strcmp (argv[1], "-help"))
550 else if (argc == 2 && !strcmp (argv[1], "-debug"))
551 si->prefs.debug_p = True; /* no resource for this one, out of paranoia. */
555 const char *s = argv[1];
556 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
559 if (s[0] == '-' && s[1] == '-') s++;
560 if (!strcmp (s, "-activate") ||
561 !strcmp (s, "-deactivate") ||
562 !strcmp (s, "-cycle") ||
563 !strcmp (s, "-next") ||
564 !strcmp (s, "-prev") ||
565 !strcmp (s, "-exit") ||
566 !strcmp (s, "-restart") ||
567 !strcmp (s, "-demo") ||
568 !strcmp (s, "-lock") ||
569 !strcmp (s, "-version") ||
570 !strcmp (s, "-time"))
572 fprintf (stderr, "\n\
573 However, %s is an option to the `xscreensaver-command' program.\n\
574 The `xscreensaver' program is a daemon that runs in the background.\n\
575 You control a running xscreensaver process by sending it messages\n\
576 with `xscreensaver-command'. See the man pages for details,\n\
577 or check the web page: http://people.netscape.com/jwz/xscreensaver/\n\n",
580 /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
581 suggest that explicitly. */
582 if (!strcmp (s, "-lock"))
584 Or perhaps you meant either the \"-lock-mode\" or the\n\
585 \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
593 #endif /* NO_SETUID */
594 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
595 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
596 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
597 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
598 XA_SCREENSAVER_TIME = XInternAtom (si->dpy, "_SCREENSAVER_TIME", False);
599 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
600 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
601 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
602 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
603 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
604 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
605 XA_PREV = XInternAtom (si->dpy, "PREV", False);
606 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
607 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
608 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
610 si->nscreens = ScreenCount(si->dpy);
611 si->screens = (saver_screen_info *)
612 calloc(sizeof(saver_screen_info), si->nscreens);
614 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
616 for (i = 0; i < si->nscreens; i++)
618 saver_screen_info *ssi = &si->screens[i];
620 ssi->screen = ScreenOfDisplay (si->dpy, i);
622 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
623 ssi->default_visual =
624 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
626 ssi->current_visual = ssi->default_visual;
627 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
629 if (ssi == si->default_screen)
630 /* Since this is the default screen, use the one already created. */
631 ssi->toplevel_shell = toplevel_shell;
633 /* Otherwise, each screen must have its own unmapped root widget. */
634 ssi->toplevel_shell =
635 XtVaAppCreateShell(progname, progclass, applicationShellWidgetClass,
637 XtNscreen, ssi->screen,
638 XtNvisual, ssi->current_visual,
639 XtNdepth, visual_depth(ssi->screen,
640 ssi->current_visual),
647 initialize (saver_info *si, int argc, char **argv)
650 saver_preferences *p = &si->prefs;
651 Bool initial_demo_mode_p = False;
652 si->version = (char *) malloc (5);
653 memcpy (si->version, screensaver_id + 17, 4);
655 progname = argv[0]; /* reset later; this is for the benefit of lock_init() */
657 if(strlen(progname) > 100) progname[99] = 0; /* keep it short. */
660 si->locking_disabled_p = True;
661 si->nolock_reason = "not compiled with locking support";
662 #else /* !NO_LOCKING */
663 si->locking_disabled_p = False;
666 set_auth_parameters(argc, argv);
669 if (! lock_init (argc, argv)) /* before hack_uid() for proper permissions */
671 si->locking_disabled_p = True;
672 si->nolock_reason = "error getting password";
674 #endif /* !NO_LOCKING */
678 #endif /* NO_SETUID */
680 progclass = "XScreenSaver";
682 /* remove -initial-demo-mode switch before saving argv */
683 for (i = 1; i < argc; i++)
684 while (!strcmp ("-initial-demo-mode", argv [i]))
687 initial_demo_mode_p = True;
688 for (j = i; j < argc; j++)
689 argv [j] = argv [j+1];
692 if (argc <= i) break;
694 save_argv (argc, argv);
695 initialize_connection (si, argc, argv);
699 %s %s, copyright (c) 1991-1998 by Jamie Zawinski <jwz@netscape.com>\n\
700 pid = %d.\n", progname, si->version, (int) getpid ());
703 for (i = 0; i < si->nscreens; i++)
704 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
707 hack_environment (si);
709 si->demo_mode_p = initial_demo_mode_p;
710 srandom ((int) time ((time_t *) 0));
712 if (p->use_sgi_saver_extension)
714 #ifdef HAVE_SGI_SAVER_EXTENSION
715 if (! query_sgi_saver_extension (si))
718 "%s: display %s does not support the SGI SCREEN_SAVER extension.\n",
719 progname, DisplayString (si->dpy));
720 p->use_sgi_saver_extension = False;
722 else if (p->use_mit_saver_extension)
724 fprintf (stderr, "%s: SGI SCREEN_SAVER extension used instead\
725 of MIT-SCREEN-SAVER extension.\n",
727 p->use_mit_saver_extension = False;
729 else if (p->use_xidle_extension)
732 "%s: SGI SCREEN_SAVER extension used instead of XIDLE extension.\n",
734 p->use_xidle_extension = False;
736 #else /* !HAVE_MIT_SAVER_EXTENSION */
738 "%s: not compiled with support for the SGI SCREEN_SAVER extension.\n",
740 p->use_sgi_saver_extension = False;
741 #endif /* !HAVE_SGI_SAVER_EXTENSION */
744 if (p->use_mit_saver_extension)
746 #ifdef HAVE_MIT_SAVER_EXTENSION
747 if (! query_mit_saver_extension (si))
750 "%s: display %s does not support the MIT-SCREEN-SAVER extension.\n",
751 progname, DisplayString (si->dpy));
752 p->use_mit_saver_extension = False;
754 else if (p->use_xidle_extension)
757 "%s: MIT-SCREEN-SAVER extension used instead of XIDLE extension.\n",
759 p->use_xidle_extension = False;
761 #else /* !HAVE_MIT_SAVER_EXTENSION */
763 "%s: not compiled with support for the MIT-SCREEN-SAVER extension.\n",
765 p->use_mit_saver_extension = False;
766 #endif /* !HAVE_MIT_SAVER_EXTENSION */
769 if (p->use_xidle_extension)
771 #ifdef HAVE_XIDLE_EXTENSION
772 int first_event, first_error;
773 if (! XidleQueryExtension (si->dpy, &first_event, &first_error))
776 "%s: display %s does not support the XIdle extension.\n",
777 progname, DisplayString (si->dpy));
778 p->use_xidle_extension = False;
780 #else /* !HAVE_XIDLE_EXTENSION */
781 fprintf (stderr, "%s: not compiled with support for XIdle.\n",
783 p->use_xidle_extension = False;
784 #endif /* !HAVE_XIDLE_EXTENSION */
787 /* Call this only after having probed for presence of desired extension. */
788 initialize_screensaver_window (si);
792 disable_builtin_screensaver (si, True);
794 if (p->verbose_p && p->use_mit_saver_extension)
795 fprintf (stderr, "%s: using MIT-SCREEN-SAVER server extension.\n",
797 if (p->verbose_p && p->use_sgi_saver_extension)
798 fprintf (stderr, "%s: using SGI SCREEN_SAVER server extension.\n",
800 if (p->verbose_p && p->use_xidle_extension)
801 fprintf (stderr, "%s: using XIdle server extension.\n",
804 initialize_stderr (si);
805 XSetErrorHandler (saver_ehandler);
807 if (initial_demo_mode_p)
808 /* If the user wants demo mode, don't wait around before doing it. */
809 p->initial_delay = 0;
811 if (!p->use_xidle_extension &&
812 !p->use_mit_saver_extension &&
813 !p->use_sgi_saver_extension)
815 if (p->initial_delay)
819 printf ("%s: waiting for %d second%s...", progname,
820 (int) p->initial_delay,
821 (p->initial_delay == 1 ? "" : "s"));
824 sleep (p->initial_delay);
830 printf ("%s: selecting events on extant windows...", progname);
834 /* Select events on the root windows of every screen. This also selects
835 for window creation events, so that new subwindows will be noticed.
837 for (i = 0; i < si->nscreens; i++)
838 start_notice_events_timer (si,
839 RootWindowOfScreen (si->screens[i].screen));
847 main_loop (saver_info *si)
849 saver_preferences *p = &si->prefs;
852 if (! si->demo_mode_p)
853 sleep_until_idle (si, True);
859 #endif /* !NO_DEMO_MODE */
862 printf ("%s: user is idle; waking up at %s.\n", progname,
865 spawn_screenhack (si, True);
867 si->cycle_id = XtAppAddTimeOut (si->app, p->cycle, cycle_timer,
871 if (p->lock_p && p->lock_timeout == 0)
873 if (p->lock_p && !si->locked_p)
874 /* locked_p might be true already because of ClientMessage */
875 si->lock_id = XtAppAddTimeOut (si->app, p->lock_timeout,
878 #endif /* !NO_LOCKING */
882 sleep_until_idle (si, False); /* until not idle */
888 if (si->locking_disabled_p) abort ();
889 si->dbox_up_p = True;
891 /* We used to ungrab the keyboard here, before calling unlock_p()
892 to pop up the dialog box. This left the keyboard ungrabbed
893 for a small window, during an insecure state. Bennett Todd
894 was seeing the bahavior that, when the load was high, he could
895 actually get characters through to a shell under the saver
896 window (he accidentally typed his password there...)
898 So the ungrab has been moved down into pop_passwd_dialog()
899 just after the server is grabbed, closing this window
902 /* ungrab_keyboard_and_mouse (si); */
905 saver_screen_info *ssi = si->default_screen;
906 suspend_screenhack (si, True);
907 XUndefineCursor (si->dpy, ssi->screensaver_window);
909 printf ("%s: prompting for password.\n", progname);
911 if (p->verbose_p && val == False)
912 printf ("%s: password incorrect!\n", progname);
913 si->dbox_up_p = False;
914 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
915 suspend_screenhack (si, False);
917 /* I think this grab is now redundant, but it shouldn't hurt.
919 if (!si->demo_mode_p)
920 grab_keyboard_and_mouse (si, ssi->screensaver_window,
926 si->locked_p = False;
928 #endif /* !NO_LOCKING */
930 /* Let's kill it before unblanking, to get it to stop drawing as
931 soon as possible... */
932 kill_screenhack (si);
937 XtRemoveTimeOut (si->cycle_id);
944 XtRemoveTimeOut (si->lock_id);
947 #endif /* !NO_LOCKING */
950 printf ("%s: user is active; going to sleep at %s.\n", progname,
959 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
961 saver_preferences *p = &si->prefs;
963 if (event->xclient.message_type != XA_SCREENSAVER)
966 str = XGetAtomName (si->dpy, event->xclient.message_type);
967 fprintf (stderr, "%s: unrecognised ClientMessage type %s received\n",
968 progname, (str ? str : "(null)"));
969 if (str) XFree (str);
972 if (event->xclient.format != 32)
974 fprintf (stderr, "%s: ClientMessage of format %d received, not 32\n",
975 progname, event->xclient.format);
979 type = event->xclient.data.l[0];
980 if (type == XA_ACTIVATE)
985 printf ("%s: ACTIVATE ClientMessage received.\n", progname);
986 if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
988 XForceScreenSaver (si->dpy, ScreenSaverActive);
997 "%s: ClientMessage ACTIVATE received while already active.\n",
1000 else if (type == XA_DEACTIVATE)
1005 printf ("%s: DEACTIVATE ClientMessage received.\n", progname);
1006 if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
1008 XForceScreenSaver (si->dpy, ScreenSaverReset);
1017 "%s: ClientMessage DEACTIVATE received while inactive.\n",
1020 else if (type == XA_CYCLE)
1025 printf ("%s: CYCLE ClientMessage received.\n", progname);
1027 XtRemoveTimeOut (si->cycle_id);
1029 cycle_timer ((XtPointer) si, 0);
1032 fprintf (stderr, "%s: ClientMessage CYCLE received while inactive.\n",
1035 else if (type == XA_NEXT || type == XA_PREV)
1038 printf ("%s: %s ClientMessage received.\n", progname,
1039 (type == XA_NEXT ? "NEXT" : "PREV"));
1040 si->next_mode_p = 1 + (type == XA_PREV);
1045 XtRemoveTimeOut (si->cycle_id);
1047 cycle_timer ((XtPointer) si, 0);
1052 else if (type == XA_EXIT)
1054 /* Ignore EXIT message if the screen is locked. */
1055 if (until_idle_p || !si->locked_p)
1058 printf ("%s: EXIT ClientMessage received.\n", progname);
1061 unblank_screen (si);
1062 kill_screenhack (si);
1063 XSync (si->dpy, False);
1068 fprintf (stderr, "%s: EXIT ClientMessage received while locked.\n",
1071 else if (type == XA_RESTART)
1073 /* The RESTART message works whether the screensaver is active or not,
1074 unless the screen is locked, in which case it doesn't work.
1076 if (until_idle_p || !si->locked_p)
1079 printf ("%s: RESTART ClientMessage received.\n", progname);
1082 unblank_screen (si);
1083 kill_screenhack (si);
1084 XSync (si->dpy, False);
1087 /* make sure error message shows up before exit. */
1088 if (real_stderr && stderr != real_stderr)
1089 dup2 (fileno(real_stderr), fileno(stderr));
1091 restart_process (si);
1092 exit (1); /* shouldn't get here; but if restarting didn't work,
1093 make this command be the same as EXIT. */
1096 fprintf(stderr, "%s: RESTART ClientMessage received while locked.\n",
1099 else if (type == XA_DEMO)
1102 fprintf (stderr, "%s: not compiled with support for DEMO mode\n",
1108 printf ("%s: DEMO ClientMessage received.\n", progname);
1109 si->demo_mode_p = True;
1113 "%s: DEMO ClientMessage received while active.\n", progname);
1116 else if (type == XA_LOCK)
1119 fprintf (stderr, "%s: not compiled with support for LOCK mode\n",
1122 if (si->locking_disabled_p)
1124 "%s: LOCK ClientMessage received, but locking is disabled.\n",
1126 else if (si->locked_p)
1128 "%s: LOCK ClientMessage received while already locked.\n",
1132 si->locked_p = True;
1134 printf ("%s: LOCK ClientMessage received;%s locking.\n",
1135 progname, until_idle_p ? " activating and" : "");
1137 if (si->lock_id) /* we're doing it now, so lose the timeout */
1139 XtRemoveTimeOut (si->lock_id);
1145 if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
1147 XForceScreenSaver (si->dpy, ScreenSaverActive);
1161 str = (type ? XGetAtomName(si->dpy, type) : 0);
1164 "%s: unrecognised screensaver ClientMessage %s received\n",
1168 "%s: unrecognised screensaver ClientMessage 0x%x received\n",
1169 progname, (unsigned int) event->xclient.data.l[0]);
1170 if (str) XFree (str);