diff --git a/audit/history.jsonl b/audit/history.jsonl index 6aa04441..a76bd8cd 100644 --- a/audit/history.jsonl +++ b/audit/history.jsonl @@ -621,3 +621,4 @@ {"date": "2026-09-30", "unit": "UserInterface/WiseLogicXTrap.cpp", "action": "port", "evidence": "verbatim non-XTRAP build; mapped PORTED"} {"date": "2026-09-30", "unit": "EterLib/MSWindow.cpp", "action": "port", "evidence": "verbatim over new winuser.h shim (platform/Win32User.cpp); 64-bit pointer casts ADAPTED; port.ms_window"} {"date": "2026-09-30", "unit": "EterLib/MSApplication.cpp", "action": "port", "evidence": "verbatim over new winuser.h shim (platform/Win32User.cpp); 64-bit pointer casts ADAPTED; port.ms_window"} +{"date": "2026-09-30", "unit": "EterLib/Input.cpp", "action": "port", "evidence": "host key events instead of DirectInput polling; IsPressed/Reset verbatim; port.input"} diff --git a/audit/port-map/EterLib/Input.cpp.json b/audit/port-map/EterLib/Input.cpp.json new file mode 100644 index 00000000..0a930329 --- /dev/null +++ b/audit/port-map/EterLib/Input.cpp.json @@ -0,0 +1,114 @@ +{ + "reference": "EterLib/Input.cpp", + "reference_sha256": "c913c73d5146584cfb358a73eacca3cb372da1de25b130c72f134422c96682be", + "priority": "P4", + "contracts": [], + "functions": { + "CInputDevice::CInputDevice": { + "status": "PORTED", + "impl": [ + "src/platform/EterLib/Input.cpp:CInputDevice::CInputDevice" + ], + "test": [ + "tests/port/port_input_test.cpp" + ] + }, + "CInputDevice::~CInputDevice": { + "status": "ADAPTED", + "impl": [ + "src/platform/EterLib/Input.cpp:CInputDevice::~CInputDevice" + ], + "test": [ + "tests/port/port_input_test.cpp" + ], + "note": "40250: SAFE_RELEASE(ms_lpDI). Port: no DirectInput object. Invariant: nothing to release." + }, + "CInputDevice::CreateDevice": { + "status": "ADAPTED", + "impl": [ + "src/platform/EterLib/Input.cpp:CInputDevice::CreateDevice" + ], + "test": [ + "tests/port/port_input_test.cpp" + ], + "note": "40250: DirectInput8Create (or AddRef) the shared object, S_OK unless it fails. Port: S_OK. Invariant: keyboard init succeeds." + }, + "CInputKeyboard::CInputKeyboard": { + "status": "PORTED", + "impl": [ + "src/platform/EterLib/Input.cpp:CInputKeyboard::CInputKeyboard" + ], + "test": [ + "tests/port/port_input_test.cpp" + ] + }, + "CInputKeyboard::~CInputKeyboard": { + "status": "ADAPTED", + "impl": [ + "src/platform/EterLib/Input.cpp:CInputKeyboard::~CInputKeyboard" + ], + "test": [ + "tests/port/port_input_test.cpp" + ], + "note": "40250: Unacquire + release the keyboard device. Port: no device. Invariant: nothing to release." + }, + "CInputKeyboard::ResetKeyboard": { + "status": "PORTED", + "impl": [ + "src/platform/EterLib/Input.cpp:CInputKeyboard::ResetKeyboard" + ], + "test": [ + "tests/port/port_input_test.cpp" + ] + }, + "CInputKeyboard::InitializeKeyboard": { + "status": "ADAPTED", + "impl": [ + "src/platform/EterLib/Input.cpp:CInputKeyboard::InitializeKeyboard" + ], + "test": [ + "tests/port/port_input_test.cpp" + ], + "note": "40250: create/SetDataFormat/SetCooperativeLevel(FOREGROUND|NONEXCLUSIVE)/Acquire the system keyboard, false on failure. Port: only CreateDevice. Invariant: true, state untouched." + }, + "CInputKeyboard::UpdateKeyboard": { + "status": "ADAPTED", + "impl": [ + "src/platform/EterLib/Input.cpp:CInputKeyboard::UpdateKeyboard" + ], + "test": [ + "tests/port/port_input_test.cpp" + ], + "note": "40250: per-frame GetDeviceState diff -> KeyDown/KeyUp. Port: empty; the host delivers key events (PythonBoot::UIKey) which call KeyDown/KeyUp at event time. Invariant: IsPressed reflects the host's key state." + }, + "CInputKeyboard::KeyDown": { + "status": "ADAPTED", + "impl": [ + "src/platform/EterLib/Input.cpp:CInputKeyboard::KeyDown" + ], + "test": [ + "tests/port/port_input_test.cpp" + ], + "note": "40250: ms_bPressedKey=true; virtual OnKeyDown. Port: records the key (+ ms_diks mirror), range-checked; PythonBoot::UIKey runs OnKeyDown's body (no constructed CPythonApplication to dispatch the virtual). Invariant: IsPressed true, then ESC/RunKeyDown as OnKeyDown." + }, + "CInputKeyboard::KeyUp": { + "status": "ADAPTED", + "impl": [ + "src/platform/EterLib/Input.cpp:CInputKeyboard::KeyUp" + ], + "test": [ + "tests/port/port_input_test.cpp" + ], + "note": "40250: ms_bPressedKey=false; virtual OnKeyUp. Port: as KeyDown, UIKey runs RunKeyUp. Invariant: IsPressed false." + }, + "CInputKeyboard::IsPressed": { + "status": "PORTED", + "impl": [ + "src/platform/EterLib/Input.cpp:CInputKeyboard::IsPressed" + ], + "test": [ + "tests/port/port_input_test.cpp" + ] + } + } +} diff --git a/src/platform/EterLib/Input.cpp b/src/platform/EterLib/Input.cpp index 93f30072..234f26da 100644 --- a/src/platform/EterLib/Input.cpp +++ b/src/platform/EterLib/Input.cpp @@ -1,81 +1,86 @@ -// Platform skeleton for EterLib/Input.h (40250 EterLib/Input.cpp), generated by platform_stub.py. -// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation. +// 40250 EterLib/Input.cpp without DirectInput. 40250 polls the DirectInput keyboard once per frame +// (CPythonApplication::Process -> UpdateKeyboard), diffs the 256-byte device state against ms_bPressedKey +// and calls KeyDown/KeyUp, which record the key and call the virtual OnKeyDown/OnKeyUp. +// +// PORT: the host delivers key events instead (PythonBoot::UIKey). It calls KeyDown/KeyUp at event time and +// then runs the OnKeyDown/OnKeyUp body itself, since the platform has no constructed CPythonApplication to +// dispatch the virtual through. ms_diks mirrors the pressed state, as the device state 40250 read. #include "EterLib/StdAfx.h" #include "EterLib/Input.h" -#include "../PlatformStub.h" +LPDIRECTINPUT8 CInputDevice::ms_lpDI = NULL; +LPDIRECTINPUTDEVICE8 CInputKeyboard::ms_lpKeyboard = NULL; +bool CInputKeyboard::ms_bPressedKey[256]; +char CInputKeyboard::ms_diks[256]; CInputDevice::CInputDevice() { - MT_PLATFORM_STUB(); } +// PORT: 40250 releases the shared DirectInput object; there is none. CInputDevice::~CInputDevice() { - MT_PLATFORM_STUB(); } -auto CInputDevice::CreateDevice(HWND) -> HRESULT +// PORT: 40250 creates (or AddRefs) the shared DirectInput8 object; the host keyboard needs none. +HRESULT CInputDevice::CreateDevice(HWND /*hWnd*/) { - MT_PLATFORM_STUB(); - return mt_platform_stub_return(); + return S_OK; } -decltype(CInputDevice::ms_lpDI) CInputDevice::ms_lpDI{}; CInputKeyboard::CInputKeyboard() { - MT_PLATFORM_STUB(); + ResetKeyboard(); } +// PORT: 40250 unacquires and releases the keyboard device; there is none. CInputKeyboard::~CInputKeyboard() { - MT_PLATFORM_STUB(); } -auto CInputKeyboard::InitializeKeyboard(HWND) -> bool +void CInputKeyboard::ResetKeyboard() { - ResetKeyboard(); + memset(ms_diks, 0, sizeof(ms_diks)); + memset(ms_bPressedKey, 0, sizeof(ms_bPressedKey)); +} + +// PORT: 40250 creates the system keyboard device (c_dfDIKeyboard, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE) +// and acquires it; it fails only if DirectInput does. +bool CInputKeyboard::InitializeKeyboard(HWND hWnd) +{ + if (FAILED(CreateDevice(hWnd))) + return false; + return true; } -auto CInputKeyboard::UpdateKeyboard() -> void +// PORT: no device state to poll; the host's key events have already called KeyDown/KeyUp. +void CInputKeyboard::UpdateKeyboard() { } -auto CInputKeyboard::ResetKeyboard() -> void +// PORT: 40250 sets ms_bPressedKey and calls OnKeyDown; the caller (PythonBoot::UIKey) runs OnKeyDown's body. +void CInputKeyboard::KeyDown(int iIndex) { - memset(ms_bPressedKey, 0, sizeof(ms_bPressedKey)); - memset(ms_diks, 0, sizeof(ms_diks)); + if (iIndex < 0 || iIndex >= 256) + return; + + ms_bPressedKey[iIndex] = true; + ms_diks[iIndex] = (char) 0x80; } -auto CInputKeyboard::IsPressed(int iIndex) -> bool +// PORT: as KeyDown, for OnKeyUp. +void CInputKeyboard::KeyUp(int iIndex) { - if (iIndex >= 0 && iIndex < 256) - return (ms_diks[iIndex] & 0x80) != 0; - return false; + if (iIndex < 0 || iIndex >= 256) + return; + + ms_bPressedKey[iIndex] = false; + ms_diks[iIndex] = 0; } -auto CInputKeyboard::KeyDown(int iIndex) -> void +bool CInputKeyboard::IsPressed(int iIndex) { - if (iIndex >= 0 && iIndex < 256) - { - ms_bPressedKey[iIndex] = true; - ms_diks[iIndex] = (char)0x80; - } + return ms_bPressedKey[iIndex]; } - -auto CInputKeyboard::KeyUp(int iIndex) -> void -{ - if (iIndex >= 0 && iIndex < 256) - { - ms_bPressedKey[iIndex] = false; - ms_diks[iIndex] = 0; - } -} - -decltype(CInputKeyboard::ms_lpKeyboard) CInputKeyboard::ms_lpKeyboard{}; - -decltype(CInputKeyboard::ms_bPressedKey) CInputKeyboard::ms_bPressedKey{}; - -decltype(CInputKeyboard::ms_diks) CInputKeyboard::ms_diks{}; diff --git a/src/port/CMakeLists.txt b/src/port/CMakeLists.txt index 9ba7a78f..45a0e7fa 100644 --- a/src/port/CMakeLists.txt +++ b/src/port/CMakeLists.txt @@ -205,6 +205,9 @@ if(BUILD_TESTING AND CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME) add_executable(port_ms_window_test ${PROJECT_SOURCE_DIR}/tests/port/port_ms_window_test.cpp) target_link_libraries(port_ms_window_test PRIVATE port_platform) add_test(NAME port.ms_window COMMAND $) + add_executable(port_input_test ${PROJECT_SOURCE_DIR}/tests/port/port_input_test.cpp) + target_link_libraries(port_input_test PRIVATE port_platform) + add_test(NAME port.input COMMAND $) # MilesLib: the 40250 sound managers over the Miles emulation (platform/MilesLib/MilesAil.cpp). add_executable(port_miles_test ${PROJECT_SOURCE_DIR}/tests/port/port_miles_test.cpp) diff --git a/tests/port/port_input_test.cpp b/tests/port/port_input_test.cpp new file mode 100644 index 00000000..2993069c --- /dev/null +++ b/tests/port/port_input_test.cpp @@ -0,0 +1,41 @@ +// 40250 CInputKeyboard with host key events in place of DirectInput polling (platform/EterLib/Input.cpp). +#include "EterLib/StdAfx.h" +#include "EterLib/Input.h" + +#include + +namespace { +struct Keyboard : CInputKeyboard +{ + int downs = 0, ups = 0; + void OnKeyDown(int) override { ++downs; } + void OnKeyUp(int) override { ++ups; } + char Diks(int i) const { return ms_diks[i]; } +}; +} + +int main() +{ + int failures = 0; + auto check = [&failures](bool ok, const char *name) { + if (!ok) { + std::fprintf(stderr, "FAIL %s\n", name); + ++failures; + } + }; + + Keyboard kb; + check(kb.InitializeKeyboard(NULL), "InitializeKeyboard"); + kb.KeyDown(DIK_LCONTROL); + check(kb.IsPressed(DIK_LCONTROL) && (kb.Diks(DIK_LCONTROL) & 0x80), "KeyDown records the key and the device byte"); + kb.UpdateKeyboard(); + check(kb.IsPressed(DIK_LCONTROL) && kb.downs == 0 && kb.ups == 0, "UpdateKeyboard neither polls nor dispatches"); + kb.KeyUp(DIK_LCONTROL); + check(!kb.IsPressed(DIK_LCONTROL) && kb.Diks(DIK_LCONTROL) == 0, "KeyUp"); + kb.KeyDown(-1); + kb.KeyDown(256); + kb.KeyDown(DIK_A); + Keyboard other; // the state is static: a new keyboard resets it, as in 40250 + check(!kb.IsPressed(DIK_A), "constructor ResetKeyboard clears the shared state"); + return failures ? 1 : 0; +}