From ffae1c309a07126eb0dcd920790ac82b608ff94c Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sat, 18 Jul 2026 02:53:17 -0400 Subject: [PATCH] More strictly adhere to nvdaControllerClient and Tolk parameters. --- docs/wine-accessibility.md | 3 ++- tests/test_wine_access_controller_contract.py | 24 +++++++++++++++++++ wine-access/main.cpp | 3 ++- 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 tests/test_wine_access_controller_contract.py diff --git a/docs/wine-accessibility.md b/docs/wine-accessibility.md index ca3103f..0cd2808 100644 --- a/docs/wine-accessibility.md +++ b/docs/wine-accessibility.md @@ -23,7 +23,8 @@ nonactivating, and excluded from ordinary window switching. Therefore applicatio `nvdaControllerClient32.dll`, `nvdaControllerClient64.dll`, and `Tolk.dll`; no DLL override is required. -Plain speech, braille, cancel, and process ID calls are implemented. Speaking-state queries return +Plain speech queues like NVDA Controller output; Tolk's interrupt flag uses the separate cancel call +before speaking. Braille, cancel, and process ID calls are also implemented. Speaking-state queries return `ERROR_CALL_NOT_IMPLEMENTED`, because Cthulhu's current speech backend does not expose an accurate answer. SSML is validated by Cthulhu and currently fails with `ERROR_NOT_SUPPORTED`. This is intentional: accepting SSML while dropping mark callbacks or reporting completion too early would diff --git a/tests/test_wine_access_controller_contract.py b/tests/test_wine_access_controller_contract.py new file mode 100644 index 0000000..ae17ca8 --- /dev/null +++ b/tests/test_wine_access_controller_contract.py @@ -0,0 +1,24 @@ +import re +import unittest +from pathlib import Path + + +class WineAccessControllerContractTests(unittest.TestCase): + def test_legacy_speak_text_queues_without_cancelling_current_speech(self): + sourcePath = Path(__file__).resolve().parents[1] / "wine-access" / "main.cpp" + source = sourcePath.read_text(encoding="utf-8") + match = re.search( + r'extern "C" error_status_t __stdcall speakText\(.*?\n\}', + source, + re.DOTALL, + ) + + self.assertIsNotNone(match) + self.assertIn( + 'g_variant_new("(sb)", utf8.c_str(), FALSE)', + match.group(0), + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/wine-access/main.cpp b/wine-access/main.cpp index 44b5d00..67a3d17 100644 --- a/wine-access/main.cpp +++ b/wine-access/main.cpp @@ -440,7 +440,8 @@ extern "C" error_status_t __stdcall testIfRunning(void) { } extern "C" error_status_t __stdcall speakText(const wchar_t *text) { const std::string utf8 = to_utf8(text); - return call_boolean("SpeakText", g_variant_new("(sb)", utf8.c_str(), TRUE)); + // Tolk calls cancelSpeech separately when its interrupt argument is true. + return call_boolean("SpeakText", g_variant_new("(sb)", utf8.c_str(), FALSE)); } extern "C" error_status_t __stdcall cancelSpeech(void) { return call_boolean("CancelSpeech", nullptr);