80 lines
5.0 KiB
Markdown
80 lines
5.0 KiB
Markdown
# Thrustmaster Sol-R 1 -> Space Engineers input mapping (Linux/Arch/Hyprland)
|
|
|
|
## Goal
|
|
Fix "throttle behaves like a trigger" bug and build a full custom input
|
|
scheme for the Sol-R1 (grip + base) in Space Engineers, on Arch/Hyprland,
|
|
without Windows/T.A.R.G.E.T.
|
|
|
|
## Stack decided on
|
|
- **joyful** (AUR: `yay -S joyful`) - Go-based Linux joystick remapper,
|
|
creates a virtual gamepad device from physical Sol-R inputs via YAML
|
|
rules in `~/.config/joyful/`. Chosen over input-remapper because it
|
|
natively supports split-axis and segmented-axis-to-button mappings,
|
|
which input-remapper doesn't (see upstream issue #900).
|
|
- **switch_sync.py** (custom, in this repo/outputs) - a separate stateful
|
|
daemon for switches that need to reconcile physical position against
|
|
actual in-game state (bypasses joyful, which can't do this since it
|
|
has no game-state feedback).
|
|
- **Steam launch options wrapper script** - starts/stops joyful and
|
|
switch_sync around the actual game session via `%command%`, instead of
|
|
a Hyprland-IPC window-watcher (Proton WM_CLASS is unreliable for this).
|
|
NOTE: Steam launch options are per-machine (stored in
|
|
`userdata/<id>/config/localconfig.vdf`, not synced) - the wrapper
|
|
script path should be identical across machines (keep it in dotfiles),
|
|
but the launch-options string itself must be re-pasted into Steam on
|
|
each machine.
|
|
|
|
## Diagnostic tools (already written, in outputs/)
|
|
- `find_codes.py` - run this first on every physical control to get real
|
|
evdev BTN_*/ABS_* codes. Nothing below has real codes yet - they're
|
|
all `TODO:` placeholders until this is run.
|
|
- `switch_sync.py` - screen-reads HUD state via `grim` region capture +
|
|
color sampling, compares against physical switch position (evdev), and
|
|
taps a virtual key only when they disagree. Has `--sample X,Y` for
|
|
pixel-color calibration and `--dump-example-config`.
|
|
|
|
## Current mapping plan
|
|
|
|
| Physical control | SE function | Mechanism |
|
|
|---|---|---|
|
|
| Throttle (base) | Move Forward/Backward | joyful axis passthrough (was original bug: SE read raw HID axis like a trigger) |
|
|
| Main stick (grip) X/Y | View/look/rotate | joyful axis passthrough |
|
|
| Ministick (grip) | Strafe horizontal/vertical | joyful axis passthrough |
|
|
| Dual-position trigger, stage 1/2 | Primary/secondary click (use/fire, alt-fire) | joyful button passthrough |
|
|
| 2 orange buttons (top of stick) | Dampeners on/off (Z) + Relative dampers (Ctrl+Z) | joyful, KEY_Z / [KEY_LEFTCTRL, KEY_Z] key emulation (not gamepad buttons - Relative Dampers isn't confirmed as a standalone bindable action, it's a modifier on the toggle key) |
|
|
| Rotary knob 1 (base) | Switch toolbar (next/prev, default `,`/`.`) | joyful, detent pulses -> KEY_COMMA/KEY_DOT - ASSUMES knob reports as BTN pulses, not an ABS axis; confirm with find_codes.py |
|
|
| Rotary knob 2 (base) | Hotbar slot select | joyful, same pulse assumption as above; if it's an ABS axis instead, needs joyful's segmented axis-to-button feature |
|
|
| 4-button group (base, left side) | Jetpack / Helmet / Flashlight / Parking toggles | joyful button passthrough to virtual gamepad buttons, bound in SE's own control menu |
|
|
| 4 tilt switches (base, top), 2 buttons each | Same 4 toggles as above, but as REAL on/off switches | switch_sync.py - NOT joyful, because this needs game-state feedback to avoid desync |
|
|
|
|
## Known caveats / open items
|
|
- SE's native joystick support only got real per-binding customization in
|
|
update 1.209; before that (and possibly still, depending on version) it
|
|
treated joysticks through an Xbox-controller-shaped abstraction. Check
|
|
Options -> Controls -> Controllers on the current version before
|
|
assuming full flexibility.
|
|
- Dampeners are a stateless toggle (Z) + contextual modifier (Ctrl+Z,
|
|
only works if dampers are off AND you're looking at a moving target in
|
|
range). No true 3-state action exists in SE.
|
|
- `switch_sync.py`'s HUD detection is pixel-color based and fragile:
|
|
breaks on HUD detail mode changes (Tab), resolution/UI scale changes,
|
|
or camera view changes. Needs recalibration after any of those.
|
|
- joyful's exact per-rule-type YAML field names (beyond the top-level
|
|
devices/modes/rules structure and evdev keycode naming) were not fully
|
|
verified against `examples/ruletypes.yml` in the actual installed
|
|
version - check that file before trusting rule blocks verbatim.
|
|
- None of the joyful config blocks have real BTN_*/ABS_* codes yet - all
|
|
placeholders pending find_codes.py output on the actual hardware.
|
|
|
|
## Next steps
|
|
1. Run find_codes.py against every physical control, replace all TODOs.
|
|
2. Confirm rotary knobs are pulse (BTN) vs continuous (ABS) - determines
|
|
which joyful rule type to use.
|
|
3. Verify SE's actual keybinds for helmet/flashlight/parking in Options
|
|
before finalizing switch_sync.py's toggle_key values.
|
|
4. Calibrate switch_sync.py's HUD regions/colors for jetpack, helmet,
|
|
light, parking indicators.
|
|
5. Write the Steam launch-options wrapper script, add to dotfiles repo.
|
|
6. Repeat launch-options paste on each machine (desktop, Framework 12,
|
|
ThinkPad T440p) - not synced by Steam.
|