Linux and Retro Distros · Volume 6
Recoil and Lights on Linux — Network Outputs, QMamehook, and What Has No Route at All
Which emulators publish recoil and lamp outputs on Linux, how QMamehook carries them to the gun's serial port, building and running it on a PC or a Pi, why MAMEHooker and Hook Of The Reaper are Windows answers, and what Batocera does instead
6.1 Two kinds of kick
An OpenFIRE gun has two ways of deciding when to fire its solenoid (Open-Source Firmware, Volumes 5 and 6). On its own, it kicks on every trigger pull while it points at the screen, with the pulse timings set in the App, and repeats while the trigger is held if autofire is on. Handed off to the host over its serial port (the S command), it stops deciding and does what the host tells it: F0x1 to fire, F0x0 to stop, F1x… for the rumble motor, F2–F4 for the LEDs. The first needs nothing on the host and works on every platform in this dive. The second is what makes a machine gun kick per bullet the game actually fires, a shotgun kick only when loaded, or the gun rumble when the player is hit — and on Linux it has exactly one working route.

6.2 Who publishes outputs on Linux
An emulator has to publish the game’s outputs before anything can act on them. Arcade hardware had real lamps and solenoids, so arcade emulators know when they fire; home consoles had none, so console emulators have nothing to publish. On Linux the publishing side looks like this:
Table 1 — An emulator has to publish the game's outputs before anything can act on them. Arcade hardware had real lamps and solenoids, so arcade emulators know when they fire; home consoles had none, so console emulators have nothing to publish. On Linux the publishing side looks like this
| Emulator | Publishes outputs on Linux? | How | Source |
|---|---|---|---|
| MAME standalone | Yes | output network in mame.ini; TCP port 8000 (“network port is fixed at 8000”) | MAME documentation; the network module is in every build |
| MAME (current) RetroArch core | Yes | the core reads a mame.ini with output network; “System/Read Configuration” on, “System/MAME INI Paths” off, the ini in $RETROARCHDIR/system/mame/ini/ | QMamehook README |
| Flycast standalone | Yes, arcade games, but unnamed | ”Broadcast Digital Outputs” — “Broadcast digital outputs and force-feedback state on TCP port 8000. Compatible with the “-output network” MAME option. Arcade games only.” The outputs are the NAOMI board’s raw output bits (lamp0, lamp1 …); a game’s gun recoil, if it drives one, is one of those bits, unnamed (PC (Windows), Volume 4) | Flycast core/ui/settings_network.cpp |
| Supermodel (Model 3) | Lamps only, no gun recoil | Outputs = net (Global section), with OutputsTCPPort and OutputsWithLF; “win” is “only on Windows”. What it publishes is lamps, the drive board and billboard digits; there is no gun recoil output (PC (Windows), Volume 4, read from Src/OSD/Outputs.cpp) | Supermodel README and source |
| Flycast RetroArch core | No | Its “Broadcast Digital Outputs” option “doesn’t currently work” (QMamehook README); libretro/flycast issue #1189, “[Linux] Device Outputs aren’t being sent”, open since January 2024 | |
| Console cores (NES, SNES, Mega Drive, PlayStation, Saturn …) | No | The consoles had no outputs to emulate; the libretro API’s only feedback channel is rumble to a gamepad | libretro.h |
| DemulShooter | Only for Windows games under Wine | network outputs from DemulShooter running in the same Wine prefix as the game | QMamehook README |
| TeknoParrot | No | ”not supported until they also follow MAME’s network output standard” | QMamehook README |
Supermodel publishes “MAME Hooker compatible outputs” over the network, but they are its cabinet lamps, not a gun kick, and QMamehook’s README does not list it among the programs it works with; The Lost World and the other Model 3 gun games get the gun’s own trigger-driven recoil on Linux as on Windows. Standalone Flycast’s bits can be driven through QMamehook if the bit that carries a given game’s recoil is identified (QMamehook’s -v mode shows them toggling); on Windows DemulShooter names them, and on Linux nothing does.
What this means in practice: on Linux, game-driven recoil exists for MAME-emulated arcade games (the large majority of arcade gun games — see Volume 7) and, with per-game work to find the right output bit, for NAOMI and Atomiswave games under standalone Flycast. Every console gun game uses the gun’s own trigger-driven recoil, on Linux and on every other platform, because there is nothing for a hook to hear.
6.3 QMamehook on Linux
QMamehook, by OpenFIRE’s maintainer, is the only hook program that runs natively on Linux and speaks the gun’s serial dialect. Its README is candid about why it exists: it is “primarily for Linux: which has no options for this specific niche (Linux home light gun users wanting native force feedback)”. It reads MAMEHooker’s per-game .ini files “verbatim”, which is why the ini anatomy in Open-Source Firmware, Volume 6, section 6.9 applies unchanged.
Getting it. QMamehook (LGPL-2.1) is at v2.2, released 12 August 2025, but its v2.x releases carry only a Windows zip. The last Linux binary is QMamehook-ubuntu in v1.8.2 (27 May 2024), built for x86-64 (“built for Ubuntu 20.04 LTS, but should work for most distros?”). Arch users have an AUR package, qmamehook. Everyone else — and every Raspberry Pi — builds it, which the README makes short:
# needs Qt 5 or 6 base and serial-port development packages, and CMake
git clone https://github.com/SeongGino/QMamehook
cd QMamehook && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make
Running it. “Just run the QMamehook executable in a terminal; send an interrupt signal (or pkill QMamehook) to stop it.” The user needs the dialout group (or the uaccess rule in Volume 1, section 1.8). Useful switches: -v “will enable verbose output, which prints the exact output stream being received from the server app”, which is the way to see what a game publishes; -p <dir> overrides the ini directory, which by default is ~/.config/QMamehook/ini.
How it finds the guns, and the two rules that follow. The README: “Serial devices must be plugged in at runtime in order to work! … Currently, only devices bearing the OpenFIRE, GUN4IR. or Blamcon vendor IDs will be detected.” And on player numbers: writes to a port number “correlating to the intended player/slot number (usually 1-4) … the index is based on the count of verified COM devices detected (meaning always starting from “1”), not their ports.” So:
- Plug the guns in before starting QMamehook, and in player order, because “player 1” in an ini file means “the first OpenFIRE-family gun QMamehook found”.
- Keep vendor ID 0xF143 on the guns. A gun with the project’s future vendor ID (decision D24) is invisible to QMamehook until QMamehook is taught it — the same conclusion Open-Source Firmware, Volume 6 reached for MiSTer and the App.
Its ini files. QMamehook writes a new ini for any game it has not seen, filled with the outputs the game publishes (“configs are generated with whatever outputs are detected in the game session”), and reports whether it found a matching file when the game’s mame_start message arrives. Edit the generated file with the gun’s commands, using the wiki’s examples (P1_GunRecoil=cmw 1 F0x%s%) but the output names the game actually publishes: MAME names its outputs per driver, so Terminator 2 needs Left_Gun_Recoil=cmw 1 F0x%s%, not P1_GunRecoil, which is DemulShooter’s convention (PC (Windows), Volume 2, section 2.10 has the table).
A known nuisance. The README’s own known issue: QMamehook does not clean up its serial ports when killed, so “QMamehook must be closed while no game/config is open”, or the guns unplugged and replugged before it is started again.
6.4 Running it with the emulators
Because the guns must be present when it starts and it must be stopped between games’ configs, QMamehook is best started once, after the guns, and left running for the session. A user service does that on a desktop; the unit below is written for this dive from the README’s instructions and has not been run:
# ~/.config/systemd/user/qmamehook.service (proposed; untested)
[Unit]
Description=QMamehook MAME network output client for light guns
[Service]
ExecStart=%h/bin/QMamehook
Restart=on-failure
[Install]
WantedBy=default.target
systemctl --user enable --now qmamehook starts it at log-in. The emulator side, once:
- MAME:
output networkinmame.ini(and, on Linux, the light-gun provider of Volume 2, section 2.6). - MAME core in RetroArch: the same line in the core’s own
mame.ini, with the two core options the README names. - Flycast standalone: enable Broadcast Digital Outputs.
Then a test that needs no game: with QMamehook stopped, the echo bench test in Volume 1, section 1.7 proves the gun’s serial side; with QMamehook running in verbose mode and a MAME gun game started, the terminal should show the game’s outputs streaming and the gun should kick when the game fires.
6.5 The Windows hook programs, and Wine
MAMEHooker (v5.1) is the long-standing Windows hook. The OpenFIRE wiki’s Linux note: it needs a Wine prefix with the vbrun6 runtime and “will only work with games running in the same prefix”, with COM ports mapped to /dev/ttyACM<n> in the prefix’s registry (Open-Source Firmware, Volume 6, section 6.9). It listens for MAME’s output windows messages, which only a Windows MAME sends, so on Linux it is useful only for Windows games run under Wine through DemulShooter.
Hook Of The Reaper (Fusion-Lightguns, GPL-3.0) is a modern, Qt-based MAMEHooker replacement that lists OpenFIRE among its “default light guns”. Qt would allow a Linux build, but its README builds it with “Qt 6.8.1 with a CMake File with MSVC 2022” and lists being “platform independent” as a goal marked “(Now Low Priority)”. There is no Linux release.
DemulShooter, the Windows input-and-outputs layer for PC arcade games, can feed QMamehook from Wine, but only for the Windows titles it supports, running in the same Wine prefix (QMamehook README). For TeknoParrot’s games there is no Linux route.
6.6 Batocera and LEDSpicer
Batocera ships no hook program (a search of its tree on 20 October 2026 found no QMamehook, MAMEHooker or network-output setting, and its MAME launcher passes no -output). On Batocera the OpenFIRE gun’s recoil is therefore the firmware’s own, trigger-driven (Volume 3, section 3.8). Adding QMamehook to Batocera would mean a self-built binary and a start script in /userdata/system/scripts/, which receives gameStart and gameStop with the system and emulator names; Batocera’s build does not include the Qt serial-port module QMamehook needs, so the binary would have to bring its own. That is an unsupported modification and has not been tried.
Batocera does include LEDSpicer (v0.7.6), which the QMamehook README itself suggests “for more elaborate setups” on Linux. It is a lighting controller for arcade cabinets — Batocera builds it with drivers for Ultimarc’s PacDrive, PacLED64, NanoLed and Ultimate I/O, the LedWiz, the Howler and Adalight — and it is the right tool for a cabinet’s button lamps. It does not speak the OpenFIRE command set.
6.7 Safety when the game drives the solenoid
Game-driven recoil hands the solenoid’s on-time to software the project does not control. Two protections already exist, both covered elsewhere: the firmware forces the solenoid off 2000 ms after an F0x1 that is never followed by F0x0 (Open-Source Firmware, Volume 6, section 6.8), and the project’s solenoid stage regulates current (decision D13; Solenoid Recoil). Whether two seconds at full current is safe for the chosen coil is a question for those dives. The Linux-specific point is simpler: a hook program that crashes with the solenoid on leaves it on until the firmware’s timeout, and QMamehook’s own known issue is that it does not clean up on exit. Use F0x2x<n> (pulse trains at the gun’s own timings) in ini files wherever a game’s output is a held state rather than a per-shot event — and the proposed project rule goes further: PC (Windows), Volume 6, section 6.8 proposes (decision PW-3) pulsing on the output’s “on” state with the F0x0|F0x2x1 form for every game, never an F0x1 hold, and that ini line works unchanged in QMamehook; Open-Source Firmware, Volume 6 explains why v5.2 made pulse trains follow the gun’s timer settings.
6.8 Why this, and why not the others
The question: which hook program carries game-driven recoil to the gun on Linux?
Table 2 — 6.8 Why this, and why not the others
| Option | Verdict | Why |
|---|---|---|
| QMamehook | Use | Native; reads MAMEHooker inis verbatim; written by OpenFIRE’s maintainer for this gun family; LGPL-2.1 |
| MAMEHooker in Wine | Only for Windows games in Wine | Needs output windows, which Linux MAME does not send |
| Hook Of The Reaper | Not on Linux | No Linux build; built with MSVC |
| LEDSpicer | Not for the gun | Cabinet LED controllers only |
| No hook (firmware recoil) | The default everywhere | Works with every game; per pull, not per shot fired |
The question: which emulators to rely on for game-driven recoil?
Table 3 — 6.8 Why this, and why not the others
| Option | Verdict | Why |
|---|---|---|
| MAME (standalone or the current core) | Use | output network works on Linux; most arcade gun games |
| Flycast standalone | Try for single-player NAOMI and Atomiswave | Network outputs for arcade games, but raw unnamed bits: find each game’s recoil bit first |
| Flycast core | Avoid for recoil | Outputs broken on Linux (issue #1189) |
| Supermodel | No game-driven recoil | Publishes lamps only, no gun recoil output |
6.9 What could not be verified
- QMamehook has not been built or run for this dive, on a PC or a Pi.
- Which of standalone Flycast’s output bits carries each NAOMI and Atomiswave game’s recoil (6.2).
- The systemd unit in 6.4.
- Whether a QMamehook binary can be made to run on Batocera.
Sources
- That One Seong, QMamehook README and releases (v1.0–v2.2), github.com/SeongGino/QMamehook — read 2026-10-20.
- TeamOpenFIRE, OpenFIRE-Firmware wiki, MAMEHOOKER Documentation and Home — read 2026-10-20.
- MAME,
docs/source/commandline/commandline-all.rst(-output),src/osd/modules/lib/osdobj_common.cpp— read 2026-10-20. - Supermodel,
Docs/README.txt(Outputs,OutputsTCPPort,OutputsWithLF) andSrc/OSD/Outputs.cpp, trzy/supermodel master — read 2026-10-20 (the source reading is PC (Windows) Volume 4’s). - libretro/flycast issue #1189, “[Linux] Device Outputs aren’t being sent” (opened 2024-01-07; open 2026-10-20).
- libretro,
libretro-common/include/libretro.h(RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE) — read 2026-10-20. - Fusion-Lightguns, Hook Of The Reaper README, github.com/Fusion-Lightguns/Hook-Of-The-Reaper — read 2026-10-20.
- Batocera,
package/batocera/utils/ledspicer/ledspicer.mk;python-src/batocera-launch-mame/batocera_launch_mame/emulator.py; wiki Launch a script — read 2026-10-20. - Flycast,
core/ui/settings_network.cpp(the Broadcast Digital Outputs option and its help text), flyinghead/flycast master — read 2026-10-20.
Comments (0)