feat: initial implementation, refs NOISSUE
This commit is contained in:
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
evdev
|
||||||
8
usev_device_access_permissions.sh
Normal file
8
usev_device_access_permissions.sh
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
sudo tee /etc/udev/rules.d/99-volume-key.rules > /dev/null <<'EOF'
|
||||||
|
# Grant read permission for the Volume‑Up key device
|
||||||
|
SUBSYSTEM=="input", ATTRS{name}=="*volume*", MODE="0660", GROUP="input"
|
||||||
|
EOF
|
||||||
|
# Replace *volume* with a substring that matches your device’s name (e.g. “Logitech”) if the rule above doesn’t apply.
|
||||||
|
sudo udevadm control --reload
|
||||||
9
volume-remapper.service
Normal file
9
volume-remapper.service
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Volume‑up key remapper
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/path/to/volume_remapper.py
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
82
volume_remapper.py
Executable file
82
volume_remapper.py
Executable file
@@ -0,0 +1,82 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
volume_remapper.py
|
||||||
|
------------------
|
||||||
|
Listen for Volume‑Up key events and emit a different key code
|
||||||
|
(“Ctrl+Shift+M” by default) so the focused application receives it.
|
||||||
|
|
||||||
|
Dependencies: python‑evdev (pip install evdev)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
from evdev import InputDevice, categorize, ecodes, UInput, list_devices
|
||||||
|
import time
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------ #
|
||||||
|
# Helpers
|
||||||
|
# ------------------------------------------------------------------ #
|
||||||
|
|
||||||
|
def find_device(name_substr="volume"):
|
||||||
|
"""Return the first InputDevice that contains a KEY_VOLUMEUP event."""
|
||||||
|
for dev_path in [d.path for d in list_devices()]:
|
||||||
|
dev = InputDevice(dev_path)
|
||||||
|
caps = dev.capabilities()
|
||||||
|
if ecodes.EV_KEY in caps and ecodes.KEY_VOLUMEUP in caps[ecodes.EV_KEY]:
|
||||||
|
if name_substr.lower() in dev.name.lower():
|
||||||
|
return dev
|
||||||
|
return None
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------ #
|
||||||
|
# Core logic
|
||||||
|
# ------------------------------------------------------------------ #
|
||||||
|
|
||||||
|
def main(remap_keys, device=None):
|
||||||
|
"""
|
||||||
|
:param remap_keys: tuple of ecodes.KEY_* codes to emit (e.g. (ecodes.KEY_LCTRL, ecodes.KEY_LSHIFT, ecodes.KEY_M))
|
||||||
|
:param device: path to /dev/input/eventX or None to auto‑detect
|
||||||
|
"""
|
||||||
|
if device is None:
|
||||||
|
dev = find_device()
|
||||||
|
if dev is None:
|
||||||
|
print("⚠️ No device with a Volume‑Up key found.", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
dev = InputDevice(device)
|
||||||
|
|
||||||
|
print(f"[INFO] Listening on {dev.path} ({dev.name}) ...")
|
||||||
|
print(f"[INFO] Remapping to: {', '.join(ecodes.KEY.get(k) for k in remap_keys)}")
|
||||||
|
|
||||||
|
# Create a virtual keyboard (uinput)
|
||||||
|
ui = UInput(events=[
|
||||||
|
ecodes.EV_KEY, # only key events
|
||||||
|
ecodes.EV_SYN
|
||||||
|
], name='volume‑remapper')
|
||||||
|
|
||||||
|
try:
|
||||||
|
for event in dev.read_loop():
|
||||||
|
if event.type == ecodes.EV_KEY:
|
||||||
|
if event.code == ecodes.KEY_VOLUMEUP and event.value == 1: # key_down
|
||||||
|
# Emit the mapped key sequence
|
||||||
|
for key in remap_keys:
|
||||||
|
ui.write(ecodes.EV_KEY, key, 1) # key down
|
||||||
|
ui.write(ecodes.EV_KEY, key, 0) # key up
|
||||||
|
ui.syn()
|
||||||
|
print(f"✅ Mapped Volume‑Up → {', '.join(ecodes.KEY.get(k) for k in remap_keys)}")
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n[INFO] Stopping …")
|
||||||
|
finally:
|
||||||
|
ui.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description="Remap Volume‑Up to any key combo.")
|
||||||
|
parser.add_argument("--device", help="Optional /dev/input/eventX path to listen on.")
|
||||||
|
parser.add_argument("--to", nargs="+", default=["KEY_LCTRL", "KEY_LSHIFT", "KEY_M"],
|
||||||
|
help="Space‑separated key names to emit. Default: Ctrl+Shift+M")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Convert key names to ecodes
|
||||||
|
remap = [ecodes.KEY[code.upper()] for code in args.to]
|
||||||
|
main(remap_keys=remap, device=args.device)
|
||||||
Reference in New Issue
Block a user