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", blurb());
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);
379 static Bool blurb_timestamp_p = False; /* kludge */
383 get_resources (saver_info *si)
386 saver_preferences *p = &si->prefs;
388 p->verbose_p = get_boolean_resource ("verbose", "Boolean");
389 p->timestamp_p = get_boolean_resource ("timestamp", "Boolean");
390 p->lock_p = get_boolean_resource ("lock", "Boolean");
391 p->fade_p = get_boolean_resource ("fade", "Boolean");
392 p->unfade_p = get_boolean_resource ("unfade", "Boolean");
393 p->fade_seconds = get_seconds_resource ("fadeSeconds", "Time");
394 p->fade_ticks = get_integer_resource ("fadeTicks", "Integer");
395 p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
396 p->nice_inferior = get_integer_resource ("nice", "Nice");
398 p->initial_delay = get_seconds_resource ("initialDelay", "Time");
399 p->timeout = 1000 * get_minutes_resource ("timeout", "Time");
400 p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
401 p->cycle = 1000 * get_minutes_resource ("cycle", "Time");
404 p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
407 p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
408 p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
410 p->shell = get_string_resource ("bourneShell", "BourneShell");
413 /* don't set use_xidle_extension unless it is explicitly specified */
414 if ((s = get_string_resource ("xidleExtension", "Boolean")))
415 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
417 #ifdef HAVE_XIDLE_EXTENSION /* pick a default */
418 p->use_xidle_extension = True; /* if we have it, use it */
419 #else /* !HAVE_XIDLE_EXTENSION */
420 p->use_xidle_extension = False;
421 #endif /* !HAVE_XIDLE_EXTENSION */
424 /* don't set use_mit_extension unless it is explicitly specified */
425 if ((s = get_string_resource ("mitSaverExtension", "Boolean")))
426 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
429 #ifdef HAVE_MIT_SAVER_EXTENSION /* pick a default */
430 p->use_mit_saver_extension = False; /* Default false, because it sucks */
431 #else /* !HAVE_MIT_SAVER_EXTENSION */
432 p->use_mit_saver_extension = False;
433 #endif /* !HAVE_MIT_SAVER_EXTENSION */
437 /* don't set use_mit_extension unless it is explicitly specified */
438 if ((s = get_string_resource ("sgiSaverExtension", "Boolean")))
439 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
442 #ifdef HAVE_SGI_SAVER_EXTENSION /* pick a default */
443 p->use_sgi_saver_extension = True; /* if we have it, use it */
444 #else /* !HAVE_SGI_SAVER_EXTENSION */
445 p->use_sgi_saver_extension = False;
446 #endif /* !HAVE_SGI_SAVER_EXTENSION */
450 /* Throttle the various timeouts to reasonable values.
453 if (p->passwd_timeout == 0) p->passwd_timeout = 30000; /* 30 secs */
455 if (p->timeout < 10000) p->timeout = 10000; /* 10 secs */
456 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
457 if (p->pointer_timeout == 0) p->pointer_timeout = 5000; /* 5 secs */
458 if (p->notice_events_timeout == 0)
459 p->notice_events_timeout = 10000; /* 10 secs */
460 if (p->fade_seconds == 0 || p->fade_ticks == 0)
462 if (! p->fade_p) p->unfade_p = False;
464 p->watchdog_timeout = p->cycle;
465 if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */
466 if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */
469 si->locking_disabled_p = True;
470 si->nolock_reason = "not compiled with locking support";
474 fprintf (stderr, "%s: not compiled with support for locking.\n",
477 #else /* ! NO_LOCKING */
478 if (p->lock_p && si->locking_disabled_p)
480 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
484 #endif /* ! NO_LOCKING */
486 get_screenhacks (si);
490 XSynchronize(si->dpy, True);
492 p->timestamp_p = True;
493 p->initial_delay = 0;
496 blurb_timestamp_p = p->timestamp_p;
503 time_t now = time ((time_t *) 0);
504 char *str = (char *) ctime (&now);
505 char *nl = (char *) strchr (str, '\n');
506 if (nl) *nl = 0; /* take off that dang newline */
510 static void initialize (saver_info *si, int argc, char **argv);
511 static void main_loop (saver_info *si);
514 main (int argc, char **argv)
517 memset(&si, 0, sizeof(si));
518 global_si_kludge = &si; /* I hate C so much... */
519 initialize (&si, argc, argv);
520 main_loop (&si); /* doesn't return */
526 saver_ehandler (Display *dpy, XErrorEvent *error)
528 saver_info *si = global_si_kludge; /* I hate C so much... */
530 fprintf (real_stderr, "\nX error in %s:\n", blurb());
531 if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
534 fprintf (real_stderr, " (nonfatal.)\n");
542 if (!blurb_timestamp_p)
546 static char buf[255];
547 time_t now = time ((time_t *) 0);
548 char *ct = (char *) ctime (&now);
549 int n = strlen(progname);
551 strncpy(buf, progname, n);
554 strncpy(buf+n, ct+11, 8);
555 strcpy(buf+n+9, ": ");
561 initialize_connection (saver_info *si, int argc, char **argv)
564 Widget toplevel_shell;
566 /* The X resource database blows up if argv[0] has a "." in it. */
569 while ((s = strchr (s, '.')))
573 toplevel_shell = XtAppInitialize (&si->app, progclass,
574 options, XtNumber (options),
575 &argc, argv, defaults, 0, 0);
577 si->dpy = XtDisplay (toplevel_shell);
578 si->db = XtDatabase (si->dpy);
579 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
581 if(strlen(progname) > 100) progname [99] = 0; /* keep it short. */
583 db = si->db; /* resources.c needs this */
585 if (argc == 2 && !strcmp (argv[1], "-help"))
588 else if (argc == 2 && !strcmp (argv[1], "-debug"))
589 si->prefs.debug_p = True; /* no resource for this one, out of paranoia. */
593 const char *s = argv[1];
594 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
597 if (s[0] == '-' && s[1] == '-') s++;
598 if (!strcmp (s, "-activate") ||
599 !strcmp (s, "-deactivate") ||
600 !strcmp (s, "-cycle") ||
601 !strcmp (s, "-next") ||
602 !strcmp (s, "-prev") ||
603 !strcmp (s, "-exit") ||
604 !strcmp (s, "-restart") ||
605 !strcmp (s, "-demo") ||
606 !strcmp (s, "-lock") ||
607 !strcmp (s, "-version") ||
608 !strcmp (s, "-time"))
610 fprintf (stderr, "\n\
611 However, %s is an option to the `xscreensaver-command' program.\n\
612 The `xscreensaver' program is a daemon that runs in the background.\n\
613 You control a running xscreensaver process by sending it messages\n\
614 with `xscreensaver-command'. See the man pages for details,\n\
615 or check the web page: http://people.netscape.com/jwz/xscreensaver/\n\n",
618 /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
619 suggest that explicitly. */
620 if (!strcmp (s, "-lock"))
622 Or perhaps you meant either the \"-lock-mode\" or the\n\
623 \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
631 #endif /* NO_SETUID */
632 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
633 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
634 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
635 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
636 XA_SCREENSAVER_TIME = XInternAtom (si->dpy, "_SCREENSAVER_TIME", False);
637 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
638 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
639 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
640 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
641 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
642 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
643 XA_PREV = XInternAtom (si->dpy, "PREV", False);
644 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
645 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
646 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
648 si->nscreens = ScreenCount(si->dpy);
649 si->screens = (saver_screen_info *)
650 calloc(sizeof(saver_screen_info), si->nscreens);
652 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
654 for (i = 0; i < si->nscreens; i++)
656 saver_screen_info *ssi = &si->screens[i];
658 ssi->screen = ScreenOfDisplay (si->dpy, i);
660 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
661 ssi->default_visual =
662 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
664 ssi->current_visual = ssi->default_visual;
665 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
667 if (ssi == si->default_screen)
668 /* Since this is the default screen, use the one already created. */
669 ssi->toplevel_shell = toplevel_shell;
671 /* Otherwise, each screen must have its own unmapped root widget. */
672 ssi->toplevel_shell =
673 XtVaAppCreateShell(progname, progclass, applicationShellWidgetClass,
675 XtNscreen, ssi->screen,
676 XtNvisual, ssi->current_visual,
677 XtNdepth, visual_depth(ssi->screen,
678 ssi->current_visual),
685 initialize (saver_info *si, int argc, char **argv)
688 saver_preferences *p = &si->prefs;
689 Bool initial_demo_mode_p = False;
690 si->version = (char *) malloc (5);
691 memcpy (si->version, screensaver_id + 17, 4);
693 progname = argv[0]; /* reset later; this is for the benefit of lock_init() */
695 if(strlen(progname) > 100) progname[99] = 0; /* keep it short. */
698 si->locking_disabled_p = True;
699 si->nolock_reason = "not compiled with locking support";
700 #else /* !NO_LOCKING */
701 si->locking_disabled_p = False;
704 set_auth_parameters(argc, argv);
707 if (! lock_init (argc, argv)) /* before hack_uid() for proper permissions */
709 si->locking_disabled_p = True;
710 si->nolock_reason = "error getting password";
712 #endif /* !NO_LOCKING */
716 #endif /* NO_SETUID */
718 progclass = "XScreenSaver";
720 /* remove -initial-demo-mode switch before saving argv */
721 for (i = 1; i < argc; i++)
722 while (!strcmp ("-initial-demo-mode", argv [i]))
725 initial_demo_mode_p = True;
726 for (j = i; j < argc; j++)
727 argv [j] = argv [j+1];
730 if (argc <= i) break;
732 save_argv (argc, argv);
733 initialize_connection (si, argc, argv);
737 %s %s, copyright (c) 1991-1998 by Jamie Zawinski <jwz@netscape.com>\n\
738 pid = %d.\n", progname, si->version, (int) getpid ());
741 for (i = 0; i < si->nscreens; i++)
742 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
745 hack_environment (si);
747 si->demo_mode_p = initial_demo_mode_p;
748 srandom ((int) time ((time_t *) 0));
750 if (p->use_sgi_saver_extension)
752 #ifdef HAVE_SGI_SAVER_EXTENSION
753 if (! query_sgi_saver_extension (si))
756 "%s: display %s does not support the SGI SCREEN_SAVER extension.\n",
757 blurb(), DisplayString (si->dpy));
758 p->use_sgi_saver_extension = False;
760 else if (p->use_mit_saver_extension)
762 fprintf (stderr, "%s: SGI SCREEN_SAVER extension used instead\
763 of MIT-SCREEN-SAVER extension.\n",
765 p->use_mit_saver_extension = False;
767 else if (p->use_xidle_extension)
770 "%s: SGI SCREEN_SAVER extension used instead of XIDLE extension.\n",
772 p->use_xidle_extension = False;
774 #else /* !HAVE_MIT_SAVER_EXTENSION */
776 "%s: not compiled with support for the SGI SCREEN_SAVER extension.\n",
778 p->use_sgi_saver_extension = False;
779 #endif /* !HAVE_SGI_SAVER_EXTENSION */
782 if (p->use_mit_saver_extension)
784 #ifdef HAVE_MIT_SAVER_EXTENSION
785 if (! query_mit_saver_extension (si))
788 "%s: display %s does not support the MIT-SCREEN-SAVER extension.\n",
789 blurb(), DisplayString (si->dpy));
790 p->use_mit_saver_extension = False;
792 else if (p->use_xidle_extension)
795 "%s: MIT-SCREEN-SAVER extension used instead of XIDLE extension.\n",
797 p->use_xidle_extension = False;
799 #else /* !HAVE_MIT_SAVER_EXTENSION */
801 "%s: not compiled with support for the MIT-SCREEN-SAVER extension.\n",
803 p->use_mit_saver_extension = False;
804 #endif /* !HAVE_MIT_SAVER_EXTENSION */
807 if (p->use_xidle_extension)
809 #ifdef HAVE_XIDLE_EXTENSION
810 int first_event, first_error;
811 if (! XidleQueryExtension (si->dpy, &first_event, &first_error))
814 "%s: display %s does not support the XIdle extension.\n",
815 blurb(), DisplayString (si->dpy));
816 p->use_xidle_extension = False;
818 #else /* !HAVE_XIDLE_EXTENSION */
819 fprintf (stderr, "%s: not compiled with support for XIdle.\n",
821 p->use_xidle_extension = False;
822 #endif /* !HAVE_XIDLE_EXTENSION */
825 /* Call this only after having probed for presence of desired extension. */
826 initialize_screensaver_window (si);
830 disable_builtin_screensaver (si, True);
832 if (p->verbose_p && p->use_mit_saver_extension)
833 fprintf (stderr, "%s: using MIT-SCREEN-SAVER server extension.\n",
835 if (p->verbose_p && p->use_sgi_saver_extension)
836 fprintf (stderr, "%s: using SGI SCREEN_SAVER server extension.\n",
838 if (p->verbose_p && p->use_xidle_extension)
839 fprintf (stderr, "%s: using XIdle server extension.\n",
842 initialize_stderr (si);
843 XSetErrorHandler (saver_ehandler);
845 if (initial_demo_mode_p)
846 /* If the user wants demo mode, don't wait around before doing it. */
847 p->initial_delay = 0;
849 if (!p->use_xidle_extension &&
850 !p->use_mit_saver_extension &&
851 !p->use_sgi_saver_extension)
853 if (p->initial_delay)
857 printf ("%s: waiting for %d second%s...", blurb(),
858 (int) p->initial_delay,
859 (p->initial_delay == 1 ? "" : "s"));
862 sleep (p->initial_delay);
868 printf ("%s: selecting events on extant windows...", blurb());
872 /* Select events on the root windows of every screen. This also selects
873 for window creation events, so that new subwindows will be noticed.
875 for (i = 0; i < si->nscreens; i++)
876 start_notice_events_timer (si,
877 RootWindowOfScreen (si->screens[i].screen));
885 main_loop (saver_info *si)
887 saver_preferences *p = &si->prefs;
890 if (! si->demo_mode_p)
891 sleep_until_idle (si, True);
897 #endif /* !NO_DEMO_MODE */
900 printf ("%s: user is idle; waking up at %s.\n", blurb(),
903 spawn_screenhack (si, True);
905 si->cycle_id = XtAppAddTimeOut (si->app, p->cycle, cycle_timer,
909 if (p->lock_p && p->lock_timeout == 0)
911 if (p->lock_p && !si->locked_p)
912 /* locked_p might be true already because of ClientMessage */
913 si->lock_id = XtAppAddTimeOut (si->app, p->lock_timeout,
916 #endif /* !NO_LOCKING */
920 sleep_until_idle (si, False); /* until not idle */
926 if (si->locking_disabled_p) abort ();
927 si->dbox_up_p = True;
929 /* We used to ungrab the keyboard here, before calling unlock_p()
930 to pop up the dialog box. This left the keyboard ungrabbed
931 for a small window, during an insecure state. Bennett Todd
932 was seeing the bahavior that, when the load was high, he could
933 actually get characters through to a shell under the saver
934 window (he accidentally typed his password there...)
936 So the ungrab has been moved down into pop_passwd_dialog()
937 just after the server is grabbed, closing this window
940 /* ungrab_keyboard_and_mouse (si); */
943 saver_screen_info *ssi = si->default_screen;
944 suspend_screenhack (si, True);
945 XUndefineCursor (si->dpy, ssi->screensaver_window);
947 printf ("%s: prompting for password.\n", blurb());
949 if (p->verbose_p && val == False)
950 printf ("%s: password incorrect!\n", blurb());
951 si->dbox_up_p = False;
952 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
953 suspend_screenhack (si, False);
955 /* I think this grab is now redundant, but it shouldn't hurt.
957 if (!si->demo_mode_p)
958 grab_keyboard_and_mouse (si, ssi->screensaver_window,
964 si->locked_p = False;
966 #endif /* !NO_LOCKING */
968 /* Let's kill it before unblanking, to get it to stop drawing as
969 soon as possible... */
970 kill_screenhack (si);
975 XtRemoveTimeOut (si->cycle_id);
982 XtRemoveTimeOut (si->lock_id);
985 #endif /* !NO_LOCKING */
988 printf ("%s: user is active; going to sleep at %s.\n", blurb(),
997 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
999 saver_preferences *p = &si->prefs;
1001 if (event->xclient.message_type != XA_SCREENSAVER)
1004 str = XGetAtomName (si->dpy, event->xclient.message_type);
1005 fprintf (stderr, "%s: unrecognised ClientMessage type %s received\n",
1006 blurb(), (str ? str : "(null)"));
1007 if (str) XFree (str);
1010 if (event->xclient.format != 32)
1012 fprintf (stderr, "%s: ClientMessage of format %d received, not 32\n",
1013 blurb(), event->xclient.format);
1017 type = event->xclient.data.l[0];
1018 if (type == XA_ACTIVATE)
1023 printf ("%s: ACTIVATE ClientMessage received.\n", blurb());
1024 if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
1026 XForceScreenSaver (si->dpy, ScreenSaverActive);
1035 "%s: ClientMessage ACTIVATE received while already active.\n",
1038 else if (type == XA_DEACTIVATE)
1043 printf ("%s: DEACTIVATE ClientMessage received.\n", blurb());
1044 if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
1046 XForceScreenSaver (si->dpy, ScreenSaverReset);
1055 "%s: ClientMessage DEACTIVATE received while inactive.\n",
1058 else if (type == XA_CYCLE)
1063 printf ("%s: CYCLE ClientMessage received.\n", blurb());
1065 XtRemoveTimeOut (si->cycle_id);
1067 cycle_timer ((XtPointer) si, 0);
1070 fprintf (stderr, "%s: ClientMessage CYCLE received while inactive.\n",
1073 else if (type == XA_NEXT || type == XA_PREV)
1076 printf ("%s: %s ClientMessage received.\n", blurb(),
1077 (type == XA_NEXT ? "NEXT" : "PREV"));
1078 si->next_mode_p = 1 + (type == XA_PREV);
1083 XtRemoveTimeOut (si->cycle_id);
1085 cycle_timer ((XtPointer) si, 0);
1090 else if (type == XA_EXIT)
1092 /* Ignore EXIT message if the screen is locked. */
1093 if (until_idle_p || !si->locked_p)
1096 printf ("%s: EXIT ClientMessage received.\n", blurb());
1099 unblank_screen (si);
1100 kill_screenhack (si);
1101 XSync (si->dpy, False);
1106 fprintf (stderr, "%s: EXIT ClientMessage received while locked.\n",
1109 else if (type == XA_RESTART)
1111 /* The RESTART message works whether the screensaver is active or not,
1112 unless the screen is locked, in which case it doesn't work.
1114 if (until_idle_p || !si->locked_p)
1117 printf ("%s: RESTART ClientMessage received.\n", blurb());
1120 unblank_screen (si);
1121 kill_screenhack (si);
1122 XSync (si->dpy, False);
1125 /* make sure error message shows up before exit. */
1126 if (real_stderr && stderr != real_stderr)
1127 dup2 (fileno(real_stderr), fileno(stderr));
1129 restart_process (si);
1130 exit (1); /* shouldn't get here; but if restarting didn't work,
1131 make this command be the same as EXIT. */
1134 fprintf(stderr, "%s: RESTART ClientMessage received while locked.\n",
1137 else if (type == XA_DEMO)
1140 fprintf (stderr, "%s: not compiled with support for DEMO mode\n",
1146 printf ("%s: DEMO ClientMessage received.\n", blurb());
1147 si->demo_mode_p = True;
1151 "%s: DEMO ClientMessage received while active.\n", blurb());
1154 else if (type == XA_LOCK)
1157 fprintf (stderr, "%s: not compiled with support for LOCK mode\n",
1160 if (si->locking_disabled_p)
1162 "%s: LOCK ClientMessage received, but locking is disabled.\n",
1164 else if (si->locked_p)
1166 "%s: LOCK ClientMessage received while already locked.\n",
1170 si->locked_p = True;
1172 printf ("%s: LOCK ClientMessage received;%s locking.\n",
1173 blurb(), until_idle_p ? " activating and" : "");
1175 if (si->lock_id) /* we're doing it now, so lose the timeout */
1177 XtRemoveTimeOut (si->lock_id);
1183 if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
1185 XForceScreenSaver (si->dpy, ScreenSaverActive);
1199 str = (type ? XGetAtomName(si->dpy, type) : 0);
1202 "%s: unrecognised screensaver ClientMessage %s received\n",
1206 "%s: unrecognised screensaver ClientMessage 0x%x received\n",
1207 blurb(), (unsigned int) event->xclient.data.l[0]);
1208 if (str) XFree (str);