ftp://ftp.zenez.com/pub/SCO/Skunk96/UnixWare/FreeBird/x11/utils/xscreensaver-1.18...
[xscreensaver] / driver / xscreensaver.c
1 /* xscreensaver, Copyright (c) 1991-1993 Jamie Zawinski <jwz@lucid.com>
2  *
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 
9  * implied warranty.
10  */
11
12 #include "version.h"
13
14 /*   ========================================================================
15  *   First we wait until the keyboard and mouse become idle for the specified
16  *   amount of time.  We do this by periodically checking with the XIdle
17  *   server extension.
18  *
19  *   Then, we map a full screen black window.  
20  *
21  *   We place a __SWM_VROOT property on this window, so that newly-started
22  *   clients will think that this window is a "virtual root" window.
23  *
24  *   If there is an existing "virtual root" window (one that already had
25  *   an __SWM_VROOT property) then we remove that property from that window.
26  *   Otherwise, clients would see that window (the real virtual root) instead
27  *   of ours (the impostor.)
28  *
29  *   Then we pick a random program to run, and start it.  Two assumptions 
30  *   are made about this program: that it has been specified with whatever
31  *   command-line options are necessary to make it run on the root window;
32  *   and that it has been compiled with vroot.h, so that it is able to find
33  *   the root window when a virtual-root window manager (or this program) is
34  *   running.
35  *
36  *   Then, we wait for keyboard or mouse events to be generated on the window.
37  *   When they are, we kill the inferior process, unmap the window, and restore
38  *   the __SWM_VROOT property to the real virtual root window if there was one.
39  *
40  *   While we are waiting, we also set up timers so that, after a certain 
41  *   amount of time has passed, we can start a different screenhack.  We do
42  *   this by killing the running child process with SIGTERM, and then starting
43  *   a new one in the same way.
44  *
45  *   If there was a real virtual root, meaning that we removed the __SWM_VROOT
46  *   property from it, meaning we must (absolutely must) restore it before we
47  *   exit, then we set up signal handlers for most signals (SIGINT, SIGTERM,
48  *   etc.) that do this.  Most Xlib and Xt routines are not reentrant, so it
49  *   is not generally safe to call them from signal handlers; however, this
50  *   program spends most of its time waiting, so the window of opportunity 
51  *   when code could be called reentrantly is fairly small; and also, the worst
52  *   that could happen is that the call would fail.  If we've gotten one of
53  *   these signals, then we're on our way out anyway.  If we didn't restore the
54  *   __SWM_VROOT property, that would be very bad, so it's worth a shot.  Note
55  *   that this means that, if you're using a virtual-root window manager, you
56  *   can really fuck up the world by killing this process with "kill -9".
57  *
58  *   This program accepts ClientMessages of type SCREENSAVER; these messages
59  *   may contain the atom ACTIVATE or DEACTIVATE, meaning to turn the 
60  *   screensaver on or off now, regardless of the idleness of the user,
61  *   and a few other things.  The included "xscreensaver_command" program
62  *   sends these messsages.
63  *
64  *   If we don't have the XIdle extension, then we do the XAutoLock trick:
65  *   notice every window that gets created, and wait 30 seconds or so until
66  *   its creating process has settled down, and then select KeyPress events on
67  *   those windows which already select for KeyPress events.  It's important
68  *   that we not select KeyPress on windows which don't select them, because
69  *   that would interfere with event propagation.  This will break if any
70  *   program changes its event mask to contain KeyRelease or PointerMotion
71  *   more than 30 seconds after creating the window, but that's probably
72  *   pretty rare.
73  *   
74  *   The reason that we can't select KeyPresses on windows that don't have
75  *   them already is that, when dispatching a KeyPress event, X finds the
76  *   lowest (leafmost) window in the hierarchy on which *any* client selects
77  *   for KeyPress, and sends the event to that window.  This means that if a
78  *   client had a window with subwindows, and expected to receive KeyPress
79  *   events on the parent window instead of the subwindows, then that client
80  *   would malfunction if some other client selected KeyPress events on the
81  *   subwindows.  It is an incredible misdesign that one client can make
82  *   another client malfunction in this way.
83  *
84  *   To detect mouse motion, we periodically wake up and poll the mouse
85  *   position and button/modifier state, and notice when something has
86  *   changed.  We make this check every five seconds by default, and since the
87  *   screensaver timeout has a granularity of one minute, this makes the
88  *   chance of a false positive very small.  We could detect mouse motion in
89  *   the same way as keyboard activity, but that would suffer from the same
90  *   "client changing event mask" problem that the KeyPress events hack does.
91  *   I think polling is more reliable.
92  *
93  *   None of this crap happens if we're using the XIdle extension, so install
94  *   it if the description above sounds just too flaky to live.  It is, but
95  *   those are your choices.
96  *
97  *   A third idle-detection option could be implement (but is not): when running
98  *   on the console display ($DISPLAY is `localhost`:0) and we're on a machine
99  *   where /dev/tty and /dev/mouse have reasonable last-modification times, we
100  *   could just stat those.  But the incremental benefit of implementing this
101  *   is really small, so forget I said anything.
102  *
103  *   Debugging hints:
104  *     - Have a second terminal handy.
105  *     - Be careful where you set your breakpoints, you don't want this to
106  *       stop under the debugger with the keyboard grabbed or the blackout
107  *       window exposed.
108  *     - you probably can't set breakpoints in functions that are called on
109  *       the other side of a call to fork() -- if your clients are dying 
110  *       with signal 5, Trace/BPT Trap, you're losing in this way.
111  *     - If you aren't using XIdle, don't leave this stopped under the
112  *       debugger for very long, or the X input buffer will get huge because
113  *       of the keypress events it's selecting for.  This can make your X
114  *       server wedge with "no more input buffers."
115  *       
116  *   ========================================================================
117  */
118
119 #if __STDC__
120 #include <stdlib.h>
121 #include <unistd.h>
122 #endif
123
124 #include <stdio.h>
125 #include <X11/Xlib.h>
126 #include <X11/Xatom.h>
127 #include <X11/Intrinsic.h>
128 #include <X11/Xos.h>
129
130 #ifdef HAVE_XIDLE
131 #include <X11/extensions/xidle.h>
132 #endif
133
134 #include "xscreensaver.h"
135
136 #if defined(SVR4) || defined(SYSV)
137 # define srandom(i) srand((unsigned int)(i))
138 #else
139 extern void srandom P((int));           /* srand() is in stdlib.h... */
140 #endif
141
142 extern char *get_string_resource P((char *, char *));
143 extern Bool get_boolean_resource P((char *, char *));
144 extern int get_integer_resource P((char *, char *));
145 extern unsigned int get_minutes_resource P((char *, char *));
146 extern unsigned int get_seconds_resource P((char *, char *));
147
148 extern Visual *get_visual_resource P((Display *, char *, char *));
149 extern int get_visual_depth P((Display *, Visual *));
150
151 extern void notice_events_timer P((XtPointer closure, void *timer));
152 extern void cycle_timer P((void *junk1, XtPointer junk2));
153 extern void activate_lock_timer P((void *junk1, XtPointer junk2));
154 extern void sleep_until_idle P((Bool until_idle_p));
155
156 extern void ensure_no_screensaver_running P((void));
157 extern void initialize_screensaver_window P((void));
158 extern void disable_builtin_screensaver P((void));
159
160 extern void hack_environment P((void));
161 extern void grab_keyboard_and_mouse P((void));
162 extern void ungrab_keyboard_and_mouse P((void));
163
164 extern void save_argv P((int argc, char **argv));
165
166
167 char *screensaver_version;
168 char *progname;
169 char *progclass;
170 XrmDatabase db;
171
172 XtAppContext app;
173
174 Display *dpy;
175 Screen *screen;
176 Visual *visual;
177 int visual_depth;
178
179 Widget toplevel_shell;
180
181 Time lock_timeout;
182
183 extern Time timeout;
184 extern Time cycle;
185 extern Time passwd_timeout;
186 extern Time pointer_timeout;
187 extern Time notice_events_timeout;
188 extern XtIntervalId lock_id, cycle_id;
189
190 Bool use_xidle;
191 Bool verbose_p;
192 Bool lock_p, locked_p;
193
194 extern char **screenhacks;
195 extern int screenhacks_count;
196 extern char *shell;
197 extern int nice_inferior;
198 extern Window screensaver_window;
199 extern Cursor cursor;
200 extern Colormap cmap, cmap2;
201 extern Bool fade_p, unfade_p;
202 extern int fade_seconds, fade_ticks;
203 extern Bool install_cmap_p;
204 extern Bool locking_disabled_p;
205 extern char *nolock_reason;
206 extern Bool demo_mode_p;
207 extern Bool dbox_up_p;
208 extern int next_mode_p;
209
210 static time_t initial_delay;
211
212 extern Atom XA_VROOT, XA_XSETROOT_ID;
213 extern Atom XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
214
215 static Atom XA_SCREENSAVER;
216 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
217 static Atom XA_EXIT, XA_RESTART, XA_DEMO, XA_LOCK;
218
219 #ifdef NO_MOTIF /* kludge */
220 Bool demo_mode_p = 0;
221 Bool dbox_up_p = 0;
222 Time passwd_timeout = 0;
223 #endif
224
225 \f
226 #ifdef NO_DEMO_MODE
227 # define demo_mode() abort()
228 #else
229 extern void demo_mode P((void));
230 #endif
231 \f
232 static XrmOptionDescRec options [] = {
233   { "-timeout",         ".timeout",     XrmoptionSepArg, 0 },
234   { "-idelay",          ".initialDelay",XrmoptionSepArg, 0 },
235   { "-cycle",           ".cycle",       XrmoptionSepArg, 0 },
236   { "-visual",          ".visual",      XrmoptionSepArg, 0 },
237   { "-lock-timeout",    ".lockTimeout", XrmoptionSepArg, 0 },
238   { "-verbose",         ".verbose",     XrmoptionNoArg, "on" },
239   { "-silent",          ".verbose",     XrmoptionNoArg, "off" },
240   { "-xidle",           ".xidle",       XrmoptionNoArg, "on" },
241   { "-no-xidle",        ".xidle",       XrmoptionNoArg, "off" },
242   { "-lock",            ".lock",        XrmoptionNoArg, "on" },
243   { "-no-lock",         ".lock",        XrmoptionNoArg, "off" }
244 };
245
246 static char *defaults[] = {
247 #include "XScreenSaver.ad.h"
248  0
249 };
250
251 static void
252 do_help ()
253 {
254   printf ("\
255 xscreensaver %s, copyright (c) 1991-1993 by Jamie Zawinski <jwz@lucid.com>.\n\
256 The standard Xt command-line options are accepted; other options include:\n\
257 \n\
258     -timeout <minutes>          when the screensaver should activate\n\
259     -cycle <minutes>            how long to let each hack run\n\
260     -idelay <seconds>           how long to sleep before startup\n\
261     -demo                       enter interactive demo mode on startup\n\
262     -verbose                    be loud\n\
263     -silent                     don't\n\
264     -xidle                      use the XIdle server extension\n\
265     -no-xidle                   don't\n\
266     -lock                       require a password before deactivating\n\
267     -no-lock                    don't\n\
268     -lock-timeout <minutes>     grace period before locking; default 0\n\
269     -help                       this message\n\
270 \n\
271 Use the `xscreensaver-command' program to control a running screensaver.\n\
272 \n\
273 The *programs, *colorPrograms, and *monoPrograms resources control which\n\
274 graphics demos will be launched by the screensaver.  See the man page for\n\
275 more details.\n\n",
276           screensaver_version);
277
278 #ifdef NO_LOCKING
279   printf("Support for locking was not enabled at compile-time.\n");
280 #endif
281 #ifdef NO_DEMO_MODE
282   printf("Support for demo mode was not enabled at compile-time.\n");
283 #endif
284 #ifndef HAVE_XIDLE
285   printf("Support for the XIdle extension was not enabled at compile-time.\n");
286 #endif
287
288   fflush (stdout);
289   exit (1);
290 }
291
292
293 static void
294 get_screenhacks ()
295 {
296   char *data[3];
297   int i, hacks_size = 10;
298
299   data[0] = get_string_resource ("programs", "Programs");
300   data[1] = ((CellsOfScreen (screen) <= 2)
301              ? get_string_resource ("monoPrograms", "MonoPrograms")
302              : get_string_resource ("colorPrograms", "ColorPrograms"));
303   data[2] = 0;
304   if (! data[0]) data[0] = data[1], data[1] = 0;
305
306   screenhacks = (char **) malloc (sizeof (char *) * hacks_size);
307   screenhacks_count = 0;
308
309   for (i = 0; data[i]; i++)
310     {
311       int j = 0;
312       char *d = data [i];
313       int size = strlen (d);
314       while (j < size)
315         {
316           int end, start = j;
317           if (d[j] == ' ' || d[j] == '\t' || d[j] == '\n' || d[j] == 0)
318             {
319               j++;
320               continue;
321             }
322           if (hacks_size <= screenhacks_count)
323             screenhacks = (char **) realloc (screenhacks,
324                                              (hacks_size = hacks_size * 2) *
325                                              sizeof (char *));
326           screenhacks [screenhacks_count++] = d + j;
327           while (d[j] != 0 && d[j] != '\n')
328             j++;
329           end = j;
330           while (j > start && (d[j-1] == ' ' || d[j-1] == '\t'))
331             j--;
332           d[j] = 0;
333           j = end + 1;
334         }
335     }
336
337   /* shrink all whitespace to one space, for the benefit of the "demo"
338      mode display.  We only do this when we can easily tell that the
339      whitespace is not significant (no shell metachars).
340    */
341   for (i = 0; i < screenhacks_count; i++)
342     {
343       char *s = screenhacks [i];
344       char *s2;
345       int L = strlen (s);
346       int j, k;
347       for (j = 0; j < L; j++)
348         {
349           switch (s[j])
350             {
351             case '\'': case '"': case '`': case '\\':
352               goto DONE;
353             case '\t':
354               s[j] = ' ';
355             case ' ':
356               k = 0;
357               for (s2 = s+j+1; *s2 == ' ' || *s2 == '\t'; s2++)
358                 k++;
359               if (k > 0)
360                 for (s2 = s + j + 1; *s2; s2++)
361                   s2 [0] = s2 [k];
362               break;
363             }
364         }
365     DONE:
366       ;
367     }
368
369   if (screenhacks_count)
370     {
371       /* Shrink down the screenhacks array to be only as big as it needs to.
372          This doesn't really matter at all. */
373       screenhacks = (char **)
374         realloc (screenhacks, ((screenhacks_count + 1) * sizeof(char *)));
375       screenhacks [screenhacks_count] = 0;
376     }
377   else
378     {
379       free (screenhacks);
380       screenhacks = 0;
381     }
382 }
383
384
385 static void
386 get_resources ()
387 {
388   visual          = get_visual_resource (dpy, "visual", "Visual");
389   timeout         = 1000 * get_minutes_resource ("timeout", "Time");
390   cycle           = 1000 * get_minutes_resource ("cycle",   "Time");
391   lock_timeout    = 1000 * get_minutes_resource ("lockTimeout", "Time");
392   nice_inferior   = get_integer_resource ("nice", "Nice");
393   verbose_p       = get_boolean_resource ("verbose", "Boolean");
394   lock_p          = get_boolean_resource ("lock", "Boolean");
395   install_cmap_p  = get_boolean_resource ("installColormap", "Boolean");
396   fade_p          = get_boolean_resource ("fade", "Boolean");
397   unfade_p        = get_boolean_resource ("unfade", "Boolean");
398   fade_seconds    = get_seconds_resource ("fadeSeconds", "Time");
399   fade_ticks      = get_integer_resource ("fadeTicks", "Integer");
400   shell           = get_string_resource ("bourneShell", "BourneShell");
401   initial_delay   = get_seconds_resource ("initialDelay", "Time");
402   passwd_timeout  = 1000 * get_seconds_resource ("passwdTimeout", "Time");
403   pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
404   notice_events_timeout = 1000 * get_seconds_resource ("windowCreationTimeout",
405                                                        "Time");
406   if (timeout < 10000) timeout = 10000;
407   if (cycle < 2000) cycle = 2000;
408   if (passwd_timeout == 0) passwd_timeout = 30000;
409   if (pointer_timeout == 0) pointer_timeout = 5000;
410   if (notice_events_timeout == 0) notice_events_timeout = 10000;
411   if (fade_seconds == 0 || fade_ticks == 0) fade_p = False;
412   if (! fade_p) unfade_p = False;
413
414   visual_depth = get_visual_depth (dpy, visual);
415
416   if (visual_depth <= 1 || CellsOfScreen (screen) <= 2)
417     install_cmap_p = False;
418
419 #ifdef NO_LOCKING
420   locking_disabled_p = True;
421   nolock_reason = "not compiled with locking support";
422   if (lock_p)
423     {
424       lock_p = False;
425       fprintf (stderr, "%s: %snot compiled with support for locking.\n",
426                progname, (verbose_p ? "## " : ""));
427     }
428 #else  /* ! NO_LOCKING */
429   if (lock_p && locking_disabled_p)
430     {
431       fprintf (stderr, "%s: %slocking is disabled (%s).\n", progname,
432                (verbose_p ? "## " : ""), nolock_reason);
433       lock_p = False;
434     }
435 #endif /* ! NO_LOCKING */
436
437   /* don't set use_xidle unless it is explicitly specified */
438   if (get_string_resource ("xidle", "Boolean"))
439     use_xidle = get_boolean_resource ("xidle", "Boolean");
440   else
441 #ifdef HAVE_XIDLE       /* pick a default */
442     use_xidle = True;
443 #else
444     use_xidle = False;
445 #endif
446   get_screenhacks ();
447 }
448
449 char *
450 timestring ()
451 {
452   long now = time ((time_t *) 0);
453   char *str = (char *) ctime (&now);
454   char *nl = (char *) strchr (str, '\n');
455   if (nl) *nl = 0; /* take off that dang newline */
456   return str;
457 }
458
459 #ifdef NO_SETUID
460 # define hack_uid()
461 # define hack_uid_warn()
462 #else /* !NO_SETUID */
463 extern void hack_uid P((void));
464 extern void hack_uid_warn P((void));
465 #endif /* NO_SETUID */
466
467
468 #ifndef NO_LOCKING
469 extern Bool unlock_p P((Widget));
470 extern Bool lock_init P((void));
471 #endif
472
473 static void initialize ();
474 static void main_loop ();
475 static void initialize ();
476
477 void
478 main (argc, argv)
479      int argc;
480      char **argv;
481 {
482   initialize (argc, argv);
483   main_loop ();
484 }
485
486
487 static void
488 initialize_connection (argc, argv)
489      int argc;
490      char **argv;
491 {
492   toplevel_shell = XtAppInitialize (&app, progclass,
493                                     options, XtNumber (options),
494                                     &argc, argv, defaults, 0, 0);
495
496   dpy = XtDisplay (toplevel_shell);
497   screen = XtScreen (toplevel_shell);
498   db = XtDatabase (dpy);
499   XtGetApplicationNameAndClass (dpy, &progname, &progclass);
500
501   if (argc == 2 && !strcmp (argv[1], "-help"))
502     do_help ();
503   else if (argc > 1)
504     {
505       fprintf (stderr, "%s: unknown option %s\n", progname, argv [1]);
506       exit (1);
507     }
508   get_resources ();
509   hack_uid_warn ();
510   hack_environment ();
511   XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
512   XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
513   XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION", False);
514   XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
515   XA_XSETROOT_ID = XInternAtom (dpy, "_XSETROOT_ID", False);
516   XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
517   XA_DEACTIVATE = XInternAtom (dpy, "DEACTIVATE", False);
518   XA_RESTART = XInternAtom (dpy, "RESTART", False);
519   XA_CYCLE = XInternAtom (dpy, "CYCLE", False);
520   XA_NEXT = XInternAtom (dpy, "NEXT", False);
521   XA_PREV = XInternAtom (dpy, "PREV", False);
522   XA_EXIT = XInternAtom (dpy, "EXIT", False);
523   XA_DEMO = XInternAtom (dpy, "DEMO", False);
524   XA_LOCK = XInternAtom (dpy, "LOCK", False);
525 }
526
527 extern void init_sigchld P((void));
528
529 static void
530 initialize (argc, argv)
531      int argc;
532      char **argv;
533 {
534   Bool initial_demo_mode_p = False;
535   screensaver_version = (char *) malloc (5);
536   memcpy (screensaver_version, screensaver_id + 17, 4);
537   screensaver_version [4] = 0;
538   progname = argv[0]; /* reset later; this is for the benefit of lock_init() */
539
540 #ifdef NO_LOCKING
541   locking_disabled_p = True;
542   nolock_reason = "not compiled with locking support";
543 #else
544   locking_disabled_p = False;
545   if (! lock_init ())   /* before hack_uid() for proper permissions */
546     {
547       locking_disabled_p = True;
548       nolock_reason = "error getting password";
549     }
550 #endif
551
552   hack_uid ();
553   progclass = "XScreenSaver";
554
555   /* remove -demo switch before saving argv */
556   {
557     int i;
558     for (i = 1; i < argc; i++)
559       while (!strcmp ("-demo", argv [i]))
560         {
561           int j;
562           initial_demo_mode_p = True;
563           for (j = i; j < argc; j++)
564             argv [j] = argv [j+1];
565           argv [j] = 0;
566           argc--;
567           if (argc <= i) break;
568         }
569   }
570   save_argv (argc, argv);
571   initialize_connection (argc, argv);
572
573   ensure_no_screensaver_running ();
574   demo_mode_p = initial_demo_mode_p;
575   screensaver_window = 0;
576   cursor = 0;
577   initialize_screensaver_window ();
578   srandom ((int) time ((time_t *) 0));
579   cycle_id = 0;
580   lock_id = 0;
581   locked_p = False;
582   if (use_xidle)
583     {
584 #ifdef HAVE_XIDLE
585       int first_event, first_error;
586       if (! XidleQueryExtension (dpy, &first_event, &first_error))
587         {
588           fprintf (stderr,
589                    "%s: display %s does not support the XIdle extension.\n",
590                    progname, DisplayString (dpy));
591           use_xidle = 0;
592         }
593 #else
594       fprintf (stderr, "%s: not compiled with support for XIdle.\n",
595                progname);
596       use_xidle = 0;
597 #endif
598     }
599
600   init_sigchld ();
601
602   if (verbose_p)
603     printf ("\
604 %s %s, copyright (c) 1991-1993 by Jamie Zawinski <jwz@lucid.com>.\n\
605  pid = %d.\n", progname, screensaver_version, getpid ());
606
607   disable_builtin_screensaver ();
608
609   if (initial_demo_mode_p)
610     /* If the user wants demo mode, don't wait around before doing it. */
611     initial_delay = 0;
612
613 #ifdef HAVE_XIDLE
614   if (! use_xidle)
615 #endif
616     {
617       if (initial_delay)
618         {
619           if (verbose_p)
620             {
621               printf ("%s: waiting for %d second%s...", progname,
622                       initial_delay, (initial_delay == 1 ? "" : "s"));
623               fflush (stdout);
624             }
625           sleep (initial_delay);
626           if (verbose_p)
627             printf (" done.\n");
628         }
629       if (verbose_p)
630         {
631           printf ("%s: selecting events on extant windows...", progname);
632           fflush (stdout);
633         }
634       notice_events_timer ((XtPointer)
635                            RootWindowOfScreen (XtScreen (toplevel_shell)),
636                            0);
637       if (verbose_p)
638         printf (" done.\n");
639     }
640 }
641
642
643 extern void suspend_screenhack P((Bool suspend_p));
644
645 static void
646 main_loop ()
647 {
648   while (1)
649     {
650       if (! demo_mode_p)
651         sleep_until_idle (True);
652
653       if (demo_mode_p)
654         demo_mode ();
655       else
656         {
657           if (verbose_p)
658             printf ("%s: user is idle; waking up at %s.\n", progname,
659                     timestring());
660           blank_screen ();
661           spawn_screenhack (True);
662           if (cycle)
663             cycle_id = XtAppAddTimeOut (app, cycle, cycle_timer, 0);
664
665 #ifndef NO_LOCKING
666           if (lock_p && lock_timeout == 0)
667             locked_p = True;
668           if (lock_p && !locked_p)
669             /* locked_p might be true already because of ClientMessage */
670             lock_id = XtAppAddTimeOut (app,lock_timeout,activate_lock_timer,0);
671 #endif
672
673         PASSWD_INVALID:
674
675           sleep_until_idle (False); /* until not idle */
676
677 #ifndef NO_LOCKING
678           if (locked_p)
679             {
680               Bool val;
681               if (locking_disabled_p) abort ();
682               dbox_up_p = True;
683               ungrab_keyboard_and_mouse ();
684               suspend_screenhack (True);
685               XUndefineCursor (dpy, screensaver_window);
686               if (verbose_p)
687                 printf ("%s: prompting for password.\n", progname);
688               val = unlock_p (toplevel_shell);
689               if (verbose_p && val == False)
690                 printf ("%s: password incorrect!\n", progname);
691               dbox_up_p = False;
692               XDefineCursor (dpy, screensaver_window, cursor);
693               suspend_screenhack (False);
694               grab_keyboard_and_mouse ();
695               if (! val)
696                 goto PASSWD_INVALID;
697               locked_p = False;
698             }
699 #endif
700           unblank_screen ();
701           kill_screenhack ();
702           if (cycle_id)
703             {
704               XtRemoveTimeOut (cycle_id);
705               cycle_id = 0;
706             }
707 #ifndef NO_LOCKING
708           if (lock_id)
709             {
710               XtRemoveTimeOut (lock_id);
711               lock_id = 0;
712             }
713 #endif
714           if (verbose_p)
715             printf ("%s: user is active; going to sleep at %s.\n", progname,
716                     timestring ());
717         }
718     }
719 }
720
721 \f
722
723 Bool
724 handle_clientmessage (event, until_idle_p)
725      XEvent *event;
726      Bool until_idle_p;
727 {
728   Atom type;
729   if (event->xclient.message_type != XA_SCREENSAVER)
730     {
731       char *str;
732       str = XGetAtomName (dpy, event->xclient.message_type);
733       fprintf (stderr, "%s: %sunrecognised ClientMessage type %s received\n",
734                progname, (verbose_p ? "## " : ""),
735                (str ? str : "(null)"));
736       if (str) XFree (str);
737     }
738   if (event->xclient.format != 32)
739     {
740       fprintf (stderr, "%s: %sClientMessage of format %d received, not 32\n",
741                progname, (verbose_p ? "## " : ""), event->xclient.format);
742     }
743   type = event->xclient.data.l[0];
744   if (type == XA_ACTIVATE)
745     {
746       if (until_idle_p)
747         {
748           if (verbose_p)
749             printf ("%s: ACTIVATE ClientMessage received.\n", progname);
750           return True;
751         }
752       fprintf (stderr,
753                "%s: %sClientMessage ACTIVATE received while already active.\n",
754                progname, (verbose_p ? "## " : ""));
755     }
756   else if (type == XA_DEACTIVATE)
757     {
758       if (! until_idle_p)
759         {
760           if (verbose_p)
761             printf ("%s: DEACTIVATE ClientMessage received.\n", progname);
762           return True;
763         }
764       fprintf (stderr,
765                "%s: %sClientMessage DEACTIVATE received while inactive.\n",
766                progname, (verbose_p ? "## " : ""));
767     }
768   else if (type == XA_CYCLE)
769     {
770       if (! until_idle_p)
771         {
772           if (verbose_p)
773             printf ("%s: CYCLE ClientMessage received.\n", progname);
774           if (cycle_id)
775             XtRemoveTimeOut (cycle_id);
776           cycle_id = 0;
777           cycle_timer (0, 0);
778           return False;
779         }
780       fprintf (stderr,
781                "%s: %sClientMessage CYCLE received while inactive.\n",
782                progname, (verbose_p ? "## " : ""));
783     }
784   else if (type == XA_NEXT || type == XA_PREV)
785     {
786       if (verbose_p)
787         printf ("%s: %s ClientMessage received.\n", progname,
788                 (type == XA_NEXT ? "NEXT" : "PREV"));
789       next_mode_p = 1 + (type == XA_PREV);
790
791       if (! until_idle_p)
792         {
793           if (cycle_id)
794             XtRemoveTimeOut (cycle_id);
795           cycle_id = 0;
796           cycle_timer (0, 0);
797         }
798       else
799         return True;
800     }
801   else if (type == XA_EXIT)
802     {
803       /* Ignore EXIT message if the screen is locked. */
804       if (until_idle_p || !locked_p)
805         {
806           if (verbose_p)
807             printf ("%s: EXIT ClientMessage received.\n", progname);
808           if (! until_idle_p)
809             {
810               unblank_screen ();
811               kill_screenhack ();
812               XSync (dpy, False);
813             }
814           exit (0);
815         }
816       else
817         fprintf (stderr, "%s: %sEXIT ClientMessage received while locked.\n",
818                  progname, (verbose_p ? "## " : ""));
819     }
820   else if (type == XA_RESTART)
821     {
822       /* The RESTART message works whether the screensaver is active or not,
823          unless the screen is locked, in which case it doesn't work.
824        */
825       if (until_idle_p || !locked_p)
826         {
827           if (verbose_p)
828             printf ("%s: RESTART ClientMessage received.\n", progname);
829           if (! until_idle_p)
830             {
831               unblank_screen ();
832               kill_screenhack ();
833               XSync (dpy, False);
834             }
835           restart_process ();
836         }
837       else
838         fprintf(stderr, "%s: %sRESTART ClientMessage received while locked.\n",
839                 progname, (verbose_p ? "## " : ""));
840     }
841   else if (type == XA_DEMO)
842     {
843 #ifdef NO_DEMO_MODE
844       fprintf (stderr,
845                "%s: %snot compiled with support for DEMO mode\n",
846                progname, (verbose_p ? "## " : ""));
847 #else
848       if (until_idle_p)
849         {
850           if (verbose_p)
851             printf ("%s: DEMO ClientMessage received.\n", progname);
852           demo_mode_p = True;
853           return True;
854         }
855       fprintf (stderr,
856                "%s: %sDEMO ClientMessage received while active.\n",
857                progname, (verbose_p ? "## " : ""));
858 #endif
859     }
860   else if (type == XA_LOCK)
861     {
862 #ifdef NO_LOCKING
863       fprintf (stderr, "%s: %snot compiled with support for LOCK mode\n",
864                progname, (verbose_p ? "## " : ""));
865 #else
866       if (locking_disabled_p)
867         fprintf (stderr,
868                "%s: %sLOCK ClientMessage received, but locking is disabled.\n",
869                  progname, (verbose_p ? "## " : ""));
870       else if (locked_p)
871         fprintf (stderr,
872                "%s: %sLOCK ClientMessage received while already locked.\n",
873                  progname, (verbose_p ? "## " : ""));
874       else
875         {
876           locked_p = True;
877           if (verbose_p) 
878             printf ("%s: LOCK ClientMessage received;%s locking.\n",
879                     progname, until_idle_p ? " activating and" : "");
880
881           if (lock_id)  /* we're doing it now, so lose the timeout */
882             {
883               XtRemoveTimeOut (lock_id);
884               lock_id = 0;
885             }
886
887           if (until_idle_p)
888             return True;
889         }
890 #endif
891     }
892   else
893     {
894       char *str;
895       str = XGetAtomName (dpy, type);
896       if (str)
897         fprintf (stderr,
898                  "%s: %sunrecognised screensaver ClientMessage %s received\n",
899                  progname, (verbose_p ? "## " : ""), str);
900       else
901         fprintf (stderr,
902                  "%s: %sunrecognised screensaver ClientMessage 0x%x received\n",
903                  progname, (verbose_p ? "## " : ""),
904                  event->xclient.data.l[0]);
905       if (str) XFree (str);
906     }
907   return False;
908 }