This project is a profile based Linux input remapper that uses deterministic evdev device selection and uinput output.
It was built because the available Linux options were not a great fit for this setup, either because features such as autofire were missing or because the overall stack was heavier than desired. The current hardware configuration targets a Razer Tartarus V2 and a Razer Naga V2 HyperSpeed.
You need Rust and Cargo, the uinput kernel module, and permission to read /dev/input/event* and create a virtual input device.
rustc --version
cargo --version
lsmod | grep uinput || sudo modprobe uinputBuild the project from the current repository root.
cargo buildPrint the detected event* devices and their USB identity so you can determine the correct vendor_id, product_id, and interface_number.
cargo run -q -- inspectPrint a short list of detected event* devices when you only need a quick overview or want to choose a target for keycodes or probe.
cargo run -q -- devicesPrint likely [[device]] blocks for config/hardware.toml based on detected devices. This is meant as a starting point for users who are less comfortable inspecting the raw device metadata by hand.
cargo run -q -- hardware
cargo run -q -- hardware --allBy default the command groups interfaces that appear to belong to the same physical device and prints the most likely candidate first. Use --all when you want to inspect every interface. The generated label is the detected device name, so you can keep it as is or rename it to something shorter for your setup. Then use inspect, probe, or keycodes if you need to confirm which interface is the correct one.
Read raw key events from one specific device to confirm which interface actually carries the keys you want to map.
sudo cargo run -q -- probe /dev/input/eventXPrint raw key names and numeric keycodes on key press. When a label such as Tartarus is used, the target device is resolved through config/hardware.toml.
sudo cargo run -q -- keycodes Tartarus
sudo cargo run -q -- keycodes /dev/input/eventX
sudo cargo run -q -- keycodes /dev/input/eventX --no-grabStore the stable hardware identity in config/hardware.toml.
[[device]]
label = "Tartarus"
expected_name = "Razer Razer Tartarus V2"
vendor_id = "1532"
product_id = "022b"
interface_number = 0
[[device]]
label = "Naga"
expected_name = "Razer Razer Naga V2 HyperSpeed"
vendor_id = "1532"
product_id = "00b4"
interface_number = 2Store profile specific mappings in config/profiles/<name>.toml, use fire_ms only on entries that should repeat automatically while the source key stays pressed, and use outputs = [...] for key combinations.
profile = "ffxiv"
[[device]]
label = "Tartarus"
mapping = [
{ input = "KEY_Q", output = "KEY_1", fire_ms = 250 },
{ input = "KEY_UP", output = "KEY_W" },
{ input = "KEY_5", outputs = ["KEY_LEFTSHIFT", "KEY_SEMICOLON"] },
]
[[device]]
label = "Naga"
mapping = [
{ input = "KEY_0", output = "KEY_SPACE" },
]Stop the main remapping program before using probe or keycodes, otherwise the grabbed physical devices may not be readable by the helper command.
Linux input key names are layout agnostic and follow evdev naming, so the correct output key may differ from the printed character on your keyboard layout. Use keycodes on the real keyboard device and map the observed keycode rather than the printed character.
Grab the configured physical devices and expose the mapped output through a virtual keyboard created with uinput, using the default ffxiv profile or the profile name passed on the command line.
sudo cargo run -q -- run
sudo cargo run -q -- run ffxivBuild a release binary before creating a service.
cargo build --releaseCreate a system service if you want firemap to start automatically at boot. Run it as root by default, because firemap needs access to /dev/uinput and /dev/input/event*. Use a user service only if you have already configured those permissions for your account.
[Unit]
Description=firemap
After=multi-user.target
[Service]
Type=simple
WorkingDirectory=/path/to/firemap
ExecStart=/path/to/firemap/target/release/firemap run ffxiv
Restart=on-failure
RestartSec=1
[Install]
WantedBy=multi-user.targetWrite that file to /etc/systemd/system/firemap.service, then reload and enable it. If you have configured sufficient device permissions for a normal user, you can instead place it in ~/.config/systemd/user/ and add User=dummy to the service.
sudo systemctl daemon-reload
sudo systemctl enable --now firemap.service