led_monitor -> led-monitor

This commit is contained in:
2026-04-20 18:59:18 +02:00
parent 454262a803
commit 294788a314
2 changed files with 23 additions and 17 deletions
@@ -18,7 +18,7 @@ Singleton {
id: capslockMonitorProcess id: capslockMonitorProcess
running: true running: true
command: ["led_monitor", "-l", "capslock"] command: ["led-monitor", "-l", "capslock"]
stdout: SplitParser { stdout: SplitParser {
splitMarker: "\n" splitMarker: "\n"
@@ -4,21 +4,17 @@ import glob
import argparse import argparse
import select import select
import os import os
import fcntl
import struct import struct
# struct input_event: struct timeval (typically 2 longs), type (uint16), code (uint16), value (int32) # struct input_event: struct timeval (typically 2 longs), type (uint16), code (uint16), value (int32)
# '@' ensures native byte order, size, and alignment. # '@' ensures native byte order, size, and alignment.
EVENT_FORMAT = '@llHHi' EVENT_FORMAT = "@llHHi"
EVENT_SIZE = struct.calcsize(EVENT_FORMAT) EVENT_SIZE = struct.calcsize(EVENT_FORMAT)
EV_LED = 0x11 EV_LED = 0x11
LED_CODES = { LED_CODES = {"numlock": 0x00, "capslock": 0x01, "scrolllock": 0x02}
'numlock': 0x00,
'capslock': 0x01,
'scrolllock': 0x02
}
def get_led_state(led_type: str) -> int: def get_led_state(led_type: str) -> int:
pattern = f"/sys/class/leds/*::{led_type}/brightness" pattern = f"/sys/class/leds/*::{led_type}/brightness"
@@ -27,24 +23,26 @@ def get_led_state(led_type: str) -> int:
return 0 return 0
for path in paths: for path in paths:
try: try:
with open(path, 'r') as f: with open(path, "r") as f:
if int(f.read().strip()) > 0: if int(f.read().strip()) > 0:
return 1 return 1
except (IOError, ValueError, OSError): except (IOError, ValueError, OSError):
continue continue
return 0 return 0
def has_led_capability(event_path: str) -> bool: def has_led_capability(event_path: str) -> bool:
try: try:
basename = os.path.basename(event_path) basename = os.path.basename(event_path)
cap_path = f"/sys/class/input/{basename}/device/capabilities/led" cap_path = f"/sys/class/input/{basename}/device/capabilities/led"
if os.path.exists(cap_path): if os.path.exists(cap_path):
with open(cap_path, 'r') as f: with open(cap_path, "r") as f:
return f.read().strip() != "0" return f.read().strip() != "0"
except OSError: except OSError:
pass pass
return False return False
class DeviceMonitor: class DeviceMonitor:
def __init__(self, target_led: str): def __init__(self, target_led: str):
self.target_led = target_led self.target_led = target_led
@@ -62,7 +60,7 @@ class DeviceMonitor:
def scan_devices(self) -> None: def scan_devices(self) -> None:
current_fds = set(self.active_fds.keys()) current_fds = set(self.active_fds.keys())
paths = glob.glob('/dev/input/event*') paths = glob.glob("/dev/input/event*")
found_fds = set() found_fds = set()
for path in paths: for path in paths:
@@ -134,9 +132,14 @@ class DeviceMonitor:
events_count = len(data) // EVENT_SIZE events_count = len(data) // EVENT_SIZE
for i in range(events_count): for i in range(events_count):
chunk = data[i * EVENT_SIZE : (i + 1) * EVENT_SIZE] chunk = data[i * EVENT_SIZE : (i + 1) * EVENT_SIZE]
_, _, ev_type, ev_code, _ = struct.unpack(EVENT_FORMAT, chunk) _, _, ev_type, ev_code, _ = struct.unpack(
EVENT_FORMAT, chunk
)
if ev_type == EV_LED and ev_code == self.target_led_code: if (
ev_type == EV_LED
and ev_code == self.target_led_code
):
# Instead of acting immediately on the value, we just flag it. # Instead of acting immediately on the value, we just flag it.
# This prevents state bouncing between multiple kernel devices. # This prevents state bouncing between multiple kernel devices.
led_event_triggered = True led_event_triggered = True
@@ -166,19 +169,22 @@ class DeviceMonitor:
except Exception: except Exception:
pass pass
def main(): def main():
parser = argparse.ArgumentParser(description="Keyboard LED monitor.") parser = argparse.ArgumentParser(description="Keyboard LED monitor.")
parser.add_argument( parser.add_argument(
'-l', '--led', "-l",
"--led",
type=str, type=str,
default='capslock', default="capslock",
choices=['capslock', 'numlock', 'scrolllock'], choices=["capslock", "numlock", "scrolllock"],
help="Target LED to monitor" help="Target LED to monitor",
) )
args = parser.parse_args() args = parser.parse_args()
monitor = DeviceMonitor(args.led) monitor = DeviceMonitor(args.led)
monitor.run() monitor.run()
if __name__ == "__main__": if __name__ == "__main__":
main() main()