#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)
/* Select for SubstructureNotify on all windows.
Select for KeyPress on all windows that already have it selected.
- Do we need to select for ButtonRelease? I don't think so.
+
+ Note that we can't select for ButtonPress, because of X braindamage:
+ only one client at a time may select for ButtonPress on a given
+ window, though any number can select for KeyPress. Someone explain
+ *that* to me.
+
+ So, if the user spends a while clicking the mouse without ever moving
+ the mouse or touching the keyboard, we won't know that they've been
+ active, and the screensaver will come on. That sucks, but I don't
+ know how to get around it.
*/
XSelectInput (si->dpy, window, SubstructureNotifyMask | events);
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)
else
{
maybe_reload_init_file (si);
- 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);
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);
}
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;
}
#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)
{
}
+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;
/* 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
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)
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.
*/
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)
{
/* 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)
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))
+ if (screenhack_running_p (si) &&
+ !monitor_powered_on_p (si))
{
if (si->prefs.verbose_p)
fprintf (stderr,
- "%s: server reports that monitor has powered down; "
+ "%s: X says monitor has powered down; "
"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);
}
}