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