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