如何从Python中确定Windows、Linux和MacOS的显示器空闲时间?

18
我想知道用户上一次按键或移动鼠标的时间有多久了 - 不仅在我的应用程序中,而且在整个“计算机”(即显示器)上,以猜测他们是否仍然在电脑前并能够观察弹出在屏幕上的通知。我希望可以纯粹使用(Py)GTK+实现这一点,但我也可以调用特定于平台的函数。理想情况下,我想调用已经从 Python 中包装好的函数,但如果不可能,我也可以使用一些 C 或 ctypes 代码,只要我知道我正在寻找什么。在 Windows 上,我认为我需要的函数是 GetLastInputInfo,但似乎它没有被 pywin32 包装;我希望我错了。
3个回答

12

在Windows、OS X和GNU/Linux(以及其他*nix)上,Gajim 是这样做的:

  1. Python包装模块(还包括使用ctypesGetTickCount来检测 Windows 空闲时间的代码);
  2. 基于 ctypes 的获取 X11 空闲时间的模块(使用XScreenSaverQueryInfo,旧版 Gajim 中是一个 C 模块);
  3. 用于获取 OS X 空闲时间的 C 模块(使用系统属性HIDIdleTime)。

这些链接是相当陈旧的 0.12 版本,因此您可能需要检查当前的源代码以了解可能的进一步改进和变化。


7
如果您在Linux上使用PyGTK和X11,您可以执行类似于Pidgin的操作,如下所示:
import ctypes
import ctypes.util
import platform

class XScreenSaverInfo(ctypes.Structure):
    _fields_ = [('window', ctypes.c_long),
                ('state', ctypes.c_int),
                ('kind', ctypes.c_int),
                ('til_or_since', ctypes.c_ulong),
                ('idle', ctypes.c_ulong),
                ('eventMask', ctypes.c_ulong)]

class IdleXScreenSaver(object):
    def __init__(self):
        self.xss = self._get_library('Xss')
        self.gdk = self._get_library('gdk-x11-2.0')

        self.gdk.gdk_display_get_default.restype = ctypes.c_void_p
        # GDK_DISPLAY_XDISPLAY expands to gdk_x11_display_get_xdisplay
        self.gdk.gdk_x11_display_get_xdisplay.restype = ctypes.c_void_p
        self.gdk.gdk_x11_display_get_xdisplay.argtypes = [ctypes.c_void_p]
        # GDK_ROOT_WINDOW expands to gdk_x11_get_default_root_xwindow
        self.gdk.gdk_x11_get_default_root_xwindow.restype = ctypes.c_void_p

        self.xss.XScreenSaverAllocInfo.restype = ctypes.POINTER(XScreenSaverInfo)
        self.xss.XScreenSaverQueryExtension.restype = ctypes.c_int
        self.xss.XScreenSaverQueryExtension.argtypes = [ctypes.c_void_p,
                                                        ctypes.POINTER(ctypes.c_int),
                                                        ctypes.POINTER(ctypes.c_int)]
        self.xss.XScreenSaverQueryInfo.restype = ctypes.c_int
        self.xss.XScreenSaverQueryInfo.argtypes = [ctypes.c_void_p,
                                                   ctypes.c_void_p,
                                                   ctypes.POINTER(XScreenSaverInfo)]

        # gtk_init() must have been called for this to work
        import gtk
        gtk  # pyflakes

        # has_extension = XScreenSaverQueryExtension(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
        #                                            &event_base, &error_base);
        event_base = ctypes.c_int()
        error_base = ctypes.c_int()
        gtk_display = self.gdk.gdk_display_get_default()
        self.dpy = self.gdk.gdk_x11_display_get_xdisplay(gtk_display)
        available = self.xss.XScreenSaverQueryExtension(self.dpy,
                                                        ctypes.byref(event_base),
                                                        ctypes.byref(error_base))
        if available == 1:
            self.xss_info = self.xss.XScreenSaverAllocInfo()
        else:
            self.xss_info = None

    def _get_library(self, libname):
        path = ctypes.util.find_library(libname)
        if not path:
            raise ImportError('Could not find library "%s"' % (libname, ))
        lib = ctypes.cdll.LoadLibrary(path)
        assert lib
        return lib

    def get_idle(self):
        if not self.xss_info:
            return 0

        # XScreenSaverQueryInfo(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
        #                       GDK_ROOT_WINDOW(), mit_info);
        drawable = self.gdk.gdk_x11_get_default_root_xwindow()
        self.xss.XScreenSaverQueryInfo(self.dpy, drawable, self.xss_info)
        # return (mit_info->idle) / 1000;
        return self.xss_info.contents.idle / 1000

以上示例使用gdk ctypes方式来访问X11特定的API。同时,也需要通过ctypes来访问Xscreensaver API。

将其改为使用PyGI和内省应该很容易。


将这些 ctypes 接口移植到 https://pypi.python.org/pypi/cffi 可能会很有趣。 - Glyph
是的,这可能是个好主意。这个评论是基于我的程序,我想避免在那里添加cffi依赖。希望cffi能够在某个时候进入CPython。 - Johan Dahlin
我真的很希望如此。坦白地说,他们应该发布一个包含它的2.7维护版本。 - Glyph
代码必须在 python2 中运行才能正常工作。测试时请尝试:time.sleep(2.1); print IdleXScreenSaver().get_idle() - SurpriseDog

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接