Fix memory leak in event_reader; autoload presets for all device instances
## Summary
- **Fix memory leak in EventReader**: `read_loop` created a new `asyncio.Task` on every input event to race `events_ready.wait()` against the stop task. For high-frequency devices (mice/trackballs at 125–1000 Hz), this produced billions of short-lived Task objects over days of uptime, fragmenting CPython's pymalloc arenas and causing monotonic RSS growth (~100 MB/day, observed 1.1 GB after 10 days). Replaced with a single `_stop_watcher` task that signals the shared `events_ready` Event, and `await events_ready.wait()` directly.
- **Autoload presets for all instances of the same device model**: When multiple pairing slots on a Logitech Unifying receiver expose the same device name (e.g. "Logitech MX Ergo", "Logitech MX Ergo 2"), only the first group matched the autoload config. `get_autoload_preset` now falls back to matching by the group's base device name, and `autoload()` iterates all discovered groups (filtering out input-remapper virtual devices) so every instance gets the preset.
## Test plan
- [x] Verify injection works for remapped devices after restart
- [ ] Monitor RSS of injector child process over hours/days — should stay flat
- [x] Test with multiple instances of the same device model (e.g. Unifying receiver with multiple slots)
- [ ] Test autoload with single-device config entry matching multiple group instances
- [ ] Verify input-remapper virtual devices are not passed to `_autoload`
合并状态:未合并 关闭于 2026-04-19 2 条评论