port 4-a: text input through CPythonIME (login typed, in-game chat)
- UserInterface/PythonIME(+Module) ported verbatim; ime module stubs removed, initime() after initdbg() - EterLib/IME.cpp in platform/: 40250 buffer/WMChar bodies, IMM/TSF processors no-op; committed multi-byte text via PythonBoot::UIChar emulating GCS_RESULTSTR - GameApplicationAdapter RunIME* -> CWindowManager; Godot keys -> ui_ime_key/ui_char - port.login_flow/login_live type the account through the IME; chat CG_CHAT round-trip on the fake server - python_game_render_test logs in with Godot key events Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1bda4390d6
commit
6ea0fa9889
@@ -540,3 +540,4 @@
|
|||||||
{"time": "2026-09-24T16:00:00Z", "event": "port_round", "unit": "batch 3-c: CMapOutdoor + PythonBackground + minimap", "ported": ["GameLib/{Area,AreaTerrain,DungeonBlock,MapBase,MapManager,MapOutdoor*,MapType,MonsterAreaInfo,Property*,Snow*,Terrain*}.cpp", "PRTerrainLib/{Terrain,TextureSet}.cpp", "UserInterface/PythonBackground(+Module).cpp", "EterLib/ColorTransitionHelper.cpp"], "adapted": ["CMapManager::BeginEnvironment: uintptr_t tag cast", "CTerrainImpl::LoadWaterMap: 32-bit long file layout", "EterBase/FileDir, TempFile (platform)", "MapOutdoorCharacterShadow texture create/release (platform file)"], "divergent": ["HTP/STP terrain render + RenderWater no-op (Godot draws terrain)", "BeginRenderCharacterShadowToTexture returns false"], "n_a": ["GameLib/AreaLoaderThread.cpp (dead in 40250)"], "needs_live": [], "tests": ["port.login_flow: IsMapOutdoor, miniMap atlas loaded, GetCurrentMapName", "python_game_render_test: minimap tiles through filter mask (9)", "port.login_live 192.168.21.203 passes", "ctest 19/19", "port_map check 0 errors"], "todo": ["weapon attach draws on the ground (since P3-b)", "default route switch"]}
|
{"time": "2026-09-24T16:00:00Z", "event": "port_round", "unit": "batch 3-c: CMapOutdoor + PythonBackground + minimap", "ported": ["GameLib/{Area,AreaTerrain,DungeonBlock,MapBase,MapManager,MapOutdoor*,MapType,MonsterAreaInfo,Property*,Snow*,Terrain*}.cpp", "PRTerrainLib/{Terrain,TextureSet}.cpp", "UserInterface/PythonBackground(+Module).cpp", "EterLib/ColorTransitionHelper.cpp"], "adapted": ["CMapManager::BeginEnvironment: uintptr_t tag cast", "CTerrainImpl::LoadWaterMap: 32-bit long file layout", "EterBase/FileDir, TempFile (platform)", "MapOutdoorCharacterShadow texture create/release (platform file)"], "divergent": ["HTP/STP terrain render + RenderWater no-op (Godot draws terrain)", "BeginRenderCharacterShadowToTexture returns false"], "n_a": ["GameLib/AreaLoaderThread.cpp (dead in 40250)"], "needs_live": [], "tests": ["port.login_flow: IsMapOutdoor, miniMap atlas loaded, GetCurrentMapName", "python_game_render_test: minimap tiles through filter mask (9)", "port.login_live 192.168.21.203 passes", "ctest 19/19", "port_map check 0 errors"], "todo": ["weapon attach draws on the ground (since P3-b)", "default route switch"]}
|
||||||
{"time": "2026-09-24T17:00:00Z", "event": "route_switch", "unit": "client_main default route -> port", "detail": "MT_PORT_PATH unset/port runs python_ui_surface + python_3d_surface (40250 system.py); MT_PORT_PATH=legacy runs AppFlow; smoke/playable test modes stay legacy; unset falls back to legacy when Metin2PythonHost or the 40250 pack is missing", "tests": ["client_main.tscn instantiated per route: default/port -> PythonUISurface+Python3DSurface with UI commands, legacy -> AppFlow+NetTrace"]}
|
{"time": "2026-09-24T17:00:00Z", "event": "route_switch", "unit": "client_main default route -> port", "detail": "MT_PORT_PATH unset/port runs python_ui_surface + python_3d_surface (40250 system.py); MT_PORT_PATH=legacy runs AppFlow; smoke/playable test modes stay legacy; unset falls back to legacy when Metin2PythonHost or the 40250 pack is missing", "tests": ["client_main.tscn instantiated per route: default/port -> PythonUISurface+Python3DSurface with UI commands, legacy -> AppFlow+NetTrace"]}
|
||||||
{"time": "2026-09-24T18:00:00Z", "event": "correction", "unit": "batch 3-c todo", "detail": "The sword next to the actor in python_game_render_test is the fake server ground item (GC_ITEM_GROUND_ADD vnum 19, P3-b), not a weapon-attach bug; the fake character has no weapon, AttachWeapon is never called. Todo withdrawn."}
|
{"time": "2026-09-24T18:00:00Z", "event": "correction", "unit": "batch 3-c todo", "detail": "The sword next to the actor in python_game_render_test is the fake server ground item (GC_ITEM_GROUND_ADD vnum 19, P3-b), not a weapon-attach bug; the fake character has no weapon, AttachWeapon is never called. Todo withdrawn."}
|
||||||
|
{"time": "2026-09-24T19:00:00Z", "event": "port", "unit": "batch 4-a text input: EterLib/IME.cpp (ADAPTED), UserInterface/PythonIME.cpp, PythonIMEModule.cpp", "detail": "ime module ported; IMM/TSF-free CIME in platform/; Godot key events -> ui_ime_key/ui_char -> CWindowManager RunIME*; login typed through the IME; in-game chat CG_CHAT round-trip", "tests": ["port.login_flow (typed id/password with edits, chat send/echo/close)", "port.login_live", "python_game_render_test (Godot key events)"]}
|
||||||
|
|||||||
@@ -0,0 +1,712 @@
|
|||||||
|
{
|
||||||
|
"reference": "EterLib/IME.cpp",
|
||||||
|
"reference_sha256": "19a16dd5ac776591a7e2def2564102ea86d7e4a98f67c4bcd585cb8a7376f611",
|
||||||
|
"priority": "P4",
|
||||||
|
"contracts": [],
|
||||||
|
"functions": {
|
||||||
|
"CIME::CIME": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::CIME"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::~CIME": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::~CIME"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::Initialize": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::Initialize"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::Uninitialize": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::Uninitialize"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::UseDefaultIME": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::UseDefaultIME"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::IsIMEEnabled": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::IsIMEEnabled"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::EnableIME": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::EnableIME"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::DisableIME": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::DisableIME"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::EnableCaptureInput": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::EnableCaptureInput"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::DisableCaptureInput": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::DisableCaptureInput"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::IsCaptureEnabled": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::IsCaptureEnabled"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::Clear": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::Clear"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::GetReading": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::GetReading"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::GetReadingError": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::GetReadingError"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::SetMax": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::SetMax"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::SetUserMax": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::SetUserMax"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::SetText": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::SetText"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::GetText": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::GetText"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::GetCodePageText": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::GetCodePageText"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::GetCodePage": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::GetCodePage"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::GetCandidatePageCount": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::GetCandidatePageCount"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::GetCandidateCount": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::GetCandidateCount"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::GetCandidate": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::GetCandidate"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::GetCandidateSelection": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::GetCandidateSelection"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::SetInputMode": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::SetInputMode"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::GetInputMode": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::GetInputMode"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::SetNumberMode": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::SetNumberMode"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::SetStringMode": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::SetStringMode"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::AddExceptKey": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::AddExceptKey"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::ClearExceptKey": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::ClearExceptKey"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::__IsWritable": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::__IsWritable"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::EnablePaste": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::EnablePaste"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::PasteTextFromClipBoard": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::PasteTextFromClipBoard"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::FinalizeString": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::FinalizeString"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::GetCompLen": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::GetCompLen"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::GetULBegin": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::GetULBegin"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::GetULEnd": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::GetULEnd"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::CloseCandidateList": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::CloseCandidateList"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::CloseReadingInformation": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::CloseReadingInformation"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::ChangeInputLanguage": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::ChangeInputLanguage"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::ChangeInputLanguageWorker": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::ChangeInputLanguageWorker"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::SetSupportLevel": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::SetSupportLevel"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::IncCurPos": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::IncCurPos"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::DecCurPos": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::DecCurPos"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::GetCurPos": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::GetCurPos"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::SetCurPos": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::SetCurPos"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::DelCurPos": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::DelCurPos"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::PasteString": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::PasteString"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::InsertString": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::InsertString"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::OnChar": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::OnChar"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::GetCodePageFromLang": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::GetCodePageFromLang"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::CompositionProcess": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::CompositionProcess"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::CompositionProcessBuilding": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::CompositionProcessBuilding"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::ResultProcess": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::ResultProcess"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::AttributeProcess": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::AttributeProcess"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::CandidateProcess": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::CandidateProcess"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::ReadingProcess": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::ReadingProcess"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::IsMax": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::IsMax"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::GetImeId": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::GetImeId"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::GetReadingWindowOrientation": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::GetReadingWindowOrientation"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::SetupImeApi": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::SetupImeApi"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"_strtoul": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:_strtoul"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::CheckInputLocale": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::CheckInputLocale"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::CheckToggleState": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::CheckToggleState"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CTsfUiLessMode::SetupSinks": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CTsfUiLessMode::SetupSinks"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CTsfUiLessMode::ReleaseSinks": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CTsfUiLessMode::ReleaseSinks"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CTsfUiLessMode::CUIElementSink::CUIElementSink": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CTsfUiLessMode::CUIElementSink::CUIElementSink"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CTsfUiLessMode::CUIElementSink::~CUIElementSink": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CTsfUiLessMode::CUIElementSink::~CUIElementSink"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CTsfUiLessMode::CUIElementSink::QueryInterface": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CTsfUiLessMode::CUIElementSink::QueryInterface"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CTsfUiLessMode::CUIElementSink::BeginUIElement": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CTsfUiLessMode::CUIElementSink::BeginUIElement"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CTsfUiLessMode::CUIElementSink::UpdateUIElement": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CTsfUiLessMode::CUIElementSink::UpdateUIElement"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CTsfUiLessMode::CUIElementSink::EndUIElement": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CTsfUiLessMode::CUIElementSink::EndUIElement"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CTsfUiLessMode::UpdateImeState": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CTsfUiLessMode::UpdateImeState"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CTsfUiLessMode::CUIElementSink::OnActivated": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CTsfUiLessMode::CUIElementSink::OnActivated"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CTsfUiLessMode::CUIElementSink::OnChange": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CTsfUiLessMode::CUIElementSink::OnChange"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CTsfUiLessMode::MakeReadingInformationString": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CTsfUiLessMode::MakeReadingInformationString"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CTsfUiLessMode::MakeCandidateStrings": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CTsfUiLessMode::MakeCandidateStrings"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CTsfUiLessMode::GetUIElement": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CTsfUiLessMode::GetUIElement"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CTsfUiLessMode::CurrentInputLocaleIsIme": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CTsfUiLessMode::CurrentInputLocaleIsIme"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CTsfUiLessMode::EnableUiUpdates": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CTsfUiLessMode::EnableUiUpdates"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CTsfUiLessMode::GetCompartments": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CTsfUiLessMode::GetCompartments"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CTsfUiLessMode::SetupCompartmentSinks": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CTsfUiLessMode::SetupCompartmentSinks"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::WMInputLanguage": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::WMInputLanguage"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::WMStartComposition": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::WMStartComposition"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::WMComposition": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::WMComposition"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::WMEndComposition": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::WMEndComposition"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::WMNotify": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::WMNotify"
|
||||||
|
],
|
||||||
|
"note": "no IMM/TSF under Godot: composition arrives as committed text (PythonBoot::UIChar → InsertString + OnUpdate); body is a no-op/constant",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
},
|
||||||
|
"CIME::WMChar": {
|
||||||
|
"status": "ADAPTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/platform/EterLib/IME.cpp:CIME::WMChar"
|
||||||
|
],
|
||||||
|
"note": "40250 body verbatim in platform/ (the file also holds the IMM/TSF-free no-ops); DelCurPos uses memmove for the overlapping wcscpy",
|
||||||
|
"test": "extension/tests/port_login_flow_test.cpp (login id/password typed via UIChar/UIIMEKeyDown incl. LEFT/DELETE/BACK/TAB/RETURN; in-game chat CG_CHAT) + project/python_game_render_test.gd (Godot key events)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
{
|
||||||
|
"reference": "UserInterface/PythonIME.cpp",
|
||||||
|
"reference_sha256": "2fb5fa35249b8a2a9bb1c82d177d36b3ec083a2cafd4957d7cbea85d6be34009",
|
||||||
|
"priority": "P4",
|
||||||
|
"contracts": [],
|
||||||
|
"functions": {
|
||||||
|
"CPythonIME::CPythonIME": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::CPythonIME"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CPythonIME::~CPythonIME": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::~CPythonIME"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CPythonIME::Create": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::Create"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CPythonIME::MoveLeft": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::MoveLeft"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CPythonIME::MoveRight": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::MoveRight"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CPythonIME::MoveHome": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::MoveHome"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CPythonIME::MoveEnd": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::MoveEnd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CPythonIME::SetCursorPosition": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::SetCursorPosition"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CPythonIME::Delete": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::Delete"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CPythonIME::OnUpdate": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::OnUpdate"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CPythonIME::OnTab": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::OnTab"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CPythonIME::OnReturn": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::OnReturn"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CPythonIME::OnEscape": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::OnEscape"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CPythonIME::OnWM_CHAR": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::OnWM_CHAR"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CPythonIME::OnChangeCodePage": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::OnChangeCodePage"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CPythonIME::OnOpenCandidateList": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::OnOpenCandidateList"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CPythonIME::OnCloseCandidateList": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::OnCloseCandidateList"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CPythonIME::OnOpenReadingWnd": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::OnOpenReadingWnd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CPythonIME::OnCloseReadingWnd": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIME.cpp:CPythonIME::OnCloseReadingWnd"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
{
|
||||||
|
"reference": "UserInterface/PythonIMEModule.cpp",
|
||||||
|
"reference_sha256": "9d2abae41a270b10f05aceb7e04709f18fe44248ea52a9c2f391c0848d10cc31",
|
||||||
|
"priority": "P4",
|
||||||
|
"contracts": [],
|
||||||
|
"functions": {
|
||||||
|
"imeEnable": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeEnable"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeDisable": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeDisable"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeEnableCaptureInput": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeEnableCaptureInput"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeDisableCaptureInput": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeDisableCaptureInput"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeSetMax": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeSetMax"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeSetUserMax": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeSetUserMax"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeSetText": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeSetText"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeGetText": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeGetText"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeGetCodePage": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeGetCodePage"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeGetCandidateCount": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeGetCandidateCount"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeGetCandidate": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeGetCandidate"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeGetCandidateSelection": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeGetCandidateSelection"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeGetReading": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeGetReading"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeGetReadingError": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeGetReadingError"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeEnableIME": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeEnableIME"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeDisableIME": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeDisableIME"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeSetInputMode": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeSetInputMode"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeSetNumberMode": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeSetNumberMode"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeAddExceptKey": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeAddExceptKey"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeClearExceptKey": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeClearExceptKey"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeSetStringMode": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeSetStringMode"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeGetInputMode": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeGetInputMode"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeMoveLeft": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeMoveLeft"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeMoveRight": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeMoveRight"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeMoveHome": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeMoveHome"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeMoveEnd": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeMoveEnd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeSetCursorPosition": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeSetCursorPosition"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeDelete": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeDelete"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imePasteTextFromClipBoard": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imePasteTextFromClipBoard"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imeEnablePaste": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imeEnablePaste"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imePasteString": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imePasteString"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imePasteBackspace": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imePasteBackspace"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"imePasteReturn": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:imePasteReturn"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"initime": {
|
||||||
|
"status": "PORTED",
|
||||||
|
"impl": [
|
||||||
|
"extension/src/port/UserInterface/PythonIMEModule.cpp:initime"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -450,6 +450,8 @@ extension/third_party/cpython-2.7.18/ # 静态库(2P 批次
|
|||||||
批次 3-b EffectLib(2026-09-24):`EffectLib/` 18 个 .cpp 原样移植(EffectManager、EffectData/Instance、EffectMesh、粒子系统、SimpleLight 等),删除 `platform/EffectLib` 生成的桩骨架;唯一改动是 `Type.h` 里 `BILLBOARD_TYPE_2FACE` 行尾注释的 `\` 被 clang 当作续行吞掉下一行,去掉反斜杠。`platform/EterBase/Utils.cpp` 剩下的 21 个桩按 §3 照抄 40250 函数体(`GetOnlyPathName`/`IsGlobalFileName` 缺失会让 .msf 以相对名挂的 .mse 丢目录,106 个 RegisterEffect 报错),Win32 调用换成 POSIX 的记 ADAPTED;平台 `CPythonResource` 补上 40250 的 `mde`(CEffectMesh)和 `mdatr`(CAttributeData)资源类型。`port.utils` 覆盖路径/分词/命令行辅助函数;`port.login_flow`:在主角脚下 `CreateEffect(level_up.mse)`,RenderGame 的 `CEffectManager::Render` 录出 levelup_1 贴图的 alpha 混合粒子绘制(dust、dropitem 同样出现)。40250 声明但未定义 `GetRenderingEffectCount`,测试不用。
|
批次 3-b EffectLib(2026-09-24):`EffectLib/` 18 个 .cpp 原样移植(EffectManager、EffectData/Instance、EffectMesh、粒子系统、SimpleLight 等),删除 `platform/EffectLib` 生成的桩骨架;唯一改动是 `Type.h` 里 `BILLBOARD_TYPE_2FACE` 行尾注释的 `\` 被 clang 当作续行吞掉下一行,去掉反斜杠。`platform/EterBase/Utils.cpp` 剩下的 21 个桩按 §3 照抄 40250 函数体(`GetOnlyPathName`/`IsGlobalFileName` 缺失会让 .msf 以相对名挂的 .mse 丢目录,106 个 RegisterEffect 报错),Win32 调用换成 POSIX 的记 ADAPTED;平台 `CPythonResource` 补上 40250 的 `mde`(CEffectMesh)和 `mdatr`(CAttributeData)资源类型。`port.utils` 覆盖路径/分词/命令行辅助函数;`port.login_flow`:在主角脚下 `CreateEffect(level_up.mse)`,RenderGame 的 `CEffectManager::Render` 录出 levelup_1 贴图的 alpha 混合粒子绘制(dust、dropitem 同样出现)。40250 声明但未定义 `GetRenderingEffectCount`,测试不用。
|
||||||
批次 3-c CMapOutdoor(2026-09-24):`GameLib/` 的 Area、AreaTerrain、DungeonBlock、MapBase、MapManager、MapOutdoor(含 IndexBuffer/Load/Quadtree/Render/Update)、MapType、MonsterAreaInfo、Property/PropertyLoader/PropertyManager、SnowEnvironment/SnowParticle、TerrainDecal/Patch/Quadtree,`PRTerrainLib/` Terrain、TextureSet,`UserInterface/PythonBackground(+Module)`,`EterLib/ColorTransitionHelper` 原样移植,删除 pending 替身与 background 模块桩(含 GameplayModuleConstants 里的常量桩);`EterBase/FileDir`、`TempFile` 在 platform 下照抄 40250 函数体(Win32 查找/临时文件走 common 垫片,记 ADAPTED)。`TEMP_CAreaLoaderThread` 在 40250 已全部注释掉,.cpp 不编(N_A)。手改两处:`CMapManager::BeginEnvironment` 的 `ApplyLight` 标签指针经 `uintptr_t` 转 DWORD;`CTerrainImpl::LoadWaterMap` 按 32 位读 water.wtr 的 `long` 再逐个扩宽(LP64 下 `long` 是 8 字节,否则报 FILE DATA SIZE 16392 != 16400)。平台文件:HTP/STP 地形绘制与 `RenderWater` 为空(地形仍由 Godot 适配层画,§3),STP 的 Initialize/Create/Destroy、水面贴图载入、角色阴影贴图创建照抄;`BeginRenderCharacterShadowToTexture` 返回 false(记 DIVERGENT)。录制设备补齐 `CreateTexture/VertexBuffer/IndexBuffer/DepthStencilSurface`、`Get/SetRenderTarget`、`LightEnable`、`Clear`(CPU 表面/贴图),离屏渲染目标的绘制丢弃,light0 只在启用且为方向光时记录。小地图:`CPythonMiniMap` 在正交投影下画 minimap.dds 瓦片并以 stage 1 相机空间坐标 × `D3DTS_TEXTURE1` 采样 `minimap_image_filter.dds` 做圆形遮罩——录制设备把「正交投影 + 未变换顶点」的绘制按四边形转成 UI 命令(带 mask 贴图与四角 mask uv),`UIRenderAddImage` 按 D3DTS_WORLD 平移、按 TFACTOR 调色(标记点),`python_ui_surface.gd` 用按序的子 canvas item 画 UI 流,带遮罩的图各用一个 shader 材质。`port.login_flow`:Warp 后 `IsMapOutdoor()`、`miniMap.GetAtlasSize()` 已载入、`background.GetCurrentMapName()` 非空;`python_game_render_test` 新增「minimap tiles drawn through the filter mask」(9 块)。截图里主角旁地上的剑是假服 entergame 时按 `GC_ITEM_GROUND_ADD` 放的地面物品(vnum 19,P3-b 拾取测试用),不是武器挂点问题;假服角色没装备武器。
|
批次 3-c CMapOutdoor(2026-09-24):`GameLib/` 的 Area、AreaTerrain、DungeonBlock、MapBase、MapManager、MapOutdoor(含 IndexBuffer/Load/Quadtree/Render/Update)、MapType、MonsterAreaInfo、Property/PropertyLoader/PropertyManager、SnowEnvironment/SnowParticle、TerrainDecal/Patch/Quadtree,`PRTerrainLib/` Terrain、TextureSet,`UserInterface/PythonBackground(+Module)`,`EterLib/ColorTransitionHelper` 原样移植,删除 pending 替身与 background 模块桩(含 GameplayModuleConstants 里的常量桩);`EterBase/FileDir`、`TempFile` 在 platform 下照抄 40250 函数体(Win32 查找/临时文件走 common 垫片,记 ADAPTED)。`TEMP_CAreaLoaderThread` 在 40250 已全部注释掉,.cpp 不编(N_A)。手改两处:`CMapManager::BeginEnvironment` 的 `ApplyLight` 标签指针经 `uintptr_t` 转 DWORD;`CTerrainImpl::LoadWaterMap` 按 32 位读 water.wtr 的 `long` 再逐个扩宽(LP64 下 `long` 是 8 字节,否则报 FILE DATA SIZE 16392 != 16400)。平台文件:HTP/STP 地形绘制与 `RenderWater` 为空(地形仍由 Godot 适配层画,§3),STP 的 Initialize/Create/Destroy、水面贴图载入、角色阴影贴图创建照抄;`BeginRenderCharacterShadowToTexture` 返回 false(记 DIVERGENT)。录制设备补齐 `CreateTexture/VertexBuffer/IndexBuffer/DepthStencilSurface`、`Get/SetRenderTarget`、`LightEnable`、`Clear`(CPU 表面/贴图),离屏渲染目标的绘制丢弃,light0 只在启用且为方向光时记录。小地图:`CPythonMiniMap` 在正交投影下画 minimap.dds 瓦片并以 stage 1 相机空间坐标 × `D3DTS_TEXTURE1` 采样 `minimap_image_filter.dds` 做圆形遮罩——录制设备把「正交投影 + 未变换顶点」的绘制按四边形转成 UI 命令(带 mask 贴图与四角 mask uv),`UIRenderAddImage` 按 D3DTS_WORLD 平移、按 TFACTOR 调色(标记点),`python_ui_surface.gd` 用按序的子 canvas item 画 UI 流,带遮罩的图各用一个 shader 材质。`port.login_flow`:Warp 后 `IsMapOutdoor()`、`miniMap.GetAtlasSize()` 已载入、`background.GetCurrentMapName()` 非空;`python_game_render_test` 新增「minimap tiles drawn through the filter mask」(9 块)。截图里主角旁地上的剑是假服 entergame 时按 `GC_ITEM_GROUND_ADD` 放的地面物品(vnum 19,P3-b 拾取测试用),不是武器挂点问题;假服角色没装备武器。
|
||||||
默认路由切换(2026-09-24):`client_main` 不设 `MT_PORT_PATH` 时走 port 路由(`python_ui_surface.gd` + `python_3d_surface.gd` 跑 40250 system.py),`MT_PORT_PATH=legacy` 回到 GDScript `AppFlow`,规则见 §2「迁移方式」。已验证:实例化 `client_main.tscn`,默认/`port` 起 PythonUISurface + Python3DSurface 并产出 UI 命令,`legacy` 起 AppFlow + NetTrace。下一步:port 路由稳定后删除 legacy 宿主与开关。
|
默认路由切换(2026-09-24):`client_main` 不设 `MT_PORT_PATH` 时走 port 路由(`python_ui_surface.gd` + `python_3d_surface.gd` 跑 40250 system.py),`MT_PORT_PATH=legacy` 回到 GDScript `AppFlow`,规则见 §2「迁移方式」。已验证:实例化 `client_main.tscn`,默认/`port` 起 PythonUISurface + Python3DSurface 并产出 UI 命令,`legacy` 起 AppFlow + NetTrace。下一步:port 路由稳定后删除 legacy 宿主与开关。
|
||||||
|
批次 4(2026-09-24 起):补齐 port 路由的玩法,以便删除 legacy。顺序:4-a 文字输入 → 4-b 战斗 → 4-c 商店 → 4-d 任务/NPC 对话 → 4-e 仓库 → 4-f 好友/公会/公会标记 → 4-g systemSetting → 4-h 声音。
|
||||||
|
批次 4-a 文字输入(2026-09-24):`UserInterface/PythonIME.cpp` 与 `PythonIMEModule.cpp` 原样移植(仅把 MSVC 专有的 `unsigned char(x)` 函数式转换改成 C 式转换),删除 ime 模块桩,`initime()` 按 40250 顺序在 `initdbg()` 之后,`CPythonApplication::Create` 按原序 `CPythonIME::Create` + `SetText("", 0)`。`EterLib/IME.cpp` 在 platform 下:缓冲区、光标、`WMChar`(退格、`|` 转义、按输入代码页 `MultiByteToWideChar`)、候选/读音状态、数字/字符串模式等函数体照抄;IMM/TSF 在 Godot 下没有对应物,组字相关处理器为空、`EnableIME` 恒关,已提交的非单字节文本由 `PythonBoot::UIChar` 模拟 `GCS_RESULTSTR`(`InsertString` + `OnUpdate`),`DelCurPos` 用 memmove 替代重叠的 wcscpy(全部记 ADAPTED)。`GameApplicationAdapter` 的 `RunIME*` 转发到 `CWindowManager`;注意 `RunIMEUpdate/RunIMEKeyDown` 只送达正在渲染的活动窗口(登录框未打开时输入被丢弃)。Godot 侧 `python_ui_surface.gd` 把按键送 `ui_ime_key`(VK)再送 `ui_char`(unicode)。`port.login_flow` 与 `port.login_live` 打开登录框后逐字键入账号(含 LEFT/DELETE/BACK 编辑)、TAB 切到密码、RETURN 登录(凭据只来自环境变量,测试随后删除 Python 局部变量);进游戏后 RETURN 打开聊天、键入 hello、RETURN 发出 `CG_CHAT`(假服回显 `GC_CHAT`),空行 RETURN 关闭聊天(40250 发完后只清空、不关闭)。`python_game_render_test` 用 Godot 按键事件走同一路径登录。
|
||||||
|
|
||||||
每个切片都必须同时跑 legacy/port 双路回归;默认路由已切到 `port`(2026-09-24);port 路由稳定后删除总开关和剩余 legacy 宿主。
|
每个切片都必须同时跑 legacy/port 双路回归;默认路由已切到 `port`(2026-09-24);port 路由稳定后删除总开关和剩余 legacy 宿主。
|
||||||
|
|
||||||
|
|||||||
@@ -1,381 +1,666 @@
|
|||||||
// Platform skeleton for EterLib/IME.h (40250 EterLib/IME.cpp), generated by platform_stub.py.
|
// Platform implementation of EterLib/IME.h (40250 EterLib/IME.cpp).
|
||||||
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
|
// The edit buffer (m_wText, cursor, SetText/GetText, OnChar/InsertString, IsMax, the colour-tag aware cursor
|
||||||
|
// moves) and WMChar are the 40250 bodies verbatim. What 40250 gets from Win32 IMM/TSF — composition strings,
|
||||||
|
// the candidate list, the reading window, keyboard-layout code pages — has no counterpart under Godot: the
|
||||||
|
// host delivers committed characters only (Metin2PythonHost.ui_char -> WMChar, or InsertString for text the
|
||||||
|
// input code page cannot hold, which is what 40250's ResultProcess does with a GCS_RESULTSTR). Those entry
|
||||||
|
// points keep 40250's "no IME present" behaviour: no composition, empty candidate/reading lists.
|
||||||
#include "EterLib/StdAfx.h"
|
#include "EterLib/StdAfx.h"
|
||||||
#include "EterLib/IME.h"
|
#include "EterLib/IME.h"
|
||||||
|
#include "EterLib/TextTag.h"
|
||||||
|
#include "EterLib/GrpTextInstance.h"
|
||||||
|
#include "EterLib/Util.h"
|
||||||
|
|
||||||
#include "../PlatformStub.h"
|
#include <algorithm>
|
||||||
|
#include <cwctype>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// 40250 s_aszIndicator[INDICATOR_NON_IME] (IME.cpp:67); CheckInputLocale overwrites it with the locale's
|
||||||
|
// two-letter abbreviation, which here is the locale code page's language.
|
||||||
|
wchar_t s_aszNonImeIndicator[3] = L"En";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------*/ /* Public */
|
||||||
CIME::CIME()
|
CIME::CIME()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
ms_hWnd = NULL;
|
||||||
|
|
||||||
|
ms_bCandidateList = false;
|
||||||
|
ms_bReadingInformation = false;
|
||||||
|
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
m_max = 0;
|
||||||
|
m_userMax = 0;
|
||||||
|
|
||||||
|
m_bOnlyNumberMode = FALSE;
|
||||||
|
m_hOrgIMC = NULL;
|
||||||
|
|
||||||
|
m_bEnablePaste = false;
|
||||||
|
m_bUseDefaultIME = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CIME::~CIME()
|
CIME::~CIME()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::Initialize(HWND) -> bool
|
// PORT: 40250 loads imm32.dll, disables Cicero, reads the keyboard layout and sets up the TSF sinks. The
|
||||||
|
// Godot host has no IMM context; only the part the edit buffer depends on — the input/output code page from
|
||||||
|
// CheckInputLocale and SetSupportLevel(2) — is kept.
|
||||||
|
bool CIME::Initialize(HWND hWnd)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
if(ms_bInitialized)
|
||||||
return mt_platform_stub_return<bool>();
|
return true;
|
||||||
|
ms_hWnd = hWnd;
|
||||||
|
|
||||||
|
ms_bDisableIMECompletely = true;
|
||||||
|
ms_bInitialized = true;
|
||||||
|
|
||||||
|
CheckInputLocale();
|
||||||
|
ChangeInputLanguageWorker();
|
||||||
|
SetSupportLevel(2);
|
||||||
|
|
||||||
|
ms_bUILessMode = false;
|
||||||
|
CheckToggleState();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::Uninitialize() -> void
|
void CIME::Uninitialize()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
if ( !ms_bInitialized )
|
||||||
|
return;
|
||||||
|
ms_hWnd = NULL;
|
||||||
|
m_hOrgIMC = NULL;
|
||||||
|
ms_bInitialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::Clear() -> void
|
void CIME::UseDefaultIME()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
m_bUseDefaultIME = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::SetMax(int) -> void
|
bool CIME::IsIMEEnabled()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
return ms_bImeEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::SetUserMax(int) -> void
|
// PORT: 40250 ImmAssociateContext()s the window; ms_bDisableIMECompletely (no imm32) forces bEnable=false,
|
||||||
|
// which is always the case here.
|
||||||
|
void CIME::EnableIME(bool bEnable)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
if (!ms_bInitialized || !ms_hWnd)
|
||||||
|
return;
|
||||||
|
if (ms_bDisableIMECompletely)
|
||||||
|
bEnable = false;
|
||||||
|
ms_bImeEnabled = bEnable;
|
||||||
|
if (bEnable)
|
||||||
|
CheckToggleState();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::SetText(const char *, int) -> void
|
void CIME::DisableIME()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
EnableIME(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::GetText(std::string &, bool) -> int
|
void CIME::EnableCaptureInput()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
ms_bCaptureInput = true;
|
||||||
return mt_platform_stub_return<int>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::GetCodePageText() -> const char *
|
void CIME::DisableCaptureInput()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
ms_bCaptureInput = false;
|
||||||
return mt_platform_stub_return<const char *>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::GetCodePage() -> int
|
bool CIME::IsCaptureEnabled()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
return ms_bCaptureInput;
|
||||||
return mt_platform_stub_return<int>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::GetCandidateCount() -> int
|
void CIME::Clear()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
ms_lastpos = 0;
|
||||||
return mt_platform_stub_return<int>();
|
ms_curpos = 0;
|
||||||
|
|
||||||
|
ms_compLen = 0;
|
||||||
|
ms_ulbegin = 0;
|
||||||
|
ms_ulend = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::GetCandidatePageCount() -> int
|
int CIME::GetReading(std::string & rstrText)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
char reading[IMEREADING_MAXLEN];
|
||||||
return mt_platform_stub_return<int>();
|
|
||||||
|
if(ms_wstrReading.size() == 0)
|
||||||
|
return 0;
|
||||||
|
int readingLen = WideCharToMultiByte(ms_uOutputCodePage, 0, &ms_wstrReading[0], ms_wstrReading.size(), reading, sizeof(reading), NULL, NULL);
|
||||||
|
|
||||||
|
rstrText.append(GetCodePageText());
|
||||||
|
rstrText.append(reading, reading + readingLen);
|
||||||
|
|
||||||
|
return rstrText.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::GetCandidate(DWORD, std::string &) -> int
|
int CIME::GetReadingError()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
return ms_iReadingError;
|
||||||
return mt_platform_stub_return<int>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::GetCandidateSelection() -> int
|
void CIME::SetMax(int iMax)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
m_max = iMax;
|
||||||
return mt_platform_stub_return<int>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::GetReading(std::string &) -> int
|
void CIME::SetUserMax(int iMax)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
m_userMax = iMax;
|
||||||
return mt_platform_stub_return<int>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::GetReadingError() -> int
|
void CIME::SetText(const char* szText, int len)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
ms_compLen = 0;
|
||||||
return mt_platform_stub_return<int>();
|
ms_ulbegin = 0;
|
||||||
|
ms_ulend = 0;
|
||||||
|
|
||||||
|
const char* begin = szText;
|
||||||
|
const char* end = begin + len;
|
||||||
|
const char* iter = FindToken(begin, end);
|
||||||
|
|
||||||
|
int m_wTextLen = sizeof(m_wText)/sizeof(wchar_t);
|
||||||
|
|
||||||
|
ms_lastpos = MultiByteToWideChar(ms_uInputCodePage, 0, begin, iter-begin, m_wText, m_wTextLen);
|
||||||
|
|
||||||
|
if (iter < end)
|
||||||
|
ms_lastpos += MultiByteToWideChar(ReadToken(iter), 0, (iter+5), end-(iter+5), m_wText+ms_lastpos, m_wTextLen-ms_lastpos);
|
||||||
|
|
||||||
|
ms_curpos = std::min(ms_curpos, ms_lastpos);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::SetInputMode(DWORD) -> void
|
int CIME::GetText(std::string & rstrText, bool addCodePage)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
int outCodePage = ms_uOutputCodePage;
|
||||||
|
int dataCodePage;
|
||||||
|
switch (outCodePage)
|
||||||
|
{
|
||||||
|
//case 1256: // ARABIC
|
||||||
|
case 1268: // VIETNAM
|
||||||
|
dataCodePage = CP_UTF8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dataCodePage = outCodePage;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::GetInputMode() -> DWORD
|
int len = 0;
|
||||||
|
char text[IMESTR_MAXLEN];
|
||||||
|
|
||||||
|
len += WideCharToMultiByte(dataCodePage, 0, m_wText, ms_curpos, text, sizeof(text)-len, NULL, NULL);
|
||||||
|
len += WideCharToMultiByte(dataCodePage, 0, m_wszComposition, ms_compLen, text+len, sizeof(text)-len, NULL, NULL);
|
||||||
|
len += WideCharToMultiByte(dataCodePage, 0, m_wText+ms_curpos, ms_lastpos-ms_curpos, text+len, sizeof(text)-len, NULL, NULL);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for(i=0; i<len; ++i)
|
||||||
|
if((BYTE)text[i] > 0x7F) break;
|
||||||
|
|
||||||
|
if(i == len)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
rstrText.append(text, text+len);
|
||||||
return mt_platform_stub_return<DWORD>();
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
auto CIME::IsIMEEnabled() -> bool
|
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
rstrText.append(text, text+i);
|
||||||
return mt_platform_stub_return<bool>();
|
|
||||||
|
//if (addCodePage)
|
||||||
|
// rstrText.append(GetCodePageText());
|
||||||
|
|
||||||
|
rstrText.append(text+i, text+len);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::EnableIME(bool) -> void
|
return rstrText.size();
|
||||||
{
|
|
||||||
MT_PLATFORM_STUB();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::DisableIME() -> void
|
const char* CIME::GetCodePageText()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
static char szCodePage[16];
|
||||||
}
|
|
||||||
|
|
||||||
auto CIME::EnableCaptureInput() -> void
|
const int defCodePage = GetDefaultCodePage();
|
||||||
|
const int outCodePage = ms_uOutputCodePage;
|
||||||
|
|
||||||
|
if (outCodePage != defCodePage)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
sprintf(szCodePage, "@%04d", outCodePage);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
auto CIME::DisableCaptureInput() -> void
|
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
szCodePage[0] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::IsCaptureEnabled() -> bool
|
return szCodePage;
|
||||||
{
|
|
||||||
MT_PLATFORM_STUB();
|
|
||||||
return mt_platform_stub_return<bool>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::SetNumberMode() -> void
|
int CIME::GetCodePage()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
return ms_uOutputCodePage;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::SetStringMode() -> void
|
int CIME::GetCandidatePageCount()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
return ms_dwCandidatePageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::__IsWritable(wchar_t) -> bool
|
int CIME::GetCandidateCount()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
return ms_dwCandidateCount;
|
||||||
return mt_platform_stub_return<bool>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::AddExceptKey(wchar_t) -> void
|
int CIME::GetCandidate(DWORD index, std::string & rstrText)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
if(index >= MAX_CANDLIST)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
LPCWSTR wszText = ms_wszCandidate[index];
|
||||||
|
if(wszText == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int wTextLen = wcslen(wszText);
|
||||||
|
if(wTextLen == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
char text[IMESTR_MAXLEN];
|
||||||
|
int len = ::WideCharToMultiByte(CP_UTF8, 0, wszText, wTextLen, text, sizeof(text), 0, 0);
|
||||||
|
|
||||||
|
rstrText.append("@9999");
|
||||||
|
rstrText.append(text, text+len);
|
||||||
|
|
||||||
|
return wTextLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::ClearExceptKey() -> void
|
int CIME::GetCandidateSelection()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
return ms_dwCandidateSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::PasteTextFromClipBoard() -> void
|
// PORT: 40250 ImmSetConversionStatus / ImmGetConversionStatus. Without an IMM context the conversion mode is
|
||||||
|
// the alphanumeric one (IME_CMODE_ALPHANUMERIC == 0) and cannot be changed.
|
||||||
|
void CIME::SetInputMode(DWORD)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::EnablePaste(bool) -> void
|
DWORD CIME::GetInputMode()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::PasteString(const char *) -> void
|
void CIME::SetNumberMode()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
m_bOnlyNumberMode = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::FinalizeString(bool) -> void
|
void CIME::SetStringMode()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
m_bOnlyNumberMode = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::UseDefaultIME() -> void
|
void CIME::AddExceptKey(wchar_t key)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
m_exceptKey.push_back(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::GetCurPos() -> int
|
void CIME::ClearExceptKey()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
m_exceptKey.clear();
|
||||||
return mt_platform_stub_return<int>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::GetCompLen() -> int
|
bool CIME::__IsWritable(wchar_t key)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
if ( m_exceptKey.end() == std::find(m_exceptKey.begin(),m_exceptKey.end(),key) )
|
||||||
return mt_platform_stub_return<int>();
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::GetULBegin() -> int
|
void CIME::EnablePaste(bool bFlag)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
m_bEnablePaste = bFlag;
|
||||||
return mt_platform_stub_return<int>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::GetULEnd() -> int
|
// PORT: 40250 reads CF_TEXT from the Win32 clipboard. The host clipboard is not wired to the extension yet,
|
||||||
|
// so this pastes nothing (the same as an empty clipboard).
|
||||||
|
void CIME::PasteTextFromClipBoard()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
if (!m_bEnablePaste)
|
||||||
return mt_platform_stub_return<int>();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::CloseCandidateList() -> void
|
// PORT: 40250 cancels the IMM composition string and closes the candidate list. There is no composition
|
||||||
|
// here; the candidate-list close (and its event) is kept.
|
||||||
|
void CIME::FinalizeString(bool)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
static bool s_bProcessing = false; // to avoid infinite recursion
|
||||||
|
if ( !ms_bInitialized || s_bProcessing )
|
||||||
|
return;
|
||||||
|
s_bProcessing = true;
|
||||||
|
CloseCandidateList();
|
||||||
|
s_bProcessing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::CloseReadingInformation() -> void
|
int CIME::GetCompLen()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
return ms_compLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::ChangeInputLanguage() -> void
|
int CIME::GetULBegin()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
return ms_ulbegin;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::ChangeInputLanguageWorker() -> void
|
int CIME::GetULEnd()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
return ms_ulend;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::WMInputLanguage(HWND, UINT, WPARAM, LPARAM) -> LRESULT
|
void CIME::CloseCandidateList()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
ms_bCandidateList = false;
|
||||||
return mt_platform_stub_return<LRESULT>();
|
ms_dwCandidateCount = 0;
|
||||||
|
memset(&ms_wszCandidate, 0, sizeof(ms_wszCandidate));
|
||||||
|
if(ms_pEvent)
|
||||||
|
ms_pEvent->OnCloseCandidateList();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::WMStartComposition(HWND, UINT, WPARAM, LPARAM) -> LRESULT
|
void CIME::CloseReadingInformation()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
CIME::ms_bReadingInformation = false;
|
||||||
return mt_platform_stub_return<LRESULT>();
|
if(CIME::ms_pEvent)
|
||||||
|
CIME::ms_pEvent->OnCloseReadingWnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::WMComposition(HWND, UINT, WPARAM, LPARAM) -> LRESULT
|
// PORT: 40250 re-reads the keyboard layout (GETLANG) and re-applies the IME support level when the language
|
||||||
|
// changed. The host has one fixed input locale, so only the toggle check and the code-page event remain.
|
||||||
|
void CIME::ChangeInputLanguage()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
CheckToggleState();
|
||||||
return mt_platform_stub_return<LRESULT>();
|
ChangeInputLanguageWorker();
|
||||||
|
|
||||||
|
if(ms_pEvent)
|
||||||
|
ms_pEvent->OnChangeCodePage();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::WMEndComposition(HWND, UINT, WPARAM, LPARAM) -> LRESULT
|
void CIME::ChangeInputLanguageWorker()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
if ( !ms_bUILessMode )
|
||||||
return mt_platform_stub_return<LRESULT>();
|
ms_iCandListIndexBase = 1;
|
||||||
|
SetupImeApi();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::WMNotify(HWND, UINT, WPARAM, LPARAM) -> LRESULT
|
// PORT: 40250 forces level 3 for the Korean keyboard layout; there is no keyboard layout here.
|
||||||
|
void CIME::SetSupportLevel( DWORD dwImeLevel )
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
if ( dwImeLevel < 2 || 3 < dwImeLevel )
|
||||||
return mt_platform_stub_return<LRESULT>();
|
return;
|
||||||
|
ms_dwIMELevel = dwImeLevel;
|
||||||
|
// cancel current composition string.
|
||||||
|
FinalizeString();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::WMChar(HWND, UINT, WPARAM, LPARAM) -> LRESULT
|
LRESULT CIME::WMInputLanguage(HWND, UINT, WPARAM, LPARAM)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
ChangeInputLanguage();
|
||||||
return mt_platform_stub_return<LRESULT>();
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::IncCurPos() -> void
|
LRESULT CIME::WMStartComposition(HWND, UINT, WPARAM, LPARAM)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
return 1L;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::DecCurPos() -> void
|
// PORT: 40250 pulls GCS_RESULTSTR/GCS_COMPSTR out of the IMM context. The host never sends compositions.
|
||||||
|
LRESULT CIME::WMComposition(HWND, UINT, WPARAM, LPARAM)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::SetCurPos(int) -> void
|
LRESULT CIME::WMEndComposition(HWND, UINT, WPARAM, LPARAM)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
ms_compLen = 0;
|
||||||
|
ms_ulbegin = 0;
|
||||||
|
ms_ulend = 0;
|
||||||
|
|
||||||
|
if(ms_pEvent)
|
||||||
|
ms_pEvent->OnUpdate();
|
||||||
|
|
||||||
|
return 0L;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::DelCurPos() -> void
|
// PORT: 40250 handles IMN_* candidate/reading/toggle notifications from IMM; none are sent here.
|
||||||
|
LRESULT CIME::WMNotify(HWND, UINT, WPARAM, LPARAM)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::CheckInputLocale() -> void
|
LRESULT CIME::WMChar(HWND /*hWnd*/, UINT /*uiMsg*/, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
unsigned char c = (unsigned char)(wParam & 0xff);
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 8:
|
||||||
|
if(ms_bCaptureInput == false)
|
||||||
|
return 0;
|
||||||
|
if (ms_curpos > 0)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
DecCurPos();
|
||||||
|
DelCurPos();
|
||||||
}
|
}
|
||||||
|
if(ms_pEvent)
|
||||||
|
ms_pEvent->OnUpdate();
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
|
||||||
auto CIME::CheckToggleState() -> void
|
default:
|
||||||
|
if(ms_pEvent) {
|
||||||
|
if (ms_pEvent->OnWM_CHAR(wParam, lParam))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(ms_bCaptureInput == false)
|
||||||
|
return 0;
|
||||||
|
wchar_t w[10];
|
||||||
|
MultiByteToWideChar(ms_uInputCodePage, 0, (char*)&c, 1, w, 1);
|
||||||
|
|
||||||
|
OnChar(w[0]);
|
||||||
|
if (w[0] == L'|')
|
||||||
|
OnChar(w[0]);
|
||||||
|
if(ms_pEvent)
|
||||||
|
ms_pEvent->OnUpdate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------*/ /* Protected */
|
||||||
|
void CIME::IncCurPos()
|
||||||
|
{
|
||||||
|
if (ms_curpos < ms_lastpos)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
int pos = FindColorTagEndPosition(m_wText + ms_curpos, ms_lastpos - ms_curpos);
|
||||||
|
|
||||||
|
if (pos > 0)
|
||||||
|
ms_curpos = std::min(ms_lastpos, std::max(0, ms_curpos + (pos + 1)));
|
||||||
|
else
|
||||||
|
++ms_curpos;
|
||||||
|
//++ms_curpos;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::SetSupportLevel(DWORD) -> void
|
void CIME::DecCurPos()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
if (ms_curpos > 0)
|
||||||
|
{
|
||||||
|
int pos = FindColorTagStartPosition(m_wText + ms_curpos - 1, ms_curpos);
|
||||||
|
|
||||||
|
if (pos > 0)
|
||||||
|
ms_curpos = std::min(ms_lastpos, std::max(0, ms_curpos - (pos + 1)));
|
||||||
|
else
|
||||||
|
--ms_curpos;
|
||||||
|
//--ms_curpos;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::InsertString(wchar_t *, int) -> void
|
int CIME::GetCurPos()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
int pos = GetTextTagOutputLen(m_wText, ms_curpos);
|
||||||
|
return pos;
|
||||||
|
//return ms_curpos;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::OnChar(wchar_t) -> void
|
void CIME::SetCurPos(int offset)
|
||||||
|
{
|
||||||
|
if (offset < 0 || offset > ms_lastpos)
|
||||||
|
{
|
||||||
|
ms_curpos = ms_lastpos;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
// offset is a position in the rendered text, so it has to be mapped back to the buffer.
|
||||||
|
//ms_curpos = min(ms_lastpos, offset);
|
||||||
|
ms_curpos = std::min(ms_lastpos, GetTextTagInternalPosFromRenderPos(m_wText, ms_lastpos, offset));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::GetCodePageFromLang(LANGID) -> UINT
|
void CIME::DelCurPos()
|
||||||
|
{
|
||||||
|
if (ms_curpos < ms_lastpos)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
int eraseCount = FindColorTagEndPosition(m_wText + ms_curpos, ms_lastpos - ms_curpos) + 1;
|
||||||
return mt_platform_stub_return<UINT>();
|
// PORT: 40250 wcscpy()s the overlapping tail down; that is undefined for overlapping buffers, and the
|
||||||
|
// buffer is not guaranteed NUL-terminated at ms_lastpos, so move exactly the live tail.
|
||||||
|
memmove(m_wText + ms_curpos, m_wText + ms_curpos + eraseCount, sizeof(wchar_t) * (ms_lastpos - ms_curpos - eraseCount));
|
||||||
|
ms_lastpos -= eraseCount;
|
||||||
|
ms_curpos = std::min(ms_lastpos, ms_curpos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::ResultProcess(HIMC) -> void
|
void CIME::PasteString(const char * str)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
const char * begin = str;
|
||||||
|
const char * end = str + strlen(str);
|
||||||
|
wchar_t m_wText[IMESTR_MAXLEN];
|
||||||
|
int wstrLen = MultiByteToWideChar(ms_uInputCodePage, 0, begin, end - begin, m_wText, IMESTR_MAXLEN);
|
||||||
|
InsertString(m_wText, wstrLen);
|
||||||
|
if(ms_pEvent)
|
||||||
|
ms_pEvent->OnUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::CompositionProcessBuilding(HIMC) -> void
|
// PORT: 40250 reads the keyboard layout's default ANSI code page (GetLocaleInfoA LOCALE_IDEFAULTANSICODEPAGE).
|
||||||
|
// The host's input locale is the client locale, so its code page (SetDefaultCodePage from locale.cfg) is used.
|
||||||
|
void CIME::CheckInputLocale()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
static UINT s_uPrevCodePage = 0xFFFF;
|
||||||
|
|
||||||
|
ms_uInputCodePage = GetDefaultCodePage();
|
||||||
|
if ( s_uPrevCodePage == ms_uInputCodePage )
|
||||||
|
return;
|
||||||
|
s_uPrevCodePage = ms_uInputCodePage;
|
||||||
|
|
||||||
|
ms_bVerticalCandidate = false;
|
||||||
|
ms_wszCurrentIndicator = s_aszNonImeIndicator;
|
||||||
|
|
||||||
|
if(ms_uOutputCodePage != 1256) {
|
||||||
|
ms_uOutputCodePage = ms_uInputCodePage;
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::CompositionProcess(HIMC) -> void
|
// PORT: 40250 asks IMM whether an IME is open; there is none, so the state is IMEUI_STATE_OFF (0).
|
||||||
|
void CIME::CheckToggleState()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
CheckInputLocale();
|
||||||
|
ms_bChineseIME = false;
|
||||||
|
ms_dwImeState = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::AttributeProcess(HIMC) -> void
|
/*---------------------------------------------------------------------------*/ /* Private */
|
||||||
|
void CIME::InsertString(wchar_t* wString, int iSize)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
if (IsMax(wString, iSize))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ms_curpos < ms_lastpos)
|
||||||
|
memmove(m_wText+ms_curpos+iSize, m_wText+ms_curpos, sizeof(wchar_t)*(ms_lastpos-ms_curpos));
|
||||||
|
|
||||||
|
memcpy(m_wText+ms_curpos, wString, sizeof(wchar_t)*iSize);
|
||||||
|
|
||||||
|
ms_curpos += iSize;
|
||||||
|
ms_lastpos += iSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::CandidateProcess(HIMC) -> void
|
void CIME::OnChar(wchar_t c)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
if (m_bOnlyNumberMode)
|
||||||
|
if (!iswdigit(c))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!__IsWritable(c))
|
||||||
|
return;
|
||||||
|
|
||||||
|
InsertString(&c, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::ReadingProcess(HIMC) -> void
|
// PORT: 40250 maps a keyboard-layout LANGID to its ANSI code page. Only CheckInputLocale's code page is used
|
||||||
|
// here (see there), so this answers with it.
|
||||||
|
UINT CIME::GetCodePageFromLang(LANGID)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
return GetDefaultCodePage();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::IsMax(const wchar_t *, int) -> bool
|
// PORT: the IMM composition/candidate/reading processors (40250 IME.cpp:951-1227). No IMM context exists.
|
||||||
|
void CIME::ResultProcess(HIMC) {}
|
||||||
|
void CIME::CompositionProcessBuilding(HIMC) {}
|
||||||
|
void CIME::CompositionProcess(HIMC) {}
|
||||||
|
void CIME::AttributeProcess(HIMC) {}
|
||||||
|
void CIME::CandidateProcess(HIMC) {}
|
||||||
|
void CIME::ReadingProcess(HIMC) {}
|
||||||
|
|
||||||
|
bool CIME::IsMax(const wchar_t* wInput, int len)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
if (ms_lastpos + len > IMESTR_MAXLEN)
|
||||||
return mt_platform_stub_return<bool>();
|
return true;
|
||||||
|
|
||||||
|
int textLen = WideCharToMultiByte(ms_uOutputCodePage, 0, m_wText, ms_lastpos, 0, 0, NULL, NULL);
|
||||||
|
int inputLen = WideCharToMultiByte(ms_uOutputCodePage, 0, wInput, len, 0, 0, NULL, NULL);
|
||||||
|
//return textLen + inputLen > m_max;
|
||||||
|
|
||||||
|
if (textLen + inputLen > m_max)
|
||||||
|
return true;
|
||||||
|
else if (m_userMax != 0 && m_max != m_userMax)
|
||||||
|
{
|
||||||
|
std::wstring str = GetTextTagOutputString(m_wText, ms_lastpos);
|
||||||
|
std::wstring input = GetTextTagOutputString(wInput, len);
|
||||||
|
int textLen = WideCharToMultiByte(ms_uOutputCodePage, 0, str.c_str(), str.length(), 0, 0, NULL, NULL);
|
||||||
|
int inputLen = WideCharToMultiByte(ms_uOutputCodePage, 0, input.c_str(), input.length(), 0, 0, NULL, NULL);
|
||||||
|
return textLen + inputLen > m_userMax;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::GetImeId(UINT) -> DWORD
|
// PORT: 40250 identifies the CHT/CHS IME DLL version and the reading-window orientation. No IME DLL here.
|
||||||
|
DWORD CIME::GetImeId(UINT)
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
return 0;
|
||||||
return mt_platform_stub_return<DWORD>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::GetReadingWindowOrientation() -> bool
|
bool CIME::GetReadingWindowOrientation()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
return true;
|
||||||
return mt_platform_stub_return<bool>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CIME::SetupImeApi() -> void
|
void CIME::SetupImeApi()
|
||||||
{
|
{
|
||||||
MT_PLATFORM_STUB();
|
_GetReadingString = NULL;
|
||||||
|
_ShowReadingWindow = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
decltype(CIME::_ImmLockIMC) CIME::_ImmLockIMC{};
|
decltype(CIME::_ImmLockIMC) CIME::_ImmLockIMC{};
|
||||||
|
|||||||
@@ -63,11 +63,6 @@ void AddStubConstants_guild(PyObject* poModule)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 40250 UserInterface/PythonIMEModule.cpp
|
// 40250 UserInterface/PythonIMEModule.cpp
|
||||||
void AddStubConstants_ime(PyObject* poModule)
|
|
||||||
{
|
|
||||||
(void) poModule;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 40250 UserInterface/PythonMessenger.cpp
|
// 40250 UserInterface/PythonMessenger.cpp
|
||||||
void AddStubConstants_messenger(PyObject* poModule)
|
void AddStubConstants_messenger(PyObject* poModule)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
void AddStubConstants_event(PyObject* poModule);
|
void AddStubConstants_event(PyObject* poModule);
|
||||||
void AddStubConstants_eventMgr(PyObject* poModule);
|
void AddStubConstants_eventMgr(PyObject* poModule);
|
||||||
void AddStubConstants_guild(PyObject* poModule);
|
void AddStubConstants_guild(PyObject* poModule);
|
||||||
void AddStubConstants_ime(PyObject* poModule);
|
|
||||||
void AddStubConstants_messenger(PyObject* poModule);
|
void AddStubConstants_messenger(PyObject* poModule);
|
||||||
void AddStubConstants_profiler(PyObject* poModule);
|
void AddStubConstants_profiler(PyObject* poModule);
|
||||||
void AddStubConstants_quest(PyObject* poModule);
|
void AddStubConstants_quest(PyObject* poModule);
|
||||||
@@ -154,76 +153,6 @@ PyMethodDef methods_guild[] = {
|
|||||||
{ "Destroy", stub_guild_Destroy, METH_VARARGS },
|
{ "Destroy", stub_guild_Destroy, METH_VARARGS },
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
// 40250 UserInterface/PythonIMEModule.cpp: ime export names.
|
|
||||||
PyObject* stub_ime_Enable(PyObject*, PyObject*) { return stub_call_named("ime.Enable", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_Disable(PyObject*, PyObject*) { return stub_call_named("ime.Disable", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_EnableCaptureInput(PyObject*, PyObject*) { return stub_call_named("ime.EnableCaptureInput", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_DisableCaptureInput(PyObject*, PyObject*) { return stub_call_named("ime.DisableCaptureInput", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_SetMax(PyObject*, PyObject*) { return stub_call_named("ime.SetMax", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_SetUserMax(PyObject*, PyObject*) { return stub_call_named("ime.SetUserMax", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_SetText(PyObject*, PyObject*) { return stub_call_named("ime.SetText", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_GetText(PyObject*, PyObject*) { return stub_call_named("ime.GetText", Py_BuildValue("s", "")); }
|
|
||||||
PyObject* stub_ime_GetCodePage(PyObject*, PyObject*) { return stub_call_named("ime.GetCodePage", Py_BuildValue("i", 0)); }
|
|
||||||
PyObject* stub_ime_GetCandidateCount(PyObject*, PyObject*) { return stub_call_named("ime.GetCandidateCount", Py_BuildValue("i", 0)); }
|
|
||||||
PyObject* stub_ime_GetCandidate(PyObject*, PyObject*) { return stub_call_named("ime.GetCandidate", Py_BuildValue("si", "", 0)); }
|
|
||||||
PyObject* stub_ime_GetCandidateSelection(PyObject*, PyObject*) { return stub_call_named("ime.GetCandidateSelection", Py_BuildValue("i", 0)); }
|
|
||||||
PyObject* stub_ime_GetReading(PyObject*, PyObject*) { return stub_call_named("ime.GetReading", Py_BuildValue("s", "")); }
|
|
||||||
PyObject* stub_ime_GetReadingError(PyObject*, PyObject*) { return stub_call_named("ime.GetReadingError", Py_BuildValue("i", 0)); }
|
|
||||||
PyObject* stub_ime_EnableIME(PyObject*, PyObject*) { return stub_call_named("ime.EnableIME", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_DisableIME(PyObject*, PyObject*) { return stub_call_named("ime.DisableIME", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_GetInputMode(PyObject*, PyObject*) { return stub_call_named("ime.GetInputMode", Py_BuildValue("i", 0)); }
|
|
||||||
PyObject* stub_ime_SetInputMode(PyObject*, PyObject*) { return stub_call_named("ime.SetInputMode", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_SetNumberMode(PyObject*, PyObject*) { return stub_call_named("ime.SetNumberMode", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_SetStringMode(PyObject*, PyObject*) { return stub_call_named("ime.SetStringMode", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_AddExceptKey(PyObject*, PyObject*) { return stub_call_named("ime.AddExceptKey", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_ClearExceptKey(PyObject*, PyObject*) { return stub_call_named("ime.ClearExceptKey", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_MoveLeft(PyObject*, PyObject*) { return stub_call_named("ime.MoveLeft", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_MoveRight(PyObject*, PyObject*) { return stub_call_named("ime.MoveRight", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_MoveHome(PyObject*, PyObject*) { return stub_call_named("ime.MoveHome", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_MoveEnd(PyObject*, PyObject*) { return stub_call_named("ime.MoveEnd", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_SetCursorPosition(PyObject*, PyObject*) { return stub_call_named("ime.SetCursorPosition", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_Delete(PyObject*, PyObject*) { return stub_call_named("ime.Delete", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_PasteString(PyObject*, PyObject*) { return stub_call_named("ime.PasteString", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_PasteBackspace(PyObject*, PyObject*) { return stub_call_named("ime.PasteBackspace", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_PasteReturn(PyObject*, PyObject*) { return stub_call_named("ime.PasteReturn", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_PasteTextFromClipBoard(PyObject*, PyObject*) { return stub_call_named("ime.PasteTextFromClipBoard", Py_BuildNone()); }
|
|
||||||
PyObject* stub_ime_EnablePaste(PyObject*, PyObject*) { return stub_call_named("ime.EnablePaste", Py_BuildNone()); }
|
|
||||||
PyMethodDef methods_ime[] = {
|
|
||||||
{ "Enable", stub_ime_Enable, METH_VARARGS },
|
|
||||||
{ "Disable", stub_ime_Disable, METH_VARARGS },
|
|
||||||
{ "EnableCaptureInput", stub_ime_EnableCaptureInput, METH_VARARGS },
|
|
||||||
{ "DisableCaptureInput", stub_ime_DisableCaptureInput, METH_VARARGS },
|
|
||||||
{ "SetMax", stub_ime_SetMax, METH_VARARGS },
|
|
||||||
{ "SetUserMax", stub_ime_SetUserMax, METH_VARARGS },
|
|
||||||
{ "SetText", stub_ime_SetText, METH_VARARGS },
|
|
||||||
{ "GetText", stub_ime_GetText, METH_VARARGS },
|
|
||||||
{ "GetCodePage", stub_ime_GetCodePage, METH_VARARGS },
|
|
||||||
{ "GetCandidateCount", stub_ime_GetCandidateCount, METH_VARARGS },
|
|
||||||
{ "GetCandidate", stub_ime_GetCandidate, METH_VARARGS },
|
|
||||||
{ "GetCandidateSelection", stub_ime_GetCandidateSelection, METH_VARARGS },
|
|
||||||
{ "GetReading", stub_ime_GetReading, METH_VARARGS },
|
|
||||||
{ "GetReadingError", stub_ime_GetReadingError, METH_VARARGS },
|
|
||||||
{ "EnableIME", stub_ime_EnableIME, METH_VARARGS },
|
|
||||||
{ "DisableIME", stub_ime_DisableIME, METH_VARARGS },
|
|
||||||
{ "GetInputMode", stub_ime_GetInputMode, METH_VARARGS },
|
|
||||||
{ "SetInputMode", stub_ime_SetInputMode, METH_VARARGS },
|
|
||||||
{ "SetNumberMode", stub_ime_SetNumberMode, METH_VARARGS },
|
|
||||||
{ "SetStringMode", stub_ime_SetStringMode, METH_VARARGS },
|
|
||||||
{ "AddExceptKey", stub_ime_AddExceptKey, METH_VARARGS },
|
|
||||||
{ "ClearExceptKey", stub_ime_ClearExceptKey, METH_VARARGS },
|
|
||||||
{ "MoveLeft", stub_ime_MoveLeft, METH_VARARGS },
|
|
||||||
{ "MoveRight", stub_ime_MoveRight, METH_VARARGS },
|
|
||||||
{ "MoveHome", stub_ime_MoveHome, METH_VARARGS },
|
|
||||||
{ "MoveEnd", stub_ime_MoveEnd, METH_VARARGS },
|
|
||||||
{ "SetCursorPosition", stub_ime_SetCursorPosition, METH_VARARGS },
|
|
||||||
{ "Delete", stub_ime_Delete, METH_VARARGS },
|
|
||||||
{ "PasteString", stub_ime_PasteString, METH_VARARGS },
|
|
||||||
{ "PasteBackspace", stub_ime_PasteBackspace, METH_VARARGS },
|
|
||||||
{ "PasteReturn", stub_ime_PasteReturn, METH_VARARGS },
|
|
||||||
{ "PasteTextFromClipBoard", stub_ime_PasteTextFromClipBoard, METH_VARARGS },
|
|
||||||
{ "EnablePaste", stub_ime_EnablePaste, METH_VARARGS },
|
|
||||||
{ NULL, NULL }
|
|
||||||
};
|
|
||||||
// 40250 UserInterface/PythonMessenger.cpp: messenger export names.
|
// 40250 UserInterface/PythonMessenger.cpp: messenger export names.
|
||||||
PyObject* stub_messenger_RemoveFriend(PyObject*, PyObject*) { return stub_call_named("messenger.RemoveFriend", Py_BuildNone()); }
|
PyObject* stub_messenger_RemoveFriend(PyObject*, PyObject*) { return stub_call_named("messenger.RemoveFriend", Py_BuildNone()); }
|
||||||
PyObject* stub_messenger_IsFriendByName(PyObject*, PyObject*) { return stub_call_named("messenger.IsFriendByName", Py_BuildValue("i", 0)); }
|
PyObject* stub_messenger_IsFriendByName(PyObject*, PyObject*) { return stub_call_named("messenger.IsFriendByName", Py_BuildValue("i", 0)); }
|
||||||
@@ -471,11 +400,6 @@ void init_2v0_gameplay_stubs() {
|
|||||||
PyModule_AddIntConstant(module, "__port_stub__", 1);
|
PyModule_AddIntConstant(module, "__port_stub__", 1);
|
||||||
AddStubConstants_guild(module);
|
AddStubConstants_guild(module);
|
||||||
}
|
}
|
||||||
{
|
|
||||||
PyObject* module = Py_InitModule("ime", methods_ime);
|
|
||||||
PyModule_AddIntConstant(module, "__port_stub__", 1);
|
|
||||||
AddStubConstants_ime(module);
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
PyObject* module = Py_InitModule("messenger", methods_messenger);
|
PyObject* module = Py_InitModule("messenger", methods_messenger);
|
||||||
PyModule_AddIntConstant(module, "__port_stub__", 1);
|
PyModule_AddIntConstant(module, "__port_stub__", 1);
|
||||||
|
|||||||
@@ -88,14 +88,15 @@ public:
|
|||||||
}
|
}
|
||||||
void BlendEventCamera(const SCameraSetting&, float) override {}
|
void BlendEventCamera(const SCameraSetting&, float) override {}
|
||||||
void SetDefaultCamera() override { CPythonApplication::Instance().SetDefaultCamera(); }
|
void SetDefaultCamera() override { CPythonApplication::Instance().SetDefaultCamera(); }
|
||||||
void RunIMEUpdate() override {}
|
// 40250 CPythonApplication::RunIME* (PythonApplicationEvent.cpp:148-195): CPythonIME's event sink.
|
||||||
void RunIMETabEvent() override {}
|
void RunIMEUpdate() override { UI::CWindowManager::Instance().RunIMEUpdate(); }
|
||||||
void RunIMEReturnEvent() override {}
|
void RunIMETabEvent() override { UI::CWindowManager::Instance().RunIMETabEvent(); }
|
||||||
void RunIMEChangeCodePage() override {}
|
void RunIMEReturnEvent() override { UI::CWindowManager::Instance().RunIMEReturnEvent(); }
|
||||||
void RunIMEOpenCandidateListEvent() override {}
|
void RunIMEChangeCodePage() override { UI::CWindowManager::Instance().RunChangeCodePage(); }
|
||||||
void RunIMECloseCandidateListEvent() override {}
|
void RunIMEOpenCandidateListEvent() override { UI::CWindowManager::Instance().RunOpenCandidate(); }
|
||||||
void RunIMEOpenReadingWndEvent() override {}
|
void RunIMECloseCandidateListEvent() override { UI::CWindowManager::Instance().RunCloseCandidate(); }
|
||||||
void RunIMECloseReadingWndEvent() override {}
|
void RunIMEOpenReadingWndEvent() override { UI::CWindowManager::Instance().RunOpenReading(); }
|
||||||
|
void RunIMECloseReadingWndEvent() override { UI::CWindowManager::Instance().RunCloseReading(); }
|
||||||
};
|
};
|
||||||
std::unique_ptr<GameApplicationAdapter> g_game_application;
|
std::unique_ptr<GameApplicationAdapter> g_game_application;
|
||||||
// The CPythonApplication members the game phase reaches through Instance() (批次 2V2), in 40250's member
|
// The CPythonApplication members the game phase reaches through Instance() (批次 2V2), in 40250's member
|
||||||
@@ -109,6 +110,7 @@ struct GameSingletons
|
|||||||
CItemManager kItemMgr;
|
CItemManager kItemMgr;
|
||||||
CPythonCharacterManager kChrMgr;
|
CPythonCharacterManager kChrMgr;
|
||||||
CPythonGraphic pyGraphic; // owns CCullingManager (m_CullingManager)
|
CPythonGraphic pyGraphic; // owns CCullingManager (m_CullingManager)
|
||||||
|
CPythonIME pyIme;
|
||||||
CPythonItem pyItem;
|
CPythonItem pyItem;
|
||||||
CPythonShop pyShop;
|
CPythonShop pyShop;
|
||||||
CPythonExchange pyExchange;
|
CPythonExchange pyExchange;
|
||||||
@@ -266,6 +268,47 @@ void UIKey(int key, bool pressed)
|
|||||||
g_window_manager->RunKeyUp(key);
|
g_window_manager->RunKeyUp(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 40250 CPythonApplication::WindowProcedure (PythonApplicationProcedure.cpp:111-117): WM_CHAR goes to
|
||||||
|
// CPythonIME::WMChar and WM_KEYDOWN to OnIMEKeyDown (the VK code, next to DirectInput's DIK stream). The host
|
||||||
|
// delivers characters as Unicode code points; the 40250 window is an ANSI window, so a character that the input
|
||||||
|
// code page holds as one byte arrives as that byte, like WM_CHAR. PORT: anything else is what a Windows IME
|
||||||
|
// commits through WM_IME_COMPOSITION/GCS_RESULTSTR, which CIME::ResultProcess inserts as a wide string.
|
||||||
|
void UIChar(unsigned codepoint)
|
||||||
|
{
|
||||||
|
if (!g_game_singletons || !codepoint) return;
|
||||||
|
CPythonIME& ime = CPythonIME::Instance();
|
||||||
|
if (codepoint < 0x80)
|
||||||
|
{
|
||||||
|
ime.WMChar(NULL, WM_CHAR, codepoint, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const wchar_t wide = wchar_t(codepoint);
|
||||||
|
char bytes[8];
|
||||||
|
BOOL used_default = FALSE;
|
||||||
|
const int n = WideCharToMultiByte(CIME::ms_uInputCodePage, 0, &wide, 1, bytes, sizeof(bytes), "?", &used_default);
|
||||||
|
if (n == 1 && !used_default)
|
||||||
|
{
|
||||||
|
ime.WMChar(NULL, WM_CHAR, (unsigned char)bytes[0], 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 40250 CIME::WMComposition with GCS_RESULTSTR: ResultProcess inserts the committed string, then OnUpdate.
|
||||||
|
if (!CIME::ms_bCaptureInput)
|
||||||
|
return;
|
||||||
|
struct ResultAccess : CPythonIME
|
||||||
|
{
|
||||||
|
static void Insert(CPythonIME& ime, wchar_t* text, int len) { (ime.*(&ResultAccess::InsertString))(text, len); }
|
||||||
|
};
|
||||||
|
wchar_t text[1] = { wide };
|
||||||
|
ResultAccess::Insert(ime, text, 1);
|
||||||
|
if (CIME::ms_pEvent)
|
||||||
|
CIME::ms_pEvent->OnUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UIIMEKeyDown(int vkey)
|
||||||
|
{
|
||||||
|
if (g_window_manager) g_window_manager->RunIMEKeyDown(vkey);
|
||||||
|
}
|
||||||
|
|
||||||
void UIUpdate()
|
void UIUpdate()
|
||||||
{
|
{
|
||||||
// While system.py is inside app.Loop(), one host frame is one CPythonApplication::Process(),
|
// While system.py is inside app.Loop(), one host frame is one CPythonApplication::Process(),
|
||||||
@@ -341,6 +384,7 @@ bool run_main_script_body(const char* lpCmdLine, std::string* error)
|
|||||||
// The module initializers follow 40250's order; missing modules arrive in later 2V steps.
|
// The module initializers follow 40250's order; missing modules arrive in later 2V steps.
|
||||||
initpack();
|
initpack();
|
||||||
initdbg();
|
initdbg();
|
||||||
|
initime();
|
||||||
initgrp();
|
initgrp();
|
||||||
initgrpImage();
|
initgrpImage();
|
||||||
initgrpText();
|
initgrpText();
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ void SetUISize(int width, int height);
|
|||||||
void UIMouseMove(int x, int y);
|
void UIMouseMove(int x, int y);
|
||||||
void UIMouseButton(int button, bool pressed, int x, int y);
|
void UIMouseButton(int button, bool pressed, int x, int y);
|
||||||
void UIKey(int key, bool pressed);
|
void UIKey(int key, bool pressed);
|
||||||
|
// WM_CHAR (a Unicode code point) and WM_KEYDOWN (a Win32 VK code) for CPythonIME and OnIMEKeyDown.
|
||||||
|
void UIChar(unsigned codepoint);
|
||||||
|
void UIIMEKeyDown(int vkey);
|
||||||
// While IsAppLooping(), UIUpdate is AppFrame() and UIRender keeps the commands that Process() drew.
|
// While IsAppLooping(), UIUpdate is AppFrame() and UIRender keeps the commands that Process() drew.
|
||||||
void UIUpdate();
|
void UIUpdate();
|
||||||
void UIRender();
|
void UIRender();
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ void CPythonApplication::SetFrameSkip(bool) { MT_PLATFORM_STUB(); }
|
|||||||
// fonts and the IME here. The Godot window and its canvas already exist when the host starts the
|
// fonts and the IME here. The Godot window and its canvas already exist when the host starts the
|
||||||
// script, so the window size is the canvas size (PythonBoot::SetUISize) rather than the width/height
|
// script, so the window size is the canvas size (PythonBoot::SetUISize) rather than the width/height
|
||||||
// passed from metin2.cfg, and the graphic device is the recording device (RecordingDevice.h) at that
|
// passed from metin2.cfg, and the graphic device is the recording device (RecordingDevice.h) at that
|
||||||
// size. Cursors, sound, keyboard, fonts and the IME are not created here yet.
|
// size. Cursors, sound, keyboard and fonts are not created here yet.
|
||||||
bool CPythonApplication::Create(PyObject*, const char*, int, int, int)
|
bool CPythonApplication::Create(PyObject*, const char*, int, int, int)
|
||||||
{
|
{
|
||||||
unsigned width = 0, height = 0;
|
unsigned width = 0, height = 0;
|
||||||
@@ -125,7 +125,11 @@ bool CPythonApplication::Create(PyObject*, const char*, int, int, int)
|
|||||||
// 40250 m_pyItem.Create() (PythonApplication.cpp:1253): registers dropitem.mse with CEffectManager.
|
// 40250 m_pyItem.Create() (PythonApplication.cpp:1253): registers dropitem.mse with CEffectManager.
|
||||||
CPythonItem::Instance().Create();
|
CPythonItem::Instance().Create();
|
||||||
|
|
||||||
// 40250 PythonApplication.cpp:1260, after DefaultFont_Startup and the IME.
|
// 40250 PythonApplication.cpp:1258-1259 (after DefaultFont_Startup).
|
||||||
|
CPythonIME::Instance().Create(GetWindowHandle());
|
||||||
|
CPythonIME::Instance().SetText("", 0);
|
||||||
|
|
||||||
|
// 40250 PythonApplication.cpp:1260.
|
||||||
CPythonTextTail::Instance().Initialize();
|
CPythonTextTail::Instance().Initialize();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
#include "StdAfx.h"
|
||||||
|
#include "PythonIME.h"
|
||||||
|
#include "AbstractApplication.h"
|
||||||
|
|
||||||
|
CPythonIME::CPythonIME()
|
||||||
|
: CIME()
|
||||||
|
{
|
||||||
|
ms_pEvent = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CPythonIME::~CPythonIME()
|
||||||
|
{
|
||||||
|
Tracen("PythonIME Clear");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPythonIME::Create(HWND hWnd)
|
||||||
|
{
|
||||||
|
Initialize(hWnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPythonIME::MoveLeft()
|
||||||
|
{
|
||||||
|
DecCurPos();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPythonIME::MoveRight()
|
||||||
|
{
|
||||||
|
IncCurPos();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPythonIME::MoveHome()
|
||||||
|
{
|
||||||
|
ms_curpos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPythonIME::MoveEnd()
|
||||||
|
{
|
||||||
|
ms_curpos = ms_lastpos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPythonIME::SetCursorPosition(int iPosition)
|
||||||
|
{
|
||||||
|
SetCurPos(iPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPythonIME::Delete()
|
||||||
|
{
|
||||||
|
DelCurPos();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPythonIME::OnUpdate()
|
||||||
|
{
|
||||||
|
IAbstractApplication::GetSingleton().RunIMEUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPythonIME::OnTab()
|
||||||
|
{
|
||||||
|
IAbstractApplication::GetSingleton().RunIMETabEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPythonIME::OnReturn()
|
||||||
|
{
|
||||||
|
IAbstractApplication::GetSingleton().RunIMEReturnEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPythonIME::OnEscape()
|
||||||
|
{
|
||||||
|
// IAbstractApplication::GetSingleton().RunIMEEscapeEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CPythonIME::OnWM_CHAR( WPARAM wParam, LPARAM lParam )
|
||||||
|
{
|
||||||
|
unsigned char c = (unsigned char)(wParam & 0xff); // PORT: functional cast of a two-word type is MSVC-only.
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case VK_RETURN:
|
||||||
|
OnReturn();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case VK_TAB:
|
||||||
|
if(ms_bCaptureInput == false)
|
||||||
|
return 0;
|
||||||
|
OnTab();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case VK_ESCAPE:
|
||||||
|
if(ms_bCaptureInput == false)
|
||||||
|
return 0;
|
||||||
|
OnEscape();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPythonIME::OnChangeCodePage()
|
||||||
|
{
|
||||||
|
IAbstractApplication::GetSingleton().RunIMEChangeCodePage();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPythonIME::OnOpenCandidateList()
|
||||||
|
{
|
||||||
|
IAbstractApplication::GetSingleton().RunIMEOpenCandidateListEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPythonIME::OnCloseCandidateList()
|
||||||
|
{
|
||||||
|
IAbstractApplication::GetSingleton().RunIMECloseCandidateListEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPythonIME::OnOpenReadingWnd()
|
||||||
|
{
|
||||||
|
IAbstractApplication::GetSingleton().RunIMEOpenReadingWndEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPythonIME::OnCloseReadingWnd()
|
||||||
|
{
|
||||||
|
IAbstractApplication::GetSingleton().RunIMECloseReadingWndEvent();
|
||||||
|
}
|
||||||
@@ -0,0 +1,289 @@
|
|||||||
|
#include "StdAfx.h"
|
||||||
|
#include "PythonIME.h"
|
||||||
|
#include "PythonApplication.h"
|
||||||
|
|
||||||
|
PyObject* imeEnable(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
CPythonIME::Instance().Initialize(CPythonApplication::Instance().GetWindowHandle());
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeDisable(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
CPythonIME::Instance().Uninitialize();
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeEnableCaptureInput(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
CPythonIME::Instance().EnableCaptureInput();
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeDisableCaptureInput(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
CPythonIME::Instance().DisableCaptureInput();
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeSetMax(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
int iMax;
|
||||||
|
if (!PyTuple_GetInteger(poArgs, 0, &iMax))
|
||||||
|
return Py_BuildException();
|
||||||
|
|
||||||
|
CPythonIME::Instance().SetMax(iMax);
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeSetUserMax(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
int iMax;
|
||||||
|
if (!PyTuple_GetInteger(poArgs, 0, &iMax))
|
||||||
|
return Py_BuildException();
|
||||||
|
|
||||||
|
CPythonIME::Instance().SetUserMax(iMax);
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeSetText(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
char* szText;
|
||||||
|
if (!PyTuple_GetString(poArgs, 0, &szText))
|
||||||
|
return Py_BuildException();
|
||||||
|
|
||||||
|
CPythonIME::Instance().SetText(szText, strlen(szText));
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeGetText(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
int bCodePage;
|
||||||
|
if (!PyTuple_GetInteger(poArgs, 0, &bCodePage))
|
||||||
|
bCodePage = 0;
|
||||||
|
|
||||||
|
std::string strText;
|
||||||
|
CPythonIME::Instance().GetText(strText, bCodePage ? true : false);
|
||||||
|
return Py_BuildValue("s", strText.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeGetCodePage(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
return Py_BuildValue("i", CPythonIME::Instance().GetCodePage());
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeGetCandidateCount(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
return Py_BuildValue("i", CPythonIME::Instance().GetCandidatePageCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeGetCandidate(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
if (!PyTuple_GetInteger(poArgs, 0, &index))
|
||||||
|
return Py_BuildException();
|
||||||
|
|
||||||
|
std::string strText;
|
||||||
|
int iLength = CPythonIME::Instance().GetCandidate(index, strText);
|
||||||
|
return Py_BuildValue("si", strText.c_str(), iLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeGetCandidateSelection(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
return Py_BuildValue("i", CPythonIME::Instance().GetCandidateSelection());
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeGetReading(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
std::string strText;
|
||||||
|
CPythonIME::Instance().GetReading(strText);
|
||||||
|
return Py_BuildValue("s", strText.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeGetReadingError(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
return Py_BuildValue("i", CPythonIME::Instance().GetReadingError());
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeEnableIME(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
CPythonIME::Instance().EnableIME();
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeDisableIME(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
CPythonIME::Instance().DisableIME();
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeSetInputMode(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
int mode;
|
||||||
|
if (!PyTuple_GetInteger(poArgs, 0, &mode))
|
||||||
|
return Py_BuildException();
|
||||||
|
|
||||||
|
CPythonIME::Instance().SetInputMode(mode);
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject * imeSetNumberMode(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
CPythonIME::Instance().SetNumberMode();
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject * imeAddExceptKey(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
int key;
|
||||||
|
if (!PyTuple_GetInteger(poArgs, 0, &key))
|
||||||
|
return Py_BuildException();
|
||||||
|
|
||||||
|
CPythonIME::Instance().AddExceptKey(key);
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject * imeClearExceptKey(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
CPythonIME::Instance().ClearExceptKey();
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject * imeSetStringMode(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
CPythonIME::Instance().SetStringMode();
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeGetInputMode(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
return Py_BuildValue("i", CPythonIME::Instance().GetInputMode());
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeMoveLeft(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
CPythonIME::Instance().MoveLeft();
|
||||||
|
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
PyObject* imeMoveRight(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
CPythonIME::Instance().MoveRight();
|
||||||
|
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeMoveHome(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
CPythonIME::Instance().MoveHome();
|
||||||
|
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
PyObject* imeMoveEnd(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
CPythonIME::Instance().MoveEnd();
|
||||||
|
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeSetCursorPosition(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
int iPosition;
|
||||||
|
if (!PyTuple_GetInteger(poArgs, 0, &iPosition))
|
||||||
|
return Py_BuildException();
|
||||||
|
|
||||||
|
CPythonIME::Instance().SetCursorPosition(iPosition);
|
||||||
|
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeDelete(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
CPythonIME::Instance().Delete();
|
||||||
|
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imePasteTextFromClipBoard(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
CPythonIME::Instance().PasteTextFromClipBoard();
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imeEnablePaste(PyObject* poSelf, PyObject* poArgs)
|
||||||
|
{
|
||||||
|
int iFlag;
|
||||||
|
if (!PyTuple_GetInteger(poArgs, 0, &iFlag))
|
||||||
|
return Py_BuildException();
|
||||||
|
|
||||||
|
CPythonIME::Instance().EnablePaste(iFlag ? true : false);
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imePasteString(PyObject * poSelf, PyObject * poArgs)
|
||||||
|
{
|
||||||
|
char* szText;
|
||||||
|
if (!PyTuple_GetString(poArgs, 0, &szText))
|
||||||
|
return Py_BuildException();
|
||||||
|
CPythonIME::Instance().PasteString(szText);
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imePasteBackspace(PyObject * poSelf, PyObject * poArgs)
|
||||||
|
{
|
||||||
|
CPythonIME::Instance().WMChar(NULL, WM_CHAR, 0x08, NULL);
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* imePasteReturn(PyObject * poSelf, PyObject * poArgs)
|
||||||
|
{
|
||||||
|
CPythonIME::Instance().WMChar(NULL, WM_CHAR, 0x0D, NULL);
|
||||||
|
return Py_BuildNone();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initime()
|
||||||
|
{
|
||||||
|
static PyMethodDef s_methods[] =
|
||||||
|
{
|
||||||
|
{ "Enable", imeEnable, METH_VARARGS },
|
||||||
|
{ "Disable", imeDisable, METH_VARARGS },
|
||||||
|
|
||||||
|
{ "EnableCaptureInput", imeEnableCaptureInput, METH_VARARGS },
|
||||||
|
{ "DisableCaptureInput", imeDisableCaptureInput, METH_VARARGS },
|
||||||
|
{ "SetMax", imeSetMax, METH_VARARGS },
|
||||||
|
{ "SetUserMax", imeSetUserMax, METH_VARARGS },
|
||||||
|
{ "SetText", imeSetText, METH_VARARGS },
|
||||||
|
{ "GetText", imeGetText, METH_VARARGS },
|
||||||
|
{ "GetCodePage", imeGetCodePage, METH_VARARGS },
|
||||||
|
{ "GetCandidateCount", imeGetCandidateCount, METH_VARARGS },
|
||||||
|
{ "GetCandidate", imeGetCandidate, METH_VARARGS },
|
||||||
|
{ "GetCandidateSelection", imeGetCandidateSelection, METH_VARARGS },
|
||||||
|
{ "GetReading", imeGetReading, METH_VARARGS },
|
||||||
|
{ "GetReadingError", imeGetReadingError, METH_VARARGS },
|
||||||
|
{ "EnableIME", imeEnableIME, METH_VARARGS },
|
||||||
|
{ "DisableIME", imeDisableIME, METH_VARARGS },
|
||||||
|
{ "GetInputMode", imeGetInputMode, METH_VARARGS },
|
||||||
|
{ "SetInputMode", imeSetInputMode, METH_VARARGS },
|
||||||
|
|
||||||
|
{ "SetNumberMode", imeSetNumberMode, METH_VARARGS },
|
||||||
|
{ "SetStringMode", imeSetStringMode, METH_VARARGS },
|
||||||
|
{ "AddExceptKey", imeAddExceptKey, METH_VARARGS },
|
||||||
|
{ "ClearExceptKey", imeClearExceptKey, METH_VARARGS },
|
||||||
|
|
||||||
|
{ "MoveLeft", imeMoveLeft, METH_VARARGS },
|
||||||
|
{ "MoveRight", imeMoveRight, METH_VARARGS },
|
||||||
|
{ "MoveHome", imeMoveHome, METH_VARARGS },
|
||||||
|
{ "MoveEnd", imeMoveEnd, METH_VARARGS },
|
||||||
|
{ "SetCursorPosition", imeSetCursorPosition, METH_VARARGS },
|
||||||
|
{ "Delete", imeDelete, METH_VARARGS },
|
||||||
|
{ "PasteString", imePasteString, METH_VARARGS },
|
||||||
|
{ "PasteBackspace", imePasteBackspace, METH_VARARGS },
|
||||||
|
{ "PasteReturn", imePasteReturn, METH_VARARGS },
|
||||||
|
{ "PasteTextFromClipBoard", imePasteTextFromClipBoard, METH_VARARGS },
|
||||||
|
{ "EnablePaste", imeEnablePaste, METH_VARARGS },
|
||||||
|
|
||||||
|
{ NULL, NULL, NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
Py_InitModule("ime", s_methods);
|
||||||
|
}
|
||||||
@@ -44,6 +44,7 @@ enum
|
|||||||
// The Python module initializers RunMainScript calls, each defined in its own Python*Module.cpp.
|
// The Python module initializers RunMainScript calls, each defined in its own Python*Module.cpp.
|
||||||
// Listed here as 40250 lists them; only the ported ones are defined.
|
// Listed here as 40250 lists them; only the ported ones are defined.
|
||||||
void initpack();
|
void initpack();
|
||||||
|
void initime();
|
||||||
void initapp();
|
void initapp();
|
||||||
void initchr();
|
void initchr();
|
||||||
void initchrmgr();
|
void initchrmgr();
|
||||||
|
|||||||
@@ -28,6 +28,12 @@
|
|||||||
#define VK_HOME 0x24
|
#define VK_HOME 0x24
|
||||||
#define VK_END 0x23
|
#define VK_END 0x23
|
||||||
#define VK_DELETE 0x2E
|
#define VK_DELETE 0x2E
|
||||||
|
#define VK_BACK 0x08
|
||||||
|
#define VK_TAB 0x09
|
||||||
|
#define VK_RETURN 0x0D
|
||||||
|
#define VK_ESCAPE 0x1B
|
||||||
|
#define WM_KEYDOWN 0x0100
|
||||||
|
#define WM_CHAR 0x0102
|
||||||
|
|
||||||
// winuser.h functions the platform layer implements (platform/ScriptLib/PythonBoot.cpp).
|
// winuser.h functions the platform layer implements (platform/ScriptLib/PythonBoot.cpp).
|
||||||
void PostQuitMessage(int nExitCode);
|
void PostQuitMessage(int nExitCode);
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ void Metin2PythonHost::ui_mouse_button(int button, bool pressed, int x, int y) {
|
|||||||
PythonBoot::UIMouseButton(button, pressed, x, y);
|
PythonBoot::UIMouseButton(button, pressed, x, y);
|
||||||
}
|
}
|
||||||
void Metin2PythonHost::ui_key(int key, bool pressed) { PythonBoot::UIKey(key, pressed); }
|
void Metin2PythonHost::ui_key(int key, bool pressed) { PythonBoot::UIKey(key, pressed); }
|
||||||
|
void Metin2PythonHost::ui_char(int codepoint) { PythonBoot::UIChar(unsigned(codepoint)); }
|
||||||
|
void Metin2PythonHost::ui_ime_key(int vkey) { PythonBoot::UIIMEKeyDown(vkey); }
|
||||||
void Metin2PythonHost::ui_update() { PythonBoot::UIUpdate(); }
|
void Metin2PythonHost::ui_update() { PythonBoot::UIUpdate(); }
|
||||||
Array Metin2PythonHost::ui_render_commands() {
|
Array Metin2PythonHost::ui_render_commands() {
|
||||||
PythonBoot::UIRender();
|
PythonBoot::UIRender();
|
||||||
@@ -264,6 +266,8 @@ void Metin2PythonHost::set_ui_size(int, int) {}
|
|||||||
void Metin2PythonHost::ui_mouse_move(int, int) {}
|
void Metin2PythonHost::ui_mouse_move(int, int) {}
|
||||||
void Metin2PythonHost::ui_mouse_button(int, bool, int, int) {}
|
void Metin2PythonHost::ui_mouse_button(int, bool, int, int) {}
|
||||||
void Metin2PythonHost::ui_key(int, bool) {}
|
void Metin2PythonHost::ui_key(int, bool) {}
|
||||||
|
void Metin2PythonHost::ui_char(int) {}
|
||||||
|
void Metin2PythonHost::ui_ime_key(int) {}
|
||||||
void Metin2PythonHost::ui_update() {}
|
void Metin2PythonHost::ui_update() {}
|
||||||
Array Metin2PythonHost::ui_render_commands() { return Array(); }
|
Array Metin2PythonHost::ui_render_commands() { return Array(); }
|
||||||
Ref<Image> Metin2PythonHost::memory_texture(const String &, int64_t) { return Ref<Image>(); }
|
Ref<Image> Metin2PythonHost::memory_texture(const String &, int64_t) { return Ref<Image>(); }
|
||||||
@@ -287,6 +291,8 @@ void Metin2PythonHost::_bind_methods() {
|
|||||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_mouse_move", "x", "y"), &Metin2PythonHost::ui_mouse_move);
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_mouse_move", "x", "y"), &Metin2PythonHost::ui_mouse_move);
|
||||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_mouse_button", "button", "pressed", "x", "y"), &Metin2PythonHost::ui_mouse_button);
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_mouse_button", "button", "pressed", "x", "y"), &Metin2PythonHost::ui_mouse_button);
|
||||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_key", "key", "pressed"), &Metin2PythonHost::ui_key);
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_key", "key", "pressed"), &Metin2PythonHost::ui_key);
|
||||||
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_char", "codepoint"), &Metin2PythonHost::ui_char);
|
||||||
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_ime_key", "vkey"), &Metin2PythonHost::ui_ime_key);
|
||||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_update"), &Metin2PythonHost::ui_update);
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_update"), &Metin2PythonHost::ui_update);
|
||||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_render_commands"), &Metin2PythonHost::ui_render_commands);
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_render_commands"), &Metin2PythonHost::ui_render_commands);
|
||||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("memory_texture", "name", "known_revision"),
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("memory_texture", "name", "known_revision"),
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ public:
|
|||||||
static void ui_mouse_move(int x, int y);
|
static void ui_mouse_move(int x, int y);
|
||||||
static void ui_mouse_button(int button, bool pressed, int x, int y);
|
static void ui_mouse_button(int button, bool pressed, int x, int y);
|
||||||
static void ui_key(int key, bool pressed);
|
static void ui_key(int key, bool pressed);
|
||||||
|
// WM_CHAR (Unicode code point) and WM_KEYDOWN (Win32 VK code) for the 40250 IME.
|
||||||
|
static void ui_char(int codepoint);
|
||||||
|
static void ui_ime_key(int vkey);
|
||||||
static void ui_update();
|
static void ui_update();
|
||||||
static godot::Array ui_render_commands();
|
static godot::Array ui_render_commands();
|
||||||
// The pixels of a "mem:<id>@<revision>" image command (the CGraphicFontTexture glyph pages) as an
|
// The pixels of a "mem:<id>@<revision>" image command (the CGraphicFontTexture glyph pages) as an
|
||||||
|
|||||||
@@ -655,6 +655,22 @@ bool FakeLoginServer::ServeGame(Connection& c, int index)
|
|||||||
if (!ok)
|
if (!ok)
|
||||||
return Fail(name + ": send GC_EXCHANGE");
|
return Fail(name + ": send GC_EXCHANGE");
|
||||||
}
|
}
|
||||||
|
// CInputMain::Chat (input_main.cpp): the line comes back to the speaker (PacketAround includes them)
|
||||||
|
// as a CHAT_TYPE_TALKING GC_CHAT "<name> : <text>" carrying their vid and empire.
|
||||||
|
if (header == HDR_CG_CHAT)
|
||||||
|
{
|
||||||
|
CGChatHead head;
|
||||||
|
std::memcpy(&head, packet, sizeof(head));
|
||||||
|
const size_t text_len = size > sizeof(head) ? size - sizeof(head) : 0;
|
||||||
|
const std::string text(reinterpret_cast<const char*>(packet + sizeof(head)),
|
||||||
|
strnlen(reinterpret_cast<const char*>(packet + sizeof(head)), text_len));
|
||||||
|
Event(name + ":chat " + std::to_string(head.type) + " " + text);
|
||||||
|
const std::string line = std::string(kCharacterName) + " : " + text;
|
||||||
|
GCChatHead chat = {HDR_GC_CHAT, static_cast<std::uint16_t>(sizeof(GCChatHead) + line.size() + 1),
|
||||||
|
0 /* CHAT_TYPE_TALKING */, kMainVID, 1};
|
||||||
|
if (!c.Send(chat) || !c.Send(line.c_str(), line.size() + 1))
|
||||||
|
return Fail(name + ": send GC_CHAT");
|
||||||
|
}
|
||||||
if (header == HDR_CG_MOVE)
|
if (header == HDR_CG_MOVE)
|
||||||
{
|
{
|
||||||
CGMove move;
|
CGMove move;
|
||||||
|
|||||||
@@ -244,9 +244,29 @@ int main(int argc, char** argv)
|
|||||||
char ports[128];
|
char ports[128];
|
||||||
std::snprintf(ports, sizeof(ports), "_auth_port, _game_port, _slot = %d, %d, %d", auth_port, game_port, slot);
|
std::snprintf(ports, sizeof(ports), "_auth_port, _game_port, _slot = %d, %d, %d", auth_port, game_port, slot);
|
||||||
CHECK(py(ports));
|
CHECK(py(ports));
|
||||||
CHECK(py("_stream.SetConnectInfo(_host, _game_port, _host, _auth_port)\n"
|
CHECK(py("_stream.SetConnectInfo(_host, _game_port, _host, _auth_port)"));
|
||||||
"_stream.curPhaseWindow.Connect(_login, _password)\n"
|
// Typed into the login board through the 40250 IME (WM_CHAR -> CPythonIME::WMChar, WM_KEYDOWN ->
|
||||||
"del __main__._login, __main__._password"));
|
// OnIMEKeyDown): Tab moves to the password line, Return runs __OnClickLoginButton -> Connect.
|
||||||
|
// The IME only reaches a rendering EditLine: open the login board as the server list's OK button does.
|
||||||
|
CHECK(py("_w = _stream.curPhaseWindow\n"
|
||||||
|
"_w._LoginWindow__OpenLoginBoard()\n"
|
||||||
|
"_w.idEditLine.SetText('')\n"
|
||||||
|
"_w.pwdEditLine.SetText('')\n"
|
||||||
|
"_w.idEditLine.SetFocus()"));
|
||||||
|
auto type = [](const std::string& text) {
|
||||||
|
for (unsigned char c : text)
|
||||||
|
PythonBoot::UIChar(c);
|
||||||
|
};
|
||||||
|
type(login + "xy");
|
||||||
|
PythonBoot::UIIMEKeyDown(VK_LEFT);
|
||||||
|
PythonBoot::UIIMEKeyDown(VK_DELETE); // y
|
||||||
|
PythonBoot::UIChar(VK_BACK); // x
|
||||||
|
CHECK(py("assert _w.idEditLine.GetText() == _login, repr(_w.idEditLine.GetText())"));
|
||||||
|
PythonBoot::UIChar(VK_TAB);
|
||||||
|
type(password);
|
||||||
|
CHECK(py("assert _w.pwdEditLine.GetText() == _password, len(_w.pwdEditLine.GetText())\n"
|
||||||
|
"del __main__._login, __main__._password, __main__._w"));
|
||||||
|
PythonBoot::UIChar(VK_RETURN);
|
||||||
|
|
||||||
// 3. The character list.
|
// 3. The character list.
|
||||||
const bool selecting = pump_until(live ? 30 : 20, [&] {
|
const bool selecting = pump_until(live ? 30 : 20, [&] {
|
||||||
@@ -614,6 +634,22 @@ int main(int argc, char** argv)
|
|||||||
// uiexchange.CloseDialog armed wndMgr.OnceIgnoreMouseLeftButtonUpEvent (in the client that release belongs
|
// uiexchange.CloseDialog armed wndMgr.OnceIgnoreMouseLeftButtonUpEvent (in the client that release belongs
|
||||||
// to the click that closed the dialog); spend it so step 8's click-release is not swallowed.
|
// to the click that closed the dialog); spend it so step 8's click-release is not swallowed.
|
||||||
PythonBoot::UIMouseButton(1, false, 400, 520);
|
PythonBoot::UIMouseButton(1, false, 400, 520);
|
||||||
|
|
||||||
|
// Chat (批次 4-a): Return on the focused game.GameWindow → OnIMEReturn → interface.ToggleChat opens the
|
||||||
|
// chat line; the typed text goes through CPythonIME, a second Return sends CG_CHAT (TALKING) and the fake
|
||||||
|
// server echoes GC_CHAT back.
|
||||||
|
CHECK(py_true("not _stream.curPhaseWindow.interface.IsOpenChat()"));
|
||||||
|
PythonBoot::UIChar(VK_RETURN);
|
||||||
|
CHECK(pump_until(1, [] { return py_true("_stream.curPhaseWindow.interface.IsOpenChat()"); }));
|
||||||
|
for (char c : std::string("hello"))
|
||||||
|
PythonBoot::UIChar((unsigned char)c);
|
||||||
|
pump_until(0.05, [] { return false; });
|
||||||
|
PythonBoot::UIChar(VK_RETURN);
|
||||||
|
CHECK(pump_until(2, [&] { return server->Has("game2:chat 0 hello"); }));
|
||||||
|
// uichat.__SendTalkingChatPacket only resets the line; a Return on the empty line closes it (eventReturn).
|
||||||
|
CHECK(py_true("_stream.curPhaseWindow.interface.IsOpenChat() and __import__('ime').GetText(0) == ''"));
|
||||||
|
PythonBoot::UIChar(VK_RETURN);
|
||||||
|
CHECK(pump_until(1, [] { return py_true("not _stream.curPhaseWindow.interface.IsOpenChat()"); }));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8. (批次 2V3) Click-to-move: a left click on the ground below the actor reaches game.GameWindow's
|
// 8. (批次 2V3) Click-to-move: a left click on the ground below the actor reaches game.GameWindow's
|
||||||
@@ -664,7 +700,7 @@ int main(int argc, char** argv)
|
|||||||
for (const auto& e : events)
|
for (const auto& e : events)
|
||||||
if (e.rfind(prefix, 0) == 0 && e.find(":move ") == std::string::npos &&
|
if (e.rfind(prefix, 0) == 0 && e.find(":move ") == std::string::npos &&
|
||||||
e.find(":pickup ") == std::string::npos &&
|
e.find(":pickup ") == std::string::npos &&
|
||||||
e.find(":exchange ") == std::string::npos)
|
e.find(":exchange ") == std::string::npos && e.find(":chat ") == std::string::npos)
|
||||||
got.push_back(e);
|
got.push_back(e);
|
||||||
CHECK(want == got);
|
CHECK(want == got);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,25 @@ func _check(ok: bool, message: String) -> void:
|
|||||||
func _py(source: String) -> bool:
|
func _py(source: String) -> bool:
|
||||||
return Metin2PythonHost.run_line(source) == ""
|
return Metin2PythonHost.run_line(source) == ""
|
||||||
|
|
||||||
|
func _key(keycode: Key, unicode: int, pressed: bool) -> void:
|
||||||
|
var event := InputEventKey.new()
|
||||||
|
event.keycode = keycode
|
||||||
|
event.physical_keycode = keycode
|
||||||
|
event.unicode = unicode
|
||||||
|
event.pressed = pressed
|
||||||
|
root.push_input(event)
|
||||||
|
|
||||||
|
func _press(keycode: Key) -> void:
|
||||||
|
_key(keycode, 0, true)
|
||||||
|
_key(keycode, 0, false)
|
||||||
|
|
||||||
|
func _type(text: String) -> void:
|
||||||
|
for i in text.length():
|
||||||
|
var ch := text.unicode_at(i)
|
||||||
|
var keycode := OS.find_keycode_from_string(text[i].to_upper())
|
||||||
|
_key(keycode, ch, true)
|
||||||
|
_key(keycode, ch, false)
|
||||||
|
|
||||||
func _pump_until(seconds: float, condition: Callable) -> bool:
|
func _pump_until(seconds: float, condition: Callable) -> bool:
|
||||||
var deadline := Time.get_ticks_msec() + int(seconds * 1000.0)
|
var deadline := Time.get_ticks_msec() + int(seconds * 1000.0)
|
||||||
while Time.get_ticks_msec() < deadline:
|
while Time.get_ticks_msec() < deadline:
|
||||||
@@ -83,13 +102,24 @@ func run() -> void:
|
|||||||
"assert isinstance(_stream.curPhaseWindow, introLogin.LoginWindow)"))
|
"assert isinstance(_stream.curPhaseWindow, introLogin.LoginWindow)"))
|
||||||
_check(at_login, "login window")
|
_check(at_login, "login window")
|
||||||
|
|
||||||
# LoginWindow.Connect with the account from the environment.
|
# The account from the environment is typed into the login board as key events (WM_KEYDOWN/WM_CHAR
|
||||||
|
# through PythonUISurface → CPythonIME); Return runs __OnClickLoginButton → LoginWindow.Connect.
|
||||||
_check(_py("_env = os.environ\n"
|
_check(_py("_env = os.environ\n"
|
||||||
+ "_slot = int(_env.get('MT_LIVE_SLOT', '0'))\n"
|
+ "_slot = int(_env.get('MT_LIVE_SLOT', '0'))\n"
|
||||||
+ "_stream.SetConnectInfo(_env['MT_LIVE_HOST'], int(_env.get('MT_LIVE_GAME_PORT', '13000')), "
|
+ "_stream.SetConnectInfo(_env['MT_LIVE_HOST'], int(_env.get('MT_LIVE_GAME_PORT', '13000')), "
|
||||||
+ "_env['MT_LIVE_HOST'], int(_env.get('MT_LIVE_AUTH_PORT', '11000')))\n"
|
+ "_env['MT_LIVE_HOST'], int(_env.get('MT_LIVE_AUTH_PORT', '11000')))\n"
|
||||||
+ "_stream.curPhaseWindow.Connect(_env['MT_LIVE_LOGIN'], _env['MT_LIVE_PASSWORD'])\n"
|
+ "_w = _stream.curPhaseWindow\n"
|
||||||
+ "del _env"), "LoginWindow.Connect")
|
+ "_w._LoginWindow__OpenLoginBoard()\n"
|
||||||
|
+ "_w.idEditLine.SetText('')\n"
|
||||||
|
+ "_w.pwdEditLine.SetText('')\n"
|
||||||
|
+ "_w.idEditLine.SetFocus()"), "login board open")
|
||||||
|
_type(OS.get_environment("MT_LIVE_LOGIN"))
|
||||||
|
_press(KEY_TAB)
|
||||||
|
_type(OS.get_environment("MT_LIVE_PASSWORD"))
|
||||||
|
_check(_py("assert _w.idEditLine.GetText() == _env['MT_LIVE_LOGIN']\n"
|
||||||
|
+ "assert _w.pwdEditLine.GetText() == _env['MT_LIVE_PASSWORD']"), "account typed through the IME")
|
||||||
|
_check(_py("del _env, _w"), "login locals dropped")
|
||||||
|
_press(KEY_ENTER)
|
||||||
var selecting := await _pump_until(30, func(): return _py(
|
var selecting := await _pump_until(30, func(): return _py(
|
||||||
"assert isinstance(_stream.curPhaseWindow, introSelect.SelectCharacterWindow)"))
|
"assert isinstance(_stream.curPhaseWindow, introSelect.SelectCharacterWindow)"))
|
||||||
_check(selecting, "character select")
|
_check(selecting, "character select")
|
||||||
|
|||||||
@@ -20,6 +20,19 @@ const KEY_TO_DIK := {
|
|||||||
KEY_RIGHT: 0xcd, KEY_DOWN: 0xd0,
|
KEY_RIGHT: 0xcd, KEY_DOWN: 0xd0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Win32 virtual-key codes of the keys EditLine.OnIMEKeyDown handles (WM_KEYDOWN → OnIMEKeyDown);
|
||||||
|
# letters and digits use their ASCII code, as VK_A..VK_Z / VK_0..VK_9 do.
|
||||||
|
const KEY_TO_VK := {
|
||||||
|
KEY_BACKSPACE: 0x08, KEY_TAB: 0x09, KEY_ENTER: 0x0d, KEY_KP_ENTER: 0x0d,
|
||||||
|
KEY_ESCAPE: 0x1b, KEY_SPACE: 0x20, KEY_END: 0x23, KEY_HOME: 0x24,
|
||||||
|
KEY_LEFT: 0x25, KEY_UP: 0x26, KEY_RIGHT: 0x27, KEY_DOWN: 0x28, KEY_DELETE: 0x2e,
|
||||||
|
}
|
||||||
|
|
||||||
|
# The control characters Windows turns these keys into (WM_CHAR); Godot reports them with unicode 0.
|
||||||
|
const KEY_TO_CHAR := {
|
||||||
|
KEY_BACKSPACE: 0x08, KEY_TAB: 0x09, KEY_ENTER: 0x0d, KEY_KP_ENTER: 0x0d, KEY_ESCAPE: 0x1b,
|
||||||
|
}
|
||||||
|
|
||||||
# "mem:<id>" → [revision, ImageTexture]: the CGraphicFontTexture glyph pages, refetched when the
|
# "mem:<id>" → [revision, ImageTexture]: the CGraphicFontTexture glyph pages, refetched when the
|
||||||
# "@<revision>" of a command's name moves on (a glyph was added to the page).
|
# "@<revision>" of a command's name moves on (a glyph was added to the page).
|
||||||
var _memory_textures := {}
|
var _memory_textures := {}
|
||||||
@@ -98,6 +111,23 @@ func _unhandled_key_input(event: InputEvent) -> void:
|
|||||||
var dik: int = KEY_TO_DIK.get(event.physical_keycode, 0)
|
var dik: int = KEY_TO_DIK.get(event.physical_keycode, 0)
|
||||||
if dik != 0:
|
if dik != 0:
|
||||||
Metin2PythonHost.ui_key(dik, event.pressed)
|
Metin2PythonHost.ui_key(dik, event.pressed)
|
||||||
|
if event.pressed:
|
||||||
|
_ime_key(event)
|
||||||
|
|
||||||
|
# 40250 gets every key press twice more through the window procedure: WM_KEYDOWN (a VK code, for
|
||||||
|
# OnIMEKeyDown) and then WM_CHAR (the typed character, for CPythonIME). Held keys repeat both.
|
||||||
|
func _ime_key(event: InputEventKey) -> void:
|
||||||
|
var keycode: int = event.keycode
|
||||||
|
var vk: int = KEY_TO_VK.get(keycode, 0)
|
||||||
|
if vk == 0 and ((keycode >= KEY_A and keycode <= KEY_Z) or (keycode >= KEY_0 and keycode <= KEY_9)):
|
||||||
|
vk = keycode
|
||||||
|
if vk != 0:
|
||||||
|
Metin2PythonHost.ui_ime_key(vk)
|
||||||
|
var ch: int = event.unicode
|
||||||
|
if ch == 0:
|
||||||
|
ch = KEY_TO_CHAR.get(keycode, 0)
|
||||||
|
if ch != 0:
|
||||||
|
Metin2PythonHost.ui_char(ch)
|
||||||
|
|
||||||
# The command stream is drawn into pooled child canvas items in order: a run of plain commands
|
# The command stream is drawn into pooled child canvas items in order: a run of plain commands
|
||||||
# shares one item; a masked image (CPythonMiniMap's terrain tiles under minimap_image_filter.dds)
|
# shares one item; a masked image (CPythonMiniMap's terrain tiles under minimap_image_filter.dds)
|
||||||
|
|||||||
Reference in New Issue
Block a user