X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=driver%2Ftimers.c;h=7e769a77301ba56e5c6e72f157849bb481672361;hb=bc7b7a8eb122206d239ec0e693676bcce31be1aa;hp=aac16586da8717ef50f5bce66517fea4a3279de8;hpb=9c9d475ff889ed8be02e8ce8c17da28b93278fca;p=xscreensaver diff --git a/driver/timers.c b/driver/timers.c index aac16586..7e769a77 100644 --- a/driver/timers.c +++ b/driver/timers.c @@ -76,12 +76,27 @@ idle_timer (XtPointer closure, XtIntervalId *id) fake_event.xany.display = si->dpy; fake_event.xany.window = 0; XPutBackEvent (si->dpy, &fake_event); + + /* If we are the timer that just went off, clear the pointer to the id. */ + if (id) + { + if (si->timer_id && *id != si->timer_id) + abort(); /* oops, scheduled timer twice?? */ + si->timer_id = 0; + } } -static void +void schedule_wakeup_event (saver_info *si, Time when, Bool verbose_p) { + if (si->timer_id) + { + if (verbose_p) + fprintf (stderr, "%s: idle_timer already running\n", blurb()); + return; + } + /* Wake up periodically to ask the server if we are idle. */ si->timer_id = XtAppAddTimeOut (si->app, when, idle_timer, (XtPointer) si); @@ -301,6 +316,7 @@ reset_timers (saver_info *si) fprintf (stderr, "%s: killing idle_timer (%ld, %ld)\n", blurb(), p->timeout, si->timer_id); XtRemoveTimeOut (si->timer_id); + si->timer_id = 0; } schedule_wakeup_event (si, p->timeout, p->debug_p); /* sets si->timer_id */ @@ -332,7 +348,13 @@ check_pointer_timer (XtPointer closure, XtIntervalId *id) */ abort (); - si->check_pointer_timer_id = + if (id && *id == si->check_pointer_timer_id) /* this is us - it's expired */ + si->check_pointer_timer_id = 0; + + if (si->check_pointer_timer_id) /* only queue one at a time */ + XtRemoveTimeOut (si->check_pointer_timer_id); + + si->check_pointer_timer_id = /* now re-queue */ XtAppAddTimeOut (si->app, p->pointer_timeout, check_pointer_timer, (XtPointer) si); @@ -597,10 +619,7 @@ sleep_until_idle (saver_info *si, Bool until_idle_p) { if (polling_for_idleness) /* This causes a no-op event to be delivered to us in a while, so that - we come back around through the event loop again. Use of this timer - is economical: for example, if the screensaver should come on in 5 - minutes, and the user has been idle for 2 minutes, then this - timeout will go off no sooner than 3 minutes from now. */ + we come back around through the event loop again. */ schedule_wakeup_event (si, p->timeout, p->debug_p); if (polling_mouse_position) @@ -618,6 +637,15 @@ sleep_until_idle (saver_info *si, Bool until_idle_p) if (until_idle_p) { Time idle; + + /* We may be idle; check one last time to see if the mouse has + moved, just in case the idle-timer went off within the 5 second + window between mouse polling. If the mouse has moved, then + check_pointer_timer() will reset last_activity_time. + */ + if (polling_mouse_position) + check_pointer_timer ((XtPointer) si, 0); + #ifdef HAVE_XIDLE_EXTENSION if (si->using_xidle_extension) { @@ -681,7 +709,10 @@ sleep_until_idle (saver_info *si, Bool until_idle_p) { /* The event went off, but it turns out that the user has not yet been idle for long enough. So re-signal the event. - */ + Be economical: if we should blank after 5 minutes, and the + user has been idle for 2 minutes, then set this timer to + go off in 3 minutes. + */ if (polling_for_idleness) schedule_wakeup_event (si, p->timeout - idle, p->debug_p); } @@ -884,6 +915,7 @@ sleep_until_idle (saver_info *si, Bool until_idle_p) XRRScreenChangeNotifyEvent *xrr_event = (XRRScreenChangeNotifyEvent *) &event; + /* XRRRootToScreen is in Xrandr.h 1.4, 2001/06/07 */ int screen = XRRRootToScreen (si->dpy, xrr_event->window); if (p->verbose_p) @@ -903,8 +935,10 @@ sleep_until_idle (saver_info *si, Bool until_idle_p) xrr_event->width, xrr_event->height); } +# ifdef RRScreenChangeNotifyMask /* Inform Xlib that it's ok to update its data structures. */ - XRRUpdateConfiguration (&event); + XRRUpdateConfiguration (&event); /* Xrandr.h 1.9, 2002/09/29 */ +# endif /* RRScreenChangeNotifyMask */ /* Resize the existing xscreensaver windows and cached ssi data. */ resize_screensaver_window (si); @@ -1007,23 +1041,35 @@ sleep_until_idle (saver_info *si, Bool until_idle_p) 0: 309453991 timer 1: 4771729 keyboard - but on later kernels with MP machines, it looks like this: + but in Linux 2.2 and 2.4 kernels with MP machines, it looks like this: CPU0 CPU1 0: 1671450 1672618 IO-APIC-edge timer 1: 13037 13495 IO-APIC-edge keyboard - Joy! So how are we expected to parse that? Well, this code doesn't - parse it: it saves the last line with the string "keyboard" in it, and - does a string-comparison to note when it has changed. + and in Linux 2.6, it's gotten even goofier: now there are two lines + labelled "i8042". One of them is the keyboard, and one of them is + the PS/2 mouse -- and of course, you can't tell them apart, except + by wiggling the mouse and noting which one changes: - Thanks to Nat Friedman for figuring out all of this crap. + CPU0 CPU1 + 1: 32051 30864 IO-APIC-edge i8042 + 12: 476577 479913 IO-APIC-edge i8042 - 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. + Joy! So how are we expected to parse that? Well, this code doesn't + parse it: it saves the first line with the string "keyboard" (or + "i8042") in it, and does a string-comparison to note when it has + changed. If there are two "i8042" lines, we assume the first is + the keyboard and the second is the mouse (doesn't matter which is + which, really, as long as we don't compare them against each other.) + + Thanks to Nat Friedman for figuring out most of this crap. + + Note that if you have a serial or USB mouse, or a USB keyboard, it won't + detect it. That's because there's no way to tell the difference between a + serial mouse and a general serial port, and all USB devices look the same + from here. It would be somewhat unfortunate to have the screensaver turn + off when the modem on COM1 burped, or when a USB disk was accessed. */ @@ -1066,6 +1112,7 @@ proc_interrupts_activity_p (saver_info *si) char new_line[sizeof(last_kbd_line)]; Bool checked_kbd = False, kbd_changed = False; Bool checked_ptr = False, ptr_changed = False; + int i8042_count = 0; if (!f0) { @@ -1112,10 +1159,14 @@ proc_interrupts_activity_p (saver_info *si) goto FAIL; } - /* Now read through the pseudo-file until we find the "keyboard" line. */ + /* Now read through the pseudo-file until we find the "keyboard", + "PS/2 mouse", or "i8042" lines. */ while (fgets (new_line, sizeof(new_line)-1, f1)) { + Bool i8042_p = !!strstr (new_line, "i8042"); + if (i8042_p) i8042_count++; + if (strchr (new_line, ',')) { /* Ignore any line that has a comma on it: this is because @@ -1129,14 +1180,24 @@ proc_interrupts_activity_p (saver_info *si) to ignore any shared IRQs. */ } - else if (!checked_kbd && strstr (new_line, "keyboard")) + else if (!checked_kbd && + (strstr (new_line, "keyboard") || + (i8042_p && i8042_count == 1))) { + /* Assume the keyboard interrupt is the line that says "keyboard", + or the *first* line that says "i8042". + */ kbd_changed = (*last_kbd_line && !!strcmp (new_line, last_kbd_line)); strcpy (last_kbd_line, new_line); checked_kbd = True; } - else if (!checked_ptr && strstr (new_line, "PS/2 Mouse")) + else if (!checked_ptr && + (strstr (new_line, "PS/2 Mouse") || + (i8042_p && i8042_count == 2))) { + /* Assume the mouse interrupt is the line that says "PS/2 mouse", + or the *second* line that says "i8042". + */ ptr_changed = (*last_ptr_line && !!strcmp (new_line, last_ptr_line)); strcpy (last_ptr_line, new_line); checked_ptr = True; @@ -1275,3 +1336,90 @@ reset_watchdog_timer (saver_info *si, Bool on_p) blurb(), p->watchdog_timeout, si->watchdog_id); } } + + +/* It's possible that a race condition could have led to the saver + window being unexpectedly still mapped. This can happen like so: + + - screen is blanked + - hack is launched + - that hack tries to grab a screen image (it does this by + first unmapping the saver window, then remapping it.) + - hack unmaps window + - hack waits + - user becomes active + - hack re-maps window (*) + - driver kills subprocess + - driver unmaps window (**) + + The race is that (*) might have been sent to the server before + the client process was killed, but, due to scheduling randomness, + might not have been received by the server until after (**). + In other words, (*) and (**) might happen out of order, meaning + the driver will unmap the window, and then after that, the + recently-dead client will re-map it. This leaves the user + locked out (it looks like a desktop, but it's not!) + + To avoid this: after un-blanking the screen, we launch a timer + that wakes up once a second for ten seconds, and makes damned + sure that the window is still unmapped. + */ + +void +de_race_timer (XtPointer closure, XtIntervalId *id) +{ + saver_info *si = (saver_info *) closure; + saver_preferences *p = &si->prefs; + int secs = 1; + + if (id == 0) /* if id is 0, this is the initialization call. */ + { + si->de_race_ticks = 10; + if (p->verbose_p) + fprintf (stderr, "%s: starting de-race timer (%d seconds.)\n", + blurb(), si->de_race_ticks); + } + else + { + int i; + XSync (si->dpy, False); + for (i = 0; i < si->nscreens; i++) + { + saver_screen_info *ssi = &si->screens[i]; + Window w = ssi->screensaver_window; + XWindowAttributes xgwa; + XGetWindowAttributes (si->dpy, w, &xgwa); + if (xgwa.map_state != IsUnmapped) + { + if (p->verbose_p) + fprintf (stderr, + "%s: %d: client race! emergency unmap 0x%lx.\n", + blurb(), i, (unsigned long) w); + XUnmapWindow (si->dpy, w); + } + else if (p->debug_p) + fprintf (stderr, "%s: %d: (de-race of 0x%lx is cool.)\n", + blurb(), i, (unsigned long) w); + } + XSync (si->dpy, False); + + si->de_race_ticks--; + } + + if (id && *id == si->de_race_id) + si->de_race_id = 0; + + if (si->de_race_id) abort(); + + if (si->de_race_ticks <= 0) + { + si->de_race_id = 0; + if (p->verbose_p) + fprintf (stderr, "%s: de-race completed.\n", blurb()); + } + else + { + si->de_race_id = XtAppAddTimeOut (si->app, secs * 1000, + de_race_timer, closure); + } +}