ftp://ftp.ntnu.no/old/pub/X11/R5contrib/xscreensaver-1.17.tar.gz
[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  *   Debugging hints:
98  *     - Have a second terminal handy.
99  *     - Be careful where you set your breakpoints, you don't want this to
100  *       stop under the debugger with the keyboard grabbed or the blackout
101  *       window exposed.
102  *     - you probably can't set breakpoints in functions that are called on
103  *       the other side of a call to fork() -- if your clients are dying 
104  *       with signal 5, Trace/BPT Trap, you're losing in this way.
105  *     - If you aren't using XIdle, don't leave this stopped under the
106  *       debugger for very long, or the X input buffer will get huge because
107  *       of the keypress events it's selecting for.  This can make your X
108  *       server wedge with "no more input buffers."
109  *       
110  *   ========================================================================
111  */
112
113 #if __STDC__
114 #include <stdlib.h>
115 #include <unistd.h>
116 #endif
117
118 #include <stdio.h>
119 #include <X11/Xlib.h>
120 #include <X11/Xatom.h>
121 #include <X11/Intrinsic.h>
122 #include <X11/Xos.h>
123
124 #ifdef HAVE_XIDLE
125 #include <X11/extensions/xidle.h>
126 #endif
127
128 #include "xscreensaver.h"
129
130 #if defined(SVR4) || defined(SYSV)
131 # define srandom(i) srand((unsigned int)(i))
132 #else
133 extern void srandom P((int));           /* srand() is in stdlib.h... */
134 #endif
135
136 extern char *get_string_resource P((char *, char *));
137 extern Bool get_boolean_resource P((char *, char *));
138 extern int get_integer_resource P((char *, char *));
139 extern unsigned int get_minutes_resource P((char *, char *));
140 extern unsigned int get_seconds_resource P((char *, char *));
141
142 extern Visual *get_visual_resource P((Display *, char *, char *));
143 extern int get_visual_depth P((Display *, Visual *));
144
145 extern void notice_events_timer P((XtPointer closure, void *timer));
146 extern void cycle_timer P((void *junk1, XtPointer junk2));
147 extern void activate_lock_timer P((void *junk1, XtPointer junk2));
148 extern void sleep_until_idle P((Bool until_idle_p));
149
150 extern void ensure_no_screensaver_running P((void));
151 extern void initialize_screensaver_window P((void));
152 extern void disable_builtin_screensaver P((void));
153
154 extern void hack_environment P((void));
155 extern void grab_keyboard_and_mouse P((void));
156 extern void ungrab_keyboard_and_mouse P((void));
157
158 extern void save_argv P((int argc, char **argv));
159
160
161 char *screensaver_version;
162 char *progname;
163 char *progclass;
164 XrmDatabase db;
165
166 XtAppContext app;
167
168 Display *dpy;
169 Screen *screen;
170 Visual *visual;
171 int visual_depth;
172
173 Widget toplevel_shell;
174
175 Time lock_timeout;
176
177 extern Time timeout;
178 extern Time cycle;
179 extern Time passwd_timeout;
180 extern Time pointer_timeout;
181 extern Time notice_events_timeout;
182 extern XtIntervalId lock_id, cycle_id;
183
184 Bool use_xidle;
185 Bool verbose_p;
186 Bool lock_p, locked_p;
187
188 extern char **screenhacks;
189 extern int screenhacks_count;
190 extern char *shell;
191 extern int nice_inferior;
192 extern Window screensaver_window;
193 extern Cursor cursor;
194 extern Colormap cmap, cmap2;
195 extern Bool fade_p, unfade_p;
196 extern int fade_seconds, fade_ticks;
197 extern Bool install_cmap_p;
198 extern Bool locking_disabled_p;
199 extern Bool demo_mode_p;
200 extern Bool dbox_up_p;
201 extern int next_mode_p;
202
203 static time_t initial_delay;
204
205 extern Atom XA_VROOT, XA_XSETROOT_ID;
206 extern Atom XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
207
208 static Atom XA_SCREENSAVER;
209 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
210 static Atom XA_EXIT, XA_RESTART, XA_DEMO, XA_LOCK;
211
212 #ifdef NO_MOTIF /* kludge */
213 Bool demo_mode_p = 0;
214 Bool dbox_up_p = 0;
215 Time passwd_timeout = 0;
216 #endif
217
218 \f
219 #ifdef NO_DEMO_MODE
220 # define demo_mode() abort()
221 #else
222 extern void demo_mode P((void));
223 #endif
224 \f
225 static XrmOptionDescRec options [] = {
226   { "-timeout",         ".timeout",     XrmoptionSepArg, 0 },
227   { "-idelay",          ".initialDelay",XrmoptionSepArg, 0 },
228   { "-cycle",           ".cycle",       XrmoptionSepArg, 0 },
229   { "-visual",          ".visual",      XrmoptionSepArg, 0 },
230   { "-lock-timeout",    ".lockTimeout", XrmoptionSepArg, 0 },
231   { "-verbose",         ".verbose",     XrmoptionNoArg, "on" },
232   { "-silent",          ".verbose",     XrmoptionNoArg, "off" },
233   { "-xidle",           ".xidle",       XrmoptionNoArg, "on" },
234   { "-no-xidle",        ".xidle",       XrmoptionNoArg, "off" },
235   { "-lock",            ".lock",        XrmoptionNoArg, "on" },
236   { "-no-lock",         ".lock",        XrmoptionNoArg, "off" }
237 };
238
239 static char *defaults[] = {
240 #include "XScreenSaver.ad.h"
241  0
242 };
243
244 static void
245 do_help ()
246 {
247   printf ("\
248 xscreensaver %s, copyright (c) 1991-1993 by Jamie Zawinski <jwz@lucid.com>.\n\
249 The standard Xt command-line options are accepted; other options include:\n\
250 \n\
251     -timeout <minutes>          when the screensaver should activate\n\
252     -cycle <minutes>            how long to let each hack run\n\
253     -idelay <seconds>           how long to sleep before startup\n\
254     -demo                       enter interactive demo mode on startup\n\
255     -verbose                    be loud\n\
256     -silent                     don't\n\
257     -xidle                      use the XIdle server extension\n\
258     -no-xidle                   don't\n\
259     -lock                       require a password before deactivating\n\
260     -no-lock                    don't\n\
261     -lock-timeout <minutes>     grace period before locking; default 0\n\
262     -help                       this message\n\
263 \n\
264 Use the `xscreensaver-command' program to control a running screensaver.\n\
265 \n\
266 The *programs, *colorPrograms, and *monoPrograms resources control which\n\
267 graphics demos will be launched by the screensaver.  See the man page for\n\
268 more details.\n\n",
269           screensaver_version);
270
271 #ifdef NO_LOCKING
272   printf("Support for locking was not enabled at compile-time.\n");
273 #endif
274 #ifdef NO_DEMO_MODE
275   printf("Support for demo mode was not enabled at compile-time.\n");
276 #endif
277 #ifndef HAVE_XIDLE
278   printf("Support for the XIdle extension was not enabled at compile-time.\n");
279 #endif
280
281   fflush (stdout);
282   exit (1);
283 }
284
285
286 static void
287 get_screenhacks ()
288 {
289   char *data[3];
290   int i, hacks_size = 10;
291
292   data[0] = get_string_resource ("programs", "Programs");
293   data[1] = ((CellsOfScreen (screen) <= 2)
294              ? get_string_resource ("monoPrograms", "MonoPrograms")
295              : get_string_resource ("colorPrograms", "ColorPrograms"));
296   data[2] = 0;
297   if (! data[0]) data[0] = data[1], data[1] = 0;
298
299   screenhacks = (char **) malloc (sizeof (char *) * hacks_size);
300   screenhacks_count = 0;
301
302   for (i = 0; data[i]; i++)
303     {
304       int j = 0;
305       char *d = data [i];
306       int size = strlen (d);
307       while (j < size)
308         {
309           int end, start = j;
310           if (d[j] == ' ' || d[j] == '\t' || d[j] == '\n' || d[j] == 0)
311             {
312               j++;
313               continue;
314             }
315           if (hacks_size <= screenhacks_count)
316             screenhacks = (char **) realloc (screenhacks,
317                                              (hacks_size = hacks_size * 2) *
318                                              sizeof (char *));
319           screenhacks [screenhacks_count++] = d + j;
320           while (d[j] != 0 && d[j] != '\n')
321             j++;
322           end = j;
323           while (j > start && (d[j-1] == ' ' || d[j-1] == '\t'))
324             j--;
325           d[j] = 0;
326           j = end + 1;
327         }
328     }
329
330   /* shrink all whitespace to one space, for the benefit of the "demo"
331      mode display.  We only do this when we can easily tell that the
332      whitespace is not significant (no shell metachars).
333    */
334   for (i = 0; i < screenhacks_count; i++)
335     {
336       char *s = screenhacks [i];
337       char *s2;
338       int L = strlen (s);
339       int j, k;
340       for (j = 0; j < L; j++)
341         {
342           switch (s[j])
343             {
344             case '\'': case '"': case '`': case '\\':
345               goto DONE;
346             case '\t':
347               s[j] = ' ';
348             case ' ':
349               k = 0;
350               for (s2 = s+j+1; *s2 == ' ' || *s2 == '\t'; s2++)
351                 k++;
352               if (k > 0)
353                 for (s2 = s + j + 1; *s2; s2++)
354                   s2 [0] = s2 [k];
355               break;
356             }
357         }
358     DONE:
359       ;
360     }
361
362   if (screenhacks_count)
363     {
364       /* Shrink down the screenhacks array to be only as big as it needs to.
365          This doesn't really matter at all. */
366       screenhacks = (char **)
367         realloc (screenhacks, ((screenhacks_count + 1) * sizeof(char *)));
368       screenhacks [screenhacks_count] = 0;
369     }
370   else
371     {
372       free (screenhacks);
373       screenhacks = 0;
374     }
375 }
376
377
378 static void
379 get_resources ()
380 {
381   visual          = get_visual_resource (dpy, "visual", "Visual");
382   timeout         = 1000 * get_minutes_resource ("timeout", "Time");
383   cycle           = 1000 * get_minutes_resource ("cycle",   "Time");
384   lock_timeout    = 1000 * get_minutes_resource ("lockTimeout", "Time");
385   nice_inferior   = get_integer_resource ("nice", "Nice");
386   verbose_p       = get_boolean_resource ("verbose", "Boolean");
387   lock_p          = get_boolean_resource ("lock", "Boolean");
388   install_cmap_p  = get_boolean_resource ("installColormap", "Boolean");
389   fade_p          = get_boolean_resource ("fade", "Boolean");
390   unfade_p        = get_boolean_resource ("unfade", "Boolean");
391   fade_seconds    = get_seconds_resource ("fadeSeconds", "Time");
392   fade_ticks      = get_integer_resource ("fadeTicks", "Integer");
393   shell           = get_string_resource ("bourneShell", "BourneShell");
394   initial_delay   = get_seconds_resource ("initialDelay", "Time");
395   passwd_timeout  = 1000 * get_seconds_resource ("passwdTimeout", "Time");
396   pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
397   notice_events_timeout = 1000 * get_seconds_resource ("windowCreationTimeout",
398                                                        "Time");
399   if (timeout < 10000) timeout = 10000;
400   if (cycle < 2000) cycle = 2000;
401   if (passwd_timeout == 0) passwd_timeout = 30000;
402   if (pointer_timeout == 0) pointer_timeout = 5000;
403   if (notice_events_timeout == 0) notice_events_timeout = 10000;
404   if (fade_seconds == 0 || fade_ticks == 0) fade_p = False;
405   if (! fade_p) unfade_p = False;
406
407   visual_depth = get_visual_depth (dpy, visual);
408
409   if (visual_depth <= 1 || CellsOfScreen (screen) <= 2)
410     install_cmap_p = False;
411
412 #ifdef NO_LOCKING
413   locking_disabled_p = True;
414   if (lock_p)
415     {
416       lock_p = False;
417       fprintf (stderr, "%s: %snot compiled with support for locking.\n",
418                progname, (verbose_p ? "## " : ""));
419     }
420 #else  /* ! NO_LOCKING */
421   if (lock_p && locking_disabled_p)
422     {
423       fprintf (stderr, "%s: %slocking is disabled.\n", progname,
424                (verbose_p ? "## " : ""));
425       lock_p = False;
426     }
427 #endif /* ! NO_LOCKING */
428
429   /* don't set use_xidle unless it is explicitly specified */
430   if (get_string_resource ("xidle", "Boolean"))
431     use_xidle = get_boolean_resource ("xidle", "Boolean");
432   else
433 #ifdef HAVE_XIDLE       /* pick a default */
434     use_xidle = True;
435 #else
436     use_xidle = False;
437 #endif
438   get_screenhacks ();
439 }
440
441 char *
442 timestring ()
443 {
444   long now = time ((time_t *) 0);
445   char *str = (char *) ctime (&now);
446   char *nl = (char *) strchr (str, '\n');
447   if (nl) *nl = 0; /* take off that dang newline */
448   return str;
449 }
450
451 #ifdef NO_SETUID
452 # define hack_uid()
453 # define hack_uid_warn()
454 #else /* !NO_SETUID */
455 extern void hack_uid P((void));
456 extern void hack_uid_warn P((void));
457 #endif /* NO_SETUID */
458
459
460 #ifndef NO_LOCKING
461 extern Bool unlock_p P((Widget));
462 extern Bool lock_init P((void));
463 #endif
464
465 static void initialize ();
466 static void main_loop ();
467 static void initialize ();
468
469 void
470 main (argc, argv)
471      int argc;
472      char **argv;
473 {
474   initialize (argc, argv);
475   main_loop ();
476 }
477
478
479 static void
480 initialize_connection (argc, argv)
481      int argc;
482      char **argv;
483 {
484   toplevel_shell = XtAppInitialize (&app, progclass,
485                                     options, XtNumber (options),
486                                     &argc, argv, defaults, 0, 0);
487
488   dpy = XtDisplay (toplevel_shell);
489   screen = XtScreen (toplevel_shell);
490   db = XtDatabase (dpy);
491   XtGetApplicationNameAndClass (dpy, &progname, &progclass);
492
493   if (argc == 2 && !strcmp (argv[1], "-help"))
494     do_help ();
495   else if (argc > 1)
496     {
497       fprintf (stderr, "%s: unknown option %s\n", progname, argv [1]);
498       exit (1);
499     }
500   get_resources ();
501   hack_uid_warn ();
502   hack_environment ();
503   XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
504   XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
505   XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION", False);
506   XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
507   XA_XSETROOT_ID = XInternAtom (dpy, "_XSETROOT_ID", False);
508   XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
509   XA_DEACTIVATE = XInternAtom (dpy, "DEACTIVATE", False);
510   XA_RESTART = XInternAtom (dpy, "RESTART", False);
511   XA_CYCLE = XInternAtom (dpy, "CYCLE", False);
512   XA_NEXT = XInternAtom (dpy, "NEXT", False);
513   XA_PREV = XInternAtom (dpy, "PREV", False);
514   XA_EXIT = XInternAtom (dpy, "EXIT", False);
515   XA_DEMO = XInternAtom (dpy, "DEMO", False);
516   XA_LOCK = XInternAtom (dpy, "LOCK", False);
517 }
518
519 extern void init_sigchld P((void));
520
521 static void
522 initialize (argc, argv)
523      int argc;
524      char **argv;
525 {
526   Bool initial_demo_mode_p = False;
527   screensaver_version = (char *) malloc (5);
528   memcpy (screensaver_version, screensaver_id + 17, 4);
529   screensaver_version [4] = 0;
530   progname = argv[0]; /* reset later; this is for the benefit of lock_init() */
531
532 #ifdef NO_LOCKING
533   locking_disabled_p = True;
534 #else
535   locking_disabled_p = False;
536   if (! lock_init ())   /* before hack_uid() for proper permissions */
537     locking_disabled_p = True;
538 #endif
539
540   hack_uid ();
541   progclass = "XScreenSaver";
542
543   /* remove -demo switch before saving argv */
544   {
545     int i;
546     for (i = 1; i < argc; i++)
547       while (!strcmp ("-demo", argv [i]))
548         {
549           int j;
550           initial_demo_mode_p = True;
551           for (j = i; j < argc; j++)
552             argv [j] = argv [j+1];
553           argv [j] = 0;
554           argc--;
555           if (argc <= i) break;
556         }
557   }
558   save_argv (argc, argv);
559   initialize_connection (argc, argv);
560
561   ensure_no_screensaver_running ();
562   demo_mode_p = initial_demo_mode_p;
563   screensaver_window = 0;
564   cursor = 0;
565   initialize_screensaver_window ();
566   srandom ((int) time ((time_t *) 0));
567   cycle_id = 0;
568   lock_id = 0;
569   locked_p = False;
570   if (use_xidle)
571     {
572 #ifdef HAVE_XIDLE
573       int first_event, first_error;
574       if (! XidleQueryExtension (dpy, &first_event, &first_error))
575         {
576           fprintf (stderr,
577                    "%s: display %s does not support the XIdle extension.\n",
578                    progname, DisplayString (dpy));
579           use_xidle = 0;
580         }
581 #else
582       fprintf (stderr, "%s: not compiled with support for XIdle.\n",
583                progname);
584       use_xidle = 0;
585 #endif
586     }
587
588   init_sigchld ();
589
590   if (verbose_p)
591     printf ("\
592 %s %s, copyright (c) 1991-1993 by Jamie Zawinski <jwz@lucid.com>.\n\
593  pid = %d.\n", progname, screensaver_version, getpid ());
594
595   disable_builtin_screensaver ();
596
597   if (initial_demo_mode_p)
598     /* If the user wants demo mode, don't wait around before doing it. */
599     initial_delay = 0;
600
601 #ifdef HAVE_XIDLE
602   if (! use_xidle)
603 #endif
604     {
605       if (initial_delay)
606         {
607           if (verbose_p)
608             {
609               printf ("%s: waiting for %d second%s...", progname,
610                       initial_delay, (initial_delay == 1 ? "" : "s"));
611               fflush (stdout);
612             }
613           sleep (initial_delay);
614           if (verbose_p)
615             printf (" done.\n");
616         }
617       if (verbose_p)
618         {
619           printf ("%s: selecting events on extant windows...", progname);
620           fflush (stdout);
621         }
622       notice_events_timer ((XtPointer)
623                            RootWindowOfScreen (XtScreen (toplevel_shell)),
624                            0);
625       if (verbose_p)
626         printf (" done.\n");
627     }
628 }
629
630
631 extern void suspend_screenhack P((Bool suspend_p));
632
633 static void
634 main_loop ()
635 {
636   while (1)
637     {
638       if (! demo_mode_p)
639         sleep_until_idle (True);
640
641       if (demo_mode_p)
642         demo_mode ();
643       else
644         {
645           if (verbose_p)
646             printf ("%s: user is idle; waking up at %s.\n", progname,
647                     timestring());
648           blank_screen ();
649           spawn_screenhack (True);
650           if (cycle)
651             cycle_id = XtAppAddTimeOut (app, cycle, cycle_timer, 0);
652
653 #ifndef NO_LOCKING
654           if (lock_p && lock_timeout == 0)
655             locked_p = True;
656           if (lock_p && !locked_p)
657             /* locked_p might be true already because of ClientMessage */
658             lock_id = XtAppAddTimeOut (app,lock_timeout,activate_lock_timer,0);
659 #endif
660
661         PASSWD_INVALID:
662
663           sleep_until_idle (False); /* until not idle */
664
665 #ifndef NO_LOCKING
666           if (locked_p)
667             {
668               Bool val;
669               if (locking_disabled_p) abort ();
670               dbox_up_p = True;
671               ungrab_keyboard_and_mouse ();
672               suspend_screenhack (True);
673               XUndefineCursor (dpy, screensaver_window);
674               if (verbose_p)
675                 printf ("%s: prompting for password.\n", progname);
676               val = unlock_p (toplevel_shell);
677               if (verbose_p && val == False)
678                 printf ("%s: password incorrect!\n", progname);
679               dbox_up_p = False;
680               XDefineCursor (dpy, screensaver_window, cursor);
681               suspend_screenhack (False);
682               grab_keyboard_and_mouse ();
683               if (! val)
684                 goto PASSWD_INVALID;
685               locked_p = False;
686             }
687 #endif
688           unblank_screen ();
689           kill_screenhack ();
690           if (cycle_id)
691             {
692               XtRemoveTimeOut (cycle_id);
693               cycle_id = 0;
694             }
695 #ifndef NO_LOCKING
696           if (lock_id)
697             {
698               XtRemoveTimeOut (lock_id);
699               lock_id = 0;
700             }
701 #endif
702           if (verbose_p)
703             printf ("%s: user is active; going to sleep at %s.\n", progname,
704                     timestring ());
705         }
706     }
707 }
708
709 \f
710
711 Bool
712 handle_clientmessage (event, until_idle_p)
713      XEvent *event;
714      Bool until_idle_p;
715 {
716   Atom type;
717   if (event->xclient.message_type != XA_SCREENSAVER)
718     {
719       char *str;
720       str = XGetAtomName (dpy, event->xclient.message_type);
721       fprintf (stderr, "%s: %sunrecognised ClientMessage type %s received\n",
722                progname, (verbose_p ? "## " : ""),
723                (str ? str : "(null)"));
724       if (str) XFree (str);
725     }
726   if (event->xclient.format != 32)
727     {
728       fprintf (stderr, "%s: %sClientMessage of format %d received, not 32\n",
729                progname, (verbose_p ? "## " : ""), event->xclient.format);
730     }
731   type = event->xclient.data.l[0];
732   if (type == XA_ACTIVATE)
733     {
734       if (until_idle_p)
735         {
736           if (verbose_p)
737             printf ("%s: ACTIVATE ClientMessage received.\n", progname);
738           return True;
739         }
740       fprintf (stderr,
741                "%s: %sClientMessage ACTIVATE received while already active.\n",
742                progname, (verbose_p ? "## " : ""));
743     }
744   else if (type == XA_DEACTIVATE)
745     {
746       if (! until_idle_p)
747         {
748           if (verbose_p)
749             printf ("%s: DEACTIVATE ClientMessage received.\n", progname);
750           return True;
751         }
752       fprintf (stderr,
753                "%s: %sClientMessage DEACTIVATE received while inactive.\n",
754                progname, (verbose_p ? "## " : ""));
755     }
756   else if (type == XA_CYCLE)
757     {
758       if (! until_idle_p)
759         {
760           if (verbose_p)
761             printf ("%s: CYCLE ClientMessage received.\n", progname);
762           if (cycle_id)
763             XtRemoveTimeOut (cycle_id);
764           cycle_id = 0;
765           cycle_timer (0, 0);
766           return False;
767         }
768       fprintf (stderr,
769                "%s: %sClientMessage CYCLE received while inactive.\n",
770                progname, (verbose_p ? "## " : ""));
771     }
772   else if (type == XA_NEXT || type == XA_PREV)
773     {
774       if (verbose_p)
775         printf ("%s: %s ClientMessage received.\n", progname,
776                 (type == XA_NEXT ? "NEXT" : "PREV"));
777       next_mode_p = 1 + (type == XA_PREV);
778
779       if (! until_idle_p)
780         {
781           if (cycle_id)
782             XtRemoveTimeOut (cycle_id);
783           cycle_id = 0;
784           cycle_timer (0, 0);
785         }
786       else
787         return True;
788     }
789   else if (type == XA_EXIT)
790     {
791       /* Ignore EXIT message if the screen is locked. */
792       if (until_idle_p || !locked_p)
793         {
794           if (verbose_p)
795             printf ("%s: EXIT ClientMessage received.\n", progname);
796           if (! until_idle_p)
797             {
798               unblank_screen ();
799               kill_screenhack ();
800               XSync (dpy, False);
801             }
802           exit (0);
803         }
804       else
805         fprintf (stderr, "%s: %sEXIT ClientMessage received while locked.\n",
806                  progname, (verbose_p ? "## " : ""));
807     }
808   else if (type == XA_RESTART)
809     {
810       /* The RESTART message works whether the screensaver is active or not,
811          unless the screen is locked, in which case it doesn't work.
812        */
813       if (until_idle_p || !locked_p)
814         {
815           if (verbose_p)
816             printf ("%s: RESTART ClientMessage received.\n", progname);
817           if (! until_idle_p)
818             {
819               unblank_screen ();
820               kill_screenhack ();
821               XSync (dpy, False);
822             }
823           restart_process ();
824         }
825       else
826         fprintf(stderr, "%s: %sRESTART ClientMessage received while locked.\n",
827                 progname, (verbose_p ? "## " : ""));
828     }
829   else if (type == XA_DEMO)
830     {
831 #ifdef NO_DEMO_MODE
832       fprintf (stderr,
833                "%s: %snot compiled with support for DEMO mode\n",
834                progname, (verbose_p ? "## " : ""));
835 #else
836       if (until_idle_p)
837         {
838           if (verbose_p)
839             printf ("%s: DEMO ClientMessage received.\n", progname);
840           demo_mode_p = True;
841           return True;
842         }
843       fprintf (stderr,
844                "%s: %sDEMO ClientMessage received while active.\n",
845                progname, (verbose_p ? "## " : ""));
846 #endif
847     }
848   else if (type == XA_LOCK)
849     {
850 #ifdef NO_LOCKING
851       fprintf (stderr, "%s: %snot compiled with support for LOCK mode\n",
852                progname, (verbose_p ? "## " : ""));
853 #else
854       if (locking_disabled_p)
855         fprintf (stderr,
856                "%s: %sLOCK ClientMessage received, but locking is disabled.\n",
857                  progname, (verbose_p ? "## " : ""));
858       else if (locked_p)
859         fprintf (stderr,
860                "%s: %sLOCK ClientMessage received while already locked.\n",
861                  progname, (verbose_p ? "## " : ""));
862       else
863         {
864           locked_p = True;
865           if (verbose_p) 
866             printf ("%s: LOCK ClientMessage received;%s locking.\n",
867                     progname, until_idle_p ? " activating and" : "");
868
869           if (lock_id)  /* we're doing it now, so lose the timeout */
870             {
871               XtRemoveTimeOut (lock_id);
872               lock_id = 0;
873             }
874
875           if (until_idle_p)
876             return True;
877         }
878 #endif
879     }
880   else
881     {
882       char *str;
883       str = XGetAtomName (dpy, type);
884       if (str)
885         fprintf (stderr,
886                  "%s: %sunrecognised screensaver ClientMessage %s received\n",
887                  progname, (verbose_p ? "## " : ""), str);
888       else
889         fprintf (stderr,
890                  "%s: %sunrecognised screensaver ClientMessage 0x%x received\n",
891                  progname, (verbose_p ? "## " : ""),
892                  event->xclient.data.l[0]);
893       if (str) XFree (str);
894     }
895   return False;
896 }