1 /* xscreensaver, Copyright (c) 1991-1998 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
12 /* ========================================================================
13 * First we wait until the keyboard and mouse become idle for the specified
14 * amount of time. We do this in one of three different ways: periodically
15 * checking with the XIdle server extension; selecting key and mouse events
16 * on (nearly) all windows; or by waiting for the MIT-SCREEN-SAVER extension
17 * to send us a "you are idle" event.
19 * Then, we map a full screen black window (or, in the case of the
20 * MIT-SCREEN-SAVER extension, use the one it gave us.)
22 * We place a __SWM_VROOT property on this window, so that newly-started
23 * clients will think that this window is a "virtual root" window.
25 * If there is an existing "virtual root" window (one that already had
26 * an __SWM_VROOT property) then we remove that property from that window.
27 * Otherwise, clients would see that window (the real virtual root) instead
28 * of ours (the impostor.)
30 * Then we pick a random program to run, and start it. Two assumptions
31 * are made about this program: that it has been specified with whatever
32 * command-line options are necessary to make it run on the root window;
33 * and that it has been compiled with vroot.h, so that it is able to find
34 * the root window when a virtual-root window manager (or this program) is
37 * Then, we wait for keyboard or mouse events to be generated on the window.
38 * When they are, we kill the inferior process, unmap the window, and restore
39 * the __SWM_VROOT property to the real virtual root window if there was one.
41 * While we are waiting, we also set up timers so that, after a certain
42 * amount of time has passed, we can start a different screenhack. We do
43 * this by killing the running child process with SIGTERM, and then starting
44 * a new one in the same way.
46 * If there was a real virtual root, meaning that we removed the __SWM_VROOT
47 * property from it, meaning we must (absolutely must) restore it before we
48 * exit, then we set up signal handlers for most signals (SIGINT, SIGTERM,
49 * etc.) that do this. Most Xlib and Xt routines are not reentrant, so it
50 * is not generally safe to call them from signal handlers; however, this
51 * program spends most of its time waiting, so the window of opportunity
52 * when code could be called reentrantly is fairly small; and also, the worst
53 * that could happen is that the call would fail. If we've gotten one of
54 * these signals, then we're on our way out anyway. If we didn't restore the
55 * __SWM_VROOT property, that would be very bad, so it's worth a shot. Note
56 * that this means that, if you're using a virtual-root window manager, you
57 * can really fuck up the world by killing this process with "kill -9".
59 * This program accepts ClientMessages of type SCREENSAVER; these messages
60 * may contain the atom ACTIVATE or DEACTIVATE, meaning to turn the
61 * screensaver on or off now, regardless of the idleness of the user,
62 * and a few other things. The included "xscreensaver_command" program
63 * sends these messsages.
65 * If we don't have the XIdle, MIT-SCREEN-SAVER, or SGI SCREEN_SAVER
66 * extensions, then we do the XAutoLock trick: notice every window that
67 * gets created, and wait 30 seconds or so until its creating process has
68 * settled down, and then select KeyPress events on those windows which
69 * already select for KeyPress events. It's important that we not select
70 * KeyPress on windows which don't select them, because that would
71 * interfere with event propagation. This will break if any program
72 * changes its event mask to contain KeyRelease or PointerMotion more than
73 * 30 seconds after creating the window, but 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_LOCK;
163 Atom XA_DEMO, XA_PREFS;
166 static XrmOptionDescRec options [] = {
167 { "-timeout", ".timeout", XrmoptionSepArg, 0 },
168 { "-cycle", ".cycle", XrmoptionSepArg, 0 },
169 { "-lock-mode", ".lock", XrmoptionNoArg, "on" },
170 { "-no-lock-mode", ".lock", XrmoptionNoArg, "off" },
171 { "-lock-timeout", ".lockTimeout", XrmoptionSepArg, 0 },
172 { "-visual", ".visualID", XrmoptionSepArg, 0 },
173 { "-install", ".installColormap", XrmoptionNoArg, "on" },
174 { "-no-install", ".installColormap", XrmoptionNoArg, "off" },
175 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
176 { "-silent", ".verbose", XrmoptionNoArg, "off" },
177 { "-timestamp", ".timestamp", XrmoptionNoArg, "on" },
178 { "-xidle-extension", ".xidleExtension", XrmoptionNoArg, "on" },
179 { "-no-xidle-extension", ".xidleExtension", XrmoptionNoArg, "off" },
180 { "-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "on" },
181 { "-no-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "off" },
182 { "-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "on" },
183 { "-no-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "off" },
184 { "-splash", ".splash", XrmoptionNoArg, "on" },
185 { "-no-splash", ".splash", XrmoptionNoArg, "off" },
186 { "-nosplash", ".splash", XrmoptionNoArg, "off" },
187 { "-idelay", ".initialDelay", XrmoptionSepArg, 0 },
188 { "-nice", ".nice", XrmoptionSepArg, 0 },
190 /* Actually this one is built in to Xt, but just to be sure... */
191 { "-synchronous", ".synchronous", XrmoptionNoArg, "on" }
194 static char *defaults[] = {
195 #include "XScreenSaver_ad.h"
200 ERROR! You must not include vroot.h in this file.
204 do_help (saver_info *si)
209 xscreensaver %s, copyright (c) 1991-1998 by Jamie Zawinski <jwz@jwz.org>\n\
210 The standard Xt command-line options are accepted; other options include:\n\
212 -timeout <minutes> When the screensaver should activate.\n\
213 -cycle <minutes> How long to let each hack run before switching.\n\
214 -lock-mode Require a password before deactivating.\n\
215 -lock-timeout <minutes> Grace period before locking; default 0.\n\
216 -visual <id-or-class> Which X visual to run on.\n\
217 -install Install a private colormap.\n\
219 -no-splash Don't display a splash-screen at startup.\n\
220 -help This message.\n\
222 See the manual for other options and X resources.\n\
224 The `xscreensaver' program should be left running in the background.\n\
225 Use the `xscreensaver-command' program to manipulate a running xscreensaver.\n\
227 The `*programs' resource controls which graphics demos will be launched by\n\
228 the screensaver. See `man xscreensaver' or the web page for more details.\n\
230 Just getting started? Try this:\n\
233 xscreensaver-command -demo\n\
235 For updates, check http://www.jwz.org/xscreensaver/\n\
245 reformat_hack(const char *hack)
248 const char *in = hack;
250 char *h2 = (char *) malloc(strlen(in) + indent + 2);
253 while (isspace(*in)) in++; /* skip whitespace */
254 while (*in && !isspace(*in) && *in != ':')
255 *out++ = *in++; /* snarf first token */
256 while (isspace(*in)) in++; /* skip whitespace */
259 *out++ = *in++; /* copy colon */
263 out = h2; /* reset to beginning */
268 while (isspace(*in)) in++; /* skip whitespace */
269 for (i = strlen(h2); i < indent; i++) /* indent */
272 while (*in) *out++ = *in++; /* copy rest of line */
280 get_screenhacks (saver_info *si)
282 saver_preferences *p = &si->prefs;
288 d = get_string_resource ("monoPrograms", "MonoPrograms");
289 if (d && !*d) { free(d); d = 0; }
291 d = get_string_resource ("colorPrograms", "ColorPrograms");
292 if (d && !*d) { free(d); d = 0; }
297 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
298 see the manual for details.\n", blurb());
302 d = get_string_resource ("programs", "Programs");
304 size = d ? strlen (d) : 0;
305 p->screenhacks = (char **) malloc (sizeof (char *) * hacks_size);
306 p->screenhacks_count = 0;
311 if (d[i] == ' ' || d[i] == '\t' || d[i] == '\n' || d[i] == 0)
316 if (hacks_size <= p->screenhacks_count)
317 p->screenhacks = (char **) realloc (p->screenhacks,
318 (hacks_size = hacks_size * 2) *
320 p->screenhacks [p->screenhacks_count++] = d + i;
321 while (d[i] != 0 && d[i] != '\n')
324 while (i > start && (d[i-1] == ' ' || d[i-1] == '\t'))
330 /* shrink all whitespace to one space, for the benefit of the "demo"
331 mode display. We only do this when we can easily tell that the
332 whitespace is not significant (no shell metachars).
334 for (i = 0; i < p->screenhacks_count; i++)
336 char *s = p->screenhacks [i];
340 for (j = 0; j < L; j++)
344 case '\'': case '"': case '`': case '\\':
350 for (s2 = s+j+1; *s2 == ' ' || *s2 == '\t'; s2++)
354 for (s2 = s+j+1; s2[k]; s2++)
362 p->screenhacks[i] = reformat_hack(s); /* mallocs */
365 if (p->screenhacks_count)
367 /* Shrink down the screenhacks array to be only as big as it needs to.
368 This doesn't really matter at all. */
369 p->screenhacks = (char **)
370 realloc (p->screenhacks, ((p->screenhacks_count + 1) *
372 p->screenhacks [p->screenhacks_count] = 0;
376 free (p->screenhacks);
382 static Bool blurb_timestamp_p = False; /* kludge */
386 get_resources (saver_info *si)
389 saver_preferences *p = &si->prefs;
391 p->xsync_p = get_boolean_resource ("synchronous", "Synchronous");
393 XSynchronize(si->dpy, True);
395 p->verbose_p = get_boolean_resource ("verbose", "Boolean");
396 p->timestamp_p = get_boolean_resource ("timestamp", "Boolean");
397 p->lock_p = get_boolean_resource ("lock", "Boolean");
398 p->fade_p = get_boolean_resource ("fade", "Boolean");
399 p->unfade_p = get_boolean_resource ("unfade", "Boolean");
400 p->fade_seconds = get_seconds_resource ("fadeSeconds", "Time");
401 p->fade_ticks = get_integer_resource ("fadeTicks", "Integer");
402 p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
403 p->nice_inferior = get_integer_resource ("nice", "Nice");
405 p->initial_delay = get_seconds_resource ("initialDelay", "Time");
406 p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time");
407 p->timeout = 1000 * get_minutes_resource ("timeout", "Time");
408 p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
409 p->cycle = 1000 * get_minutes_resource ("cycle", "Time");
412 p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
415 p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
416 p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
418 p->shell = get_string_resource ("bourneShell", "BourneShell");
420 p->help_url = get_string_resource("helpURL", "URL");
421 p->load_url_command = get_string_resource("loadURL", "LoadURL");
423 if ((s = get_string_resource ("splash", "Boolean")))
424 if (!get_boolean_resource("splash", "Boolean"))
425 p->splash_duration = 0;
428 /* don't set use_xidle_extension unless it is explicitly specified */
429 if ((s = get_string_resource ("xidleExtension", "Boolean")))
430 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
432 #ifdef HAVE_XIDLE_EXTENSION /* pick a default */
433 p->use_xidle_extension = True; /* if we have it, use it */
434 #else /* !HAVE_XIDLE_EXTENSION */
435 p->use_xidle_extension = False;
436 #endif /* !HAVE_XIDLE_EXTENSION */
439 /* don't set use_mit_extension unless it is explicitly specified */
440 if ((s = get_string_resource ("mitSaverExtension", "Boolean")))
441 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
444 #ifdef HAVE_MIT_SAVER_EXTENSION /* pick a default */
445 p->use_mit_saver_extension = False; /* Default false, because it sucks */
446 #else /* !HAVE_MIT_SAVER_EXTENSION */
447 p->use_mit_saver_extension = False;
448 #endif /* !HAVE_MIT_SAVER_EXTENSION */
452 /* don't set use_mit_extension unless it is explicitly specified */
453 if ((s = get_string_resource ("sgiSaverExtension", "Boolean")))
454 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
457 #ifdef HAVE_SGI_SAVER_EXTENSION /* pick a default */
458 p->use_sgi_saver_extension = True; /* if we have it, use it */
459 #else /* !HAVE_SGI_SAVER_EXTENSION */
460 p->use_sgi_saver_extension = False;
461 #endif /* !HAVE_SGI_SAVER_EXTENSION */
465 /* Throttle the various timeouts to reasonable values.
468 if (p->passwd_timeout == 0) p->passwd_timeout = 30000; /* 30 secs */
470 if (p->timeout < 10000) p->timeout = 10000; /* 10 secs */
471 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
472 if (p->pointer_timeout == 0) p->pointer_timeout = 5000; /* 5 secs */
473 if (p->notice_events_timeout == 0)
474 p->notice_events_timeout = 10000; /* 10 secs */
475 if (p->fade_seconds == 0 || p->fade_ticks == 0)
477 if (! p->fade_p) p->unfade_p = False;
479 p->watchdog_timeout = p->cycle;
480 if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */
481 if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */
484 si->locking_disabled_p = True;
485 si->nolock_reason = "not compiled with locking support";
486 #endif /* NO_LOCKING */
488 get_screenhacks (si);
492 XSynchronize(si->dpy, True);
495 p->timestamp_p = True;
496 p->initial_delay = 0;
499 blurb_timestamp_p = p->timestamp_p;
506 time_t now = time ((time_t *) 0);
507 char *str = (char *) ctime (&now);
508 char *nl = (char *) strchr (str, '\n');
509 if (nl) *nl = 0; /* take off that dang newline */
513 static void initialize (saver_info *si, int argc, char **argv);
514 static void main_loop (saver_info *si);
517 main (int argc, char **argv)
520 memset(&si, 0, sizeof(si));
521 global_si_kludge = &si; /* I hate C so much... */
522 initialize (&si, argc, argv);
524 pop_splash_dialog (&si);
525 main_loop (&si); /* doesn't return */
531 saver_ehandler (Display *dpy, XErrorEvent *error)
533 saver_info *si = global_si_kludge; /* I hate C so much... */
535 fprintf (real_stderr, "\n"
536 "#######################################"
537 "#######################################\n\n"
538 "%s: X Error! PLEASE REPORT THIS BUG.\n\n"
539 "#######################################"
540 "#######################################\n\n",
542 if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
544 fprintf (real_stderr, "\n");
545 if (si->prefs.xsync_p)
547 saver_exit (si, -1, "because of synchronous X Error");
552 "%s: to dump a core file, re-run with `-sync'.\n\n",
554 saver_exit (si, -1, 0);
558 fprintf (real_stderr, " (nonfatal.)\n");
566 if (!blurb_timestamp_p)
570 static char buf[255];
571 char *ct = timestring();
572 int n = strlen(progname);
574 strncpy(buf, progname, n);
577 strncpy(buf+n, ct+11, 8);
578 strcpy(buf+n+9, ": ");
584 initialize_connection (saver_info *si, int argc, char **argv)
587 Widget toplevel_shell;
588 saver_preferences *p = &si->prefs;
590 /* The X resource database blows up if argv[0] has a "." in it. */
593 while ((s = strchr (s, '.')))
597 toplevel_shell = XtAppInitialize (&si->app, progclass,
598 options, XtNumber (options),
599 &argc, argv, defaults, 0, 0);
601 si->dpy = XtDisplay (toplevel_shell);
602 si->db = XtDatabase (si->dpy);
603 XtGetApplicationNameAndClass (si->dpy, &progname, &progclass);
605 if(strlen(progname) > 100) progname [99] = 0; /* keep it short. */
607 db = si->db; /* resources.c needs this */
610 (!strcmp (argv[1], "-h") ||
611 !strcmp (argv[1], "-help") ||
612 !strcmp (argv[1], "--help")))
615 else if (argc == 2 && !strcmp (argv[1], "-debug"))
616 si->prefs.debug_p = True; /* no resource for this one, out of paranoia. */
620 const char *s = argv[1];
621 fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n",
624 if (s[0] == '-' && s[1] == '-') s++;
625 if (!strcmp (s, "-activate") ||
626 !strcmp (s, "-deactivate") ||
627 !strcmp (s, "-cycle") ||
628 !strcmp (s, "-next") ||
629 !strcmp (s, "-prev") ||
630 !strcmp (s, "-exit") ||
631 !strcmp (s, "-restart") ||
632 !strcmp (s, "-demo") ||
633 !strcmp (s, "-prefs") ||
634 !strcmp (s, "-preferences") ||
635 !strcmp (s, "-lock") ||
636 !strcmp (s, "-version") ||
637 !strcmp (s, "-time"))
639 fprintf (stderr, "\n\
640 However, %s is an option to the `xscreensaver-command' program.\n\
641 The `xscreensaver' program is a daemon that runs in the background.\n\
642 You control a running xscreensaver process by sending it messages\n\
643 with `xscreensaver-command'. See the man pages for details,\n\
644 or check the web page: http://www.jwz.org/xscreensaver/\n\n",
647 /* Since version 1.21 renamed the "-lock" option to "-lock-mode",
648 suggest that explicitly. */
649 if (!strcmp (s, "-lock"))
651 Or perhaps you meant either the \"-lock-mode\" or the\n\
652 \"-lock-timeout <minutes>\" options to xscreensaver?\n\n");
659 if (p->lock_p && si->locking_disabled_p)
662 fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(),
664 if (strstr (si->nolock_reason, "passw"))
665 fprintf (stderr, "%s: does xscreensaver need to be setuid? "
666 "consult the manual.\n", blurb());
669 /* Defer the printing of this message until after we have loaded the
670 resources and know whether `verbose' is on.
672 if (p->verbose_p && si->uid_message)
674 if (si->orig_uid && *si->orig_uid)
675 fprintf (stderr, "%s: initial effective uid/gid was %s.\n", blurb(),
677 fprintf (stderr, "%s: %s\n", blurb(), si->uid_message);
680 XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False);
681 XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
682 XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
683 XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
684 XA_SCREENSAVER_TIME = XInternAtom (si->dpy, "_SCREENSAVER_TIME", False);
685 XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
686 XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False);
687 XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False);
688 XA_RESTART = XInternAtom (si->dpy, "RESTART", False);
689 XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False);
690 XA_NEXT = XInternAtom (si->dpy, "NEXT", False);
691 XA_PREV = XInternAtom (si->dpy, "PREV", False);
692 XA_EXIT = XInternAtom (si->dpy, "EXIT", False);
693 XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
694 XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
695 XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
697 si->nscreens = ScreenCount(si->dpy);
698 si->screens = (saver_screen_info *)
699 calloc(sizeof(saver_screen_info), si->nscreens);
701 si->default_screen = &si->screens[DefaultScreen(si->dpy)];
703 for (i = 0; i < si->nscreens; i++)
705 saver_screen_info *ssi = &si->screens[i];
707 ssi->screen = ScreenOfDisplay (si->dpy, i);
709 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
710 ssi->default_visual =
711 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
713 ssi->current_visual = ssi->default_visual;
714 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
716 if (ssi == si->default_screen)
717 /* Since this is the default screen, use the one already created. */
718 ssi->toplevel_shell = toplevel_shell;
720 /* Otherwise, each screen must have its own unmapped root widget. */
721 ssi->toplevel_shell =
722 XtVaAppCreateShell(progname, progclass, applicationShellWidgetClass,
724 XtNscreen, ssi->screen,
725 XtNvisual, ssi->current_visual,
726 XtNdepth, visual_depth(ssi->screen,
727 ssi->current_visual),
734 initialize (saver_info *si, int argc, char **argv)
737 saver_preferences *p = &si->prefs;
738 Bool initial_demo_mode_p = False;
739 si->version = (char *) malloc (5);
740 memcpy (si->version, screensaver_id + 17, 4);
742 progname = argv[0]; /* reset later; this is for the benefit of lock_init() */
744 if(strlen(progname) > 100) progname[99] = 0; /* keep it short. */
747 si->locking_disabled_p = True;
748 si->nolock_reason = "not compiled with locking support";
749 #else /* !NO_LOCKING */
750 si->locking_disabled_p = False;
753 set_auth_parameters(argc, argv);
756 if (! lock_init (argc, argv)) /* before hack_uid() for proper permissions */
758 si->locking_disabled_p = True;
759 si->nolock_reason = "error getting password";
761 #endif /* !NO_LOCKING */
765 #endif /* NO_SETUID */
767 progclass = "XScreenSaver";
769 /* remove -initial-demo-mode switch before saving argv */
770 for (i = 1; i < argc; i++)
771 while (!strcmp ("-initial-demo-mode", argv [i]))
774 initial_demo_mode_p = True;
775 for (j = i; j < argc; j++)
776 argv [j] = argv [j+1];
779 if (argc <= i) break;
781 save_argv (argc, argv);
782 initialize_connection (si, argc, argv);
786 %s %s, copyright (c) 1991-1998 by Jamie Zawinski <jwz@jwz.org>\n\
787 pid = %d.\n", progname, si->version, (int) getpid ());
790 for (i = 0; i < si->nscreens; i++)
791 if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
794 hack_environment (si);
796 si->demo_mode_p = initial_demo_mode_p;
797 srandom ((int) time ((time_t *) 0));
800 fprintf (stderr, "\n"
801 "%s: Warning: running in DEBUG MODE. Be afraid.\n"
803 "\tNote that in debug mode, the xscreensaver window will only\n"
804 "\tcover the left half of the screen. (The idea is that you\n"
805 "\tcan still see debugging output in a shell, if you position\n"
806 "\tit on the right side of the screen.)\n"
808 "\tDebug mode is NOT SECURE. Do not run with -debug in\n"
809 "\tuntrusted environments.\n"
813 if (p->use_sgi_saver_extension)
815 #ifdef HAVE_SGI_SAVER_EXTENSION
816 if (! query_sgi_saver_extension (si))
819 "%s: display %s does not support the SGI SCREEN_SAVER extension.\n",
820 blurb(), DisplayString (si->dpy));
821 p->use_sgi_saver_extension = False;
823 else if (p->use_mit_saver_extension)
825 fprintf (stderr, "%s: SGI SCREEN_SAVER extension used instead\
826 of MIT-SCREEN-SAVER extension.\n",
828 p->use_mit_saver_extension = False;
830 else if (p->use_xidle_extension)
833 "%s: SGI SCREEN_SAVER extension used instead of XIDLE extension.\n",
835 p->use_xidle_extension = False;
837 #else /* !HAVE_MIT_SAVER_EXTENSION */
839 "%s: not compiled with support for the SGI SCREEN_SAVER extension.\n",
841 p->use_sgi_saver_extension = False;
842 #endif /* !HAVE_SGI_SAVER_EXTENSION */
845 if (p->use_mit_saver_extension)
847 #ifdef HAVE_MIT_SAVER_EXTENSION
848 if (! query_mit_saver_extension (si))
851 "%s: display %s does not support the MIT-SCREEN-SAVER extension.\n",
852 blurb(), DisplayString (si->dpy));
853 p->use_mit_saver_extension = False;
855 else if (p->use_xidle_extension)
858 "%s: MIT-SCREEN-SAVER extension used instead of XIDLE extension.\n",
860 p->use_xidle_extension = False;
862 #else /* !HAVE_MIT_SAVER_EXTENSION */
864 "%s: not compiled with support for the MIT-SCREEN-SAVER extension.\n",
866 p->use_mit_saver_extension = False;
867 #endif /* !HAVE_MIT_SAVER_EXTENSION */
870 if (p->use_xidle_extension)
872 #ifdef HAVE_XIDLE_EXTENSION
873 int first_event, first_error;
874 if (! XidleQueryExtension (si->dpy, &first_event, &first_error))
877 "%s: display %s does not support the XIdle extension.\n",
878 blurb(), DisplayString (si->dpy));
879 p->use_xidle_extension = False;
881 #else /* !HAVE_XIDLE_EXTENSION */
882 fprintf (stderr, "%s: not compiled with support for XIdle.\n",
884 p->use_xidle_extension = False;
885 #endif /* !HAVE_XIDLE_EXTENSION */
888 /* Call this only after having probed for presence of desired extension. */
889 initialize_screensaver_window (si);
893 disable_builtin_screensaver (si, True);
895 if (p->verbose_p && p->use_mit_saver_extension)
896 fprintf (stderr, "%s: using MIT-SCREEN-SAVER server extension.\n",
898 if (p->verbose_p && p->use_sgi_saver_extension)
899 fprintf (stderr, "%s: using SGI SCREEN_SAVER server extension.\n",
901 if (p->verbose_p && p->use_xidle_extension)
902 fprintf (stderr, "%s: using XIdle server extension.\n",
905 initialize_stderr (si);
906 XSetErrorHandler (saver_ehandler);
908 if (initial_demo_mode_p)
909 /* If the user wants demo mode, don't wait around before doing it. */
910 p->initial_delay = 0;
912 if (!p->use_xidle_extension &&
913 !p->use_mit_saver_extension &&
914 !p->use_sgi_saver_extension)
916 if (p->initial_delay)
920 fprintf (stderr, "%s: waiting for %d second%s...", blurb(),
921 (int) p->initial_delay,
922 (p->initial_delay == 1 ? "" : "s"));
926 sleep (p->initial_delay);
928 fprintf (stderr, " done.\n");
932 fprintf (stderr, "%s: selecting events on extant windows...",
938 /* Select events on the root windows of every screen. This also selects
939 for window creation events, so that new subwindows will be noticed.
941 for (i = 0; i < si->nscreens; i++)
942 start_notice_events_timer (si,
943 RootWindowOfScreen (si->screens[i].screen));
946 fprintf (stderr, " done.\n");
951 main_loop (saver_info *si)
953 saver_preferences *p = &si->prefs;
956 if (! si->demo_mode_p)
957 sleep_until_idle (si, True);
963 #endif /* !NO_DEMO_MODE */
966 fprintf (stderr, "%s: user is idle; waking up at %s.\n", blurb(),
969 spawn_screenhack (si, True);
971 si->cycle_id = XtAppAddTimeOut (si->app, p->cycle, cycle_timer,
975 if (p->lock_p && p->lock_timeout == 0)
977 if (p->lock_p && !si->locked_p)
978 /* locked_p might be true already because of ClientMessage */
979 si->lock_id = XtAppAddTimeOut (si->app, p->lock_timeout,
982 #endif /* !NO_LOCKING */
986 sleep_until_idle (si, False); /* until not idle */
992 if (si->locking_disabled_p) abort ();
993 si->dbox_up_p = True;
996 saver_screen_info *ssi = si->default_screen;
997 suspend_screenhack (si, True);
998 XUndefineCursor (si->dpy, ssi->screensaver_window);
1000 fprintf (stderr, "%s: prompting for password.\n", blurb());
1001 val = unlock_p (si);
1002 if (p->verbose_p && val == False)
1003 fprintf (stderr, "%s: password incorrect!\n", blurb());
1004 si->dbox_up_p = False;
1005 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
1006 suspend_screenhack (si, False);
1010 goto PASSWD_INVALID;
1011 si->locked_p = False;
1013 #endif /* !NO_LOCKING */
1016 fprintf (stderr, "%s: user is active at %s.\n",
1017 blurb(), timestring ());
1019 /* Let's kill it before unblanking, to get it to stop drawing as
1020 soon as possible... */
1021 kill_screenhack (si);
1022 unblank_screen (si);
1026 XtRemoveTimeOut (si->cycle_id);
1033 XtRemoveTimeOut (si->lock_id);
1036 #endif /* !NO_LOCKING */
1039 fprintf (stderr, "%s: going to sleep.\n", blurb());
1047 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
1049 saver_preferences *p = &si->prefs;
1051 if (event->xclient.message_type != XA_SCREENSAVER)
1054 str = XGetAtomName (si->dpy, event->xclient.message_type);
1055 fprintf (stderr, "%s: unrecognised ClientMessage type %s received\n",
1056 blurb(), (str ? str : "(null)"));
1057 if (str) XFree (str);
1060 if (event->xclient.format != 32)
1062 fprintf (stderr, "%s: ClientMessage of format %d received, not 32\n",
1063 blurb(), event->xclient.format);
1067 type = event->xclient.data.l[0];
1068 if (type == XA_ACTIVATE)
1074 "%s: ACTIVATE ClientMessage received.\n", blurb());
1075 if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
1077 XForceScreenSaver (si->dpy, ScreenSaverActive);
1086 "%s: ClientMessage ACTIVATE received while already active.\n",
1089 else if (type == XA_DEACTIVATE)
1094 fprintf (stderr, "%s: DEACTIVATE ClientMessage received.\n",
1096 if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
1098 XForceScreenSaver (si->dpy, ScreenSaverReset);
1107 "%s: ClientMessage DEACTIVATE received while inactive.\n",
1110 else if (type == XA_CYCLE)
1115 fprintf (stderr, "%s: CYCLE ClientMessage received.\n", blurb());
1117 XtRemoveTimeOut (si->cycle_id);
1119 cycle_timer ((XtPointer) si, 0);
1122 fprintf (stderr, "%s: ClientMessage CYCLE received while inactive.\n",
1125 else if (type == XA_NEXT || type == XA_PREV)
1128 fprintf (stderr, "%s: %s ClientMessage received.\n", blurb(),
1129 (type == XA_NEXT ? "NEXT" : "PREV"));
1130 si->next_mode_p = 1 + (type == XA_PREV);
1135 XtRemoveTimeOut (si->cycle_id);
1137 cycle_timer ((XtPointer) si, 0);
1142 else if (type == XA_EXIT)
1144 /* Ignore EXIT message if the screen is locked. */
1145 if (until_idle_p || !si->locked_p)
1148 fprintf (stderr, "%s: EXIT ClientMessage received.\n", blurb());
1151 unblank_screen (si);
1152 kill_screenhack (si);
1153 XSync (si->dpy, False);
1155 saver_exit (si, 0, 0);
1158 fprintf (stderr, "%s: EXIT ClientMessage received while locked.\n",
1161 else if (type == XA_RESTART)
1163 /* The RESTART message works whether the screensaver is active or not,
1164 unless the screen is locked, in which case it doesn't work.
1166 if (until_idle_p || !si->locked_p)
1169 fprintf (stderr, "%s: RESTART ClientMessage received.\n", blurb());
1172 unblank_screen (si);
1173 kill_screenhack (si);
1174 XSync (si->dpy, False);
1177 /* make sure error message shows up before exit. */
1178 if (real_stderr && stderr != real_stderr)
1179 dup2 (fileno(real_stderr), fileno(stderr));
1181 restart_process (si);
1182 exit (1); /* shouldn't get here; but if restarting didn't work,
1183 make this command be the same as EXIT. */
1186 fprintf(stderr, "%s: RESTART ClientMessage received while locked.\n",
1189 else if (type == XA_DEMO)
1192 fprintf (stderr, "%s: not compiled with support for DEMO mode\n",
1198 fprintf (stderr, "%s: DEMO ClientMessage received.\n", blurb());
1199 si->demo_mode_p = True;
1203 "%s: DEMO ClientMessage received while active.\n", blurb());
1206 else if (type == XA_PREFS)
1209 fprintf (stderr, "%s: not compiled with support for DEMO mode\n",
1215 fprintf (stderr, "%s: PREFS ClientMessage received.\n", blurb());
1216 si->demo_mode_p = (Bool) 2; /* kludge, so sue me. */
1220 "%s: PREFS ClientMessage received while active.\n", blurb());
1223 else if (type == XA_LOCK)
1226 fprintf (stderr, "%s: not compiled with support for LOCK mode\n",
1229 if (si->locking_disabled_p)
1231 "%s: LOCK ClientMessage received, but locking is disabled.\n",
1233 else if (si->locked_p)
1235 "%s: LOCK ClientMessage received while already locked.\n",
1239 si->locked_p = True;
1241 fprintf (stderr, "%s: LOCK ClientMessage received;%s locking.\n",
1242 blurb(), until_idle_p ? " activating and" : "");
1244 if (si->lock_id) /* we're doing it now, so lose the timeout */
1246 XtRemoveTimeOut (si->lock_id);
1252 if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
1254 XForceScreenSaver (si->dpy, ScreenSaverActive);
1268 str = (type ? XGetAtomName(si->dpy, type) : 0);
1271 "%s: unrecognised screensaver ClientMessage %s received\n",
1275 "%s: unrecognised screensaver ClientMessage 0x%x received\n",
1276 blurb(), (unsigned int) event->xclient.data.l[0]);
1277 if (str) XFree (str);