BUTTONS (shipped). Win32 hands mouse buttons out as virtual keys, and the
binding path already stores VKs and polls GetAsyncKeyState -- so this needed no
verb, no parser change and no new machinery, just names:
key MOUSE4 button 0x40 # side buttons
key MOUSE5 button 0x46
key MOUSEMIDDLE button 0x41
MOUSELEFT/MOUSERIGHT are named too, with a warning in the file header and the
docs: they are how the player presses cockpit buttons (left = press, right =
latch), and the poll is focus-guarded but not click-aware, so binding either
ALSO fires on every panel click. The side and middle buttons are the free
ones. Axes work as well (`key MOUSE4 axis Throttle slew + 0.7`).
One guard: the RGB lamp mirror skips mouse VKs when building its map -- a
keyboard lamp array has no mouse buttons to paint (BTPadBindingIsMouseKey).
Verified live: a bindings.txt carrying two mouse rows loads 76 keys (74 + 2),
and a real injected XBUTTON1 press produced `[emitter] FIRED #1`. Clean A/B --
the earlier run whose SendInput failed the struct-size check logged no fire at
all. Shipped commented-out examples in the default profile so the capability
is discoverable.
LOOK (scoped only). docs/MOUSELOOK_PLAN.md. Mouse MOVEMENT is a real feature,
not a table entry, and the hard part is not reading the device:
- The cursor is already spoken for. The glass cockpit's premise is that all
72 buttons are mouse targets; mouse-look wants a captured, hidden, recentred
cursor. Both cannot be true, so this is a MODE question first -- four
models compared, recommending hold-to-look (costs nothing when unbound,
cannot strand a player, behaves the same in surround and exploded layouts).
- The channels are POSITIONAL (JoystickX/Y are -1..1 deflections the mapper
reads per control mode); a mouse gives deltas. Accumulate-into-a-virtual-
stick reuses every existing rule; a rate model would need new mapper
semantics.
- Control mode changes what it MEANS: BASIC steers with the stick, MID/ADV
twist the torso -- so mouse-look steers in one and aims in the other.
Proposed grammar (new row type, so old builds skip it with a warning),
where the code goes, ~250 lines, and a verification plan whose load-bearing
item is "panel clicks still work when a mouselook binding exists but is not
engaged". Also flagged: the pod had NO mouse, so nothing here is
reconstruction -- every choice is design, judged on feel, and it must stay out
of the pod build's path.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
7.1 KiB
MOUSELOOK_PLAN — scope: mouse movement as a cockpit axis
Status: SCOPED, NOT IMPLEMENTED. Written 2026-07-26 at the author's request, alongside the mouse-BUTTON support that did ship (4.11.565). Read §2 before agreeing to build it — the hard part is not reading the mouse.
1. What shipped, and what this is instead
Shipped: mouse buttons, as key names (MOUSE4, MOUSE5, MOUSEMIDDLE, MOUSELEFT,
MOUSERIGHT). Zero new machinery — Win32 hands mouse buttons out as virtual keys and the binding
path already stores VKs and polls GetAsyncKeyState, so five table entries were the whole change.
This document: mouse movement driving JoystickX/JoystickY (torso twist and aim) — the
thing a modern player means by "mouse look". That is a real feature, not a table entry, for three
reasons that have nothing to do with reading the device.
2. The three real problems
2.1 The cursor is already spoken for ⚠ (the blocking one)
The glass cockpit's whole premise is that every one of the 72 pod buttons is a mouse target — left-click presses, right-click latches, and under the exploded layout there are seven separate windows to click in. Mouse-look wants the opposite: a captured, hidden, recentred cursor that never touches a button.
These cannot both be true at once, so the feature is really a mode question, and the answer decides the whole design:
| Model | Feel | Cost |
|---|---|---|
A. Hold-to-look (key MOUSE5 mouselook or a modifier) |
Cursor stays free; press to steer, release to click buttons | No mode confusion, discoverable, but you cannot look and hold a trigger comfortably |
| B. Toggle (a key flips captured/free) | Full mouse-look while engaged | Needs a visible on-screen indicator or players get stuck "unable to click anything" |
| C. Region (look while the pointer is over the 3D view, click when over a display) | No mode at all | Fails in the exploded layout (displays are separate windows) and near the surround's corner MFDs, which overlap the view by design |
| D. Always captured, panel clicks via a "release" key | Cleanest for a shooter | Throws away the cockpit's defining interaction; not recommended |
Recommendation: A, with B as an option. A costs nothing when unbound and cannot strand a player. It is also the only one that behaves identically in the surround and exploded layouts.
2.2 The pod's channels are POSITIONAL; a mouse is not
JoystickX/JoystickY are absolute stick deflections in −1..1 that MechControlsMapper
interprets per control mode. A mouse produces deltas. Bridging that is a design choice, not a
conversion:
- Accumulate (recommended): integrate deltas into the channel, clamp to ±1 — a "virtual stick". Reuses every existing rule (composition, the #36 release edge, the gait detent).
- Rate: treat the delta as a velocity of torso angle. Feels better for aiming, but the mapper has no rate input — it would mean a new channel semantic and a new path through the mapper. Bigger and less authentic.
With Accumulate, the stick does not spring back (a mouse has no centre). Options, all cheap:
a spring <rate> trailing option that decays toward 0, and/or a recentre key (0x42 torso centre
is already bound to the Up arrow).
2.3 Control mode changes what it means
BASIC steers with the stick; MID/ADV twist the torso with it and steer with the pedals (experience-levels, pod-hardware). So mouse-look steers the mech in BASIC and aims the torso in MID/ADV — authentic, but it will read as a bug to anyone who hasn't been told. The docs must say so, and the feature is most useful in MID/ADV.
3. Proposed shape (if we build it)
New row type — the sanctioned way to extend the grammar (old builds skip unknown rows with a logged warning, so a player's file stays forward-compatible):
mouseaxis <X|Y> axis <channel> [invert] [sensitivity <n>] [spring <rate>] [deadzone <f>]
key <KEY> mouselook # hold-to-look (model A)
Example default (shipped commented out):
#mouseaxis X axis JoystickX sensitivity 1.0 spring 0
#mouseaxis Y axis JoystickY sensitivity 1.0 spring 0 invert
#key MOUSE5 mouselook
Where the code goes
| Piece | Where | Notes |
|---|---|---|
| Raw delta capture | L4PADRIO (new), fed from the window proc |
WM_INPUT (RawInput), not WM_MOUSEMOVE: relative motion, no cursor clamping at screen edges, no pointer acceleration. Register HID_USAGE_GENERIC_MOUSE with RIDEV_INPUTSINK off (foreground only). |
| Capture/hide | same | SetCapture + ShowCursor(FALSE) + recentre per frame while engaged; release on focus loss |
| Bindings | L4PADBINDINGS |
mouseAxisBindings[] + the mouselook action, parallel to padAxisBindings |
| Channel write | PadRIO::Poll |
Beside the pad/joy axis writes, with the SAME per-binding previous-value array so it inherits the #36 release edge and the #24 disconnect rule |
| Docs | bindings.txt header, docs/CONTROLS.md, docs/CONTROLS.html |
including the control-mode caveat |
Estimated size: ~250 lines plus docs. No engine or reconstruction changes — this is entirely in the glass desktop layer, like PadRIO itself.
4. Authenticity note
The pod had no mouse. There is nothing to reconstruct here and no binary behaviour to be
faithful to — this is a desktop-convenience layer, exactly like PadRIO and the glass cockpit
itself. That is fine (the layer exists precisely so developers and players can fly without a
cabinet), but it means every decision above is a design decision and should be judged on feel,
not fidelity. It also means it must stay out of the pod build's path: gate it the way PadRIO is
gated, so BT_PLATFORM=pod never sees it.
5. Verification plan
mouseaxisrows parse; unknown-option rows warn and skip (grammar-compatibility rule).- Hold-to-look engages/releases; cursor visible again on release AND on focus loss (the #24 disconnect rule, mouse edition).
- Channel values track motion, clamp at ±1, and release cleanly to 0 (the #36 latch bug is exactly the hazard here).
- Panel clicks still work while a mouselook binding exists but is not engaged — in BOTH the surround and exploded layouts. This is the regression that matters.
- Control-mode sweep: BASIC steers, MID/ADV twists — confirmed against
[mppr]trace. - Pod + dev profiles: no mouse path constructed at all.
6. Open questions
- Model A or B (§2.1)? Everything else follows from it.
- Should mouse-look imply a crosshair/reticle change, so the player can see it is engaged?
- Does the exploded layout need its own answer, given the seven windows each have their own client area and the main window may not have focus?
- Is this wanted for the tester builds at all, or only for the operator/dev seat? It is not pod-authentic, and the cockpit's mouse-click identity is a feature people have praised.
Key Relationships
- Extends: glass-cockpit (PadRIO, the desktop input layer) · Constrained by: pod-hardware (the RIO channel model), experience-levels (control modes)
- Prior art in-tree:
L4JOY(DirectInput layer + the wizard) is the closest analogue for adding a whole new input device cleanly.