From 068240e7be5342e6469035ae12682d99b26e2387 Mon Sep 17 00:00:00 2001 From: Antoine Monnet <39551484+AntoineMontane@users.noreply.github.com> Date: Mon, 23 Feb 2026 04:59:49 +0100 Subject: [PATCH] fix(evdev): remove disconnected devices to prevent CPU spin (#242) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When an input device is disconnected, select() reports the fd as readable but read() fails with ENODEV. Previously, the code caught the error but kept the dead fd in the list, causing an infinite busy loop that consumed 100% CPU. Now disconnected devices are removed from both self.devices and self.device_fds when read() fails, preventing the busy loop. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: AntoineMontane Co-authored-by: Claude Opus 4.5 --- src/vocalinux/ui/keyboard_backends/evdev_backend.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/vocalinux/ui/keyboard_backends/evdev_backend.py b/src/vocalinux/ui/keyboard_backends/evdev_backend.py index 46aa5fa..6d4aa9e 100644 --- a/src/vocalinux/ui/keyboard_backends/evdev_backend.py +++ b/src/vocalinux/ui/keyboard_backends/evdev_backend.py @@ -315,8 +315,16 @@ class EvdevKeyboardBackend(KeyboardBackend): if event.type == ecodes.EV_KEY: self._handle_key_event(event, device) - except (OSError, IOError): - # Device was likely disconnected + except (OSError, IOError) as e: + # Device was disconnected - remove it to avoid busy loop + logger.debug(f"Device disconnected (fd={fd}): {e}") + if device is not None: + try: + self.devices.remove(device) + except ValueError: + pass + if fd in self.device_fds: + self.device_fds.remove(fd) continue except (OSError, ValueError) as e: