Linux equivalent of HidHide — hide USB and Bluetooth input devices (HOTAS, rudder pedals, flight sticks, gamepads) from games at the kernel level using eBPF LSM.
Problem: Your HOTAS shows up as a game controller in every game. Playing Elden Ring with rudder pedals plugged in causes menu scrolling. A Bluetooth gamepad connected for one game interferes with another. On Windows, HidHide solves this. On Linux, nothing existed — until now.
Solution: ArchJoyHide uses eBPF LSM to intercept device access at the kernel level. Per-device rules let you whitelist or blacklist specific games. A background process watcher automatically detects game launches (including Wine/Proton) and manages access. Works with both USB and Bluetooth input devices.
- Kernel-level device hiding via eBPF LSM (
file_openhook) - USB and Bluetooth support — discovers and manages both transport types
- Per-device rules — whitelist or blacklist mode for each device
- Multiple identical controllers — each physical device is tracked separately by unique ID (BT MAC address or USB serial/bus path), so two of the same gamepad get independent rules
- Wine/Proton support — automatically detects game process trees including
winedevice.exe - GUI (PyQt6) — device list with toggles, app management, system tray
- CLI — full control from the terminal
- Persistent rules — saved to config, auto-applied on boot via systemd
- Process watcher — monitors game launches by exe path, adds entire process trees to allow/deny maps
- No kernel module needed — pure eBPF, works on any kernel with BPF LSM enabled
┌───────────────────────────────────────┐
│ GUI (PyQt6) / CLI │
│ Configure per-device rules │
│ Process watcher detects game launches│
└──────────────┬────────────────────────┘
│
┌──────────────▼────────────────────────┐
│ Loader (C + libbpf) │
│ Loads eBPF programs at boot │
│ Pins maps to /run/archjh/ │
└──────────────┬────────────────────────┘
│
┌──────────────▼────────────────────────┐
│ eBPF Programs │
│ lsm/file_open — gate device access│
│ sched_process_fork — inherit PIDs │
│ sched_process_exit — cleanup PIDs │
└───────────────────────────────────────┘
| Mode | Behavior | Empty app list |
|---|---|---|
| Off | No rules. Device visible to everything. | — |
| Whitelist | Device hidden from everything EXCEPT listed apps. | Hidden from everything. |
| Blacklist | Device visible to everything EXCEPT listed apps. | Same as Off. |
When you add a game exe path (e.g. /mnt/gaming/.../DCS.exe), the process watcher:
- Polls
/procfor processes whose cmdline contains the path - Walks up the process tree to find the Wine/Proton root
- Adds the entire process tree (including
winedevice.exe) to the BPF allow/deny map - The BPF fork tracepoint propagates access to any new child processes
This means Wine's shared winedevice.exe (which actually reads input devices) gets the correct access, even though it's a sibling of the game process, not a child.
- Linux kernel with BPF LSM enabled (
CONFIG_BPF_LSM=y,bpfin LSM list) - CachyOS, Arch, and most modern distros have this by default
clang,bpftool,libbpf- Python 3.10+,
python-yaml,python-pyqt6
zcat /proc/config.gz | grep BPF_LSM
# CONFIG_BPF_LSM=y
cat /sys/kernel/security/lsm
# should contain "bpf"yay -S archjh
sudo systemctl enable --now archjhUpdates automatically with yay -Syu.
# Install dependencies
sudo pacman -S clang bpf libbpf python-yaml python-pyqt6
# Clone and install
git clone https://github.com/defconxt/ArchJoyHide.git
cd ArchJoyHide
sudo ./install.shUpdate with git pull && sudo ./install.sh.
# Enable the BPF loader service (runs at boot, auto-applies saved rules)
sudo systemctl enable --now archjh
# Launch the GUI
archjhLaunch from your app menu or run:
archjh- Find your device in the list
- Set mode to Whitelist or Blacklist
- Click + Add App and browse to the game's
.exe - Click Apply Rules
- Launch your game — rules must be active before the game starts
The GUI minimizes to the system tray. The process watcher runs in the background.
archjh status # Show rules and BPF status
archjh devices # List detected input devices
archjh whitelist 231d:011f:SER123 /path/to/DCS.exe # Whitelist device for DCS
archjh blacklist 054c:05c4:aa:bb:cc:dd:ee:ff /path/to/game.exe # Blacklist by device ID
archjh disable 231d:011f:SER123 # Disable rules for device
archjh run -- /path/to/app # Run app with device access
archjh watch # Run process watcher standaloneDevice IDs use the format vid:pid:uniq where uniq is the Bluetooth MAC address or USB serial/bus path. Run archjh devices to see the full device IDs for your hardware.
sudo systemctl status archjh # Check service status
sudo systemctl restart archjh # Restart (reloads BPF + applies rules)
sudo systemctl stop archjh # Stop (removes all hiding)Rules are saved to ~/.config/archjh/config.yaml:
devices:
"231d:011f:A02B2515W08":
name: "VKBSim T-Rudder"
mode: whitelist
apps:
- /mnt/gaming/dcs-world/drive_c/Program Files/Eagle Dynamics/DCS World/bin-mt/DCS.exe
- /mnt/gaming/star-citizen/drive_c/Program Files/StarCitizen/LIVE/StarCitizen.exe
"054c:05c4:1c:a0:b8:ff:22:a3":
name: "Wireless Controller"
mode: whitelist
transport: bluetooth
apps:
- SO2R.exe
"054c:05c4:aa:bb:cc:dd:ee:ff":
name: "Wireless Controller"
mode: "off"
transport: bluetooth
apps: []Device keys use the format vid:pid:uniq — the unique identifier is a Bluetooth MAC address, USB serial number, or USB bus path. This allows multiple identical controllers (same model) to have independent rules.
Upgrading from v1.1.0 or earlier? Old configs with
vid:pidkeys are automatically migrated to the new format when your devices are connected. No manual editing needed.
sudo ./uninstall.shConfig at ~/.config/archjh/ is preserved.
| Windows HidHide | ArchJoyHide | |
|---|---|---|
| Mechanism | Kernel-mode filter driver | eBPF LSM |
| Install | DKMS/driver signing | No kernel module needed |
| Kernel updates | May break | Portable via CO-RE |
| Per-process filtering | By exe path | By exe path + process tree detection |
| Wine/Proton | N/A | Full support with automatic process tree walking |
GPL-2.0