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