From bfc044c1b0469df81dbce810caae8065ac76e8a8 Mon Sep 17 00:00:00 2001 From: DeeJanuz <45082401+DeeJanuz@users.noreply.github.com> Date: Thu, 1 Oct 2026 09:38:32 -0600 Subject: [PATCH] Try every volume key when one swap fails remap_volume stopped at the first EVIOCSKEYCODE_V2 that failed, so the volume entries after it were never tried. On a keyboard where swapping volume up failed, volume down stayed a real key that gamescope reads, and one press with nothing focused aborts gamescope and the VR session. Restoring had the same hole: a failed entry left the later stand-ins in place after the relay exited. Now every entry is tried and the first failure is raised at the end. take_volume already marks the device remapped on that error, so the relay routes the stand-ins that did swap and restores them on exit. Checked against a fake keymap with a stubbed fcntl.ioctl: with volume up's swap failing, volume down now swaps (it stayed real before), the same for restore, and full swaps, restores and the no-keymap case come out as before. Found by 0x1f6 in PR #8. Co-Authored-By: Claude Opus 5.5 --- input/input-relay.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/input/input-relay.py b/input/input-relay.py index fb0a491..dc8793d 100755 --- a/input/input-relay.py +++ b/input/input-relay.py @@ -324,21 +324,34 @@ def remap_volume(fd, restore=False): Returns how many keymap entries are volume keys or stand-ins, or None when the device has no keymap to change (uinput devices, some platform buttons). + + A swap that fails doesn't stop the others: every entry is still tried, then the + first failure is raised. The entries that did swap stay swapped, for the caller + to handle their stand-ins and restore them. """ swap = VOLUME_ORIGINAL if restore else VOLUME_STANDIN found = 0 + failed = None # the first swap that failed for index in range(8192): entry = bytearray(KEYMAP_ENTRY.pack(INPUT_KEYMAP_BY_INDEX, 0, index, 0, b"")) try: fcntl.ioctl(fd, EVIOCGKEYCODE_V2, entry) except OSError: - return found if index else None # past the last entry + if not index: + return None + break # past the last entry _, length, _, code, scancode = KEYMAP_ENTRY.unpack(entry) if code in VOLUME_CODES: found += 1 if code in swap: - fcntl.ioctl(fd, EVIOCSKEYCODE_V2, - KEYMAP_ENTRY.pack(INPUT_KEYMAP_BY_INDEX, length, index, swap[code], scancode)) + try: + fcntl.ioctl(fd, EVIOCSKEYCODE_V2, + KEYMAP_ENTRY.pack(INPUT_KEYMAP_BY_INDEX, length, index, swap[code], scancode)) + except OSError as e: + if failed is None: + failed = e + if failed is not None: + raise failed return found