http://ftp.x.org/contrib/applications/xscreensaver-3.20.tar.gz
[xscreensaver] / driver / xscreensaver.c
index dda587a12f81c2da057bdff4c884b2760224a66e..fc8b89a47e4cd542f838f0e1636503b19b5abc54 100644 (file)
@@ -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
@@ -162,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 [] = {
@@ -313,15 +315,17 @@ saver_ehandler (Display *dpy, XErrorEvent *error)
                    "#######################################"
                    "#######################################\n\n");
           fprintf (real_stderr,
-   "    If at all possible, please re-run xscreensaver with the command line\n"
-   "    arguments `-sync -verbose', and reproduce this bug.  That will cause\n"
-   "    xscreensaver to dump a `core' file to the current directory.  Please\n"
-   "    include the stack trace from that core file in your bug report.\n"
+   "    If at all possible, please re-run xscreensaver with the command\ e\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 the\n"
-   "    most useful bug reports, and how to examine core files.\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 report\n"
+   "    The more information you can provide, the better.  But please\n"
    "    report this bug, regardless!\n"
    "\n");
           fprintf (real_stderr,
@@ -367,12 +371,13 @@ startup_ehandler (String name, String type, String class,
 
   describe_uids (si, stderr);
   fprintf (stderr, "\n"
-           "%s: Errors at startup are usually authorization problems.\n"
-           "              Did you read the manual?  Specifically, the parts\n"
-           "              that talk about XAUTH, XDM, and root logins?\n"
-           "\n"
-           "              http://www.jwz.org/xscreensaver/man.html\n"
-           "\n",
+          "%s: Errors at startup are usually authorization problems.\n"
+          "              Did you read the manual and the FAQ?  Specifically,\n"
+          "              the parts of the manual that talk about XAUTH, XDM,\n"
+          "              and root logins?\n"
+          "\n"
+          "              http://www.jwz.org/xscreensaver/man.html\n"
+          "\n",
            blurb());
 
   fflush (stderr);
@@ -419,12 +424,26 @@ 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;
-  /* 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;
@@ -432,9 +451,7 @@ privileged_initialization (saver_info *si, int *argc, char **argv)
     }
 #endif /* NO_LOCKING */
 
-#ifndef NO_SETUID
   hack_uid (si);
-#endif /* NO_SETUID */
 }
 
 
@@ -466,7 +483,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);
@@ -481,6 +498,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;
 }
@@ -685,7 +705,7 @@ 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;
 }
 
 
@@ -864,6 +884,7 @@ main_loop (saver_info *si)
 
   while (1)
     {
+      Bool was_locked = False;
       sleep_until_idle (si, True);
 
       if (p->verbose_p)
@@ -894,36 +915,62 @@ main_loop (saver_info *si)
 
           fprintf (stderr,
                   "%s: unable to grab keyboard or mouse!  Blanking aborted.\n",
-                   blurb(), timestring ());
+                   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 */
 
 
@@ -939,6 +986,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);
@@ -962,10 +1010,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);
@@ -1008,20 +1067,31 @@ 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);
@@ -1063,6 +1133,7 @@ clientmessage_response (saver_info *si, Window w, Bool error,
 Bool
 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
 {
+  saver_preferences *p = &si->prefs;
   Atom type = 0;
   Window window = event->xclient.window;
 
@@ -1095,6 +1166,11 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
                                 "activating.");
          si->selection_mode = 0;
          si->demoing_p = False;
+
+          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);
@@ -1113,6 +1189,10 @@ 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.");
@@ -1139,6 +1219,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;
@@ -1159,6 +1244,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)
@@ -1183,6 +1272,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)
@@ -1231,6 +1324,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));
@@ -1264,6 +1361,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;
            }
 
@@ -1305,11 +1406,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 */
            {
@@ -1333,6 +1434,58 @@ 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];
@@ -1368,16 +1521,18 @@ 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" }
+    { "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" },
+    { "XFree86-VidModeExtension", "XF86 Video-Mode" },
+    { "XINERAMA",                "Xinerama" }
   };
 
   fprintf (stderr, "%s: running on display \"%s\"\n", blurb(),