LALT -> button 0x3F was bound in both binding layers and PadRIO pushed the RIO ButtonPressed event correctly, but nothing ever set the mapper's ReverseThrust: - the authentic route is BTL4.RES 'L4' streamed record [2] (Button Throttle1 0x3F -> attr 6 ReverseThrust@0x124), but our streamed BUTTON mappings are not consumed at all -- MechControlsMapper::AddOrErase is still the unreconstructed Fail stub, so the event never reaches the attribute; - the ONLY writer of reverseThrust was the keyboard bridge's 'key_throttle < 0' test, which is bypassed whenever a RIO is operational (the pad RIO always is, so the bridge is OFF on the desktop) AND is unreachable regardless, because L4PADRIO clamps the Throttle channel to [0,1]. Net: Alt did nothing and the flag was re-zeroed every frame. FIX: publish the 0x3F hold state from PadRIO::EmitButton -- the one chokepoint every desktop source funnels through (keyboard, gamepad, DirectInput joystick, glass-panel clicks via SetScreenButton) -- and apply it in InterpretControls gated on BTPadRIOActive() so real pod hardware is untouched (there the streamed mapping owns the flag). Marked [T3]; delete once streamed button mappings land. Reverse is a HOLD (manual: hold the red throttle button, release = forward). Verified with scratchpad/revtest.py (keybd_event injection, BT_KEY_NOFOCUS): forward = rev=0 dmd +61.501; LALT held = rev=1 dmd -61.501; release edge logged. KB: pod-hardware.md now records the streamed-button-mapping gap + this seam. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0166KTsC7ADm7VXEi1HF1jNg
89 lines
2.6 KiB
Python
89 lines
2.6 KiB
Python
"""Reverse-thrust verification: hold W to build throttle, then hold LALT and
|
|
confirm the mapper flips to reverse (speedDemand goes negative).
|
|
|
|
Injects keys with keybd_event into a btl4 launched with BT_KEY_NOFOCUS=1, so it
|
|
does not need window focus. Kills ONLY the PID it spawned.
|
|
"""
|
|
import ctypes
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
VK_W = 0x57
|
|
VK_LMENU = 0xA4 # left Alt
|
|
KEYEVENTF_KEYUP = 0x0002
|
|
|
|
user32 = ctypes.windll.user32
|
|
|
|
def hold(vk, seconds):
|
|
user32.keybd_event(vk, 0, 0, 0)
|
|
time.sleep(seconds)
|
|
|
|
def release(vk):
|
|
user32.keybd_event(vk, 0, KEYEVENTF_KEYUP, 0)
|
|
|
|
CONTENT = r"C:\git\bt411\content"
|
|
EXE = r"C:\git\bt411\build\Release\btl4.exe"
|
|
LOG = r"C:\git\bt411\scratchpad_rev.log"
|
|
|
|
if os.path.exists(LOG):
|
|
os.remove(LOG)
|
|
|
|
env = dict(os.environ)
|
|
env.update({
|
|
"BT_START_INSIDE": "1",
|
|
"BT_KEY_NOFOCUS": "1",
|
|
"BT_MPPR_TRACE": "1", # [mppr-c] thr= rev= -> dmd=
|
|
"BT_PAD_LOG": "1", # [padwrite] reverse button
|
|
"BT_LOG": LOG,
|
|
})
|
|
proc = subprocess.Popen([EXE, "-egg", "LAST.EGG"], cwd=CONTENT, env=env)
|
|
print("spawned pid", proc.pid)
|
|
|
|
try:
|
|
# wait for the mission to be running (the trace only prints in-mission)
|
|
deadline = time.time() + 180
|
|
while time.time() < deadline:
|
|
if os.path.exists(LOG):
|
|
txt = open(LOG, errors="replace").read()
|
|
if "[mppr-c]" in txt:
|
|
break
|
|
time.sleep(2)
|
|
else:
|
|
print("FAIL: never reached the mapper trace")
|
|
sys.exit(1)
|
|
|
|
# 1) throttle up with W for 3s, no Alt -> expect rev=0, dmd > 0
|
|
hold(VK_W, 6.0)
|
|
release(VK_W)
|
|
time.sleep(1.5)
|
|
mark = len(open(LOG, errors="replace").read())
|
|
|
|
# 2) now hold LALT for 4s -> expect rev=1 and dmd < 0
|
|
hold(VK_LMENU, 5.0)
|
|
release(VK_LMENU)
|
|
time.sleep(1.5)
|
|
|
|
txt = open(LOG, errors="replace").read()
|
|
before = txt[:mark]
|
|
during = txt[mark:]
|
|
|
|
def rows(chunk):
|
|
return re.findall(r"\[mppr-c\] thr=([-\d.]+) rev=(\d+).*?dmd=([-\d.]+)", chunk)
|
|
|
|
b = rows(before)
|
|
d = rows(during)
|
|
print("\n--- forward phase (last 3 samples):", b[-3:])
|
|
print("--- alt-held phase (samples): ", d[:6])
|
|
print("--- [padwrite] lines:", re.findall(r"\[padwrite\] reverse button.*", txt)[:4])
|
|
|
|
fwd_ok = any(int(r[1]) == 0 and float(r[2]) > 0.5 for r in b)
|
|
rev_ok = any(int(r[1]) == 1 and float(r[2]) < -0.5 for r in d)
|
|
print("\nforward (rev=0, dmd>0): %s" % ("PASS" if fwd_ok else "FAIL"))
|
|
print("reverse (rev=1, dmd<0): %s" % ("PASS" if rev_ok else "FAIL"))
|
|
finally:
|
|
proc.terminate()
|
|
print("killed", proc.pid)
|