X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=driver%2Ftimers.c;h=318a61f47e467ee91e3d45b09177b08ff3f162a9;hp=81c8adb6fe9958bd816627e2de2cc1bced866cd0;hb=aa75c7476aeaa84cf3abc192b376a8b03c325213;hpb=88cfe534a698a0562e81345957a50714af1453bc diff --git a/driver/timers.c b/driver/timers.c index 81c8adb6..318a61f4 100644 --- a/driver/timers.c +++ b/driver/timers.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #ifdef HAVE_XMU @@ -989,29 +990,90 @@ sleep_until_idle (saver_info *si, Bool until_idle_p) case PropertyNotify: + /* Starting in late 2014, GNOME programs don't actually select for + or receive KeyPress events: they do it behind the scenes through + some kind of Input Method magic, even when running in an en_US + locale. However, those applications *do* update the WM_USER_TIME + property on their own windows every time they recieve a secret + KeyPress, so we must *also* monitor that property on every + window, and treat changes to it as identical to KeyPress. + + _NET_WM_USER_TIME is documented (such as it is) here: + + http://standards.freedesktop.org/wm-spec/latest/ar01s05.html + #idm139870829932528 + + Specifically: + + "Contains the XServer time at which last user activity in this + window took place. [...] A client [...] might, for example, + use the timestamp of the last KeyPress or ButtonPress event." + + As of early 2016, KDE4 does something really stupid, though: some + hidden power management thing reduces the display brightness 150 + seconds after the screen is blanked -- and sets a WM_USER_TIME + property on a hidden "kded4" window whose time is in the distant + past (the time at which the X server launched). + + So we ignore any WM_USER_TIME whose timestamp is more than a + couple seconds old. + */ if (event.x_event.xproperty.state == PropertyNewValue && event.x_event.xproperty.atom == XA_NET_WM_USER_TIME) { - /* Let's just assume that they only ever set USER_TIME to the - current time, and don't do something stupid like repeatedly - setting it to 20 minutes ago. */ + int threshold = 2; /* seconds */ + Bool bogus_p = True; + Window w = event.x_event.xproperty.window; + + Atom type; + int format; + unsigned long nitems, bytesafter; + unsigned char *data = 0; + Cardinal user_time = 0; + XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler); + + if (XGetWindowProperty (si->dpy, w, + XA_NET_WM_USER_TIME, 0L, 1L, False, + XA_CARDINAL, &type, &format, &nitems, + &bytesafter, &data) + == Success && + data && + type == XA_CARDINAL && + format == 32 && + nitems == 1) + { + long diff; + user_time = ((Cardinal *) data)[0]; + diff = event.x_event.xproperty.time - user_time; + if (diff >= 0 && diff < threshold) + bogus_p = False; + } + + if (data) XFree (data); why = "WM_USER_TIME"; if (p->debug_p) { - Window w = event.x_event.xproperty.window; XWindowAttributes xgwa; int i; + XGetWindowAttributes (si->dpy, w, &xgwa); for (i = 0; i < si->nscreens; i++) if (xgwa.root == RootWindowOfScreen (si->screens[i].screen)) break; - fprintf (stderr,"%s: %d: %s on 0x%lx\n", - blurb(), i, why, (unsigned long) w); + fprintf (stderr,"%s: %d: %s = %ld%s on 0x%lx\n", + blurb(), i, why, (unsigned long) user_time, + (bogus_p ? " (bad)" : ""), + (unsigned long) w); } - if (until_idle_p) + XSync (si->dpy, False); + XSetErrorHandler (old_handler); + + if (bogus_p) + break; + else if (until_idle_p) reset_timers (si); else goto DONE;