Linux and Retro Distros · Volume 2

RetroArch and MAME on Linux — Input Drivers, Mouse Indexes, Light-Gun Binds and Latency

Which RetroArch input driver sees two guns and which sees one, how a player port finds its gun, the binds that must be set by hand, MAME's sdl, x11 and udev light-gun providers, and what run-ahead and frame delay can and cannot buy

2.1 Which input driver

RetroArch separates the input driver (keyboard, mouse, pointer and light gun) from the controller driver (gamepads). For a light gun only the input driver matters, and on Linux the choice decides whether two guns are possible at all. RetroArch’s own table, from its documentation (current master):

Table 1 — RetroArch separates the input driver (keyboard, mouse, pointer and light gun) from the controller driver (gamepads). For a light gun only the input driver matters, and on Linux the choice decides whether two guns are possible at all. RetroArch's own table, from its documentation (current master)

Input driverWhen it appliesMultiple miceLight-gun device
udev”No desktop environment (KMS) or X11”YesYes
xX11 desktop, OpenGL or Vulkan videoYes, after RetroArch 1.20.0, built with XInputYes
waylandWayland desktopNo”Yes, button map is fixed”
sdlSDL or SDL2 video driverNo”Yes, button map is fixed”
linuxrawKMS”No mouse support at all”No

Two details from the source sharpen the table. The wayland driver’s code says of the light gun: “All ports report the same lightgun: its buttons are the mouse’s”, and it answers Reload with the middle mouse button, not the right — so on a Wayland desktop with the wayland driver, an OpenFIRE gun’s Button A (right mouse) is not reload; Button B (middle) is. A comment beside it explains why there is only one: “The one system-wide mouse is every port’s; several would be several Wayland seats”. And the udev driver, although documented for KMS or X11, reads /dev/input/event* itself and needs no help from the display server at all, which is why it is the driver every light-gun distribution uses: Batocera’s launcher writes input_driver = udev with the comment “udev required for guns”, and RetroPie’s documentation names it the one Linux driver with multi-mouse support and the RetroPie default.

For this gun: use udev. The rest of this volume assumes it.

Figure 1 — How RetroArch finds each player's gun with the udev input driver. The driver lists every mouse-like device it can open and numbers them in the order it finds them; each player port reads the device…
Figure 1 — How RetroArch finds each player's gun with the udev input driver. The driver lists every mouse-like device it can open and numbers them in the order it finds them; each player port reads the device at its own Mouse Index. Unplugging or adding any mouse renumbers everything after it, which is why the RetroPie documentation warns that indexes "ratchet down". Own-work diagram.

2.2 The udev driver and the mouse index

Set-up is two lines in retroarch.cfg plus permissions:

input_driver = "udev"
input_player1_mouse_index = "0"      # which mouse device player 1 reads
input_player2_mouse_index = "1"

The driver needs read access to the event nodes: the input group or the uaccess rule in Volume 1, section 1.8. Without it, RetroArch runs but sees no mice and no keyboards at all.

How a port finds its gun. The driver opens every input device udev tags as a mouse, touchpad or touch screen and numbers them in the order it adds them. Each player port then reads the device at its own index: udev_get_mouse() looks up input_mouse_port_index(port) in the driver’s list. The default index for player n is n − 1 (configuration.c). To find which index is which gun, RetroPie’s documentation gives the reliable method: launch with verbose logging and read the lines the driver prints,

[INFO] [udev]: Added Device mouse#0 ... (/dev/input/event9)
[INFO] [udev]: Added Device mouse#1 USB Laser Game Mouse (/dev/input/event14)

and look for the gun’s name (OpenFIRE FIRECon P1 Mouse). The catch, in RetroPie’s words: “The index of a specific device may change depending on what device you have attached to which port … if you boot the same system without the external mouse attached, everything might ratchet down.” A keyboard with a built-in touchpad, a wireless keyboard’s dongle, even a gamepad that exposes a mouse function, all take index numbers. Batocera avoids the problem by computing the index at every launch from the udev database and writing it into the config (Volume 3).

How the aim is scaled. For an absolute device the driver reads the axis range with EVIOCGABS(ABS_X) and EVIOCGABS(ABS_Y) when it adds the device, then scales the reported position onto the viewport: width × (x − x_min) / (x_max − x_min + 1). Because the range comes from the device, OpenFIRE’s 0–32767 needs no configuration. Off-screen is decided by RetroArch from the scaled position (input_driver_pointer_is_offscreen()), so a gun pinned at an edge of its range — which is how OpenFIRE signals off-screen — reads as off-screen to the core.

2.3 Light-gun binds

RetroArch’s light-gun buttons are separate binds, not the gamepad’s, and only two have defaults (config.def.keybinds.h):

Table 2 — RetroArch's light-gun buttons are separate binds, not the gamepad's, and only two have defaults (config.def.keybinds.h)

RetroArch bindDefaultOpenFIRE input that sends it by defaultSet
Triggerleft mouse buttonTrigger—
Reload (“Forced off-screen shot”)right mouse buttonButton A (and the pump action)—
Aux AunboundButton B sends middle mousebind to mouse 3
Aux BunboundButton C sends mouse 4bind to mouse 4
Aux CunboundAlt pedal sends mouse 5bind to mouse 5, if fitted
StartunboundStart sends key 1 (P1) / 2 (P2)bind to that key
SelectunboundSelect sends key 5 (P1) / 6 (P2)bind to that key
D-padunboundarrow keys or gamepad D-pad (firmware table differs by version)bind if used

The OpenFIRE defaults are from Open-Source Firmware, Volume 6, section 6.3. Start and Select arrive from the gun’s keyboard node as ordinary keys, so they are bound as keyboard keys in each player’s light-gun binds; the identity presets give each gun its own pair (1 and 5, 2 and 6, …) precisely so that player 2’s Start is not player 1’s. In retroarch.cfg the binds are input_player<n>_gun_trigger_mbtn, ..._gun_offscreen_shot_mbtn, ..._gun_aux_a_mbtn and so on, with _btn, _axis and plain keyboard variants.

A core only uses these binds when its port’s device type is a light gun (Zapper, Super Scope, GunCon and the like, section 2.5). With the device type left on a gamepad, the gun’s mouse buttons do nothing in the game.

2.4 Two guns on an X11 desktop: the x driver

On a desktop running X11 rather than Wayland, the x driver can also give one gun per player, by a route that sounds odd but is X’s own: create a second master pointer and move the second gun onto it. RetroArch’s documentation: “Use xinput create-master and xinput reattach commands to set up a second pointer and assign the physical device.” The driver tracks each master pointer’s position separately (x11_input.c keeps a position per master pointer, from XInput 2 events). The steps:

xinput list                                   # find "OpenFIRE FIRECon P2 Mouse" and its id
xinput create-master Gun2                     # adds "Gun2 pointer" and "Gun2 keyboard"
xinput reattach <id-of-P2-mouse> "Gun2 pointer"

The second gun then moves its own cursor on the desktop, and RetroArch maps master pointers to player ports. The arrangement does not survive a re-plug or log-out without a script, and nothing else on the desktop expects two cursors. It is the fallback for a desktop that must stay on X11; udev is simpler and does not depend on the session.

2.5 Cores

Which cores take a gun, and the core options that make them work, are the same on Linux and Windows and are set out in PC (Windows). The Linux-specific points:

  • The core decides nothing about the platform. A core asks the frontend for RETRO_DEVICE_LIGHTGUN positions; the input driver answers. Everything in sections 2.1–2.4 applies to every core.
  • MAME (current) as a core has its own switch, mame_lightgun_mode, with values none, lightgun and touchscreen and default none (libretro_core_options.h). Left at the default, the core ignores the gun. A companion option sets where an off-screen gun is reported (free, top left, bottom right).
  • Flycast as a core has a known Linux gap for force feedback (Volume 6): its “Broadcast Digital Outputs” option does not send outputs on Linux (libretro/flycast issue #1189, open since January 2024).
  • Batocera’s per-system table of which core takes a gun, with the version that added it, is the most complete public record for Linux and is reproduced in Volume 7.

2.6 MAME standalone on Linux

MAME’s light-gun input on Linux comes from one of its providers, chosen with -lightgunprovider. Its documentation lists, for Linux: auto (which on SDL builds means sdl), sdl, x11 and none.

Table 3 — 2.6 MAME standalone on Linux

ProviderGunsHowUse
sdl (the default)OneThe system pointerOne player, any session
x11Up to 8, by nameX’s input extension, per device; each slot is named with -lightgun_index1 … -lightgun_index8Two guns on an X11 desktop
udevSeveralReads the event nodesBatocera only — added by Batocera’s patch, absent upstream

The x11 provider maps a gun only if its slot is named. The code compares the configured name with the X device names after removing spaces, or takes a number as an X device ID, and warns “There are multiple devices named … please use the device ID instead” if two match. With presets P1 and P2:

# mame.ini (Linux, X11 session) — two OpenFIRE guns
lightgun                  1
lightgunprovider          x11
lightgun_index1           "OpenFIRE FIRECon P1 Mouse"
lightgun_index2           "OpenFIRE FIRECon P2 Mouse"
output                    network

xinput list shows the exact names. output network publishes the game’s recoil and lamp outputs on TCP port 8000 for QMamehook (Volume 6); MAME’s Linux builds include the network output module. MAME has no off-screen-reload command-line option; it has a Lua plugin, offscreenreload (“Off-screen reload helper plugin”, by Vas Crabb, off by default), enabled with -plugin offscreenreload and configured per game from the plugin’s menu. Batocera patches the plugin to configure itself — it binds each player’s GUNCODE_<n>_BUTTON2 as the off-screen reload — and turns it on from its OFF-SCREEN RELOAD option (Volume 3). The alternative is to let the gun do it, with OpenFIRE’s off-screen button mode (Open-Source Firmware, Volume 6, section 6.3). Crosshair options behave as on Windows (PC (Windows)). MAME’s documentation also warns that “most lightguns also produce mouse input, so enabling mouse and lightgun controllers simultaneously … may produce strange behaviour”, which applies here unchanged.

Under Wayland the x11 provider is not a supported route: it is an X11 input module and a Wayland desktop gives an X program only XWayland’s emulated devices. Whether XWayland exposes each physical gun as a separate XInput device was not tested for this dive; the safe statement is that MAME standalone gets two guns on Linux either in an X11 session or on Batocera.

2.7 Latency: what the settings do and what they cost

A light gun is less forgiving of lag than a gamepad, because the player sees the crosshair or the hit land behind where the gun points. The firmware side is already fast: OpenFIRE reads the camera and recomputes the aim point on a 209 Hz timer, under 5 ms per update (Open-Source Firmware, Volume 2), a fraction of one 16.7 ms frame. What remains is the emulator, the video path and the display.

RetroArch’s documentation states the ceiling: with its settings, “next-frame response time (≤16ms!)” is achievable, and, quoting a researcher, “At 60fps (16ms) this could be anywhere from 0ms to 16ms, which averages out to about 8ms”. The tools, and what each costs:

Table 4 — RetroArch's documentation states the ceiling: with its settings, "next-frame response time (≤16ms!)" is achievable, and, quoting a researcher, "At 60fps (16ms) this could be anywhere from 0ms to 16ms, which averages out to about 8ms". The tools, and what each costs

SettingWhat it doesCostSource
Run-ahead (run_ahead_frames)Runs the core ahead and rolls back, removing the game’s own internal frames of lag”Relies on save states”; “The higher the number of frames … the higher demands it places on your CPU”libretro run-ahead guide
Second instanceRun-ahead with a second core instance; “works around some save states limitation”More memory and CPUsame
Preemptive framesA cheaper alternative to run-ahead offered in RetroArch’s latency menuCore must support save statesRetroArch settings (preemptive_frames_enable)
Frame delay (video_frame_delay, 0–15 ms)Waits after vsync before running the core, so input is read later”Can reduce latency at cost of higher risk of stuttering”retroarch.cfg
Max swapchain imagesTwo buffers instead of threeThree “generally increases input lag by one frame”; two risks stutter on slow hardwareRetroPie Input Lag page
KMS/DRM, no desktopRetroArch drives the display directlySome DRM contexts “allow for granular swapchain control”libretro latency guide

How many run-ahead frames a game needs is measured, not guessed: pause, hold an input, advance a frame at a time, and “the frames of lag are the amount of time you pushed “K” minus 1”. For a light-gun game the test input is the trigger: count frames until the shot registers (a muzzle flash, a hit). Setting more frames than the game’s lag produces visible rollback glitches.

On a Raspberry Pi the advice reverses. RetroPie’s documentation lists run-ahead and frame delay under “Unsupported Tweaks” because the Pi lacks the headroom: frame delay “can reduce input lag at the cost of CPU usage. Emulation on even a Raspberry Pi 3 has very little headroom”, and for run-ahead “A generally safe value to set (when the required CPU headroom is available) is 1 frames”. Volume 4 takes this up for the Pi 4 and Pi 5. And whatever the settings, the television’s game mode is usually worth more than any of them; RetroPie’s page puts it first.

2.8 Why this, and why not the others

The question: which RetroArch input driver for the OpenFIRE gun on Linux?

Table 5 — 2.8 Why this, and why not the others

OptionVerdictWhy
udevUse itOne gun per player; works with or without a desktop; the absolute range is read from the device; the driver every light-gun distribution uses
x with a second master pointerFallback on an X11 desktopWorks, but needs xinput set-up each session; X11 only
waylandOne player onlyNo multi-mouse; light-gun buttons fixed, with Reload on the middle button
sdl2One player onlyNo multi-mouse; light-gun buttons fixed
linuxrawRejectedNo mouse support

The question: which MAME light-gun provider?

Table 6 — 2.8 Why this, and why not the others

OptionVerdictWhy
x11 with named slotsUpstream MAME, two gunsPer device, by name; needs an X11 session
udevUse on Batocera (automatic)Not in upstream MAME
sdlOne player onlyThe system pointer

2.9 What could not be verified

  • How RetroArch’s udev driver behaves inside a Wayland desktop session (the documentation lists KMS and X11; the code does not depend on the display server, but focus handling under Wayland was not tested).
  • Whether XWayland presents each physical gun as its own XInput device for MAME’s x11 provider.
  • The run-ahead frame counts for specific light-gun games: none is published by RetroArch or RetroPie; they are measured per game (2.7).

Sources

  1. libretro, Input and Controller Drivers, docs.libretro.com/guides/input-controller-drivers/ (libretro/docs) — read 2026-10-20.
  2. RetroArch master: input/drivers/udev_input.c, input/drivers/x11_input.c, input/drivers/wayland_input.c, config.def.keybinds.h, configuration.c, retroarch.cfg — read 2026-10-20. Latest release v1.22.2 (2025-11-20).
  3. libretro, RetroArch Run Ahead and Input latency guides, docs.libretro.com/guides/runahead/ and /guides/latency/ — read 2026-10-20.
  4. RetroPie, Spinners, Trackballs, Lightguns, and other Mouse Devices and Input Lag, RetroPie/RetroPie-Docs — read 2026-10-20.
  5. libretro/mame, src/osd/libretro/libretro-internal/libretro_core_options.h (_lightgun_mode, _lightgun_offscreen_mode) — read 2026-10-20.
  6. MAME, plugins/offscreenreload/plugin.json, docs/source/commandline/commandline-all.rst, docs/source/commandline/sdlconfig.rst, src/osd/modules/input/input_x11.cpp, src/osd/modules/lib/osdobj_common.cpp, mamedev/mame master (docs 0.289) — read 2026-10-20.
  7. Batocera, python-src/batocera-launch-libretro/batocera_launch_libretro/emulator.py, batocera-linux/batocera.linux master — read 2026-10-20.
  8. libretro/flycast issue #1189, “[Linux] Device Outputs aren’t being sent”, opened 2024-01-07, open on 2026-10-20.

Comments (0)

  1. Loading…

Comments are held for moderation — nothing appears until approved.