tools/msfs/generate_behaviors.py --sci data/sci gives every click spot of the
cockpit an MSFS-style interaction: knobs turn with the wheel and a drag,
switches flip by click or drag, levers drag through their detents. Every gesture
sends only the stable-interface events the click spot sent before
(H:LU737_<CONTROL>_<ACTION>, docs/SCI_IMPLEMENTATION.md §8), so the WASM
module and the IO instrument are unchanged. --plain-clicks brings back the
old plain click spots (same output as before, byte for byte).
How it is built#
The callback code follows Asobo's generic interaction templates
(Asobo\Generic\Interactions.xml: ASOBO_GT_Interaction_WheelAndContinuousLeft_LeftSingle,
ASOBO_GT_Interaction_LeftSingle_Code, ASOBO_GT_Interaction_DraggingAxis_Code;
offline copy in docs/reference/msfs2024/). It is expanded inline into each
<MouseRect> instead of <UseTemplate> so that the exact SCI events are sent and
the model does not depend on the SDK template include path. One code serves
both cockpit interaction modes (IMDefault = legacy, IMDrag = lock):
- press =
LeftSingleorLock, release =LeftReleaseorUnlock(O:_Downdrops the duplicate); - a drag compares
M:RelativeX/Ywith the position of the last step: one step perDRAG_STEPof mouse travel (constants at the top of the interaction code, tune them there); - a left press that is released without a drag step is a click.
Mapping (counts for the 737-800 cockpit, 991 click spots)#
| Class | Count | From | Gestures → SCI events |
|---|---|---|---|
| button | 615 | command push buttons, EFB / CDU / keypad keys |
unchanged plain click: PRESS on down, RELEASE on up |
| guard | 22 | covers (*cover*, GUARD_TOGGLE) |
unchanged plain click (the click toggles the guard) |
| toggle_cmd | 70 | single toggle command on a switch / selector / lever (up_down, left_right, rotate cursors; parking brake, manual gear handle) | PRESS on down (held), RELEASE on up, so a click or a drag flips it; rotary ones also step with the wheel (PRESS+RELEASE) |
| toggle | 17 | X-Plane toggle (fuel pumps, light switches, start-lever toggles) |
click or drag (once per gesture) → TOGGLE |
| switch | 74 | command_switch_up_down (Y) / _left_right (X) with arrow cursors |
drag up/right → first command (INC side), down/left → second; the dragged position is held until mouse up, then RELEASE (spring positions return); click on the upper / right half (cursor UpArrow / RightArrow) → first command, on the lower / left half (DownArrow / LeftArrow) → second, + RELEASE; wheel up/down → one step each way |
| knob | 133 | command_knob, axis_knob, rotary-cursor command_switch_* |
wheel up = INC, down = DEC; drag right/up = INC, left/down = DEC (one step per threshold); left click on the left half (cursor TurnLeft) = DEC, on the right half (TurnRight) = INC, decided where the button went down; RELEASE where the control had it |
| lever | 22 | drag_rotate, drag_axis, drag_xy (throttles, reversers, start levers, flaps, speed brake, gear, tiller, checklist bugs, EFB stand) |
drag along the lever: INC/DEC per step (the control goes to the next detent, flaps / speed brake / gear, or 10 % of the range); wheel up = INC, down = DEC; no click action |
| pair | 38 | split controls: two neighbouring spots with opposite commands (overwing exits open/close, yoke electric trim, standby altimeter +/−) | each half keeps its own click (PRESS at once, held until mouse up, as in X-Plane); both halves: wheel up/down = the up/plus half's / the other half's PRESS+RELEASE; drag up (or right/up for +/−) = up half, down = other half (switch pairs hold the dragged half until mouse up) |
Event set: the 2123 H:LU737_* events emitted before are all still emitted;
none added (data/cockpit_io.json → sci_events, interactions).
Lever drag direction comes from the X-Plane manipulator: rotation about +x
(flaps, speed brake, gear, reversers) increases when the handle moves aft/down,
about −x (throttles, start levers) when it moves forward/up; drag_axis uses
the axis vector (forward counts as up). drag_xy drives only its X dataref
(the Y dataref has no SCI control).
What to test in MSFS (both cockpit interaction modes: Legacy and Lock)#
- Knobs (heading, course, altitude, EFIS range/mode, engine start, lights
dimmers): wheel both ways; left click on the left half (TurnLeft cursor) = decrease, right half (TurnRight) = increase;
press and drag left/right and up/down. Check the step size (
DRAG_STEP["knob"]). - Switches (overhead toggles, GEN / GPU / APU GEN spring switches, battery, standby power, APU START): click, drag up and down, wheel. Spring positions: drag and keep the button down: the switch must stay there and return on release (GEN ON/OFF, APU START, fire test).
- Guards: click opens/closes; a guarded switch under a closed guard must still be blocked.
- Levers: throttles (each and both), reversers, start levers, flaps through every detent, speed brake (ARM / FLT / UP), gear, parking brake, tiller: drag direction feels right, detents are reached, wheel works.
- Split controls: overwing exits, yoke trim (hold to trim, drag down = nose up), standby altimeter baro: click each half, wheel, drag.
- Buttons and EFB / CDU keys: unchanged; check a few still press and release.
Known limits: a plain left click on a switch acts on mouse up (so that a drag never first sends the opposite command); to hold a spring position use a drag. The right button does nothing on any control (build 36): MSFS looks around with it held, and a right press over a control used to move it. No control in the contract has INC_FAST/DEC_FAST or DRAG, so fast turning and absolute lever positions are not used yet; a later contract with DRAG/SET_n can map lever drags directly to positions.
Cursors (2026-11-01)#
Like Asobo's templates, the MouseRects use position-dependent cursors
(CursorLeft/CursorRight/CursorUp/CursorDown): knobs show TurnLeft
on their left half and TurnRight on their right half, up/down switches
UpArrow/DownArrow, left/right switches LeftArrow/RightArrow,
levers Grab; while dragging (drag interaction mode) the cursor is Grab.
A left click acts toward the half the cursor shows. If a switch or knob
turns the opposite way to its cursor, (M:RelativeY) / (M:RelativeX) have
the other sign than assumed: report it (one sign flip in the generator).