http://ftp.x.org/contrib/applications/xscreensaver-3.20.tar.gz
[xscreensaver] / driver / timers.c
index 5f1bd418fc3b82ec53c6925d71a73181a6b971f4..61a0155d097f26d2d73d1224d877ae2d1acc9e4b 100644 (file)
 #include <X11/extensions/XScreenSaver.h>
 #endif /* HAVE_SGI_SAVER_EXTENSION */
 
-#ifdef HAVE_XHPDISABLERESET
-# include <X11/XHPlib.h>
-  extern Bool hp_locked_p;     /* from windows.c */
-#endif /* HAVE_XHPDISABLERESET */
-
 #include "xscreensaver.h"
 
 #ifdef HAVE_PROC_INTERRUPTS
 static Bool proc_interrupts_activity_p (saver_info *si);
 #endif /* HAVE_PROC_INTERRUPTS */
 
+static void check_for_clock_skew (saver_info *si);
+
 
 void
 idle_timer (XtPointer closure, XtIntervalId *id)
@@ -213,6 +210,16 @@ cycle_timer (XtPointer closure, XtIntervalId *id)
   saver_info *si = (saver_info *) closure;
   saver_preferences *p = &si->prefs;
   Time how_long = p->cycle;
+
+  if (si->selection_mode > 0 &&
+      screenhack_running_p (si))
+    /* If we're in "SELECT n" mode, the cycle timer going off will just
+       restart this same hack again.  There's not much point in doing this
+       every 5 or 10 minutes, but on the other hand, leaving one hack running
+       for days is probably not a great idea, since they tend to leak and/or
+       crash.  So, restart the thing once an hour. */
+    how_long = 1000 * 60 * 60;
+
   if (si->dbox_up_p)
     {
       if (p->verbose_p)
@@ -226,8 +233,18 @@ cycle_timer (XtPointer closure, XtIntervalId *id)
       if (p->verbose_p)
        fprintf (stderr, "%s: changing graphics hacks.\n", blurb());
       kill_screenhack (si);
-      spawn_screenhack (si, False);
+
+      if (!si->throttled_p)
+        spawn_screenhack (si, False);
+      else
+        {
+          raise_window (si, True, True, False);
+          if (p->verbose_p)
+            fprintf (stderr, "%s: not launching new hack (throttled.)\n",
+                     blurb());
+        }
     }
+
   si->cycle_id = XtAppAddTimeOut (si->app, how_long, cycle_timer,
                                  (XtPointer) si);
 
@@ -246,16 +263,8 @@ activate_lock_timer (XtPointer closure, XtIntervalId *id)
   saver_preferences *p = &si->prefs;
 
   if (p->verbose_p)
-    fprintf (stderr, "%s: timed out; activating lock\n", blurb());
-  si->locked_p = True;
-
-#ifdef HAVE_XHPDISABLERESET
-  if (!hp_locked_p)
-    {
-      XHPDisableReset (si->dpy);       /* turn off C-Sh-Reset */
-      hp_locked_p = True;
-    }
-#endif
+    fprintf (stderr, "%s: timed out; activating lock.\n", blurb());
+  set_locked_p (si, True);
 }
 
 
@@ -339,6 +348,16 @@ check_pointer_timer (XtPointer closure, XtIntervalId *id)
        else
          fprintf (stderr, "%s: pointer moved at %s on screen %d.\n",
                   blurb(), timestring(), i);
+
+# if 0
+      fprintf (stderr, "%s: old: %d %d 0x%x ; new: %d %d 0x%x\n",
+               blurb(), 
+               ssi->poll_mouse_last_root_x,
+               ssi->poll_mouse_last_root_y,
+               (unsigned int) ssi->poll_mouse_last_child,
+               root_x, root_y, (unsigned int) child);
+# endif /* 0 */
+
 #endif /* DEBUG_TIMERS */
 
       si->last_activity_screen    = ssi;
@@ -362,11 +381,61 @@ check_pointer_timer (XtPointer closure, XtIntervalId *id)
     }
 #endif /* HAVE_PROC_INTERRUPTS */
 
+
   if (active_p)
     reset_timers (si);
+
+  check_for_clock_skew (si);
+}
+
+
+/* An unfortunate situation is this: the saver is not active, because the
+   user has been typing.  The machine is a laptop.  The user closes the lid
+   and suspends it.  The CPU halts.  Some hours later, the user opens the
+   lid.  At this point, Xt's timers will fire, and xscreensaver will blank
+   the screen.
+
+   So far so good -- well, not really, but it's the best that we can do,
+   since the OS doesn't send us a signal *before* shutdown -- but if the
+   user had delayed locking (lockTimeout > 0) then we should start off
+   in the locked state, rather than only locking N minutes from when the
+   lid was opened.  Also, eschewing fading is probably a good idea, to
+   clamp down as soon as possible.
+
+   We only do this when we'd be polling the mouse position anyway.
+   This amounts to an assumption that machines with APM support also
+   have /proc/interrupts.
+ */
+static void
+check_for_clock_skew (saver_info *si)
+{
+  saver_preferences *p = &si->prefs;
+  time_t now = time ((time_t *) 0);
+  long shift = now - si->last_wall_clock_time;
+
+#ifdef DEBUG_TIMERS
+  if (p->verbose_p)
+    fprintf (stderr, "%s: checking wall clock (%d).\n", blurb(),
+             (si->last_wall_clock_time == 0 ? 0 : shift));
+#endif /* DEBUG_TIMERS */
+
+  if (si->last_wall_clock_time != 0 &&
+      shift > (p->timeout / 1000))
+    {
+      if (p->verbose_p)
+        fprintf (stderr, "%s: wall clock has jumped by %d:%02d:%02d!\n",
+                 blurb(),
+                 (shift / (60 * 60)), ((shift / 60) % 60), (shift % 60));
+
+      si->emergency_lock_p = True;
+      idle_timer ((XtPointer) si, 0);
+    }
+
+  si->last_wall_clock_time = now;
 }
 
 
+
 static void
 dispatch_event (saver_info *si, XEvent *event)
 {
@@ -381,6 +450,63 @@ dispatch_event (saver_info *si, XEvent *event)
 }
 
 
+static void
+swallow_unlock_typeahead_events (saver_info *si, XEvent *e)
+{
+  XEvent event;
+  char buf [100];
+  int i = 0;
+
+  memset (buf, 0, sizeof(buf));
+
+  event = *e;
+
+  do
+    {
+      if (event.xany.type == KeyPress)
+        {
+          char s[2];
+          int size = XLookupString ((XKeyEvent *) &event, s, 1, 0, 0);
+          if (size != 1) continue;
+          switch (*s)
+            {
+            case '\010': case '\177':                  /* Backspace */
+              if (i > 0) i--;
+              break;
+            case '\025': case '\030':                  /* Erase line */
+            case '\012': case '\015':                  /* Enter */
+              i = 0;
+              break;
+            case '\040':                               /* Space */
+              if (i == 0)
+                break;  /* ignore space at beginning of line */
+              /* else, fall through */
+            default:
+              buf [i++] = *s;
+              break;
+            }
+        }
+
+    } while (i < sizeof(buf)-1 &&
+             XCheckMaskEvent (si->dpy, KeyPressMask, &event));
+
+  buf[i] = 0;
+
+  if (si->unlock_typeahead)
+    {
+      memset (si->unlock_typeahead, 0, strlen(si->unlock_typeahead));
+      free (si->unlock_typeahead);
+    }
+
+  if (i > 0)
+    si->unlock_typeahead = strdup (buf);
+  else
+    si->unlock_typeahead = 0;
+
+  memset (buf, 0, sizeof(buf));
+}
+
+
 /* methods of detecting idleness:
 
       explicitly informed by SGI SCREEN_SAVER server event;
@@ -512,6 +638,12 @@ sleep_until_idle (saver_info *si, Bool until_idle_p)
                 /* Look, we've been idle long enough.  We're done. */
                 goto DONE;
               }
+            else if (si->emergency_lock_p)
+              {
+                /* Oops, the wall clock has jumped far into the future, so
+                   we need to lock down in a hurry! */
+                goto DONE;
+              }
             else
               {
                 /* The event went off, but it turns out that the user has not
@@ -687,8 +819,13 @@ sleep_until_idle (saver_info *si, Bool until_idle_p)
      doesn't exhibit itself without an extension, because in that case,
      there's only one event generated by user activity, not two.)
    */
-  XCheckMaskEvent (si->dpy, (KeyPressMask|ButtonPressMask|PointerMotionMask),
-                  &event);
+  if (!until_idle_p && si->locked_p)
+    swallow_unlock_typeahead_events (si, &event);
+  else
+    while (XCheckMaskEvent (si->dpy,
+                            (KeyPressMask|ButtonPressMask|PointerMotionMask),
+                     &event))
+      ;
 
 
   if (si->check_pointer_timer_id)
@@ -774,11 +911,11 @@ sleep_until_idle (saver_info *si, Bool until_idle_p)
 
    Thanks to Nat Friedman <nat@nat.org> for figuring out all of this crap.
 
-   Note that this only checks for lines with "keyboard" in them.  Perhaps we
-   should also be checking for lines with "PS/2 Mouse" in them.  But that
-   would obviously fail to work for regular serial mice, and obviously just
-   using COM1 would be bad news (turn off the screensaver because the modem
-   is active, yum.)
+   Note that this only checks for lines with "keyboard" or "PS/2 Mouse" in
+   them.  If you have a serial mouse, it won't detect that, it will only detect
+   keyboard activity.  That's because there's no way to tell the difference
+   between a serial mouse and a general serial port, and it would be somewhat
+   unfortunate to have the screensaver turn off when the modem on COM1 burped.
  */
 
 
@@ -816,8 +953,11 @@ proc_interrupts_activity_p (saver_info *si)
   static FILE *f0 = 0;
   FILE *f1 = 0;
   int fd;
-  static char last_line[255] = { 0, };
-  char new_line[sizeof(last_line)];
+  static char last_kbd_line[255] = { 0, };
+  static char last_ptr_line[255] = { 0, };
+  char new_line[sizeof(last_kbd_line)];
+  Bool got_kbd = False, kbd_diff = False;
+  Bool got_ptr = False, ptr_diff = False;
 
   if (!f0)
     {
@@ -867,17 +1007,35 @@ proc_interrupts_activity_p (saver_info *si)
   /* Now read through the pseudo-file until we find the "keyboard" line. */
 
   while (fgets (new_line, sizeof(new_line)-1, f1))
-    if (strstr (new_line, "keyboard"))
-      {
-        Bool diff = (*last_line &&
-                     !!strcmp (new_line, last_line));
-        strcpy (last_line, new_line);  /* save this line for next time */
-        fclose (f1);
-        return diff;
-      }
+    {
+      if (!got_kbd && strstr (new_line, "keyboard"))
+        {
+          kbd_diff = (*last_kbd_line && !!strcmp (new_line, last_kbd_line));
+          strcpy (last_kbd_line, new_line);
+          got_kbd = True;
+        }
+      else if (!got_ptr && strstr (new_line, "PS/2 Mouse"))
+        {
+          ptr_diff = (*last_ptr_line && !!strcmp (new_line, last_ptr_line));
+          strcpy (last_ptr_line, new_line);
+          got_ptr = True;
+        }
+
+      if (got_kbd && got_ptr)
+        break;
+    }
+
+  if (got_kbd || got_ptr)
+    {
+      fclose (f1);
+      return (kbd_diff || ptr_diff);
+    }
+
 
-  /* If we got here, we didn't find a "keyboard" line in the file at all. */
-  fprintf (stderr, "%s: no keyboard data in %s?\n", blurb(), PROC_INTERRUPTS);
+  /* If we got here, we didn't find either a "keyboard" or a "PS/2 Mouse"
+     line in the file at all. */
+  fprintf (stderr, "%s: no keyboard or mouse data in %s?\n",
+           blurb(), PROC_INTERRUPTS);
 
  FAIL:
   if (f1)
@@ -920,15 +1078,26 @@ watchdog_timer (XtPointer closure, XtIntervalId *id)
 
   if (si->screen_blanked_p)
     {
-      Bool running_p = screenhack_running_p(si);
+      Bool running_p = screenhack_running_p (si);
 
+      if (si->dbox_up_p)
+        {
+#ifdef DEBUG_TIMERS
+          if (si->prefs.verbose_p)
+            fprintf (stderr, "%s: dialog box is up: not raising screen.\n",
+                     blurb());
+#endif /* DEBUG_TIMERS */
+        }
+      else
+        {
 #ifdef DEBUG_TIMERS
-      if (si->prefs.verbose_p)
-       fprintf (stderr, "%s: watchdog timer raising %sscreen.\n",
-                blurb(), (running_p ? "" : "and clearing "));
+          if (si->prefs.verbose_p)
+            fprintf (stderr, "%s: watchdog timer raising %sscreen.\n",
+                     blurb(), (running_p ? "" : "and clearing "));
 #endif /* DEBUG_TIMERS */
 
-      raise_window (si, True, True, running_p);
+          raise_window (si, True, True, running_p);
+        }
 
       if (!monitor_powered_on_p (si))
        {
@@ -938,6 +1107,12 @@ watchdog_timer (XtPointer closure, XtIntervalId *id)
                     "killing running hacks.\n", blurb());
          kill_screenhack (si);
        }
+
+      /* Re-schedule this timer.  The watchdog timer defaults to a bit less
+         than the hack cycle period, but is never longer than one hour.
+       */
+      si->watchdog_id = 0;
+      reset_watchdog_timer (si, True);
     }
 }