New Power page: - toggle for the firmware's 30 s keyboard backlight timeout - idle timeout for keyboard and lightbar, separately for AC and battery; predatord watches all input devices and restores the lighting on the next input - battery rules: dimmed keyboard brightness, keyboard off, lightbar off The rules only change what is sent to the hardware; the saved lighting settings stay untouched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
57 lines
1.7 KiB
Python
57 lines
1.7 KiB
Python
"""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()
|