"""Input activity watcher used for the 'turn lighting off when idle' feature.""" import glob import os import select import threading import time class IdleWatcher(threading.Thread): """Tracks the time of the last keyboard/touchpad/mouse event on any input device.""" RESCAN = 30 # seconds between scans for new devices (hotplug, resume) def __init__(self, on_activity=None): super().__init__(daemon=True) self.last_input = time.monotonic() self.on_activity = on_activity def idle_seconds(self): return time.monotonic() - self.last_input def _open_all(self, fds): for path in glob.glob("/dev/input/event*"): if path in fds.values(): continue try: fds[os.open(path, os.O_RDONLY | os.O_NONBLOCK)] = path except OSError: pass def run(self): fds = {} next_scan = 0.0 while True: if time.monotonic() >= next_scan: self._open_all(fds) next_scan = time.monotonic() + self.RESCAN if not fds: time.sleep(5) continue ready, _, _ = select.select(list(fds), [], [], 5) active = False for fd in ready: try: if os.read(fd, 4096): active = True except BlockingIOError: pass except OSError: os.close(fd) fds.pop(fd, None) if active: self.last_input = time.monotonic() if self.on_activity: self.on_activity()