1 /* xscreensaver, Copyright (c) 1991-1995 Jamie Zawinski <jwz@mcom.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 or MIT-SCREENSAVER extensions, then we do the
68 * XAutoLock trick: notice every window that gets created, and wait 30
69 * seconds or so until its creating process has settled down, and then
70 * select KeyPress events on those windows which already select for
71 * KeyPress events. It's important that we not select KeyPress on windows
72 * which don't select them, because that would interfere with event
73 * propagation. This will break if any program changes its event mask to
74 * contain KeyRelease or PointerMotion more than 30 seconds after creating
75 * 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 implement (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 XIdle, don't leave this stopped under the
115 * debugger for very long, or the X input buffer will get huge because
116 * of the keypress events it's selecting for. This can make your X
117 * 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_SAVER_EXTENSION
138 #include <X11/extensions/scrnsaver.h>
139 #endif /* HAVE_SAVER_EXTENSION */
141 #include "xscreensaver.h"
143 #if defined(SVR4) || defined(SYSV)
144 # define srandom(i) srand((unsigned int)(i))
147 extern void srandom P((int)); /* srand() is in stdlib.h... */
151 extern char *get_string_resource P((char *, char *));
152 extern Bool get_boolean_resource P((char *, char *));
153 extern int get_integer_resource P((char *, char *));
154 extern unsigned int get_minutes_resource P((char *, char *));
155 extern unsigned int get_seconds_resource P((char *, char *));
157 extern Visual *get_visual_resource P((Display *, char *, char *));
158 extern int get_visual_depth P((Display *, Visual *));
160 extern void notice_events_timer P((XtPointer closure, XtIntervalId *timer));
161 extern void cycle_timer P((void *junk1, XtPointer junk2));
162 extern void activate_lock_timer P((void *junk1, XtPointer junk2));
163 extern void sleep_until_idle P((Bool until_idle_p));
165 extern void ensure_no_screensaver_running P((void));
166 extern void initialize_screensaver_window P((void));
167 extern void disable_builtin_screensaver P((void));
169 extern void hack_environment P((void));
170 extern void grab_keyboard_and_mouse P((void));
171 extern void ungrab_keyboard_and_mouse P((void));
173 extern void save_argv P((int argc, char **argv));
175 extern void initialize_stderr P((void));
177 char *screensaver_version;
189 Widget toplevel_shell;
196 extern Time passwd_timeout;
198 extern Time pointer_timeout;
199 extern Time notice_events_timeout;
200 extern XtIntervalId lock_id, cycle_id;
202 Bool use_xidle_extension;
203 Bool use_saver_extension;
205 Bool lock_p, locked_p;
207 extern char **screenhacks;
208 extern int screenhacks_count;
210 extern int nice_inferior;
211 extern Window screensaver_window;
212 extern Cursor cursor;
213 extern Colormap cmap, cmap2;
214 extern Bool fade_p, unfade_p;
215 extern int fade_seconds, fade_ticks;
216 extern Bool install_cmap_p;
217 extern Bool locking_disabled_p;
218 extern char *nolock_reason;
219 extern Bool demo_mode_p;
220 extern Bool dbox_up_p;
221 extern int next_mode_p;
223 #ifdef HAVE_SAVER_EXTENSION
224 int saver_ext_event_number = 0;
225 int saver_ext_error_number = 0;
226 #endif /* HAVE_SAVER_EXTENSION */
228 static time_t initial_delay;
230 extern Atom XA_VROOT, XA_XSETROOT_ID;
231 extern Atom XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
233 static Atom XA_SCREENSAVER;
234 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
235 static Atom XA_EXIT, XA_RESTART, XA_DEMO, XA_LOCK;
237 #ifdef NO_MOTIF /* kludge */
238 Bool demo_mode_p = 0;
241 Time passwd_timeout = 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 { "-visual", ".visualID", XrmoptionSepArg, 0 },
257 { "-lock-timeout", ".lockTimeout", XrmoptionSepArg, 0 },
258 { "-install", ".installColormap", XrmoptionNoArg, "on" },
259 { "-no-install", ".installColormap", XrmoptionNoArg, "off" },
260 { "-verbose", ".verbose", XrmoptionNoArg, "on" },
261 { "-silent", ".verbose", XrmoptionNoArg, "off" },
262 { "-xidle-extension", ".xidleExtension", XrmoptionNoArg, "on" },
263 { "-no-xidle-extension", ".xidleExtension", XrmoptionNoArg, "off" },
264 { "-ss-extension", ".saverExtension", XrmoptionNoArg, "on" },
265 { "-no-ss-extension", ".saverExtension", XrmoptionNoArg, "off" },
266 { "-lock", ".lock", XrmoptionNoArg, "on" },
267 { "-no-lock", ".lock", XrmoptionNoArg, "off" }
270 static char *defaults[] = {
271 #include "XScreenSaver.ad.h"
279 xscreensaver %s, copyright (c) 1991-1995 by Jamie Zawinski <jwz@mcom.com>.\n\
280 The standard Xt command-line options are accepted; other options include:\n\
282 -timeout <minutes> When the screensaver should activate.\n\
283 -cycle <minutes> How long to let each hack run.\n\
284 -idelay <seconds> How long to sleep before startup.\n\
285 -visual <id-or-class> Which X visual to run on.\n\
286 -demo Enter interactive demo mode on startup.\n\
287 -install Install a private colormap.\n\
288 -no-install Don't.\n\
291 -xidle-extension Use the R5 XIdle server extension.\n\
292 -no-xidle-extension Don't.\n\
293 -saver-extension Use the R6 MIT-SCREEN-SAVER server extension.\n\
294 -no-saver-extension Don't.\n\
295 -lock Require a password before deactivating.\n\
297 -lock-timeout <minutes> Grace period before locking; default 0.\n\
298 -help This message.\n\
300 Use the `xscreensaver-command' program to control a running screensaver.\n\
302 The *programs, *colorPrograms, and *monoPrograms resources control which\n\
303 graphics demos will be launched by the screensaver. See the man page for\n\
305 screensaver_version);
308 printf ("Support for locking was not enabled at compile-time.\n");
311 printf ("Support for demo mode was not enabled at compile-time.\n");
313 #if !defined(HAVE_XIDLE_EXTENSION) && !defined(HAVE_SAVER_EXTENSION)
314 printf ("Support for the XIDLE and MIT-SCREEN-SAVER server extensions\
315 was not\n\enabled at compile-time.\n");
316 #endif /* !HAVE_XIDLE_EXTENSION && !HAVE_SAVER_EXTENSION */
324 get_screenhacks P((void))
327 int i, hacks_size = 10;
329 data[0] = get_string_resource ("programs", "Programs");
330 data[1] = ((CellsOfScreen (screen) <= 2)
331 ? get_string_resource ("monoPrograms", "MonoPrograms")
332 : get_string_resource ("colorPrograms", "ColorPrograms"));
334 if (! data[0]) data[0] = data[1], data[1] = 0;
336 screenhacks = (char **) malloc (sizeof (char *) * hacks_size);
337 screenhacks_count = 0;
339 for (i = 0; data[i]; i++)
343 int size = strlen (d);
347 if (d[j] == ' ' || d[j] == '\t' || d[j] == '\n' || d[j] == 0)
352 if (hacks_size <= screenhacks_count)
353 screenhacks = (char **) realloc (screenhacks,
354 (hacks_size = hacks_size * 2) *
356 screenhacks [screenhacks_count++] = d + j;
357 while (d[j] != 0 && d[j] != '\n')
360 while (j > start && (d[j-1] == ' ' || d[j-1] == '\t'))
367 /* shrink all whitespace to one space, for the benefit of the "demo"
368 mode display. We only do this when we can easily tell that the
369 whitespace is not significant (no shell metachars).
371 for (i = 0; i < screenhacks_count; i++)
373 char *s = screenhacks [i];
377 for (j = 0; j < L; j++)
381 case '\'': case '"': case '`': case '\\':
387 for (s2 = s+j+1; *s2 == ' ' || *s2 == '\t'; s2++)
390 for (s2 = s + j + 1; *s2; s2++)
399 if (screenhacks_count)
401 /* Shrink down the screenhacks array to be only as big as it needs to.
402 This doesn't really matter at all. */
403 screenhacks = (char **)
404 realloc (screenhacks, ((screenhacks_count + 1) * sizeof(char *)));
405 screenhacks [screenhacks_count] = 0;
416 get_resources P((void))
418 /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
419 visual = get_visual_resource (dpy, "visualID", "VisualID");
420 timeout = 1000 * get_minutes_resource ("timeout", "Time");
421 cycle = 1000 * get_minutes_resource ("cycle", "Time");
422 lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
423 nice_inferior = get_integer_resource ("nice", "Nice");
424 verbose_p = get_boolean_resource ("verbose", "Boolean");
425 lock_p = get_boolean_resource ("lock", "Boolean");
426 install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
427 fade_p = get_boolean_resource ("fade", "Boolean");
428 unfade_p = get_boolean_resource ("unfade", "Boolean");
429 fade_seconds = get_seconds_resource ("fadeSeconds", "Time");
430 fade_ticks = get_integer_resource ("fadeTicks", "Integer");
431 shell = get_string_resource ("bourneShell", "BourneShell");
432 initial_delay = get_seconds_resource ("initialDelay", "Time");
433 pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
434 notice_events_timeout = 1000 * get_seconds_resource ("windowCreationTimeout",
437 passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
438 if (passwd_timeout == 0) passwd_timeout = 30000;
440 if (timeout < 10000) timeout = 10000;
441 if (cycle != 0 && cycle < 2000) cycle = 2000;
442 if (pointer_timeout == 0) pointer_timeout = 5000;
443 if (notice_events_timeout == 0) notice_events_timeout = 10000;
444 if (fade_seconds == 0 || fade_ticks == 0) fade_p = False;
445 if (! fade_p) unfade_p = False;
447 visual_depth = get_visual_depth (dpy, visual);
449 if (visual_depth <= 1 || CellsOfScreen (screen) <= 2)
450 install_cmap_p = False;
453 locking_disabled_p = True;
454 nolock_reason = "not compiled with locking support";
458 fprintf (stderr, "%s: %snot compiled with support for locking.\n",
459 progname, (verbose_p ? "## " : ""));
461 #else /* ! NO_LOCKING */
462 if (lock_p && locking_disabled_p)
464 fprintf (stderr, "%s: %slocking is disabled (%s).\n", progname,
465 (verbose_p ? "## " : ""), nolock_reason);
468 #endif /* ! NO_LOCKING */
470 /* don't set use_xidle_extension unless it is explicitly specified */
471 if (get_string_resource ("xidleExtension", "Boolean"))
472 use_xidle_extension = get_boolean_resource ("xidleExtension", "Boolean");
474 #ifdef HAVE_XIDLE_EXTENSION /* pick a default */
475 use_xidle_extension = True;
476 #else /* !HAVE_XIDLE_EXTENSION */
477 use_xidle_extension = False;
478 #endif /* !HAVE_XIDLE_EXTENSION */
480 /* don't set use_saver_extension unless it is explicitly specified */
481 if (get_string_resource ("saverExtension", "Boolean"))
482 use_xidle_extension = get_boolean_resource ("saverExtension", "Boolean");
484 #ifdef HAVE_SAVER_EXTENSION /* pick a default */
485 use_saver_extension = True;
486 #else /* !HAVE_SAVER_EXTENSION */
487 use_saver_extension = False;
488 #endif /* !HAVE_SAVER_EXTENSION */
497 long now = time ((time_t *) 0);
498 char *str = (char *) ctime (&now);
499 char *nl = (char *) strchr (str, '\n');
500 if (nl) *nl = 0; /* take off that dang newline */
506 # define hack_uid_warn()
507 #else /* !NO_SETUID */
508 extern void hack_uid P((void));
509 extern void hack_uid_warn P((void));
510 #endif /* NO_SETUID */
514 extern Bool unlock_p P((Widget));
515 extern Bool lock_init P((void));
518 static void initialize P((int argc, char **argv));
519 static void main_loop P((void));
526 initialize (argc, argv);
532 saver_ehandler (dpy, error)
536 fprintf (real_stderr, "\nX error in %s:\n", progname);
537 if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
540 fprintf (real_stderr, " (nonfatal.)\n");
546 initialize_connection (int argc, char **argv)
548 initialize_connection (argc, argv)
553 toplevel_shell = XtAppInitialize (&app, progclass,
554 options, XtNumber (options),
555 &argc, argv, defaults, 0, 0);
557 dpy = XtDisplay (toplevel_shell);
558 screen = XtScreen (toplevel_shell);
559 db = XtDatabase (dpy);
560 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
562 if (argc == 2 && !strcmp (argv[1], "-help"))
566 fprintf (stderr, "%s: unknown option %s\n", progname, argv [1]);
572 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
573 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
574 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION", False);
575 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
576 XA_XSETROOT_ID = XInternAtom (dpy, "_XSETROOT_ID", False);
577 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
578 XA_DEACTIVATE = XInternAtom (dpy, "DEACTIVATE", False);
579 XA_RESTART = XInternAtom (dpy, "RESTART", False);
580 XA_CYCLE = XInternAtom (dpy, "CYCLE", False);
581 XA_NEXT = XInternAtom (dpy, "NEXT", False);
582 XA_PREV = XInternAtom (dpy, "PREV", False);
583 XA_EXIT = XInternAtom (dpy, "EXIT", False);
584 XA_DEMO = XInternAtom (dpy, "DEMO", False);
585 XA_LOCK = XInternAtom (dpy, "LOCK", False);
588 #ifdef HAVE_SAVER_EXTENSION
591 ignore_all_errors_ehandler (dpy, error)
599 init_saver_extension ()
603 Window root = RootWindowOfScreen (screen);
604 XScreenSaverInfo *info;
605 Pixmap blank_pix = XCreatePixmap (dpy, root, 1, 1, 1);
607 /* Kill off the old MIT-SCREEN-SAVER client if there is one.
608 This tends to generate X errors, though (possibly due to a bug
609 in the server extension itself?) so just ignore errors here. */
610 if (XScreenSaverGetRegistered (dpy, XScreenNumberOfScreen (screen),
611 &kill_id, &kill_type)
612 && kill_id != blank_pix)
614 int (*old_handler) ();
615 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
616 XKillClient (dpy, kill_id);
618 XSetErrorHandler (old_handler);
621 XScreenSaverSelectInput (dpy, root, ScreenSaverNotifyMask);
623 XScreenSaverRegister (dpy, XScreenNumberOfScreen (screen),
624 (XID) blank_pix, XA_PIXMAP);
625 info = XScreenSaverAllocInfo ();
628 /* #### I think this is noticing that the saver is on, and replacing it
629 without turning it off first. */
630 saver = info->window;
631 if (info->state == ScreenSaverOn)
633 if (info->kind != ScreenSaverExternal)
635 XResetScreenSaver (display);
636 XActivateScreenSaver (display);
642 #endif /* HAVE_SAVER_EXTENSION */
645 extern void init_sigchld P((void));
648 initialize (argc, argv)
652 Bool initial_demo_mode_p = False;
653 screensaver_version = (char *) malloc (5);
654 memcpy (screensaver_version, screensaver_id + 17, 4);
655 screensaver_version [4] = 0;
656 progname = argv[0]; /* reset later; this is for the benefit of lock_init() */
659 locking_disabled_p = True;
660 nolock_reason = "not compiled with locking support";
662 locking_disabled_p = False;
663 if (! lock_init ()) /* before hack_uid() for proper permissions */
665 locking_disabled_p = True;
666 nolock_reason = "error getting password";
671 progclass = "XScreenSaver";
673 /* remove -demo switch before saving argv */
676 for (i = 1; i < argc; i++)
677 while (!strcmp ("-demo", argv [i]))
680 initial_demo_mode_p = True;
681 for (j = i; j < argc; j++)
682 argv [j] = argv [j+1];
685 if (argc <= i) break;
688 save_argv (argc, argv);
689 initialize_connection (argc, argv);
690 ensure_no_screensaver_running ();
694 %s %s, copyright (c) 1991-1995 by Jamie Zawinski <jwz@mcom.com>.\n\
695 pid = %d.\n", progname, screensaver_version, getpid ());
696 ensure_no_screensaver_running ();
698 demo_mode_p = initial_demo_mode_p;
699 screensaver_window = 0;
701 initialize_screensaver_window ();
702 srandom ((int) time ((time_t *) 0));
707 if (use_saver_extension)
709 #ifdef HAVE_SAVER_EXTENSION
710 if (! XScreenSaverQueryExtension (dpy,
711 &saver_ext_event_number,
712 &saver_ext_error_number))
715 "%s: %sdisplay %s does not support the MIT-SCREEN-SAVER extension.\n",
716 progname, (verbose_p ? "## " : ""), DisplayString (dpy));
717 use_saver_extension = False;
719 else if (use_xidle_extension)
722 "%s: %sMIT-SCREEN-SAVER extension used instead of XIDLE extension.\n",
723 progname, (verbose_p ? "## " : ""));
724 use_xidle_extension = False;
726 #else /* !HAVE_SAVER_EXTENSION */
728 "%s: %snot compiled with support for the MIT-SCREEN-SAVER extension.\n",
729 progname, (verbose_p ? "## " : ""));
730 use_saver_extension = False;
731 #endif /* !HAVE_SAVER_EXTENSION */
734 if (use_xidle_extension)
736 #ifdef HAVE_XIDLE_EXTENSION
737 int first_event, first_error;
738 if (! XidleQueryExtension (dpy, &first_event, &first_error))
741 "%s: %sdisplay %s does not support the XIdle extension.\n",
742 progname, (verbose_p ? "## " : ""), DisplayString (dpy));
743 use_xidle_extension = False;
745 #else /* !HAVE_XIDLE_EXTENSION */
746 fprintf (stderr, "%s: %snot compiled with support for XIdle.\n",
747 progname, (verbose_p ? "## " : ""));
748 use_xidle_extension = False;
749 #endif /* !HAVE_XIDLE_EXTENSION */
754 disable_builtin_screensaver ();
756 #ifdef HAVE_SAVER_EXTENSION
757 if (use_saver_extension)
758 init_saver_extension ();
759 #endif /* HAVE_SAVER_EXTENSION */
761 if (verbose_p && use_saver_extension)
762 fprintf (stderr, "%s: using MIT-SCREEN-SAVER server extension.\n",
764 if (verbose_p && use_xidle_extension)
765 fprintf (stderr, "%s: using XIdle server extension.\n",
768 initialize_stderr ();
769 XSetErrorHandler (saver_ehandler);
771 if (initial_demo_mode_p)
772 /* If the user wants demo mode, don't wait around before doing it. */
775 if (!use_xidle_extension && !use_saver_extension)
781 printf ("%s: waiting for %d second%s...", progname,
782 (int) initial_delay, (initial_delay == 1 ? "" : "s"));
785 sleep (initial_delay);
791 printf ("%s: selecting events on extant windows...", progname);
794 notice_events_timer ((XtPointer)
795 RootWindowOfScreen (XtScreen (toplevel_shell)),
803 extern void suspend_screenhack P((Bool suspend_p));
811 sleep_until_idle (True);
818 printf ("%s: user is idle; waking up at %s.\n", progname,
821 spawn_screenhack (True);
823 cycle_id = XtAppAddTimeOut (app, cycle, (XtPointer)cycle_timer, 0);
826 if (lock_p && lock_timeout == 0)
828 if (lock_p && !locked_p)
829 /* locked_p might be true already because of ClientMessage */
830 lock_id = XtAppAddTimeOut (app,lock_timeout,
831 (XtPointer)activate_lock_timer,0);
836 sleep_until_idle (False); /* until not idle */
842 if (locking_disabled_p) abort ();
845 /* We used to ungrab the keyboard here, before calling unlock_p()
846 to pop up the dialog box. This left the keyboard ungrabbed
847 for a small window, during an insecure state. Bennett Todd
848 was seeing the bahavior that, when the load was high, he could
849 actually get characters through to a shell under the saver
850 window (he accidentally typed his password there...)
852 So the ungrab has been moved down into pop_passwd_dialog()
853 just after the server is grabbed, closing this window
856 /* ungrab_keyboard_and_mouse (); */
858 suspend_screenhack (True);
859 XUndefineCursor (dpy, screensaver_window);
861 printf ("%s: prompting for password.\n", progname);
862 val = unlock_p (toplevel_shell);
863 if (verbose_p && val == False)
864 printf ("%s: password incorrect!\n", progname);
866 XDefineCursor (dpy, screensaver_window, cursor);
867 suspend_screenhack (False);
869 /* I think this grab is now redundant, but it shouldn't hurt. */
870 grab_keyboard_and_mouse ();
881 XtRemoveTimeOut (cycle_id);
887 XtRemoveTimeOut (lock_id);
892 printf ("%s: user is active; going to sleep at %s.\n", progname,
901 handle_clientmessage (event, until_idle_p)
906 if (event->xclient.message_type != XA_SCREENSAVER)
909 str = XGetAtomName (dpy, event->xclient.message_type);
910 fprintf (stderr, "%s: %sunrecognised ClientMessage type %s received\n",
911 progname, (verbose_p ? "## " : ""),
912 (str ? str : "(null)"));
913 if (str) XFree (str);
915 if (event->xclient.format != 32)
917 fprintf (stderr, "%s: %sClientMessage of format %d received, not 32\n",
918 progname, (verbose_p ? "## " : ""), event->xclient.format);
920 type = event->xclient.data.l[0];
921 if (type == XA_ACTIVATE)
926 printf ("%s: ACTIVATE ClientMessage received.\n", progname);
930 "%s: %sClientMessage ACTIVATE received while already active.\n",
931 progname, (verbose_p ? "## " : ""));
933 else if (type == XA_DEACTIVATE)
938 printf ("%s: DEACTIVATE ClientMessage received.\n", progname);
942 "%s: %sClientMessage DEACTIVATE received while inactive.\n",
943 progname, (verbose_p ? "## " : ""));
945 else if (type == XA_CYCLE)
950 printf ("%s: CYCLE ClientMessage received.\n", progname);
952 XtRemoveTimeOut (cycle_id);
958 "%s: %sClientMessage CYCLE received while inactive.\n",
959 progname, (verbose_p ? "## " : ""));
961 else if (type == XA_NEXT || type == XA_PREV)
964 printf ("%s: %s ClientMessage received.\n", progname,
965 (type == XA_NEXT ? "NEXT" : "PREV"));
966 next_mode_p = 1 + (type == XA_PREV);
971 XtRemoveTimeOut (cycle_id);
978 else if (type == XA_EXIT)
980 /* Ignore EXIT message if the screen is locked. */
981 if (until_idle_p || !locked_p)
984 printf ("%s: EXIT ClientMessage received.\n", progname);
994 fprintf (stderr, "%s: %sEXIT ClientMessage received while locked.\n",
995 progname, (verbose_p ? "## " : ""));
997 else if (type == XA_RESTART)
999 /* The RESTART message works whether the screensaver is active or not,
1000 unless the screen is locked, in which case it doesn't work.
1002 if (until_idle_p || !locked_p)
1005 printf ("%s: RESTART ClientMessage received.\n", progname);
1015 fprintf(stderr, "%s: %sRESTART ClientMessage received while locked.\n",
1016 progname, (verbose_p ? "## " : ""));
1018 else if (type == XA_DEMO)
1022 "%s: %snot compiled with support for DEMO mode\n",
1023 progname, (verbose_p ? "## " : ""));
1028 printf ("%s: DEMO ClientMessage received.\n", progname);
1033 "%s: %sDEMO ClientMessage received while active.\n",
1034 progname, (verbose_p ? "## " : ""));
1037 else if (type == XA_LOCK)
1040 fprintf (stderr, "%s: %snot compiled with support for LOCK mode\n",
1041 progname, (verbose_p ? "## " : ""));
1043 if (locking_disabled_p)
1045 "%s: %sLOCK ClientMessage received, but locking is disabled.\n",
1046 progname, (verbose_p ? "## " : ""));
1049 "%s: %sLOCK ClientMessage received while already locked.\n",
1050 progname, (verbose_p ? "## " : ""));
1055 printf ("%s: LOCK ClientMessage received;%s locking.\n",
1056 progname, until_idle_p ? " activating and" : "");
1058 if (lock_id) /* we're doing it now, so lose the timeout */
1060 XtRemoveTimeOut (lock_id);
1072 str = XGetAtomName (dpy, type);
1075 "%s: %sunrecognised screensaver ClientMessage %s received\n",
1076 progname, (verbose_p ? "## " : ""), str);
1079 "%s: %sunrecognised screensaver ClientMessage 0x%x received\n",
1080 progname, (verbose_p ? "## " : ""),
1081 (unsigned int) event->xclient.data.l[0]);
1082 if (str) XFree (str);