http://www.ibiblio.org/pub/historic-linux/ftp-archives/sunsite.unc.edu/Sep-29-1996...
[xscreensaver] / driver / xscreensaver.c
1 /* xscreensaver, Copyright (c) 1991-1995 Jamie Zawinski <jwz@netscape.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 "yarandom.h"
142 #include "xscreensaver.h"
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, XtIntervalId *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 extern void initialize_stderr P((void));
169
170 char *screensaver_version;
171 char *progname;
172 char *progclass;
173 XrmDatabase db;
174
175 XtAppContext app;
176
177 Display *dpy;
178 Screen *screen;
179 Visual *visual;
180 int visual_depth;
181
182 Widget toplevel_shell;
183
184 Time lock_timeout;
185
186 extern Time timeout;
187 extern Time cycle;
188 #ifndef NO_LOCKING
189 extern Time passwd_timeout;
190 #endif
191 extern Time pointer_timeout;
192 extern Time notice_events_timeout;
193 extern XtIntervalId lock_id, cycle_id;
194
195 Bool use_xidle_extension;
196 Bool use_saver_extension;
197 Bool verbose_p;
198 Bool lock_p, locked_p;
199
200 extern char **screenhacks;
201 extern int screenhacks_count;
202 extern char *shell;
203 extern int nice_inferior;
204 extern Window screensaver_window;
205 extern Cursor cursor;
206 extern Colormap cmap, cmap2;
207 extern Bool fade_p, unfade_p;
208 extern int fade_seconds, fade_ticks;
209 extern Bool install_cmap_p;
210 extern Bool locking_disabled_p;
211 extern char *nolock_reason;
212 extern Bool demo_mode_p;
213 extern Bool dbox_up_p;
214 extern int next_mode_p;
215
216 #ifdef HAVE_SAVER_EXTENSION
217 int saver_ext_event_number = 0;
218 int saver_ext_error_number = 0;
219 #endif /* HAVE_SAVER_EXTENSION */
220
221 static time_t initial_delay;
222
223 extern Atom XA_VROOT, XA_XSETROOT_ID;
224 extern Atom XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID;
225
226 static Atom XA_SCREENSAVER;
227 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
228 static Atom XA_EXIT, XA_RESTART, XA_DEMO, XA_LOCK;
229
230 #ifdef NO_MOTIF /* kludge */
231 Bool demo_mode_p = 0;
232 Bool dbox_up_p = 0;
233 #endif
234
235 \f
236 #ifdef NO_DEMO_MODE
237 # define demo_mode() abort()
238 #else
239 extern void demo_mode P((void));
240 #endif
241 \f
242 static XrmOptionDescRec options [] = {
243   { "-timeout",            ".timeout",          XrmoptionSepArg, 0 },
244   { "-cycle",              ".cycle",            XrmoptionSepArg, 0 },
245   { "-idelay",             ".initialDelay",     XrmoptionSepArg, 0 },
246   { "-nice",               ".nice",             XrmoptionSepArg, 0 },
247   { "-visual",             ".visualID",         XrmoptionSepArg, 0 },
248   { "-lock-timeout",       ".lockTimeout",      XrmoptionSepArg, 0 },
249   { "-install",            ".installColormap",  XrmoptionNoArg, "on" },
250   { "-no-install",         ".installColormap",  XrmoptionNoArg, "off" },
251   { "-verbose",            ".verbose",          XrmoptionNoArg, "on" },
252   { "-silent",             ".verbose",          XrmoptionNoArg, "off" },
253   { "-xidle-extension",    ".xidleExtension",   XrmoptionNoArg, "on" },
254   { "-no-xidle-extension", ".xidleExtension",   XrmoptionNoArg, "off" },
255   { "-ss-extension",       ".saverExtension",   XrmoptionNoArg, "on" },
256   { "-no-ss-extension",    ".saverExtension",   XrmoptionNoArg, "off" },
257   { "-lock",               ".lock",             XrmoptionNoArg, "on" },
258   { "-no-lock",            ".lock",             XrmoptionNoArg, "off" }
259 };
260
261 static char *defaults[] = {
262 #include "XScreenSaver.ad.h"
263  0
264 };
265
266 static void
267 do_help P((void))
268 {
269   printf ("\
270 xscreensaver %s, copyright (c) 1991-1995 by Jamie Zawinski <jwz@netscape.com>.\n\
271 The standard Xt command-line options are accepted; other options include:\n\
272 \n\
273     -timeout <minutes>         When the screensaver should activate.\n\
274     -cycle <minutes>           How long to let each hack run.\n\
275     -idelay <seconds>          How long to sleep before startup.\n\
276     -visual <id-or-class>      Which X visual to run on.\n\
277     -demo                      Enter interactive demo mode on startup.\n\
278     -install                   Install a private colormap.\n\
279     -no-install                Don't.\n\
280     -verbose                   Be loud.\n\
281     -silent                    Don't.\n\
282     -xidle-extension           Use the R5 XIdle server extension.\n\
283     -no-xidle-extension        Don't.\n\
284     -ss-extension              Use the R6 MIT-SCREEN-SAVER server extension.\n\
285     -no-ss-extension           Don't.\n\
286     -lock                      Require a password before deactivating.\n\
287     -no-lock                   Don't.\n\
288     -lock-timeout <minutes>    Grace period before locking; default 0.\n\
289     -help                      This message.\n\
290 \n\
291 Use the `xscreensaver-command' program to control a running screensaver.\n\
292 \n\
293 The *programs, *colorPrograms, and *monoPrograms resources control which\n\
294 graphics demos will be launched by the screensaver.  See the man page for\n\
295 more details.\n\n",
296           screensaver_version);
297
298 #ifdef NO_LOCKING
299   printf ("Support for locking was not enabled at compile-time.\n");
300 #endif
301 #ifdef NO_DEMO_MODE
302   printf ("Support for demo mode was not enabled at compile-time.\n");
303 #endif
304 #if !defined(HAVE_XIDLE_EXTENSION) && !defined(HAVE_SAVER_EXTENSION)
305   printf ("Support for the XIDLE and MIT-SCREEN-SAVER server extensions\
306  was not\nenabled at compile-time.\n");
307 #endif /* !HAVE_XIDLE_EXTENSION && !HAVE_SAVER_EXTENSION */
308
309   fflush (stdout);
310   exit (1);
311 }
312
313
314 static void
315 get_screenhacks P((void))
316 {
317   char *data[3];
318   int i, hacks_size = 10;
319
320   data[0] = get_string_resource ("programs", "Programs");
321   data[1] = ((CellsOfScreen (screen) <= 2)
322              ? get_string_resource ("monoPrograms", "MonoPrograms")
323              : get_string_resource ("colorPrograms", "ColorPrograms"));
324   data[2] = 0;
325   if (! data[0]) data[0] = data[1], data[1] = 0;
326
327   screenhacks = (char **) malloc (sizeof (char *) * hacks_size);
328   screenhacks_count = 0;
329
330   for (i = 0; data[i]; i++)
331     {
332       int j = 0;
333       char *d = data [i];
334       int size = strlen (d);
335       while (j < size)
336         {
337           int end, start = j;
338           if (d[j] == ' ' || d[j] == '\t' || d[j] == '\n' || d[j] == 0)
339             {
340               j++;
341               continue;
342             }
343           if (hacks_size <= screenhacks_count)
344             screenhacks = (char **) realloc (screenhacks,
345                                              (hacks_size = hacks_size * 2) *
346                                              sizeof (char *));
347           screenhacks [screenhacks_count++] = d + j;
348           while (d[j] != 0 && d[j] != '\n')
349             j++;
350           end = j;
351           while (j > start && (d[j-1] == ' ' || d[j-1] == '\t'))
352             j--;
353           d[j] = 0;
354           j = end + 1;
355         }
356     }
357
358   /* shrink all whitespace to one space, for the benefit of the "demo"
359      mode display.  We only do this when we can easily tell that the
360      whitespace is not significant (no shell metachars).
361    */
362   for (i = 0; i < screenhacks_count; i++)
363     {
364       char *s = screenhacks [i];
365       char *s2;
366       int L = strlen (s);
367       int j, k;
368       for (j = 0; j < L; j++)
369         {
370           switch (s[j])
371             {
372             case '\'': case '"': case '`': case '\\':
373               goto DONE;
374             case '\t':
375               s[j] = ' ';
376             case ' ':
377               k = 0;
378               for (s2 = s+j+1; *s2 == ' ' || *s2 == '\t'; s2++)
379                 k++;
380               if (k > 0)
381                 for (s2 = s + j + 1; *s2; s2++)
382                   s2 [0] = s2 [k];
383               break;
384             }
385         }
386     DONE:
387       ;
388     }
389
390   if (screenhacks_count)
391     {
392       /* Shrink down the screenhacks array to be only as big as it needs to.
393          This doesn't really matter at all. */
394       screenhacks = (char **)
395         realloc (screenhacks, ((screenhacks_count + 1) * sizeof(char *)));
396       screenhacks [screenhacks_count] = 0;
397     }
398   else
399     {
400       free (screenhacks);
401       screenhacks = 0;
402     }
403 }
404
405
406 static void
407 get_resources P((void))
408 {
409   /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
410   visual          = get_visual_resource (dpy, "visualID", "VisualID");
411   timeout         = 1000 * get_minutes_resource ("timeout", "Time");
412   cycle           = 1000 * get_minutes_resource ("cycle",   "Time");
413   lock_timeout    = 1000 * get_minutes_resource ("lockTimeout", "Time");
414   nice_inferior   = get_integer_resource ("nice", "Nice");
415   verbose_p       = get_boolean_resource ("verbose", "Boolean");
416   lock_p          = get_boolean_resource ("lock", "Boolean");
417   install_cmap_p  = get_boolean_resource ("installColormap", "Boolean");
418   fade_p          = get_boolean_resource ("fade", "Boolean");
419   unfade_p        = get_boolean_resource ("unfade", "Boolean");
420   fade_seconds    = get_seconds_resource ("fadeSeconds", "Time");
421   fade_ticks      = get_integer_resource ("fadeTicks", "Integer");
422   shell           = get_string_resource ("bourneShell", "BourneShell");
423   initial_delay   = get_seconds_resource ("initialDelay", "Time");
424   pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
425   notice_events_timeout = 1000 * get_seconds_resource ("windowCreationTimeout",
426                                                        "Time");
427 #ifndef NO_LOCKING
428   passwd_timeout  = 1000 * get_seconds_resource ("passwdTimeout", "Time");
429   if (passwd_timeout == 0) passwd_timeout = 30000;
430 #endif
431   if (timeout < 10000) timeout = 10000;
432   if (cycle != 0 && cycle < 2000) cycle = 2000;
433   if (pointer_timeout == 0) pointer_timeout = 5000;
434   if (notice_events_timeout == 0) notice_events_timeout = 10000;
435   if (fade_seconds == 0 || fade_ticks == 0) fade_p = False;
436   if (! fade_p) unfade_p = False;
437
438   visual_depth = get_visual_depth (dpy, visual);
439
440   if (visual_depth <= 1 || CellsOfScreen (screen) <= 2)
441     install_cmap_p = False;
442
443 #ifdef NO_LOCKING
444   locking_disabled_p = True;
445   nolock_reason = "not compiled with locking support";
446   if (lock_p)
447     {
448       lock_p = False;
449       fprintf (stderr, "%s: %snot compiled with support for locking.\n",
450                progname, (verbose_p ? "## " : ""));
451     }
452 #else  /* ! NO_LOCKING */
453   if (lock_p && locking_disabled_p)
454     {
455       fprintf (stderr, "%s: %slocking is disabled (%s).\n", progname,
456                (verbose_p ? "## " : ""), nolock_reason);
457       lock_p = False;
458     }
459 #endif /* ! NO_LOCKING */
460
461   /* don't set use_xidle_extension unless it is explicitly specified */
462   if (get_string_resource ("xidleExtension", "Boolean"))
463     use_xidle_extension = get_boolean_resource ("xidleExtension", "Boolean");
464   else
465 #ifdef HAVE_XIDLE_EXTENSION     /* pick a default */
466     use_xidle_extension = True;
467 #else  /* !HAVE_XIDLE_EXTENSION */
468     use_xidle_extension = False;
469 #endif /* !HAVE_XIDLE_EXTENSION */
470
471   /* don't set use_saver_extension unless it is explicitly specified */
472   if (get_string_resource ("saverExtension", "Boolean"))
473     use_saver_extension = get_boolean_resource ("saverExtension", "Boolean");
474   else
475 #ifdef HAVE_SAVER_EXTENSION     /* pick a default */
476     use_saver_extension = True;
477 #else  /* !HAVE_SAVER_EXTENSION */
478     use_saver_extension = False;
479 #endif /* !HAVE_SAVER_EXTENSION */
480
481
482   get_screenhacks ();
483 }
484
485 char *
486 timestring P((void))
487 {
488   long now = time ((time_t *) 0);
489   char *str = (char *) ctime (&now);
490   char *nl = (char *) strchr (str, '\n');
491   if (nl) *nl = 0; /* take off that dang newline */
492   return str;
493 }
494
495 #ifdef NO_SETUID
496 # define hack_uid()
497 # define hack_uid_warn()
498 #else /* !NO_SETUID */
499 extern void hack_uid P((void));
500 extern void hack_uid_warn P((void));
501 #endif /* NO_SETUID */
502
503
504 #ifndef NO_LOCKING
505 extern Bool unlock_p P((Widget));
506 extern Bool lock_init P((void));
507 #endif
508
509 static void initialize P((int argc, char **argv));
510 static void main_loop P((void));
511
512 void
513 main (argc, argv)
514      int argc;
515      char **argv;
516 {
517   initialize (argc, argv);
518   main_loop ();
519 }
520
521
522 static int
523 saver_ehandler (dpy, error)
524      Display *dpy;
525      XErrorEvent *error;
526 {
527   fprintf (real_stderr, "\nX error in %s:\n", progname);
528   if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
529     exit (-1);
530   else
531     fprintf (real_stderr, " (nonfatal.)\n");
532   return 0;
533 }
534
535 static void
536 #if __STDC__
537 initialize_connection (int argc, char **argv)
538 #else
539 initialize_connection (argc, argv)
540      int argc;
541      char **argv;
542 #endif
543 {
544   toplevel_shell = XtAppInitialize (&app, progclass,
545                                     options, XtNumber (options),
546                                     &argc, argv, defaults, 0, 0);
547
548   dpy = XtDisplay (toplevel_shell);
549   screen = XtScreen (toplevel_shell);
550   db = XtDatabase (dpy);
551   XtGetApplicationNameAndClass (dpy, &progname, &progclass);
552
553   if (argc == 2 && !strcmp (argv[1], "-help"))
554     do_help ();
555   else if (argc > 1)
556     {
557       fprintf (stderr, "%s: unknown option %s\n", progname, argv [1]);
558       exit (1);
559     }
560   get_resources ();
561   hack_uid_warn ();
562   hack_environment ();
563   XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
564   XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
565   XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION", False);
566   XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
567   XA_XSETROOT_ID = XInternAtom (dpy, "_XSETROOT_ID", False);
568   XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
569   XA_DEACTIVATE = XInternAtom (dpy, "DEACTIVATE", False);
570   XA_RESTART = XInternAtom (dpy, "RESTART", False);
571   XA_CYCLE = XInternAtom (dpy, "CYCLE", False);
572   XA_NEXT = XInternAtom (dpy, "NEXT", False);
573   XA_PREV = XInternAtom (dpy, "PREV", False);
574   XA_EXIT = XInternAtom (dpy, "EXIT", False);
575   XA_DEMO = XInternAtom (dpy, "DEMO", False);
576   XA_LOCK = XInternAtom (dpy, "LOCK", False);
577 }
578
579 #ifdef HAVE_SAVER_EXTENSION
580
581 static int
582 ignore_all_errors_ehandler (dpy, error)
583      Display *dpy;
584      XErrorEvent *error;
585 {
586   return 0;
587 }
588
589 static void
590 init_saver_extension ()
591 {
592   XID kill_id;
593   Atom kill_type;
594   Window root = RootWindowOfScreen (screen);
595   XScreenSaverInfo *info;
596   Pixmap blank_pix = XCreatePixmap (dpy, root, 1, 1, 1);
597
598   /* Kill off the old MIT-SCREEN-SAVER client if there is one.
599      This tends to generate X errors, though (possibly due to a bug
600      in the server extension itself?) so just ignore errors here. */
601   if (XScreenSaverGetRegistered (dpy, XScreenNumberOfScreen (screen),
602                                  &kill_id, &kill_type)
603       && kill_id != blank_pix)
604     {
605       int (*old_handler) ();
606       old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
607       XKillClient (dpy, kill_id);
608       XSync (dpy, False);
609       XSetErrorHandler (old_handler);
610     }
611
612   XScreenSaverSelectInput (dpy, root, ScreenSaverNotifyMask);
613
614   XScreenSaverRegister (dpy, XScreenNumberOfScreen (screen),
615                         (XID) blank_pix, XA_PIXMAP);
616   info = XScreenSaverAllocInfo ();
617
618 #if 0
619   /* #### I think this is noticing that the saver is on, and replacing it
620      without turning it off first. */
621   saver = info->window;
622   if (info->state == ScreenSaverOn)
623     {
624       if (info->kind != ScreenSaverExternal) 
625         {
626           XResetScreenSaver (display);
627           XActivateScreenSaver (display);
628         }
629       StartSaver ();
630     }
631 #endif
632 }
633 #endif /* HAVE_SAVER_EXTENSION */
634
635
636 extern void init_sigchld P((void));
637
638 static void
639 initialize (argc, argv)
640      int argc;
641      char **argv;
642 {
643   Bool initial_demo_mode_p = False;
644   screensaver_version = (char *) malloc (5);
645   memcpy (screensaver_version, screensaver_id + 17, 4);
646   screensaver_version [4] = 0;
647   progname = argv[0]; /* reset later; this is for the benefit of lock_init() */
648
649 #ifdef NO_LOCKING
650   locking_disabled_p = True;
651   nolock_reason = "not compiled with locking support";
652 #else
653   locking_disabled_p = False;
654
655 #ifdef SCO
656   set_auth_parameters(argc, argv);
657 #endif
658
659   if (! lock_init ())   /* before hack_uid() for proper permissions */
660     {
661       locking_disabled_p = True;
662       nolock_reason = "error getting password";
663     }
664 #endif
665
666   hack_uid ();
667   progclass = "XScreenSaver";
668
669   /* remove -demo switch before saving argv */
670   {
671     int i;
672     for (i = 1; i < argc; i++)
673       while (!strcmp ("-demo", argv [i]))
674         {
675           int j;
676           initial_demo_mode_p = True;
677           for (j = i; j < argc; j++)
678             argv [j] = argv [j+1];
679           argv [j] = 0;
680           argc--;
681           if (argc <= i) break;
682         }
683   }
684   save_argv (argc, argv);
685   initialize_connection (argc, argv);
686   ensure_no_screensaver_running ();
687
688   if (verbose_p)
689     printf ("\
690 %s %s, copyright (c) 1991-1995 by Jamie Zawinski <jwz@netscape.com>.\n\
691  pid = %d.\n", progname, screensaver_version, getpid ());
692   ensure_no_screensaver_running ();
693
694   demo_mode_p = initial_demo_mode_p;
695   screensaver_window = 0;
696   cursor = 0;
697   initialize_screensaver_window ();
698   srandom ((int) time ((time_t *) 0));
699   cycle_id = 0;
700   lock_id = 0;
701   locked_p = False;
702
703   if (use_saver_extension)
704     {
705 #ifdef HAVE_SAVER_EXTENSION
706       if (! XScreenSaverQueryExtension (dpy,
707                                         &saver_ext_event_number,
708                                         &saver_ext_error_number))
709         {
710           fprintf (stderr,
711          "%s: %sdisplay %s does not support the MIT-SCREEN-SAVER extension.\n",
712                    progname, (verbose_p ? "## " : ""), DisplayString (dpy));
713           use_saver_extension = False;
714         }
715       else if (use_xidle_extension)
716         {
717           fprintf (stderr,
718          "%s: %sMIT-SCREEN-SAVER extension used instead of XIDLE extension.\n",
719                    progname, (verbose_p ? "## " : ""));
720           use_xidle_extension = False;
721         }
722 #else  /* !HAVE_SAVER_EXTENSION */
723       fprintf (stderr,
724        "%s: %snot compiled with support for the MIT-SCREEN-SAVER extension.\n",
725                progname, (verbose_p ? "## " : ""));
726       use_saver_extension = False;
727 #endif /* !HAVE_SAVER_EXTENSION */
728     }
729
730   if (use_xidle_extension)
731     {
732 #ifdef HAVE_XIDLE_EXTENSION
733       int first_event, first_error;
734       if (! XidleQueryExtension (dpy, &first_event, &first_error))
735         {
736           fprintf (stderr,
737                    "%s: %sdisplay %s does not support the XIdle extension.\n",
738                    progname, (verbose_p ? "## " : ""), DisplayString (dpy));
739           use_xidle_extension = False;
740         }
741 #else  /* !HAVE_XIDLE_EXTENSION */
742       fprintf (stderr, "%s: %snot compiled with support for XIdle.\n",
743                progname, (verbose_p ? "## " : ""));
744       use_xidle_extension = False;
745 #endif /* !HAVE_XIDLE_EXTENSION */
746     }
747
748   init_sigchld ();
749
750   disable_builtin_screensaver ();
751
752 #ifdef HAVE_SAVER_EXTENSION
753   if (use_saver_extension)
754     init_saver_extension ();
755 #endif /* HAVE_SAVER_EXTENSION */
756
757   if (verbose_p && use_saver_extension)
758     fprintf (stderr, "%s: using MIT-SCREEN-SAVER server extension.\n",
759              progname);
760   if (verbose_p && use_xidle_extension)
761     fprintf (stderr, "%s: using XIdle server extension.\n",
762              progname);
763
764   initialize_stderr ();
765   XSetErrorHandler (saver_ehandler);
766
767   if (initial_demo_mode_p)
768     /* If the user wants demo mode, don't wait around before doing it. */
769     initial_delay = 0;
770
771   if (!use_xidle_extension && !use_saver_extension)
772     {
773       if (initial_delay)
774         {
775           if (verbose_p)
776             {
777               printf ("%s: waiting for %d second%s...", progname,
778                       (int) initial_delay, (initial_delay == 1 ? "" : "s"));
779               fflush (stdout);
780             }
781           sleep (initial_delay);
782           if (verbose_p)
783             printf (" done.\n");
784         }
785       if (verbose_p)
786         {
787           printf ("%s: selecting events on extant windows...", progname);
788           fflush (stdout);
789         }
790       notice_events_timer ((XtPointer)
791                            RootWindowOfScreen (XtScreen (toplevel_shell)),
792                            0);
793       if (verbose_p)
794         printf (" done.\n");
795     }
796 }
797
798
799 extern void suspend_screenhack P((Bool suspend_p));
800
801 static void
802 main_loop ()
803 {
804   while (1)
805     {
806       if (! demo_mode_p)
807         sleep_until_idle (True);
808
809       if (demo_mode_p)
810         demo_mode ();
811       else
812         {
813           if (verbose_p)
814             printf ("%s: user is idle; waking up at %s.\n", progname,
815                     timestring());
816           blank_screen ();
817           spawn_screenhack (True);
818           if (cycle)
819             cycle_id = XtAppAddTimeOut (app, cycle,
820                                         (XtTimerCallbackProc)cycle_timer, 0);
821
822 #ifndef NO_LOCKING
823           if (lock_p && lock_timeout == 0)
824             locked_p = True;
825           if (lock_p && !locked_p)
826             /* locked_p might be true already because of ClientMessage */
827             lock_id = XtAppAddTimeOut (app,lock_timeout,
828                                        (XtTimerCallbackProc)
829                                        activate_lock_timer,0);
830 #endif
831
832         PASSWD_INVALID:
833
834           sleep_until_idle (False); /* until not idle */
835
836 #ifndef NO_LOCKING
837           if (locked_p)
838             {
839               Bool val;
840               if (locking_disabled_p) abort ();
841               dbox_up_p = True;
842
843               /* We used to ungrab the keyboard here, before calling unlock_p()
844                  to pop up the dialog box.  This left the keyboard ungrabbed
845                  for a small window, during an insecure state.  Bennett Todd
846                  was seeing the bahavior that, when the load was high, he could
847                  actually get characters through to a shell under the saver
848                  window (he accidentally typed his password there...)
849
850                  So the ungrab has been moved down into pop_passwd_dialog()
851                  just after the server is grabbed, closing this window
852                  entirely.
853                */
854               /* ungrab_keyboard_and_mouse (); */
855
856               suspend_screenhack (True);
857               XUndefineCursor (dpy, screensaver_window);
858               if (verbose_p)
859                 printf ("%s: prompting for password.\n", progname);
860               val = unlock_p (toplevel_shell);
861               if (verbose_p && val == False)
862                 printf ("%s: password incorrect!\n", progname);
863               dbox_up_p = False;
864               XDefineCursor (dpy, screensaver_window, cursor);
865               suspend_screenhack (False);
866
867               /* I think this grab is now redundant, but it shouldn't hurt. */
868               grab_keyboard_and_mouse ();
869
870               if (! val)
871                 goto PASSWD_INVALID;
872               locked_p = False;
873             }
874 #endif
875           unblank_screen ();
876           kill_screenhack ();
877           if (cycle_id)
878             {
879               XtRemoveTimeOut (cycle_id);
880               cycle_id = 0;
881             }
882 #ifndef NO_LOCKING
883           if (lock_id)
884             {
885               XtRemoveTimeOut (lock_id);
886               lock_id = 0;
887             }
888 #endif
889           if (verbose_p)
890             printf ("%s: user is active; going to sleep at %s.\n", progname,
891                     timestring ());
892         }
893     }
894 }
895
896 \f
897
898 Bool
899 handle_clientmessage (event, until_idle_p)
900      XEvent *event;
901      Bool until_idle_p;
902 {
903   Atom type = 0;
904   if (event->xclient.message_type != XA_SCREENSAVER)
905     {
906       char *str;
907       str = XGetAtomName (dpy, event->xclient.message_type);
908       fprintf (stderr, "%s: %sunrecognised ClientMessage type %s received\n",
909                progname, (verbose_p ? "## " : ""),
910                (str ? str : "(null)"));
911       if (str) XFree (str);
912       return False;
913     }
914   if (event->xclient.format != 32)
915     {
916       fprintf (stderr, "%s: %sClientMessage of format %d received, not 32\n",
917                progname, (verbose_p ? "## " : ""), event->xclient.format);
918       return False;
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           if (use_saver_extension)
928             {
929               XForceScreenSaver (dpy, ScreenSaverActive);
930               return False;
931             }
932           else
933             {
934               return True;
935             }
936         }
937       fprintf (stderr,
938                "%s: %sClientMessage ACTIVATE received while already active.\n",
939                progname, (verbose_p ? "## " : ""));
940     }
941   else if (type == XA_DEACTIVATE)
942     {
943       if (! until_idle_p)
944         {
945           if (verbose_p)
946             printf ("%s: DEACTIVATE ClientMessage received.\n", progname);
947           if (use_saver_extension)
948             {
949               XForceScreenSaver (dpy, ScreenSaverReset);
950               return False;
951             }
952           else
953             {
954               return True;
955             }
956         }
957       fprintf (stderr,
958                "%s: %sClientMessage DEACTIVATE received while inactive.\n",
959                progname, (verbose_p ? "## " : ""));
960     }
961   else if (type == XA_CYCLE)
962     {
963       if (! until_idle_p)
964         {
965           if (verbose_p)
966             printf ("%s: CYCLE ClientMessage received.\n", progname);
967           if (cycle_id)
968             XtRemoveTimeOut (cycle_id);
969           cycle_id = 0;
970           cycle_timer (0, 0);
971           return False;
972         }
973       fprintf (stderr,
974                "%s: %sClientMessage CYCLE received while inactive.\n",
975                progname, (verbose_p ? "## " : ""));
976     }
977   else if (type == XA_NEXT || type == XA_PREV)
978     {
979       if (verbose_p)
980         printf ("%s: %s ClientMessage received.\n", progname,
981                 (type == XA_NEXT ? "NEXT" : "PREV"));
982       next_mode_p = 1 + (type == XA_PREV);
983
984       if (! until_idle_p)
985         {
986           if (cycle_id)
987             XtRemoveTimeOut (cycle_id);
988           cycle_id = 0;
989           cycle_timer (0, 0);
990         }
991       else
992         return True;
993     }
994   else if (type == XA_EXIT)
995     {
996       /* Ignore EXIT message if the screen is locked. */
997       if (until_idle_p || !locked_p)
998         {
999           if (verbose_p)
1000             printf ("%s: EXIT ClientMessage received.\n", progname);
1001           if (! until_idle_p)
1002             {
1003               unblank_screen ();
1004               kill_screenhack ();
1005               XSync (dpy, False);
1006             }
1007           exit (0);
1008         }
1009       else
1010         fprintf (stderr, "%s: %sEXIT ClientMessage received while locked.\n",
1011                  progname, (verbose_p ? "## " : ""));
1012     }
1013   else if (type == XA_RESTART)
1014     {
1015       /* The RESTART message works whether the screensaver is active or not,
1016          unless the screen is locked, in which case it doesn't work.
1017        */
1018       if (until_idle_p || !locked_p)
1019         {
1020           if (verbose_p)
1021             printf ("%s: RESTART ClientMessage received.\n", progname);
1022           if (! until_idle_p)
1023             {
1024               unblank_screen ();
1025               kill_screenhack ();
1026               XSync (dpy, False);
1027             }
1028           restart_process ();
1029         }
1030       else
1031         fprintf(stderr, "%s: %sRESTART ClientMessage received while locked.\n",
1032                 progname, (verbose_p ? "## " : ""));
1033     }
1034   else if (type == XA_DEMO)
1035     {
1036 #ifdef NO_DEMO_MODE
1037       fprintf (stderr,
1038                "%s: %snot compiled with support for DEMO mode\n",
1039                progname, (verbose_p ? "## " : ""));
1040 #else
1041       if (until_idle_p)
1042         {
1043           if (verbose_p)
1044             printf ("%s: DEMO ClientMessage received.\n", progname);
1045           demo_mode_p = True;
1046           return True;
1047         }
1048       fprintf (stderr,
1049                "%s: %sDEMO ClientMessage received while active.\n",
1050                progname, (verbose_p ? "## " : ""));
1051 #endif
1052     }
1053   else if (type == XA_LOCK)
1054     {
1055 #ifdef NO_LOCKING
1056       fprintf (stderr, "%s: %snot compiled with support for LOCK mode\n",
1057                progname, (verbose_p ? "## " : ""));
1058 #else
1059       if (locking_disabled_p)
1060         fprintf (stderr,
1061                "%s: %sLOCK ClientMessage received, but locking is disabled.\n",
1062                  progname, (verbose_p ? "## " : ""));
1063       else if (locked_p)
1064         fprintf (stderr,
1065                "%s: %sLOCK ClientMessage received while already locked.\n",
1066                  progname, (verbose_p ? "## " : ""));
1067       else
1068         {
1069           locked_p = True;
1070           if (verbose_p) 
1071             printf ("%s: LOCK ClientMessage received;%s locking.\n",
1072                     progname, until_idle_p ? " activating and" : "");
1073
1074           if (lock_id)  /* we're doing it now, so lose the timeout */
1075             {
1076               XtRemoveTimeOut (lock_id);
1077               lock_id = 0;
1078             }
1079
1080           if (until_idle_p)
1081             {
1082               if (use_saver_extension)
1083                 {
1084                   XForceScreenSaver (dpy, ScreenSaverActive);
1085                   return False;
1086                 }
1087               else
1088                 {
1089                   return True;
1090                 }
1091             }
1092         }
1093 #endif
1094     }
1095   else
1096     {
1097       char *str;
1098       str = (type ? XGetAtomName(dpy, type) : 0);
1099       if (str)
1100         fprintf (stderr,
1101                  "%s: %sunrecognised screensaver ClientMessage %s received\n",
1102                  progname, (verbose_p ? "## " : ""), str);
1103       else
1104         fprintf (stderr,
1105                 "%s: %sunrecognised screensaver ClientMessage 0x%x received\n",
1106                  progname, (verbose_p ? "## " : ""),
1107                  (unsigned int) event->xclient.data.l[0]);
1108       if (str) XFree (str);
1109     }
1110   return False;
1111 }