Retropie Console – Two trackballs, one console

For this console, I have duplicate controllers. I have the arcade console that has two joysticks, buttons and a trackball. I have two 8BitDo Ultimate Controllers and a Logitech MX Ergo. Why? Sometimes I want to use the arcade panel, sometimes I want to sit on the couch, I might want multiple players too.

Marble Madness is one of those games that immediately explains why an arcade cabinet needs a trackball. I grew up playing this game with a joystick on a Commodore 64, and it is painful to play that way. This game needs a trackball, just as Duck Hunt needs a lightgun, just as the original arcade game was designed. The speed, momentum, diagonals, narrow paths, and frantic changes in direction all feel different when the control is a rolling ball rather than a joystick or D-pad.

Our cabinet has a trackball built into the centre of its control panel. We also use a Logitech MX Ergo, which is convenient when sitting farther back or when someone would rather use a handheld thumb-operated trackball.

Although, the MX was purchased for a different purpose – to help with Amiga emulation, and the added benefit is that it can be used for trackball games.

The requirement sounded uncomplicated:

  • launch Marble Madness;
  • roll either physical trackball; and
  • have the game respond as though there were one consistent Player 1 trackball.

As with many seemingly small arcade-cabinet requirements, the difficult part was not getting each device to work. The difficult part was making both devices behave predictably without changing how every other mouse-driven game and computer system behaved.

Two working devices do not automatically become one control

Linux can detect multiple mouse-like devices at the same time. The panel trackball and the MX Ergo can each produce pointer movement, and diagnostic tools can see their events.

That does not guarantee that an emulator will combine them in the way we want.

An application may select one mouse index, choose whichever device it discovers first, or remember a device assignment that changes after a reboot. A configuration that works while the panel trackball is Mouse 1 can fail when the wireless receiver appears first and changes the order.

This is the same category of problem we encountered with gamepads and USB serial devices elsewhere in the cabinet: a temporary device number is not a durable identity, and several physical devices do not automatically represent one logical player.

We could have tried to configure MAME to understand both physical devices directly. That would have pushed hardware-specific knowledge into the emulator, however, and it would have made the result dependent on device enumeration and MAME input details.

Instead, we gave the emulator one stable control.

Creating one virtual trackball

We built a small service called:

/usr/local/sbin/arcade-trackball-merger

Its job is to combine movement from the two approved physical sources into one virtual pointer:

Panel trackball ─────┐
                     ├── Virtual Player 1 trackball ──> MAME
Logitech MX Ergo ────┘

MAME no longer has to decide which physical trackball is Player 1. It receives one logical device. Movement from either source reaches the same destination.

This is an important separation of responsibilities:

  • Linux still detects the physical hardware;
  • the merger understands which devices are acceptable sources;
  • the virtual device represents the control the game needs; and
  • MAME maps that one stable logical device.

The idea is similar to the cabinet’s virtual gamepad arrangement, where an I-PAC panel side and an assigned wireless controller become alternate inputs for one player. The trackball implementation is narrower because mouse-like devices affect more than arcade games.

Why we did not merge the trackballs globally

A global merger would have been easy to describe: start it at boot and leave it active forever.

It would also have been too broad.

The cabinet runs classic computers, adventure-game engines, menus, ports, and other software that can use a mouse or trackball. Some of those programs should see the panel trackball normally. Others may need the Razer mouse, the MX Ergo, or their own device selection. Taking exclusive control of the physical devices throughout the entire session could make those systems harder to use.

Our actual requirement was much smaller: either trackball should control Marble Madness.

We therefore made activation game-specific.

The list of games that request merged-trackball behavior is stored in:

/opt/retropie/configs/all/arcade-trackball-games.txt

At the time of writing, it contains the known Marble Madness ROM basenames:

marble
marble2
marble3
marble4
marble5

Using ROM basenames rather than a display title matters. A front-end title may be reformatted, translated, or supplied by metadata, while the launch hook receives the actual file being started. The basename gives the automation a small and explicit allowlist.

The rule is effectively:

IF system is mame-libretro
AND ROM basename is in arcade-trackball-games.txt
THEN activate exclusive trackball merging
ELSE leave the physical pointer devices alone

This is a useful design principle for cabinet automation: apply unusual behavior only where it is needed.

Connecting the merger to a game launch

RetroPie provides launch hooks that run around an emulator session. Our start hook is:

/opt/retropie/configs/all/runcommand-onstart.sh

When a game begins, that hook performs three independent jobs:

  1. It notifies RGB Commander, with a two-second limit so a lighting problem cannot prevent the game from starting.
  2. It resolves the cabinet’s virtual Player 1 and Player 2 gamepads for Libretro.
  3. It asks the trackball game hook whether this launch requires the merged pointer.

The trackball-specific decision is handled by:

/usr/local/sbin/arcade-trackball-game-hook

Before evaluating the new launch, the hook clears stale state. It then checks both the system name and ROM basename. Only an approved mame-libretro title activates the exclusive merge.

When the emulator closes, RetroPie’s end hook runs:

/opt/retropie/configs/all/runcommand-onend.sh

That deactivates the trackball merge and releases the physical devices. The next program once again sees the ordinary mouse and trackball arrangement.

Cleaning up at both ends is deliberate. The end hook handles a normal exit, while clearing stale state at the next start prevents an earlier abnormal termination from permanently contaminating later launches.

The service and the policy are separate

The virtual trackball service is managed by systemd:

arcade-trackball-merger.service

The service provides the mechanism. The game list and launch hook provide the policy.

That distinction makes the setup easier to maintain. Adding another compatible trackball game does not require rewriting the merger. We can review the game, add its exact ROM basename to the allowlist, launch it, and test whether its MAME mapping behaves correctly.

The relevant diagnostic commands are:

systemctl status arcade-trackball-merger.service --no-pager
journalctl -u arcade-trackball-merger.service -b --no-pager

The service status tells us whether the mechanism is available. The journal tells us what happened during discovery and activation. If Marble Madness launches but only one source works, we can examine the physical input events, the service log, and the virtual output separately instead of treating the whole path as one opaque configuration.

Testing the behavior that matters

A successful service start is not the acceptance test. The user experience is.

We test the complete sequence:

  1. Start in EmulationStation with both physical trackballs available normally.
  2. Launch a listed Marble Madness ROM.
  3. Confirm that the panel trackball moves the marble in both axes.
  4. Confirm that the MX Ergo moves the same player without changing the MAME input assignment.
  5. Alternate between the two devices during the same session.
  6. Exit the game.
  7. Confirm that the physical pointer devices have been released.
  8. Launch a non-listed mouse or trackball application and confirm that it retains its ordinary behavior.
  9. Reboot and repeat the test so success does not depend on device discovery order.

That last test is important. A setup that works only until the next reboot is not a finished cabinet feature.

Building the solution with an agent

As with the cabinet’s controller and metadata tools, the supporting code was developed agentically under my direction.

I defined the behavior and the boundaries: both trackballs should operate Player 1 in Marble Madness, the physical devices should remain normal elsewhere, game selection should be explicit, a failed lighting action must not block a launch, and the result had to survive both game exit and reboot.

The agent helped inspect device behavior, connect the requirement to RetroPie’s launch hooks, implement the merger and allowlist, examine service logs, and refine cleanup behavior. The actual cabinet remained the source of truth. We tested physical movement and game behavior rather than accepting a syntactically correct service file as proof.

This is the kind of task for which agentic development is particularly useful. The individual pieces—Linux input events, virtual devices, systemd, shell hooks, and MAME configuration—are not enormous. The difficulty lies in following their interaction across an entire launch lifecycle and noticing where a broad solution would introduce a new problem.

The human contribution is deciding what “correct” means. Here, correct did not mean “Linux can see two trackballs.” It meant:

  • either trackball controls the same marble;
  • only selected games receive the merged behavior;
  • other pointer-driven systems are unaffected;
  • cleanup occurs after play; and
  • the design can be understood and recovered later.

The larger lesson

The final experience is exactly as simple as we wanted. Launch Marble Madness and use whichever trackball feels right.

That simplicity exists because the complexity has been placed at a deliberate boundary. The game sees one stable logical control. The merger understands the physical alternatives. The launch hooks decide when that behavior is appropriate. Everything else continues to work normally.

Good cabinet integration often looks like nothing happened. There is no controller-selection menu and no instruction to unplug one device before using another. The player rolls a ball, and the marble moves.

That is the entire point.