X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=driver%2Ftimers.c;h=7c2c363a071bb1a256d93dee8fa67091f0716a33;hb=8eb2873d7054e705c4e83f22d18c40946a9e2529;hp=5f1bd418fc3b82ec53c6925d71a73181a6b971f4;hpb=a719ec12b8b2563112366a8ac3196816fd64d2c7;p=xscreensaver diff --git a/driver/timers.c b/driver/timers.c index 5f1bd418..7c2c363a 100644 --- a/driver/timers.c +++ b/driver/timers.c @@ -43,17 +43,14 @@ #include #endif /* HAVE_SGI_SAVER_EXTENSION */ -#ifdef HAVE_XHPDISABLERESET -# include - 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) @@ -102,6 +99,7 @@ notice_events (saver_info *si, Window window, Bool top_p) unsigned long events; Window root, parent, *kids; unsigned int nkids; + int screen_no; if (XtWindowToWidget (si->dpy, window)) /* If it's one of ours, don't mess up its event mask. */ @@ -112,6 +110,11 @@ notice_events (saver_info *si, Window window, Bool top_p) if (window == root) top_p = False; + /* Figure out which screen this window is on, for the diagnostics. */ + for (screen_no = 0; screen_no < si->nscreens; screen_no++) + if (root == RootWindowOfScreen (si->screens[screen_no].screen)) + break; + XGetWindowAttributes (si->dpy, window, &attrs); events = ((attrs.all_event_masks | attrs.do_not_propagate_mask) & KeyPressMask); @@ -134,8 +137,8 @@ notice_events (saver_info *si, Window window, Bool top_p) if (top_p && p->verbose_p && (events & KeyPressMask)) { /* Only mention one window per tree (hack hack). */ - fprintf (stderr, "%s: selected KeyPress on 0x%lX\n", blurb(), - (unsigned long) window); + fprintf (stderr, "%s: %d: selected KeyPress on 0x%lX\n", + blurb(), screen_no, (unsigned long) window); top_p = False; } @@ -213,6 +216,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) @@ -223,19 +236,38 @@ cycle_timer (XtPointer closure, XtIntervalId *id) 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); -#ifdef DEBUG_TIMERS - if (p->verbose_p) - fprintf (stderr, "%s: starting cycle_timer (%ld, %ld)\n", - blurb(), how_long, si->cycle_id); -#endif /* DEBUG_TIMERS */ + if (how_long > 0) + { + si->cycle_id = XtAppAddTimeOut (si->app, how_long, cycle_timer, + (XtPointer) si); + +# ifdef DEBUG_TIMERS + if (p->verbose_p) + fprintf (stderr, "%s: starting cycle_timer (%ld, %ld)\n", + blurb(), how_long, si->cycle_id); +# endif /* DEBUG_TIMERS */ + } +# ifdef DEBUG_TIMERS + else + { + if (p->verbose_p) + fprintf (stderr, "%s: not starting cycle_timer: how_long == %d\n", + blurb(), how_long); + } +# endif /* DEBUG_TIMERS */ } @@ -246,22 +278,14 @@ 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); } /* Call this when user activity (or "simulated" activity) has been noticed. */ -static void +void reset_timers (saver_info *si) { saver_preferences *p = &si->prefs; @@ -318,8 +342,14 @@ check_pointer_timer (XtPointer closure, XtIntervalId *id) int root_x, root_y, x, y; unsigned int mask; - XQueryPointer (si->dpy, ssi->screensaver_window, &root, &child, - &root_x, &root_y, &x, &y, &mask); + if (!XQueryPointer (si->dpy, ssi->screensaver_window, &root, &child, + &root_x, &root_y, &x, &y, &mask)) + { + /* If XQueryPointer() returns false, the mouse is not on this screen. + */ + root_x = -1; + root_y = -1; + } if (root_x == ssi->poll_mouse_last_root_x && root_y == ssi->poll_mouse_last_root_y && @@ -331,14 +361,38 @@ check_pointer_timer (XtPointer closure, XtIntervalId *id) #ifdef DEBUG_TIMERS if (p->verbose_p) - if (root_x == ssi->poll_mouse_last_root_x && - root_y == ssi->poll_mouse_last_root_y && - child == ssi->poll_mouse_last_child) - fprintf (stderr, "%s: modifiers changed at %s on screen %d.\n", - blurb(), timestring(), i); - else - fprintf (stderr, "%s: pointer moved at %s on screen %d.\n", - blurb(), timestring(), i); + { + if (root_x == ssi->poll_mouse_last_root_x && + root_y == ssi->poll_mouse_last_root_y && + child == ssi->poll_mouse_last_child) + fprintf (stderr, "%s: %d: modifiers changed: 0x%04x -> 0x%04x.\n", + blurb(), i, ssi->poll_mouse_last_mask, mask); + else + { + fprintf (stderr, "%s: %d: pointer moved: ", blurb(), i); + if (ssi->poll_mouse_last_root_x == -1) + fprintf (stderr, "off screen"); + else + fprintf (stderr, "%d,%d", + ssi->poll_mouse_last_root_x, + ssi->poll_mouse_last_root_y); + fprintf (stderr, " -> "); + if (root_x == -1) + fprintf (stderr, "off screen."); + else + fprintf (stderr, "%d,%d", root_x, root_y); + if (ssi->poll_mouse_last_root_x == -1 || root_x == -1) + fprintf (stderr, ".\n"); + else +# undef ABS +# define ABS(x)((x)<0?-(x):(x)) + fprintf (stderr, " (%d,%d).\n", + ABS(ssi->poll_mouse_last_root_x - root_x), + ABS(ssi->poll_mouse_last_root_y - root_y)); +# undef ABS + } + } + #endif /* DEBUG_TIMERS */ si->last_activity_screen = ssi; @@ -362,11 +416,66 @@ 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) + { + int i = (si->last_wall_clock_time == 0 ? 0 : shift); + fprintf (stderr, + "%s: checking wall clock for hibernation (%d:%02d:%02d).\n", + blurb(), + (i / (60 * 60)), ((i / 60) % 60), (i % 60)); + } +#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 +490,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 +678,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 @@ -551,14 +723,46 @@ sleep_until_idle (saver_info *si, Bool until_idle_p) #ifdef DEBUG_TIMERS if (p->verbose_p) { + Window root, window; + int x, y; + const char *type = 0; if (event.xany.type == MotionNotify) - fprintf (stderr,"%s: MotionNotify at %s\n",blurb(),timestring()); + { + type = "MotionNotify"; + root = event.xmotion.root; + window = event.xmotion.window; + x = event.xmotion.x_root; + y = event.xmotion.y_root; + } else if (event.xany.type == KeyPress) - fprintf (stderr, "%s: KeyPress seen on 0x%X at %s\n", blurb(), - (unsigned int) event.xkey.window, timestring ()); + { + type = "KeyPress"; + root = event.xkey.root; + window = event.xkey.window; + x = y = -1; + } else if (event.xany.type == ButtonPress) - fprintf (stderr, "%s: ButtonPress seen on 0x%X at %s\n", blurb(), - (unsigned int) event.xbutton.window, timestring ()); + { + type = "ButtonPress"; + root = event.xkey.root; + window = event.xkey.window; + x = event.xmotion.x_root; + y = event.xmotion.y_root; + } + + if (type) + { + int i; + for (i = 0; i < si->nscreens; i++) + if (root == RootWindowOfScreen (si->screens[i].screen)) + break; + fprintf (stderr,"%s: %d: %s on 0x%x", + blurb(), i, type, (unsigned long) window); + if (x == -1) + fprintf (stderr, "\n"); + else + fprintf (stderr, " at %d,%d.\n", x, y); + } } #endif /* DEBUG_TIMERS */ @@ -687,8 +891,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 +983,11 @@ sleep_until_idle (saver_info *si, Bool until_idle_p) Thanks to Nat Friedman 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 +1025,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 +1079,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 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 (got_kbd || got_ptr) + { + fclose (f1); + return (kbd_diff || ptr_diff); + } + + + /* 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) @@ -915,29 +1145,58 @@ static void watchdog_timer (XtPointer closure, XtIntervalId *id) { saver_info *si = (saver_info *) closure; + saver_preferences *p = &si->prefs; disable_builtin_screensaver (si, False); + /* If the DPMS settings on the server have changed, change them back to + what ~/.xscreensaver says they should be. */ + sync_server_dpms_settings (si->dpy, + (p->dpms_enabled_p && + p->mode != DONT_BLANK), + p->dpms_standby / 1000, + p->dpms_suspend / 1000, + p->dpms_off / 1000, + False); + 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: watchdog timer raising %sscreen.\n", - blurb(), (running_p ? "" : "and clearing ")); + 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 ")); #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); } }