http://packetstormsecurity.org/UNIX/admin/xscreensaver-3.34.tar.gz
[xscreensaver] / driver / xscreensaver.c
index d0efd9b0359f7942e5c2bef28f37c4e81c8c0ba8..08d98e4f82d3474d4f0628e451f593098528a3de 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1991-1998 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1991-2001 Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
@@ -59,7 +59,7 @@
  *   This program accepts ClientMessages of type SCREENSAVER; these messages
  *   may contain the atom ACTIVATE or DEACTIVATE, meaning to turn the 
  *   screensaver on or off now, regardless of the idleness of the user,
- *   and a few other things.  The included "xscreensaver_command" program
+ *   and a few other things.  The included "xscreensaver-command" program
  *   sends these messsages.
  *
  *   If we don't have the XIdle, MIT-SCREEN-SAVER, or SGI SCREEN_SAVER
@@ -70,8 +70,9 @@
  *   KeyPress on windows which don't select them, because that would
  *   interfere with event propagation.  This will break if any program
  *   changes its event mask to contain KeyRelease or PointerMotion more than
- *   30 seconds after creating the window, but that's probably pretty rare.
- *   
+ *   30 seconds after creating the window, but such programs do not seem to
+ *   occur in nature (I've never seen it happen in all these years.)
+ *
  *   The reason that we can't select KeyPresses on windows that don't have
  *   them already is that, when dispatching a KeyPress event, X finds the
  *   lowest (leafmost) window in the hierarchy on which *any* client selects
  *       to keep your emacs window alive even when xscreensaver has grabbed.
  *     - Go read the code related to `debug_p'.
  *     - You probably can't set breakpoints in functions that are called on
- *       the other side of a call to fork() -- if your clients are dying 
- *       with signal 5, Trace/BPT Trap, you're losing in this way.
+ *       the other side of a call to fork() -- if your subprocesses are
+ *       dying with signal 5, Trace/BPT Trap, you're losing in this way.
  *     - If you aren't using a server extension, don't leave this stopped
  *       under the debugger for very long, or the X input buffer will get
  *       huge because of the keypress events it's selecting for.  This can
  *       make your X server wedge with "no more input buffers."
- *       
+ *
  * ======================================================================== */
 
 #ifdef HAVE_CONFIG_H
 #include <X11/StringDefs.h>
 #include <X11/Shell.h>
 #include <X11/Xos.h>
+#include <netdb.h>     /* for gethostbyname() */
 #ifdef HAVE_XMU
 # ifndef VMS
 #  include <X11/Xmu/Error.h>
 #endif /* !HAVE_XMU */
 
 #ifdef HAVE_XIDLE_EXTENSION
-#include <X11/extensions/xidle.h>
+# include <X11/extensions/xidle.h>
 #endif /* HAVE_XIDLE_EXTENSION */
 
 #include "xscreensaver.h"
 #include "yarandom.h"
 #include "resources.h"
 #include "visual.h"
+#include "usleep.h"
 
 saver_info *global_si_kludge = 0;      /* I hate C so much... */
 
@@ -160,8 +163,9 @@ XrmDatabase db = 0;
 
 static Atom XA_SCREENSAVER_RESPONSE;
 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
-static Atom XA_EXIT, XA_RESTART, XA_LOCK, XA_SELECT;
-Atom XA_DEMO, XA_PREFS;
+static Atom XA_RESTART, XA_SELECT;
+static Atom XA_THROTTLE, XA_UNTHROTTLE;
+Atom XA_DEMO, XA_PREFS, XA_EXIT, XA_LOCK, XA_BLANK;
 
 \f
 static XrmOptionDescRec options [] = {
@@ -187,6 +191,8 @@ static XrmOptionDescRec options [] = {
   { "-no-mit-extension",   ".mitSaverExtension",XrmoptionNoArg, "off" },
   { "-sgi-extension",     ".sgiSaverExtension",XrmoptionNoArg, "on" },
   { "-no-sgi-extension",   ".sgiSaverExtension",XrmoptionNoArg, "off" },
+  { "-proc-interrupts",           ".procInterrupts",   XrmoptionNoArg, "on" },
+  { "-no-proc-interrupts", ".procInterrupts",  XrmoptionNoArg, "off" },
   { "-splash",            ".splash",           XrmoptionNoArg, "on" },
   { "-no-splash",         ".splash",           XrmoptionNoArg, "off" },
   { "-nosplash",          ".splash",           XrmoptionNoArg, "off" },
@@ -213,7 +219,7 @@ do_help (saver_info *si)
   fflush (stdout);
   fflush (stderr);
   fprintf (stdout, "\
-xscreensaver %s, copyright (c) 1991-1998 by Jamie Zawinski <jwz@jwz.org>\n\
+xscreensaver %s, copyright (c) 1991-2001 by Jamie Zawinski <jwz@jwz.org>\n\
 The standard Xt command-line options are accepted; other options include:\n\
 \n\
     -timeout <minutes>       When the screensaver should activate.\n\
@@ -286,14 +292,27 @@ int
 saver_ehandler (Display *dpy, XErrorEvent *error)
 {
   saver_info *si = global_si_kludge;   /* I hate C so much... */
+  int i;
+
+  if (!real_stderr) real_stderr = stderr;
 
   fprintf (real_stderr, "\n"
           "#######################################"
           "#######################################\n\n"
-          "%s: X Error!  PLEASE REPORT THIS BUG.\n\n"
-          "#######################################"
-          "#######################################\n\n",
+          "%s: X Error!  PLEASE REPORT THIS BUG.\n",
           blurb());
+
+  for (i = 0; i < si->nscreens; i++)
+    fprintf (real_stderr, "%s: screen %d: 0x%x, 0x%x, 0x%x\n",
+             blurb(), i,
+             RootWindowOfScreen (si->screens[i].screen),
+             si->screens[i].real_vroot,
+             si->screens[i].screensaver_window);
+
+  fprintf (real_stderr, "\n"
+          "#######################################"
+          "#######################################\n\n");
+
   if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
     {
       fprintf (real_stderr, "\n");
@@ -303,11 +322,27 @@ saver_ehandler (Display *dpy, XErrorEvent *error)
        }
       else
        {
-         fprintf(real_stderr,
-                 "%s: to dump a core file, re-run with `-sync'.\n"
-                 "%s: see http://www.jwz.org/xscreensaver/bugs.html\n"
-                 "\t\tfor bug reporting information.\n\n",
-                 blurb(), blurb());
+          fprintf (real_stderr,
+                   "#######################################"
+                   "#######################################\n\n");
+          fprintf (real_stderr,
+   "    If at all possible, please re-run xscreensaver with the command\n"
+   "    line arguments `-sync -verbose -no-capture', and reproduce this\n"
+   "    bug.  That will cause xscreensaver to dump a `core' file to the\n"
+   "    current directory.  Please include the stack trace from that core\n"
+   "    file in your bug report.  *DO NOT* mail the core file itself!\n"
+   "    That won't work.\n"
+   "\n"
+   "    http://www.jwz.org/xscreensaver/bugs.html explains how to create\n"
+   "    the most useful bug reports, and how to examine core files.\n"
+   "\n"
+   "    The more information you can provide, the better.  But please\n"
+   "    report this bug, regardless!\n"
+   "\n");
+          fprintf (real_stderr,
+                   "#######################################"
+                   "#######################################\n\n");
+
          saver_exit (si, -1, 0);
        }
     }
@@ -316,6 +351,68 @@ saver_ehandler (Display *dpy, XErrorEvent *error)
   return 0;
 }
 
+
+/* This error handler is used only while the X connection is being set up;
+   after we've got a connection, we don't use this handler again.  The only
+   reason for having this is so that we can present a more idiot-proof error
+   message than "cannot open display."
+ */
+static void 
+startup_ehandler (String name, String type, String class,
+                  String defalt,  /* one can't even spel properly
+                                     in this joke of a language */
+                  String *av, Cardinal *ac)
+{
+  char fmt[512];
+  String p[10];
+  saver_info *si = global_si_kludge;   /* I hate C so much... */
+  XrmDatabase *db = XtAppGetErrorDatabase(si->app);
+  *fmt = 0;
+  XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
+                            fmt, sizeof(fmt)-1, *db);
+
+  fprintf (stderr, "%s: ", blurb());
+
+  memset (p, 0, sizeof(p));
+  if (*ac > countof (p)) *ac = countof (p);
+  memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
+  fprintf (stderr, fmt,                /* Did I mention that I hate C? */
+           p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
+  fprintf (stderr, "\n");
+
+  describe_uids (si, stderr);
+
+  if (si->orig_uid && !strncmp (si->orig_uid, "root/", 5))
+    {
+      fprintf (stderr, "\n"
+          "%s: This is probably because you're logging in as root.  You\n"
+"              shouldn't log in as root: you should log in as a normal user,\n"
+"              and then `su' as needed.  If you insist on logging in as\n"
+"              root, you will have to turn off X's security features before\n"
+"              xscreensaver will work.\n"
+               "\n"
+"              Please read the manual and FAQ for more information:\n",
+               blurb());
+    }
+  else
+    {
+      fprintf (stderr, "\n"
+          "%s: Errors at startup are usually authorization problems.\n"
+"              But you're not logging in as root (good!) so something\n"
+"              else must be wrong.  Did you read the manual and the FAQ?\n",
+           blurb());
+    }
+
+  fprintf (stderr, "\n"
+          "              http://www.jwz.org/xscreensaver/faq.html\n"
+          "              http://www.jwz.org/xscreensaver/man.html\n"
+          "\n");
+
+  fflush (stderr);
+  fflush (stdout);
+  exit (1);
+}
+
 \f
 /* The zillions of initializations.
  */
@@ -355,21 +452,34 @@ set_version_string (saver_info *si, int *argc, char **argv)
 static void
 privileged_initialization (saver_info *si, int *argc, char **argv)
 {
+#ifndef NO_LOCKING
+  /* before hack_uid() for proper permissions */
+  lock_priv_init (*argc, argv, si->prefs.verbose_p);
+#endif /* NO_LOCKING */
+
+  hack_uid (si);
+}
+
+
+/* Figure out what locking mechanisms are supported.
+ */
+static void
+lock_initialization (saver_info *si, int *argc, char **argv)
+{
 #ifdef NO_LOCKING
   si->locking_disabled_p = True;
   si->nolock_reason = "not compiled with locking support";
 #else /* !NO_LOCKING */
-  si->locking_disabled_p = False;
-  if (! lock_init (*argc, argv)) /* before hack_uid() for proper permissions */
+
+  /* Finish initializing locking, now that we're out of privileged code. */
+  if (! lock_init (*argc, argv, si->prefs.verbose_p))
     {
       si->locking_disabled_p = True;
       si->nolock_reason = "error getting password";
     }
 #endif /* NO_LOCKING */
 
-#ifndef NO_SETUID
   hack_uid (si);
-#endif /* NO_SETUID */
 }
 
 
@@ -380,10 +490,27 @@ connect_to_server (saver_info *si, int *argc, char **argv)
 {
   Widget toplevel_shell;
 
+#ifdef HAVE_PUTENV
+  char *d = getenv ("DISPLAY");
+  if (!d || !*d)
+    {
+      char ndpy[] = "DISPLAY=:0.0";
+      /* if (si->prefs.verbose_p) */      /* sigh, too early to test this... */
+        fprintf (stderr,
+                 "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
+                 blurb(), ndpy+8);
+      if (putenv (ndpy))
+        abort ();
+    }
+#endif /* HAVE_PUTENV */
+
   XSetErrorHandler (saver_ehandler);
+
+  XtAppSetErrorMsgHandler (si->app, startup_ehandler);
   toplevel_shell = XtAppInitialize (&si->app, progclass,
                                    options, XtNumber (options),
                                    argc, argv, defaults, 0, 0);
+  XtAppSetErrorMsgHandler (si->app, 0);
 
   si->dpy = XtDisplay (toplevel_shell);
   si->prefs.db = XtDatabase (si->dpy);
@@ -398,7 +525,7 @@ connect_to_server (saver_info *si, int *argc, char **argv)
   XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False);
   XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False);
   XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False);
-  XA_SCREENSAVER_TIME = XInternAtom (si->dpy, "_SCREENSAVER_TIME", False);
+  XA_SCREENSAVER_STATUS = XInternAtom (si->dpy, "_SCREENSAVER_STATUS", False);
   XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE",
                                         False);
   XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False);
@@ -413,6 +540,9 @@ connect_to_server (saver_info *si, int *argc, char **argv)
   XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
   XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
   XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
+  XA_BLANK = XInternAtom (si->dpy, "BLANK", False);
+  XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
+  XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
 
   return toplevel_shell;
 }
@@ -505,7 +635,7 @@ print_banner (saver_info *si)
 
   if (p->verbose_p)
     fprintf (stderr,
-            "%s %s, copyright (c) 1991-1998 "
+            "%s %s, copyright (c) 1991-2001 "
             "by Jamie Zawinski <jwz@jwz.org>.\n",
             progname, si->version);
 
@@ -561,7 +691,7 @@ print_banner (saver_info *si)
 
 
 /* Examine all of the display's screens, and populate the `saver_screen_info'
-   structures.
+   structures.  Make sure this is called after hack_environment() sets $PATH.
  */
 static void
 initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
@@ -588,6 +718,9 @@ initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
       ssi->current_visual = ssi->default_visual;
       ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
 
+      /* Execute a subprocess to find the GL visual. */
+      ssi->best_gl_visual = get_best_gl_visual (ssi);
+
       if (ssi == si->default_screen)
        /* Since this is the default screen, use the one already created. */
        ssi->toplevel_shell = toplevel_shell;
@@ -617,7 +750,11 @@ initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
        }
     }
 
-  si->prefs.fading_possible_p = found_any_writable_cells;
+  si->fading_possible_p = found_any_writable_cells;
+
+#ifdef HAVE_XF86VMODE_GAMMA
+  si->fading_possible_p = True;  /* if we can gamma fade, go for it */
+#endif
 }
 
 
@@ -632,6 +769,13 @@ initialize_server_extensions (saver_info *si)
   Bool server_has_xidle_extension_p = False;
   Bool server_has_sgi_saver_extension_p = False;
   Bool server_has_mit_saver_extension_p = False;
+  Bool system_has_proc_interrupts_p = False;
+  const char *piwhy = 0;
+
+  si->using_xidle_extension = p->use_xidle_extension;
+  si->using_sgi_saver_extension = p->use_sgi_saver_extension;
+  si->using_mit_saver_extension = p->use_mit_saver_extension;
+  si->using_proc_interrupts = p->use_proc_interrupts;
 
 #ifdef HAVE_XIDLE_EXTENSION
   server_has_xidle_extension_p = query_xidle_extension (si);
@@ -642,22 +786,25 @@ initialize_server_extensions (saver_info *si)
 #ifdef HAVE_MIT_SAVER_EXTENSION
   server_has_mit_saver_extension_p = query_mit_saver_extension (si);
 #endif
+#ifdef HAVE_PROC_INTERRUPTS
+  system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
+#endif
 
   if (!server_has_xidle_extension_p)
-    p->use_xidle_extension = False;
+    si->using_xidle_extension = False;
   else if (p->verbose_p)
     {
-      if (p->use_xidle_extension)
+      if (si->using_xidle_extension)
        fprintf (stderr, "%s: using XIDLE extension.\n", blurb());
       else
        fprintf (stderr, "%s: not using server's XIDLE extension.\n", blurb());
     }
 
   if (!server_has_sgi_saver_extension_p)
-    p->use_sgi_saver_extension = False;
+    si->using_sgi_saver_extension = False;
   else if (p->verbose_p)
     {
-      if (p->use_sgi_saver_extension)
+      if (si->using_sgi_saver_extension)
        fprintf (stderr, "%s: using SGI SCREEN_SAVER extension.\n", blurb());
       else
        fprintf (stderr,
@@ -666,10 +813,10 @@ initialize_server_extensions (saver_info *si)
     }
 
   if (!server_has_mit_saver_extension_p)
-    p->use_mit_saver_extension = False;
+    si->using_mit_saver_extension = False;
   else if (p->verbose_p)
     {
-      if (p->use_mit_saver_extension)
+      if (si->using_mit_saver_extension)
        fprintf (stderr, "%s: using lame MIT-SCREEN-SAVER extension.\n",
                 blurb());
       else
@@ -677,6 +824,25 @@ initialize_server_extensions (saver_info *si)
                 "%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
                 blurb());
     }
+
+  if (!system_has_proc_interrupts_p)
+    {
+      si->using_proc_interrupts = False;
+      if (p->verbose_p && piwhy)
+       fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
+                 piwhy);
+    }
+  else if (p->verbose_p)
+    {
+      if (si->using_proc_interrupts)
+       fprintf (stderr,
+                 "%s: consulting /proc/interrupts for keyboard activity.\n",
+                blurb());
+      else
+       fprintf (stderr,
+                "%s: not consulting /proc/interrupts for keyboard activity.\n",
+                blurb());
+    }
 }
 
 
@@ -692,9 +858,9 @@ select_events (saver_info *si)
   saver_preferences *p = &si->prefs;
   int i;
 
-  if (p->use_xidle_extension ||
-      p->use_mit_saver_extension ||
-      p->use_sgi_saver_extension)
+  if (si->using_xidle_extension ||
+      si->using_mit_saver_extension ||
+      si->using_sgi_saver_extension)
     return;
 
   if (p->initial_delay)
@@ -723,7 +889,8 @@ select_events (saver_info *si)
      for window creation events, so that new subwindows will be noticed.
    */
   for (i = 0; i < si->nscreens; i++)
-    start_notice_events_timer (si, RootWindowOfScreen (si->screens[i].screen));
+    start_notice_events_timer (si, RootWindowOfScreen (si->screens[i].screen),
+                               False);
 
   if (p->verbose_p)
     fprintf (stderr, " done.\n");
@@ -745,6 +912,14 @@ maybe_reload_init_file (saver_info *si)
       /* If a server extension is in use, and p->timeout has changed,
         we need to inform the server of the new timeout. */
       disable_builtin_screensaver (si, False);
+
+      /* If the DPMS settings in the init file have changed,
+         change the settings on the server to match. */
+      sync_server_dpms_settings (si->dpy, p->dpms_enabled_p,
+                                 p->dpms_standby / 1000,
+                                 p->dpms_suspend / 1000,
+                                 p->dpms_off / 1000,
+                                 False);
     }
 }
 
@@ -766,6 +941,7 @@ main_loop (saver_info *si)
 
   while (1)
     {
+      Bool was_locked = False;
       sleep_until_idle (si, True);
 
       if (p->verbose_p)
@@ -781,33 +957,77 @@ main_loop (saver_info *si)
 
       maybe_reload_init_file (si);
 
-      blank_screen (si);
+      if (! blank_screen (si))
+        {
+          /* We were unable to grab either the keyboard or mouse.
+             This means we did not (and must not) blank the screen.
+             If we were to blank the screen while some other program
+             is holding both the mouse and keyboard grabbed, then
+             we would never be able to un-blank it!  We would never
+             see any events, and the display would be wedged.
+
+             So, just go around the loop again and wait for the
+             next bout of idleness.
+          */
+
+          fprintf (stderr,
+                  "%s: unable to grab keyboard or mouse!  Blanking aborted.\n",
+                   blurb());
+          continue;
+        }
+
       kill_screenhack (si);
-      spawn_screenhack (si, True);
+
+      if (!si->throttled_p)
+        spawn_screenhack (si, True);
+      else if (p->verbose_p)
+        fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
 
       /* Don't start the cycle timer in demo mode. */
       if (!si->demoing_p && p->cycle)
-       si->cycle_id = XtAppAddTimeOut (si->app, p->cycle, cycle_timer,
+       si->cycle_id = XtAppAddTimeOut (si->app,
+                                        (si->selection_mode
+                                         /* see comment in cycle_timer() */
+                                         ? 1000 * 60 * 60
+                                         : p->cycle),
+                                        cycle_timer,
                                        (XtPointer) si);
 
 
 #ifndef NO_LOCKING
-      if (!si->demoing_p &&            /* if not going into demo mode */
-         p->lock_p &&                  /* and locking is enabled */
-         !si->locking_disabled_p &&    /* and locking is possible */
-         p->lock_timeout == 0)         /* and locking is not timer-deferred */
-       si->locked_p = True;            /* then lock right now. */
-
-      /* locked_p might be true already because of the above, or because of
-        the LOCK ClientMessage.  But if not, and if we're supposed to lock
-        after some time, set up a timer to do so.
-       */
-      if (p->lock_p &&
-         !si->locked_p &&
-         p->lock_timeout > 0)
-       si->lock_id = XtAppAddTimeOut (si->app, p->lock_timeout,
-                                      activate_lock_timer,
-                                      (XtPointer) si);
+      {
+        Time lock_timeout = p->lock_timeout;
+
+        if (si->emergency_lock_p && p->lock_p && lock_timeout)
+          {
+            int secs = p->lock_timeout / 1000;
+            if (p->verbose_p)
+              fprintf (stderr,
+                     "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
+                       blurb(),
+                       (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
+            lock_timeout = 0;
+          }
+
+        si->emergency_lock_p = False;
+
+        if (!si->demoing_p &&           /* if not going into demo mode */
+            p->lock_p &&                /* and locking is enabled */
+            !si->locking_disabled_p &&  /* and locking is possible */
+            lock_timeout == 0)          /* and locking is not timer-deferred */
+          set_locked_p (si, True);      /* then lock right now. */
+
+        /* locked_p might be true already because of the above, or because of
+           the LOCK ClientMessage.  But if not, and if we're supposed to lock
+           after some time, set up a timer to do so.
+        */
+        if (p->lock_p &&
+            !si->locked_p &&
+            lock_timeout > 0)
+          si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
+                                         activate_lock_timer,
+                                         (XtPointer) si);
+      }
 #endif /* !NO_LOCKING */
 
 
@@ -823,6 +1043,7 @@ main_loop (saver_info *si)
            saver_screen_info *ssi = si->default_screen;
            if (si->locking_disabled_p) abort ();
 
+            was_locked = True;
            si->dbox_up_p = True;
            suspend_screenhack (si, True);
            XUndefineCursor (si->dpy, ssi->screensaver_window);
@@ -832,6 +1053,20 @@ main_loop (saver_info *si)
            si->dbox_up_p = False;
            XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
            suspend_screenhack (si, False);     /* resume */
+
+            if (!ok_to_unblank &&
+                !screenhack_running_p (si))
+              {
+                /* If the lock dialog has been dismissed and we're not about to
+                   unlock the screen, and there is currently no hack running,
+                   then launch one.  (There might be no hack running if DPMS
+                   had kicked in.  But DPMS is off now, so bring back the hack)
+                 */
+                if (si->cycle_id)
+                  XtRemoveTimeOut (si->cycle_id);
+                si->cycle_id = 0;
+                cycle_timer ((XtPointer) si, 0);
+              }
          }
 #endif /* !NO_LOCKING */
 
@@ -846,10 +1081,21 @@ main_loop (saver_info *si)
       kill_screenhack (si);
       unblank_screen (si);
 
-      si->locked_p = False;
+      set_locked_p (si, False);
+      si->emergency_lock_p = False;
       si->demoing_p = 0;
       si->selection_mode = 0;
 
+      /* If we're throttled, and the user has explicitly unlocked the screen,
+         then unthrottle.  If we weren't locked, then don't unthrottle
+         automatically, because someone might have just bumped the desk... */
+      if (was_locked)
+        {
+          if (si->throttled_p && p->verbose_p)
+            fprintf (stderr, "%s: unthrottled.\n", blurb());
+          si->throttled_p = False;
+        }
+
       if (si->cycle_id)
        {
          XtRemoveTimeOut (si->cycle_id);
@@ -881,7 +1127,8 @@ main (int argc, char **argv)
   memset(si, 0, sizeof(*si));
   global_si_kludge = si;       /* I hate C so much... */
 
-  srandom ((int) time ((time_t *) 0));
+# undef ya_rand_init
+  ya_rand_init (0);
 
   save_argv (argc, argv);
   set_version_string (si, &argc, argv);
@@ -892,23 +1139,41 @@ main (int argc, char **argv)
   process_command_line (si, &argc, argv);
   print_banner (si);
 
-  initialize_per_screen_info (si, shell);  /* also sets p->fading_possible_p */
+  load_init_file (p);  /* must be before initialize_per_screen_info() */
+  initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
+
+  /* We can only issue this warnings now. */
+  if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
+    fprintf (stderr,
+             "%s: there are no PseudoColor or GrayScale visuals.\n"
+             "%s: ignoring the request for fading/unfading.\n",
+             blurb(), blurb());
 
   for (i = 0; i < si->nscreens; i++)
     if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
       exit (1);
 
-  load_init_file (p);
+  lock_initialization (si, &argc, argv);
 
   if (p->xsync_p) XSynchronize (si->dpy, True);
   blurb_timestamp_p = p->timestamp_p;  /* kludge */
 
   if (p->verbose_p) analyze_display (si);
   initialize_server_extensions (si);
+
+  si->blank_time = time ((time_t) 0); /* must be before ..._window */
   initialize_screensaver_window (si);
+
   select_events (si);
   init_sigchld ();
+
   disable_builtin_screensaver (si, True);
+  sync_server_dpms_settings (si->dpy, p->dpms_enabled_p,
+                             p->dpms_standby / 1000,
+                             p->dpms_suspend / 1000,
+                             p->dpms_off / 1000,
+                             False);
+
   initialize_stderr (si);
 
   make_splash_dialog (si);
@@ -921,6 +1186,47 @@ main (int argc, char **argv)
 /* Processing ClientMessage events.
  */
 
+
+static Bool error_handler_hit_p = False;
+
+static int
+ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
+{
+  error_handler_hit_p = True;
+  return 0;
+}
+
+/* Sometimes some systems send us ClientMessage events with bogus atoms in
+   them.  We only look up the atom names for printing warning messages,
+   so don't bomb out when it happens...
+ */
+static char *
+XGetAtomName_safe (Display *dpy, Atom atom)
+{
+  char *result;
+  XErrorHandler old_handler;
+  if (!atom) return 0;
+
+  XSync (dpy, False);
+  error_handler_hit_p = False;
+  old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+  result = XGetAtomName (dpy, atom);
+  XSync (dpy, False);
+  XSetErrorHandler (old_handler);
+  XSync (dpy, False);
+  if (error_handler_hit_p) result = 0;
+
+  if (result)
+    return result;
+  else
+    {
+      char buf[100];
+      sprintf (buf, "<<undefined atom 0x%04X>>", (unsigned long) atom);
+      return strdup (buf);
+    }
+}
+
+
 static void
 clientmessage_response (saver_info *si, Window w, Bool error,
                        const char *stderr_msg,
@@ -929,6 +1235,8 @@ clientmessage_response (saver_info *si, Window w, Bool error,
   char *proto;
   int L;
   saver_preferences *p = &si->prefs;
+  XErrorHandler old_handler;
+
   if (error || p->verbose_p)
     fprintf (stderr, "%s: %s\n", blurb(), stderr_msg);
 
@@ -938,9 +1246,23 @@ clientmessage_response (saver_info *si, Window w, Bool error,
   strcpy (proto+1, protocol_msg);
   L++;
 
+  /* Ignore all X errors while sending a response to a ClientMessage.
+     Pretty much the only way we could get an error here is if the
+     window we're trying to send the reply on has been deleted, in
+     which case, the sender of the ClientMessage won't see our response
+     anyway.
+   */
+  XSync (si->dpy, False);
+  error_handler_hit_p = False;
+  old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+
   XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
-                  PropModeReplace, proto, L);
+                  PropModeReplace, (unsigned char *) proto, L);
+
+  XSync (si->dpy, False);
+  XSetErrorHandler (old_handler);
   XSync (si->dpy, False);
+
   free (proto);
 }
 
@@ -957,7 +1279,7 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
   if (event->xclient.message_type != XA_SCREENSAVER)
     {
       char *str;
-      str = XGetAtomName (si->dpy, event->xclient.message_type);
+      str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
       fprintf (stderr, "%s: unrecognised ClientMessage type %s received\n",
               blurb(), (str ? str : "(null)"));
       if (str) XFree (str);
@@ -980,7 +1302,12 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
                                 "activating.");
          si->selection_mode = 0;
          si->demoing_p = False;
-         if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
+
+          if (si->throttled_p && p->verbose_p)
+            fprintf (stderr, "%s: unthrottled.\n", blurb());
+         si->throttled_p = False;
+
+         if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
            {
              XForceScreenSaver (si->dpy, ScreenSaverActive);
              return False;
@@ -998,10 +1325,14 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
     {
       if (! until_idle_p)
        {
+          if (si->throttled_p && p->verbose_p)
+            fprintf (stderr, "%s: unthrottled.\n", blurb());
+         si->throttled_p = False;
+
          clientmessage_response(si, window, False,
                                 "DEACTIVATE ClientMessage received.",
                                 "deactivating.");
-         if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
+         if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
            {
              XForceScreenSaver (si->dpy, ScreenSaverReset);
              return False;
@@ -1011,9 +1342,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
              return True;
            }
        }
-      clientmessage_response(si, window, True,
-                          "ClientMessage DEACTIVATE received while inactive.",
-                            "not active.");
+      clientmessage_response(si, window, False,
+     "ClientMessage DEACTIVATE received while inactive: resetting idle timer.",
+                            "not active: idle timer reset.");
+      reset_timers (si);
     }
   else if (type == XA_CYCLE)
     {
@@ -1024,6 +1356,11 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
                                 "cycling.");
          si->selection_mode = 0;       /* 0 means randomize when its time. */
          si->demoing_p = False;
+
+          if (si->throttled_p && p->verbose_p)
+            fprintf (stderr, "%s: unthrottled.\n", blurb());
+         si->throttled_p = False;
+
          if (si->cycle_id)
            XtRemoveTimeOut (si->cycle_id);
          si->cycle_id = 0;
@@ -1044,6 +1381,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
       si->selection_mode = (type == XA_NEXT ? -1 : -2);
       si->demoing_p = False;
 
+      if (si->throttled_p && p->verbose_p)
+        fprintf (stderr, "%s: unthrottled.\n", blurb());
+      si->throttled_p = False;
+
       if (! until_idle_p)
        {
          if (si->cycle_id)
@@ -1068,6 +1409,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
       si->selection_mode = which;
       si->demoing_p = False;
 
+      if (si->throttled_p && p->verbose_p)
+        fprintf (stderr, "%s: unthrottled.\n", blurb());
+      si->throttled_p = False;
+
       if (! until_idle_p)
        {
          if (si->cycle_id)
@@ -1116,6 +1461,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
              XSync (si->dpy, False);
            }
 
+          fflush (stdout);
+          fflush (stderr);
+          if (real_stdout) fflush (real_stdout);
+          if (real_stderr) fflush (real_stderr);
          /* make sure error message shows up before exit. */
          if (real_stderr && stderr != real_stderr)
            dup2 (fileno(real_stderr), fileno(stderr));
@@ -1149,6 +1498,10 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
              si->selection_mode = which;
              si->demoing_p = True;
 
+              if (si->throttled_p && p->verbose_p)
+                fprintf (stderr, "%s: unthrottled.\n", blurb());
+              si->throttled_p = False;
+
              return True;
            }
 
@@ -1190,11 +1543,11 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
          char *response = (until_idle_p
                            ? "activating and locking."
                            : "locking.");
-         si->locked_p = True;
-         si->selection_mode = 0;
-         si->demoing_p = False;
          sprintf (buf, "LOCK ClientMessage received; %s", response);
          clientmessage_response (si, window, False, buf, response);
+         set_locked_p (si, True);
+         si->selection_mode = 0;
+         si->demoing_p = False;
 
          if (si->lock_id)      /* we're doing it now, so lose the timeout */
            {
@@ -1204,7 +1557,8 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
 
          if (until_idle_p)
            {
-             if (p->use_mit_saver_extension || p->use_sgi_saver_extension)
+             if (si->using_mit_saver_extension ||
+                  si->using_sgi_saver_extension)
                {
                  XForceScreenSaver (si->dpy, ScreenSaverActive);
                  return False;
@@ -1217,11 +1571,63 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
        }
 #endif /* !NO_LOCKING */
     }
+  else if (type == XA_THROTTLE)
+    {
+      if (si->throttled_p)
+       clientmessage_response (si, window, True,
+                                "THROTTLE ClientMessage received, but "
+                                "already throttled.",
+                                "already throttled.");
+      else
+       {
+         char buf [255];
+         char *response = "throttled.";
+         si->throttled_p = True;
+         si->selection_mode = 0;
+         si->demoing_p = False;
+         sprintf (buf, "THROTTLE ClientMessage received; %s", response);
+         clientmessage_response (si, window, False, buf, response);
+
+          if (! until_idle_p)
+            {
+              if (si->cycle_id)
+                XtRemoveTimeOut (si->cycle_id);
+              si->cycle_id = 0;
+              cycle_timer ((XtPointer) si, 0);
+            }
+       }
+    }
+  else if (type == XA_UNTHROTTLE)
+    {
+      if (! si->throttled_p)
+       clientmessage_response (si, window, True,
+                                "UNTHROTTLE ClientMessage received, but "
+                                "not throttled.",
+                                "not throttled.");
+      else
+       {
+         char buf [255];
+         char *response = "unthrottled.";
+         si->throttled_p = False;
+         si->selection_mode = 0;
+         si->demoing_p = False;
+         sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
+         clientmessage_response (si, window, False, buf, response);
+
+          if (! until_idle_p)
+            {
+              if (si->cycle_id)
+                XtRemoveTimeOut (si->cycle_id);
+              si->cycle_id = 0;
+              cycle_timer ((XtPointer) si, 0);
+            }
+       }
+    }
   else
     {
       char buf [1024];
       char *str;
-      str = (type ? XGetAtomName(si->dpy, type) : 0);
+      str = XGetAtomName_safe (si->dpy, type);
 
       if (str)
        {
@@ -1251,17 +1657,79 @@ static void
 analyze_display (saver_info *si)
 {
   int i, j;
-  static const char *exts[][2] = {
-    { "SCREEN_SAVER",     "SGI Screen-Saver" },
-    { "SCREEN-SAVER",     "SGI Screen-Saver" },
-    { "MIT-SCREEN-SAVER",  "MIT Screen-Saver" },
-    { "XIDLE",            "XIdle" },
-    { "SGI-VIDEO-CONTROL", "SGI Video-Control" },
-    { "READDISPLAY",      "SGI Read-Display" },
-    { "MIT-SHM",          "Shared Memory" },
-    { "DOUBLE-BUFFER",    "Double-Buffering" },
-    { "DPMS",             "Power Management" },
-    { "GLX",              "GLX" }
+  static struct {
+    const char *name; const char *desc; Bool useful_p;
+  } exts[] = {
+
+   { "SCREEN_SAVER",                            "SGI Screen-Saver",
+#     ifdef HAVE_SGI_SAVER_EXTENSION
+        True
+#     else
+        False
+#     endif
+   }, { "SCREEN-SAVER",                         "SGI Screen-Saver",
+#     ifdef HAVE_SGI_SAVER_EXTENSION
+        True
+#     else
+        False
+#     endif
+   }, { "MIT-SCREEN-SAVER",                     "MIT Screen-Saver",
+#     ifdef HAVE_MIT_SAVER_EXTENSION
+        True
+#     else
+        False
+#     endif
+   }, { "XIDLE",                                "XIdle",           
+#     ifdef HAVE_XIDLE_EXTENSION
+        True
+#     else
+        False
+#     endif
+   }, { "SGI-VIDEO-CONTROL",                    "SGI Video-Control",
+#     ifdef HAVE_SGI_VC_EXTENSION
+        True
+#     else
+        False
+#     endif
+   }, { "READDISPLAY",                          "SGI Read-Display",
+#     ifdef HAVE_READ_DISPLAY_EXTENSION
+        True
+#     else
+        False
+#     endif
+   }, { "MIT-SHM",                              "Shared Memory",   
+#     ifdef HAVE_XSHM_EXTENSION
+        True
+#     else
+        False
+#     endif
+   }, { "DOUBLE-BUFFER",                        "Double-Buffering",
+#     ifdef HAVE_DOUBLE_BUFFER_EXTENSION
+        True
+#     else
+        False
+#     endif
+   }, { "DPMS",                                 "Power Management",
+#     ifdef HAVE_DPMS_EXTENSION
+        True
+#     else
+        False
+#     endif
+   }, { "GLX",                                  "GLX",             
+#     ifdef HAVE_GL
+        True
+#     else
+        False
+#     endif
+   }, { "XFree86-VidModeExtension",             "XF86 Video-Mode", 
+#     ifdef HAVE_XF86VMODE
+        True
+#     else
+        False
+#     endif
+   }, { "XINERAMA",                             "Xinerama",
+        True
+   },
   };
 
   fprintf (stderr, "%s: running on display \"%s\"\n", blurb(),
@@ -1273,8 +1741,11 @@ analyze_display (saver_info *si)
   for (i = 0; i < countof(exts); i++)
     {
       int op = 0, event = 0, error = 0;
-      if (XQueryExtension (si->dpy, exts[i][0], &op, &event, &error))
-       fprintf (stderr, "%s:   %s\n", blurb(), exts[i][1]);
+      if (XQueryExtension (si->dpy, exts[i].name, &op, &event, &error))
+       fprintf (stderr, "%s:  %s%s\n", blurb(),
+                 exts[i].desc,
+                 (exts[i].useful_p ? "" :
+                  "       \t<== unsupported at compile-time!"));
     }
 
   for (i = 0; i < si->nscreens; i++)
@@ -1311,3 +1782,50 @@ analyze_display (saver_info *si)
        }
     }
 }
+
+Bool
+display_is_on_console_p (saver_info *si)
+{
+  Bool not_on_console = True;
+  char *dpystr = DisplayString (si->dpy);
+  char *tail = (char *) strchr (dpystr, ':');
+  if (! tail || strncmp (tail, ":0", 2))
+    not_on_console = True;
+  else
+    {
+      char dpyname[255], localname[255];
+      strncpy (dpyname, dpystr, tail-dpystr);
+      dpyname [tail-dpystr] = 0;
+      if (!*dpyname ||
+         !strcmp(dpyname, "unix") ||
+         !strcmp(dpyname, "localhost"))
+       not_on_console = False;
+      else if (gethostname (localname, sizeof (localname)))
+       not_on_console = True;  /* can't find hostname? */
+      else
+       {
+         /* We have to call gethostbyname() on the result of gethostname()
+            because the two aren't guarenteed to be the same name for the
+            same host: on some losing systems, one is a FQDN and the other
+            is not.  Here in the wide wonderful world of Unix it's rocket
+            science to obtain the local hostname in a portable fashion.
+            
+            And don't forget, gethostbyname() reuses the structure it
+            returns, so we have to copy the fucker before calling it again.
+            Thank you master, may I have another.
+          */
+         struct hostent *h = gethostbyname (dpyname);
+         if (!h)
+           not_on_console = True;
+         else
+           {
+             char hn [255];
+             struct hostent *l;
+             strcpy (hn, h->h_name);
+             l = gethostbyname (localname);
+             not_on_console = (!l || !!(strcmp (l->h_name, hn)));
+           }
+       }
+    }
+  return !not_on_console;
+}