1 /* xscreensaver, Copyright (c) 1991-1996 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
14 /* ========================================================================
15 * First we wait until the keyboard and mouse become idle for the specified
16 * amount of time. We do this in one of three different ways: periodically
17 * checking with the XIdle server extension; selecting key and mouse events
18 * on (nearly) all windows; or by waiting for the MIT-SCREEN-SAVER extension
19 * to send us a "you are idle" event.
21 * Then, we map a full screen black window (or, in the case of the
22 * MIT-SCREEN-SAVER extension, use the one it gave us.)
24 * We place a __SWM_VROOT property on this window, so that newly-started
25 * clients will think that this window is a "virtual root" window.
27 * If there is an existing "virtual root" window (one that already had
28 * an __SWM_VROOT property) then we remove that property from that window.
29 * Otherwise, clients would see that window (the real virtual root) instead
30 * of ours (the impostor.)
32 * Then we pick a random program to run, and start it. Two assumptions
33 * are made about this program: that it has been specified with whatever
34 * command-line options are necessary to make it run on the root window;
35 * and that it has been compiled with vroot.h, so that it is able to find
36 * the root window when a virtual-root window manager (or this program) is
39 * Then, we wait for keyboard or mouse events to be generated on the window.
40 * When they are, we kill the inferior process, unmap the window, and restore
41 * the __SWM_VROOT property to the real virtual root window if there was one.
43 * While we are waiting, we also set up timers so that, after a certain
44 * amount of time has passed, we can start a different screenhack. We do
45 * this by killing the running child process with SIGTERM, and then starting
46 * a new one in the same way.
48 * If there was a real virtual root, meaning that we removed the __SWM_VROOT
49 * property from it, meaning we must (absolutely must) restore it before we
50 * exit, then we set up signal handlers for most signals (SIGINT, SIGTERM,
51 * etc.) that do this. Most Xlib and Xt routines are not reentrant, so it
52 * is not generally safe to call them from signal handlers; however, this
53 * program spends most of its time waiting, so the window of opportunity
54 * when code could be called reentrantly is fairly small; and also, the worst
55 * that could happen is that the call would fail. If we've gotten one of
56 * these signals, then we're on our way out anyway. If we didn't restore the
57 * __SWM_VROOT property, that would be very bad, so it's worth a shot. Note
58 * that this means that, if you're using a virtual-root window manager, you
59 * can really fuck up the world by killing this process with "kill -9".
61 * This program accepts ClientMessages of type SCREENSAVER; these messages
62 * may contain the atom ACTIVATE or DEACTIVATE, meaning to turn the
63 * screensaver on or off now, regardless of the idleness of the user,
64 * and a few other things. The included "xscreensaver_command" program
65 * sends these messsages.
67 * If we don't have the XIdle, MIT-SCREEN-SAVER, or SGI SCREEN_SAVER
68 * extensions, then we do the XAutoLock trick: notice every window that
69 * gets created, and wait 30 seconds or so until its creating process has
70 * settled down, and then select KeyPress events on those windows which
71 * already select for KeyPress events. It's important that we not select
72 * KeyPress on windows which don't select them, because that would
73 * interfere with event propagation. This will break if any program
74 * changes its event mask to contain KeyRelease or PointerMotion more than
75 * 30 seconds after creating the window, but that's probably pretty rare.
77 * The reason that we can't select KeyPresses on windows that don't have
78 * them already is that, when dispatching a KeyPress event, X finds the
79 * lowest (leafmost) window in the hierarchy on which *any* client selects
80 * for KeyPress, and sends the event to that window. This means that if a
81 * client had a window with subwindows, and expected to receive KeyPress
82 * events on the parent window instead of the subwindows, then that client
83 * would malfunction if some other client selected KeyPress events on the
84 * subwindows. It is an incredible misdesign that one client can make
85 * another client malfunction in this way.
87 * To detect mouse motion, we periodically wake up and poll the mouse
88 * position and button/modifier state, and notice when something has
89 * changed. We make this check every five seconds by default, and since the
90 * screensaver timeout has a granularity of one minute, this makes the
91 * chance of a false positive very small. We could detect mouse motion in
92 * the same way as keyboard activity, but that would suffer from the same
93 * "client changing event mask" problem that the KeyPress events hack does.
94 * I think polling is more reliable.
96 * None of this crap happens if we're using one of the extensions, so install
97 * one of them if the description above sounds just too flaky to live. It
98 * is, but those are your choices.
100 * A third idle-detection option could be implemented (but is not): when
101 * running on the console display ($DISPLAY is `localhost`:0) and we're on a
102 * machine where /dev/tty and /dev/mouse have reasonable last-modification
103 * times, we could just stat() those. But the incremental benefit of
104 * implementing this is really small, so forget I said anything.
107 * - Have a second terminal handy.
108 * - Be careful where you set your breakpoints, you don't want this to
109 * stop under the debugger with the keyboard grabbed or the blackout
111 * - you probably can't set breakpoints in functions that are called on
112 * the other side of a call to fork() -- if your clients are dying
113 * with signal 5, Trace/BPT Trap, you're losing in this way.
114 * - If you aren't using a server extension, don't leave this stopped
115 * under the debugger for very long, or the X input buffer will get
116 * huge because of the keypress events it's selecting for. This can
117 * make your X server wedge with "no more input buffers."
119 * ======================================================================== */
127 #include <X11/Xlib.h>
128 #include <X11/Xatom.h>
129 #include <X11/Intrinsic.h>
131 #include <X11/Xmu/Error.h>
133 #ifdef HAVE_XIDLE_EXTENSION
134 #include <X11/extensions/xidle.h>
135 #endif /* HAVE_XIDLE_EXTENSION */
137 #ifdef HAVE_MIT_SAVER_EXTENSION
138 #include <X11/extensions/scrnsaver.h>
139 #endif /* HAVE_MIT_SAVER_EXTENSION */
141 #ifdef HAVE_SGI_SAVER_EXTENSION
142 #include <X11/extensions/XScreenSaver.h>
143 #endif /* HAVE_SGI_SAVER_EXTENSION */
145 #include "yarandom.h"
146 #include "xscreensaver.h"
148 extern char *get_string_resource P((char *, char *));
149 extern Bool get_boolean_resource P((char *, char *));
150 extern int get_integer_resource P((char *, char *));
151 extern unsigned int get_minutes_resource P((char *, char *));
152 extern unsigned int get_seconds_resource P((char *, char *));
154 extern Visual *get_visual_resource P((Display *, char *, char *));
155 extern int get_visual_depth P((Display *, Visual *));
157 extern void notice_events_timer P((XtPointer closure, XtIntervalId *timer));
158 extern void cycle_timer P((void *junk1, XtPointer junk2));
159 extern void activate_lock_timer P((void *junk1, XtPointer junk2));
160 extern void sleep_until_idle P((Bool until_idle_p));
162 extern void ensure_no_screensaver_running P((void));
163 extern void initialize_screensaver_window P((void));
164 extern void disable_builtin_screensaver P((void));
166 extern void hack_environment P((void));
167 extern void grab_keyboard_and_mouse P((void));
168 extern void ungrab_keyboard_and_mouse P((void));
170 extern void save_argv P((int argc, char **argv));
172 extern void initialize_stderr P((void));
174 char *screensaver_version;
186 Widget toplevel_shell;
193 extern Time passwd_timeout;
195 extern Time pointer_timeout;
196 extern Time notice_events_timeout;
197 extern XtIntervalId lock_id, cycle_id;
199 Bool use_xidle_extension;
200 Bool use_mit_saver_extension;
201 Bool use_sgi_saver_extension;
203 Bool lock_p, locked_p;
205 extern char **screenhacks;
206 extern int screenhacks_count;
208 extern int nice_inferior;
209 extern Window screensaver_window;
210 extern Cursor cursor;
211 extern Colormap cmap, cmap2;
212 extern Bool fade_p, unfade_p;
213 extern int fade_seconds, fade_ticks;
214 extern Bool install_cmap_p;
215 extern Bool locking_disabled_p;
216 extern char *nolock_reason;
217 extern Bool demo_mode_p;
218 extern Bool dbox_up_p;
219 extern int next_mode_p;
221 #ifdef HAVE_MIT_SAVER_EXTENSION
222 int mit_saver_ext_event_number = 0;
223 int mit_saver_ext_error_number = 0;
224 #endif /* HAVE_MIT_SAVER_EXTENSION */
226 #ifdef HAVE_SGI_SAVER_EXTENSION
227 int sgi_saver_ext_event_number = 0;
228 int sgi_saver_ext_error_number = 0;
229 #endif /* HAVE_SGI_SAVER_EXTENSION */
231 static time_t initial_delay;
233 extern Atom XA_VROOT, XA_XSETROOT_ID;
234 extern Atom XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
236 static Atom XA_SCREENSAVER;
237 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
238 static Atom XA_EXIT, XA_RESTART, XA_DEMO, XA_LOCK;
240 #ifdef NO_MOTIF /* kludge */
241 Bool demo_mode_p = 0;
247 # define demo_mode() abort()
249 extern void demo_mode P((void));
252 static XrmOptionDescRec options [] = {
253 { "-timeout", ".timeout", XrmoptionSepArg, 0 },
254 { "-cycle", ".cycle", XrmoptionSepArg, 0 },
255 { "-idelay", ".initialDelay", XrmoptionSepArg, 0 },
256 { "-nice", ".nice", XrmoptionSepArg, 0 },
257 { "-visual", ".visualID", XrmoptionSepArg, 0 },
258 { "-lock-timeout", ".lockTimeout", XrmoptionSepArg, 0 },
259 { "-install", ".installColormap", XrmoptionNoArg, "on" },
260 { "-no-install", ".installColormap", XrmoptionNoArg, "off" },
261 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
262 { "-silent", ".verbose", XrmoptionNoArg, "off" },
263 { "-xidle-extension", ".xidleExtension", XrmoptionNoArg, "on" },
264 { "-no-xidle-extension", ".xidleExtension", XrmoptionNoArg, "off" },
265 { "-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "on" },
266 { "-no-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "off" },
267 { "-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "on" },
268 { "-no-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "off" },
269 { "-lock", ".lock", XrmoptionNoArg, "on" },
270 { "-no-lock", ".lock", XrmoptionNoArg, "off" }
273 static char *defaults[] = {
274 #include "XScreenSaver.ad.h"
282 xscreensaver %s, copyright (c) 1991-1996 by Jamie Zawinski <jwz@netscape.com>.\n\
283 The standard Xt command-line options are accepted; other options include:\n\
285 -timeout <minutes> When the screensaver should activate.\n\
286 -cycle <minutes> How long to let each hack run.\n\
287 -idelay <seconds> How long to sleep before startup.\n\
288 -visual <id-or-class> Which X visual to run on.\n\
289 -demo Enter interactive demo mode on startup.\n\
290 -install Install a private colormap.\n\
291 -no-install Don't.\n\
294 -xidle-extension Use the R5 XIdle server extension.\n\
295 -no-xidle-extension Don't.\n\
296 -mit-extension Use the R6 MIT_SCREEN_SAVER server extension.\n\
297 -no-mit-extension Don't.\n\
298 -sgi-extension Use the SGI SCREEN-SAVER server extension.\n\
299 -no-sgi-extension Don't.\n\
300 -lock Require a password before deactivating.\n\
302 -lock-timeout <minutes> Grace period before locking; default 0.\n\
303 -help This message.\n\
305 Use the `xscreensaver-command' program to control a running screensaver.\n\
307 The *programs, *colorPrograms, and *monoPrograms resources control which\n\
308 graphics demos will be launched by the screensaver. See the man page for\n\
310 screensaver_version);
313 printf ("Support for locking was not enabled at compile-time.\n");
316 printf ("Support for demo mode was not enabled at compile-time.\n");
318 #if !defined(HAVE_XIDLE_EXTENSION) && !defined(HAVE_MIT_SAVER_EXTENSION) && !defined(HAVE_SGI_SAVER_EXTENSION)
319 printf ("Support for the XIDLE, SCREEN_SAVER, and MIT-SCREEN-SAVER server\
320 extensions\nwas not enabled at compile-time.\n");
321 #endif /* !HAVE_XIDLE_EXTENSION && !HAVE_MIT_SAVER_EXTENSION && !HAVE_SGI_SAVER_EXTENSION */
329 get_screenhacks P((void))
332 int i, hacks_size = 10;
334 data[0] = get_string_resource ("programs", "Programs");
335 data[1] = ((CellsOfScreen (screen) <= 2)
336 ? get_string_resource ("monoPrograms", "MonoPrograms")
337 : get_string_resource ("colorPrograms", "ColorPrograms"));
339 if (! data[0]) data[0] = data[1], data[1] = 0;
341 screenhacks = (char **) malloc (sizeof (char *) * hacks_size);
342 screenhacks_count = 0;
344 for (i = 0; data[i]; i++)
348 int size = strlen (d);
352 if (d[j] == ' ' || d[j] == '\t' || d[j] == '\n' || d[j] == 0)
357 if (hacks_size <= screenhacks_count)
358 screenhacks = (char **) realloc (screenhacks,
359 (hacks_size = hacks_size * 2) *
361 screenhacks [screenhacks_count++] = d + j;
362 while (d[j] != 0 && d[j] != '\n')
365 while (j > start && (d[j-1] == ' ' || d[j-1] == '\t'))
372 /* shrink all whitespace to one space, for the benefit of the "demo"
373 mode display. We only do this when we can easily tell that the
374 whitespace is not significant (no shell metachars).
376 for (i = 0; i < screenhacks_count; i++)
378 char *s = screenhacks [i];
382 for (j = 0; j < L; j++)
386 case '\'': case '"': case '`': case '\\':
392 for (s2 = s+j+1; *s2 == ' ' || *s2 == '\t'; s2++)
395 for (s2 = s + j + 1; *s2; s2++)
404 if (screenhacks_count)
406 /* Shrink down the screenhacks array to be only as big as it needs to.
407 This doesn't really matter at all. */
408 screenhacks = (char **)
409 realloc (screenhacks, ((screenhacks_count + 1) * sizeof(char *)));
410 screenhacks [screenhacks_count] = 0;
421 get_resources P((void))
423 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
424 visual = get_visual_resource (dpy, "visualID", "VisualID");
425 timeout = 1000 * get_minutes_resource ("timeout", "Time");
426 cycle = 1000 * get_minutes_resource ("cycle", "Time");
427 lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
428 nice_inferior = get_integer_resource ("nice", "Nice");
429 verbose_p = get_boolean_resource ("verbose", "Boolean");
430 lock_p = get_boolean_resource ("lock", "Boolean");
431 install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
432 fade_p = get_boolean_resource ("fade", "Boolean");
433 unfade_p = get_boolean_resource ("unfade", "Boolean");
434 fade_seconds = get_seconds_resource ("fadeSeconds", "Time");
435 fade_ticks = get_integer_resource ("fadeTicks", "Integer");
436 shell = get_string_resource ("bourneShell", "BourneShell");
437 initial_delay = get_seconds_resource ("initialDelay", "Time");
438 pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
439 notice_events_timeout = 1000 * get_seconds_resource ("windowCreationTimeout",
442 passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
443 if (passwd_timeout == 0) passwd_timeout = 30000;
445 if (timeout < 10000) timeout = 10000;
446 if (cycle != 0 && cycle < 2000) cycle = 2000;
447 if (pointer_timeout == 0) pointer_timeout = 5000;
448 if (notice_events_timeout == 0) notice_events_timeout = 10000;
449 if (fade_seconds == 0 || fade_ticks == 0) fade_p = False;
450 if (! fade_p) unfade_p = False;
452 visual_depth = get_visual_depth (dpy, visual);
454 if (visual_depth <= 1 || CellsOfScreen (screen) <= 2)
455 install_cmap_p = False;
458 locking_disabled_p = True;
459 nolock_reason = "not compiled with locking support";
463 fprintf (stderr, "%s: %snot compiled with support for locking.\n",
464 progname, (verbose_p ? "## " : ""));
466 #else /* ! NO_LOCKING */
467 if (lock_p && locking_disabled_p)
469 fprintf (stderr, "%s: %slocking is disabled (%s).\n", progname,
470 (verbose_p ? "## " : ""), nolock_reason);
473 #endif /* ! NO_LOCKING */
475 /* don't set use_xidle_extension unless it is explicitly specified */
476 if (get_string_resource ("xidleExtension", "Boolean"))
477 use_xidle_extension = get_boolean_resource ("xidleExtension", "Boolean");
479 #ifdef HAVE_XIDLE_EXTENSION /* pick a default */
480 use_xidle_extension = True;
481 #else /* !HAVE_XIDLE_EXTENSION */
482 use_xidle_extension = False;
483 #endif /* !HAVE_XIDLE_EXTENSION */
485 /* don't set use_saver_extension unless it is explicitly specified */
486 if (get_string_resource ("mitSaverExtension", "Boolean"))
487 use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
490 #ifdef HAVE_MIT_SAVER_EXTENSION /* pick a default */
491 use_mit_saver_extension = True;
492 #else /* !HAVE_MIT_SAVER_EXTENSION */
493 use_mit_saver_extension = False;
494 #endif /* !HAVE_MIT_SAVER_EXTENSION */
496 /* don't set use_saver_extension unless it is explicitly specified */
497 if (get_string_resource ("sgiSaverExtension", "Boolean"))
498 use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
501 #ifdef HAVE_SGI_SAVER_EXTENSION /* pick a default */
502 use_sgi_saver_extension = True;
503 #else /* !HAVE_SGI_SAVER_EXTENSION */
504 use_sgi_saver_extension = False;
505 #endif /* !HAVE_SGI_SAVER_EXTENSION */
513 long now = time ((time_t *) 0);
514 char *str = (char *) ctime (&now);
515 char *nl = (char *) strchr (str, '\n');
516 if (nl) *nl = 0; /* take off that dang newline */
522 # define hack_uid_warn()
523 #else /* !NO_SETUID */
524 extern void hack_uid P((void));
525 extern void hack_uid_warn P((void));
526 #endif /* NO_SETUID */
530 extern Bool unlock_p P((Widget));
531 extern Bool lock_init P((void));
534 static void initialize P((int argc, char **argv));
535 static void main_loop P((void));
542 initialize (argc, argv);
548 saver_ehandler (dpy, error)
552 fprintf (real_stderr, "\nX error in %s:\n", progname);
553 if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
556 fprintf (real_stderr, " (nonfatal.)\n");
562 initialize_connection (int argc, char **argv)
564 initialize_connection (argc, argv)
569 toplevel_shell = XtAppInitialize (&app, progclass,
570 options, XtNumber (options),
571 &argc, argv, defaults, 0, 0);
573 dpy = XtDisplay (toplevel_shell);
574 screen = XtScreen (toplevel_shell);
575 db = XtDatabase (dpy);
576 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
578 if (argc == 2 && !strcmp (argv[1], "-help"))
582 fprintf (stderr, "%s: unknown option %s\n", progname, argv [1]);
588 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
589 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
590 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION", False);
591 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
592 XA_XSETROOT_ID = XInternAtom (dpy, "_XSETROOT_ID", False);
593 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
594 XA_DEACTIVATE = XInternAtom (dpy, "DEACTIVATE", False);
595 XA_RESTART = XInternAtom (dpy, "RESTART", False);
596 XA_CYCLE = XInternAtom (dpy, "CYCLE", False);
597 XA_NEXT = XInternAtom (dpy, "NEXT", False);
598 XA_PREV = XInternAtom (dpy, "PREV", False);
599 XA_EXIT = XInternAtom (dpy, "EXIT", False);
600 XA_DEMO = XInternAtom (dpy, "DEMO", False);
601 XA_LOCK = XInternAtom (dpy, "LOCK", False);
604 #ifdef HAVE_MIT_SAVER_EXTENSION
607 ignore_all_errors_ehandler (dpy, error)
615 init_mit_saver_extension ()
619 Window root = RootWindowOfScreen (screen);
620 Pixmap blank_pix = XCreatePixmap (dpy, root, 1, 1, 1);
622 /* Kill off the old MIT-SCREEN-SAVER client if there is one.
623 This tends to generate X errors, though (possibly due to a bug
624 in the server extension itself?) so just ignore errors here. */
625 if (XScreenSaverGetRegistered (dpy, XScreenNumberOfScreen (screen),
626 &kill_id, &kill_type)
627 && kill_id != blank_pix)
629 int (*old_handler) ();
630 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
631 XKillClient (dpy, kill_id);
633 XSetErrorHandler (old_handler);
636 XScreenSaverSelectInput (dpy, root, ScreenSaverNotifyMask);
638 XScreenSaverRegister (dpy, XScreenNumberOfScreen (screen),
639 (XID) blank_pix, XA_PIXMAP);
641 #endif /* HAVE_MIT_SAVER_EXTENSION */
644 #ifdef HAVE_SGI_SAVER_EXTENSION
647 init_sgi_saver_extension ()
649 if (! XScreenSaverEnable (dpy, XScreenNumberOfScreen(screen)))
652 "%s: %sSGI SCREEN_SAVER extension exists, but can't be initialized;\n\
653 perhaps some other screensaver program is already running?\n",
654 progname, (verbose_p ? "## " : ""));
655 use_sgi_saver_extension = False;
659 #endif /* HAVE_SGI_SAVER_EXTENSION */
662 extern void init_sigchld P((void));
665 initialize (argc, argv)
669 Bool initial_demo_mode_p = False;
670 screensaver_version = (char *) malloc (5);
671 memcpy (screensaver_version, screensaver_id + 17, 4);
672 screensaver_version [4] = 0;
673 progname = argv[0]; /* reset later; this is for the benefit of lock_init() */
676 locking_disabled_p = True;
677 nolock_reason = "not compiled with locking support";
679 locking_disabled_p = False;
682 set_auth_parameters(argc, argv);
685 if (! lock_init ()) /* before hack_uid() for proper permissions */
687 locking_disabled_p = True;
688 nolock_reason = "error getting password";
693 progclass = "XScreenSaver";
695 /* remove -demo switch before saving argv */
698 for (i = 1; i < argc; i++)
699 while (!strcmp ("-demo", argv [i]))
702 initial_demo_mode_p = True;
703 for (j = i; j < argc; j++)
704 argv [j] = argv [j+1];
707 if (argc <= i) break;
710 save_argv (argc, argv);
711 initialize_connection (argc, argv);
712 ensure_no_screensaver_running ();
716 %s %s, copyright (c) 1991-1996 by Jamie Zawinski <jwz@netscape.com>.\n\
717 pid = %d.\n", progname, screensaver_version, getpid ());
718 ensure_no_screensaver_running ();
720 demo_mode_p = initial_demo_mode_p;
721 screensaver_window = 0;
723 srandom ((int) time ((time_t *) 0));
728 if (use_sgi_saver_extension)
730 #ifdef HAVE_SGI_SAVER_EXTENSION
731 if (! XScreenSaverQueryExtension (dpy,
732 &sgi_saver_ext_event_number,
733 &sgi_saver_ext_error_number))
736 "%s: %sdisplay %s does not support the SGI SCREEN_SAVER extension.\n",
737 progname, (verbose_p ? "## " : ""), DisplayString (dpy));
738 use_sgi_saver_extension = False;
740 else if (use_mit_saver_extension)
742 fprintf (stderr, "%s: %sSGI SCREEN_SAVER extension used instead\
743 of MIT-SCREEN-SAVER extension.\n",
744 progname, (verbose_p ? "## " : ""));
745 use_mit_saver_extension = False;
747 else if (use_xidle_extension)
750 "%s: %sSGI SCREEN_SAVER extension used instead of XIDLE extension.\n",
751 progname, (verbose_p ? "## " : ""));
752 use_xidle_extension = False;
754 #else /* !HAVE_MIT_SAVER_EXTENSION */
756 "%s: %snot compiled with support for the SGI SCREEN_SAVER extension.\n",
757 progname, (verbose_p ? "## " : ""));
758 use_sgi_saver_extension = False;
759 #endif /* !HAVE_SGI_SAVER_EXTENSION */
762 if (use_mit_saver_extension)
764 #ifdef HAVE_MIT_SAVER_EXTENSION
765 if (! XScreenSaverQueryExtension (dpy,
766 &mit_saver_ext_event_number,
767 &mit_saver_ext_error_number))
770 "%s: %sdisplay %s does not support the MIT-SCREEN-SAVER extension.\n",
771 progname, (verbose_p ? "## " : ""), DisplayString (dpy));
772 use_mit_saver_extension = False;
774 else if (use_xidle_extension)
777 "%s: %sMIT-SCREEN-SAVER extension used instead of XIDLE extension.\n",
778 progname, (verbose_p ? "## " : ""));
779 use_xidle_extension = False;
781 #else /* !HAVE_MIT_SAVER_EXTENSION */
783 "%s: %snot compiled with support for the MIT-SCREEN-SAVER extension.\n",
784 progname, (verbose_p ? "## " : ""));
785 use_mit_saver_extension = False;
786 #endif /* !HAVE_MIT_SAVER_EXTENSION */
789 if (use_xidle_extension)
791 #ifdef HAVE_XIDLE_EXTENSION
792 int first_event, first_error;
793 if (! XidleQueryExtension (dpy, &first_event, &first_error))
796 "%s: %sdisplay %s does not support the XIdle extension.\n",
797 progname, (verbose_p ? "## " : ""), DisplayString (dpy));
798 use_xidle_extension = False;
800 #else /* !HAVE_XIDLE_EXTENSION */
801 fprintf (stderr, "%s: %snot compiled with support for XIdle.\n",
802 progname, (verbose_p ? "## " : ""));
803 use_xidle_extension = False;
804 #endif /* !HAVE_XIDLE_EXTENSION */
807 /* Call this only after having probed for presence of desired extension. */
808 initialize_screensaver_window ();
812 disable_builtin_screensaver ();
814 #ifdef HAVE_MIT_SAVER_EXTENSION
815 if (use_mit_saver_extension)
816 init_mit_saver_extension ();
817 #endif /* HAVE_MIT_SAVER_EXTENSION */
819 #ifdef HAVE_SGI_SAVER_EXTENSION
820 if (use_sgi_saver_extension)
821 init_sgi_saver_extension ();
822 #endif /* HAVE_SGI_SAVER_EXTENSION */
824 if (verbose_p && use_mit_saver_extension)
825 fprintf (stderr, "%s: using MIT-SCREEN-SAVER server extension.\n",
827 if (verbose_p && use_sgi_saver_extension)
828 fprintf (stderr, "%s: using SGI SCREEN_SAVER server extension.\n",
830 if (verbose_p && use_xidle_extension)
831 fprintf (stderr, "%s: using XIdle server extension.\n",
834 initialize_stderr ();
835 XSetErrorHandler (saver_ehandler);
837 if (initial_demo_mode_p)
838 /* If the user wants demo mode, don't wait around before doing it. */
841 if (!use_xidle_extension &&
842 !use_mit_saver_extension &&
843 !use_sgi_saver_extension)
849 printf ("%s: waiting for %d second%s...", progname,
850 (int) initial_delay, (initial_delay == 1 ? "" : "s"));
853 sleep (initial_delay);
859 printf ("%s: selecting events on extant windows...", progname);
862 notice_events_timer ((XtPointer)
863 RootWindowOfScreen (XtScreen (toplevel_shell)),
871 extern void suspend_screenhack P((Bool suspend_p));
879 sleep_until_idle (True);
886 printf ("%s: user is idle; waking up at %s.\n", progname,
889 spawn_screenhack (True);
891 cycle_id = XtAppAddTimeOut (app, cycle,
892 (XtTimerCallbackProc)cycle_timer, 0);
895 if (lock_p && lock_timeout == 0)
897 if (lock_p && !locked_p)
898 /* locked_p might be true already because of ClientMessage */
899 lock_id = XtAppAddTimeOut (app,lock_timeout,
900 (XtTimerCallbackProc)
901 activate_lock_timer,0);
906 sleep_until_idle (False); /* until not idle */
912 if (locking_disabled_p) abort ();
915 /* We used to ungrab the keyboard here, before calling unlock_p()
916 to pop up the dialog box. This left the keyboard ungrabbed
917 for a small window, during an insecure state. Bennett Todd
918 was seeing the bahavior that, when the load was high, he could
919 actually get characters through to a shell under the saver
920 window (he accidentally typed his password there...)
922 So the ungrab has been moved down into pop_passwd_dialog()
923 just after the server is grabbed, closing this window
926 /* ungrab_keyboard_and_mouse (); */
928 suspend_screenhack (True);
929 XUndefineCursor (dpy, screensaver_window);
931 printf ("%s: prompting for password.\n", progname);
932 val = unlock_p (toplevel_shell);
933 if (verbose_p && val == False)
934 printf ("%s: password incorrect!\n", progname);
936 XDefineCursor (dpy, screensaver_window, cursor);
937 suspend_screenhack (False);
939 /* I think this grab is now redundant, but it shouldn't hurt. */
940 grab_keyboard_and_mouse ();
951 XtRemoveTimeOut (cycle_id);
957 XtRemoveTimeOut (lock_id);
962 printf ("%s: user is active; going to sleep at %s.\n", progname,
971 handle_clientmessage (event, until_idle_p)
976 if (event->xclient.message_type != XA_SCREENSAVER)
979 str = XGetAtomName (dpy, event->xclient.message_type);
980 fprintf (stderr, "%s: %sunrecognised ClientMessage type %s received\n",
981 progname, (verbose_p ? "## " : ""),
982 (str ? str : "(null)"));
983 if (str) XFree (str);
986 if (event->xclient.format != 32)
988 fprintf (stderr, "%s: %sClientMessage of format %d received, not 32\n",
989 progname, (verbose_p ? "## " : ""), event->xclient.format);
992 type = event->xclient.data.l[0];
993 if (type == XA_ACTIVATE)
998 printf ("%s: ACTIVATE ClientMessage received.\n", progname);
999 if (use_mit_saver_extension || use_sgi_saver_extension)
1001 XForceScreenSaver (dpy, ScreenSaverActive);
1010 "%s: %sClientMessage ACTIVATE received while already active.\n",
1011 progname, (verbose_p ? "## " : ""));
1013 else if (type == XA_DEACTIVATE)
1018 printf ("%s: DEACTIVATE ClientMessage received.\n", progname);
1019 if (use_mit_saver_extension || use_sgi_saver_extension)
1021 XForceScreenSaver (dpy, ScreenSaverReset);
1030 "%s: %sClientMessage DEACTIVATE received while inactive.\n",
1031 progname, (verbose_p ? "## " : ""));
1033 else if (type == XA_CYCLE)
1038 printf ("%s: CYCLE ClientMessage received.\n", progname);
1040 XtRemoveTimeOut (cycle_id);
1046 "%s: %sClientMessage CYCLE received while inactive.\n",
1047 progname, (verbose_p ? "## " : ""));
1049 else if (type == XA_NEXT || type == XA_PREV)
1052 printf ("%s: %s ClientMessage received.\n", progname,
1053 (type == XA_NEXT ? "NEXT" : "PREV"));
1054 next_mode_p = 1 + (type == XA_PREV);
1059 XtRemoveTimeOut (cycle_id);
1066 else if (type == XA_EXIT)
1068 /* Ignore EXIT message if the screen is locked. */
1069 if (until_idle_p || !locked_p)
1072 printf ("%s: EXIT ClientMessage received.\n", progname);
1082 fprintf (stderr, "%s: %sEXIT ClientMessage received while locked.\n",
1083 progname, (verbose_p ? "## " : ""));
1085 else if (type == XA_RESTART)
1087 /* The RESTART message works whether the screensaver is active or not,
1088 unless the screen is locked, in which case it doesn't work.
1090 if (until_idle_p || !locked_p)
1093 printf ("%s: RESTART ClientMessage received.\n", progname);
1103 fprintf(stderr, "%s: %sRESTART ClientMessage received while locked.\n",
1104 progname, (verbose_p ? "## " : ""));
1106 else if (type == XA_DEMO)
1110 "%s: %snot compiled with support for DEMO mode\n",
1111 progname, (verbose_p ? "## " : ""));
1116 printf ("%s: DEMO ClientMessage received.\n", progname);
1121 "%s: %sDEMO ClientMessage received while active.\n",
1122 progname, (verbose_p ? "## " : ""));
1125 else if (type == XA_LOCK)
1128 fprintf (stderr, "%s: %snot compiled with support for LOCK mode\n",
1129 progname, (verbose_p ? "## " : ""));
1131 if (locking_disabled_p)
1133 "%s: %sLOCK ClientMessage received, but locking is disabled.\n",
1134 progname, (verbose_p ? "## " : ""));
1137 "%s: %sLOCK ClientMessage received while already locked.\n",
1138 progname, (verbose_p ? "## " : ""));
1143 printf ("%s: LOCK ClientMessage received;%s locking.\n",
1144 progname, until_idle_p ? " activating and" : "");
1146 if (lock_id) /* we're doing it now, so lose the timeout */
1148 XtRemoveTimeOut (lock_id);
1154 if (use_mit_saver_extension || use_sgi_saver_extension)
1156 XForceScreenSaver (dpy, ScreenSaverActive);
1170 str = (type ? XGetAtomName(dpy, type) : 0);
1173 "%s: %sunrecognised screensaver ClientMessage %s received\n",
1174 progname, (verbose_p ? "## " : ""), str);
1177 "%s: %sunrecognised screensaver ClientMessage 0x%x received\n",
1178 progname, (verbose_p ? "## " : ""),
1179 (unsigned int) event->xclient.data.l[0]);
1180 if (str) XFree (str);