From ee494f921cd2235fd42a28f9c57813015a1dd3f8 Mon Sep 17 00:00:00 2001 From: Jatin K Malik Date: Thu, 5 Feb 2026 00:04:19 -0800 Subject: [PATCH] fix(keyboard): verify Ctrl key capability in evdev backend availability check (#128) * fix(keyboard): verify Ctrl key capability in evdev backend availability check The evdev backend's is_available() method was incorrectly returning True when keyboard devices were found, even if the user lacked permission to access them or if the accessible devices didn't have Ctrl key capability. This caused keyboard shortcuts to fail silently on Wayland when the user wasn't in the 'input' group, as the backend was selected but couldn't actually monitor keyboard events. Changes: - Add device_has_ctrl_key() helper to verify a device supports Ctrl keys - Update is_available() to check both device accessibility AND Ctrl key capability - Now correctly returns False when no usable keyboard device is found, triggering proper error messages about permissions This ensures users get clear feedback about missing permissions instead of silently non-working keyboard shortcuts. * fix(install): use sg command to apply input group without logout The install.sh script adds the user to the 'input' group for keyboard shortcuts on Wayland, but this change doesn't take effect until logout. Updated wrapper scripts to use 'sg input -c ...' to run vocalinux with the input group applied, even if the user hasn't logged out yet. This provides a much better user experience as keyboard shortcuts work immediately after installation. The wrapper detects if: 1. User is in /etc/group for 'input' group 2. Current session doesn't have the group active If both conditions are true, it uses 'sg' to apply the group. --- install.sh | 22 +++++++- .../ui/keyboard_backends/evdev_backend.py | 52 +++++++++++++++++-- 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/install.sh b/install.sh index fb8a50e..5e6da32 100755 --- a/install.sh +++ b/install.sh @@ -1019,11 +1019,21 @@ VOSK_CONFIG mkdir -p "$HOME/.local/bin" # Create vocalinux wrapper script + # Uses 'sg input' to run with input group for keyboard shortcuts on Wayland + # This allows shortcuts to work without logging out after installation cat > "$HOME/.local/bin/vocalinux" << WRAPPER_EOF #!/bin/bash # Wrapper script for Vocalinux that sets required environment variables +# and applies the 'input' group for keyboard shortcuts on Wayland export GI_TYPELIB_PATH=/usr/lib/girepository-1.0 -exec "$VENV_DIR/bin/vocalinux" "\$@" + +# Check if user is in input group but current session doesn't have it +if grep -q "^input:.*\b\$(whoami)\b" /etc/group 2>/dev/null && ! groups | grep -q '\binput\b'; then + # Use sg to run with input group without requiring logout + exec sg input -c "$VENV_DIR/bin/vocalinux \$*" +else + exec "$VENV_DIR/bin/vocalinux" "\$@" +fi WRAPPER_EOF chmod +x "$HOME/.local/bin/vocalinux" print_info "Created wrapper: ~/.local/bin/vocalinux" @@ -1032,8 +1042,16 @@ WRAPPER_EOF cat > "$HOME/.local/bin/vocalinux-gui" << WRAPPER_EOF #!/bin/bash # Wrapper script for Vocalinux GUI that sets required environment variables +# and applies the 'input' group for keyboard shortcuts on Wayland export GI_TYPELIB_PATH=/usr/lib/girepository-1.0 -exec "$VENV_DIR/bin/vocalinux-gui" "\$@" + +# Check if user is in input group but current session doesn't have it +if grep -q "^input:.*\b\$(whoami)\b" /etc/group 2>/dev/null && ! groups | grep -q '\binput\b'; then + # Use sg to run with input group without requiring logout + exec sg input -c "$VENV_DIR/bin/vocalinux-gui \$*" +else + exec "$VENV_DIR/bin/vocalinux-gui" "\$@" +fi WRAPPER_EOF chmod +x "$HOME/.local/bin/vocalinux-gui" print_info "Created wrapper: ~/.local/bin/vocalinux-gui" diff --git a/src/vocalinux/ui/keyboard_backends/evdev_backend.py b/src/vocalinux/ui/keyboard_backends/evdev_backend.py index 8129fd5..4164baa 100644 --- a/src/vocalinux/ui/keyboard_backends/evdev_backend.py +++ b/src/vocalinux/ui/keyboard_backends/evdev_backend.py @@ -76,6 +76,36 @@ def find_keyboard_devices() -> List[str]: return keyboard_devices +def device_has_ctrl_key(device_path: str) -> bool: + """ + Check if a device has Ctrl key capability. + + Args: + device_path: Path to the input device + + Returns: + True if the device can send Ctrl key events + """ + if not EVDEV_AVAILABLE: + return False + + try: + device = InputDevice(device_path) + capabilities = device.capabilities() + device.close() + + # Check if device has EV_KEY capability and supports Ctrl keys + if ecodes.EV_KEY in capabilities: + key_caps = capabilities[ecodes.EV_KEY] + # Check for left or right Ctrl key + if KEY_LEFTCTRL in key_caps or KEY_RIGHTCTRL in key_caps: + return True + except (OSError, IOError): + pass + + return False + + class EvdevKeyboardBackend(KeyboardBackend): """ Keyboard backend using python-evdev. @@ -102,14 +132,23 @@ class EvdevKeyboardBackend(KeyboardBackend): logger.error("python-evdev not available") def is_available(self) -> bool: - """Check if evdev is available.""" + """Check if evdev is available and we can access a keyboard device with Ctrl key.""" if not EVDEV_AVAILABLE: return False - # Check if we can access at least one keyboard device + # Check if we can access at least one keyboard device with Ctrl key capability try: devices = find_keyboard_devices() - return len(devices) > 0 + if not devices: + return False + + # Try to find at least one device with Ctrl key that we can open + for device_path in devices: + if device_has_ctrl_key(device_path): + return True + + # Could not find any accessible device with Ctrl key + return False except Exception: return False @@ -292,4 +331,9 @@ class EvdevKeyboardBackend(KeyboardBackend): # Export availability -__all__ = ["EvdevKeyboardBackend", "EVDEV_AVAILABLE", "find_keyboard_devices"] +__all__ = [ + "EvdevKeyboardBackend", + "EVDEV_AVAILABLE", + "find_keyboard_devices", + "device_has_ctrl_key", +]