From cb0bf83f9dc060331c181b50082f72e8bf45924b Mon Sep 17 00:00:00 2001 From: Leonard de Ruijter Date: Thu, 26 Oct 2023 18:25:56 +0200 Subject: [PATCH] Add support to speak SSMl to the NVDA controller client --- extras/controllerClient/readme.md | 13 ++- extras/controllerClient/x86/example_python.py | 31 +++++- include/nvda-cldr | 2 +- nvdaHelper/client/client.cpp | 9 ++ nvdaHelper/client/nvdaControllerClient.def | 5 +- .../nvdaController/nvdaController.acf | 5 +- .../nvdaController/nvdaController.idl | 61 +++++++++++- .../{nvdaController.c => nvdaController.cpp} | 36 ++++++- nvdaHelper/local/nvdaHelperLocal.def | 2 + nvdaHelper/local/rpcSrv.cpp | 2 +- nvdaHelper/local/sconscript | 2 +- nvdaHelper/remote/nvdaController.cpp | 21 ++++ nvdaHelper/remote/sconscript | 1 + source/NVDAHelper.py | 96 ++++++++++++++++++- source/winAPI/constants.py | 1 + user_docs/en/changes.t2t | 14 ++- 16 files changed, 279 insertions(+), 22 deletions(-) rename nvdaHelper/local/{nvdaController.c => nvdaController.cpp} (51%) create mode 100644 nvdaHelper/remote/nvdaController.cpp diff --git a/extras/controllerClient/readme.md b/extras/controllerClient/readme.md index a0f8c821f0a..9cf047edc67 100644 --- a/extras/controllerClient/readme.md +++ b/extras/controllerClient/readme.md @@ -1,12 +1,19 @@ -# NVDA Controller Client API 1.0 Documentation +# NVDA Controller Client API 1.1 Documentation ## Introduction -This client API allows an application to communicate with NVDA, in order to do such things as speak text or braille a message. +This client API allows an application to communicate with NVDA 2024.1 and above, in order to do such things as speak text or braille a message. The client API is implemented as a dll (dynamic link library). The functions in this dll can be called from any programming language that supports looking up and calling of any symbol in a dll (such as ctypes in Python), or by linking to it for languages like C and C++. +## Compatibility notice + +Version 1.1 of the controller client was introduced in NVDA 2024.1, offering support to speak SSML speech sequences. +While this version of the client adds more functionality, it is *not compatible* with NVDA 2023.3 and below. +If you do not necessarily rely on the support to speak SSML, you are encouraged to use version 1.0 of the client library that is linked below. + ## Security practices + Developers should be aware that NVDA runs on the lock screen and [secure screens](https://www.nvaccess.org/files/nvda/documentation/userGuide.html#SecureScreens). Before providing information to the end user (e.g. via `nvdaController_speakText`), developers should check if Windows is locked or running on a secure screen to prevent secure data being leaked. @@ -18,6 +25,8 @@ You can build locally or download pre-built, details: - **Latest, in development version:** - The libraries are built by Appveyor (our CI). - Downloads are available from the artifacts tab. +- **Latest build of version 1.0 of the library, supporting NVDA 2023.3 and below:** + - Download the `nvda_2023.3_controllerClient.zip` file: [Direct link](https://www.nvaccess.org/files/nvda/releases/2023.3/nvda_2023.3_controllerClient.zip) - **Build them yourself:** - Follow the project `readme.txt` for general build requirements/dependencies. - Run `scons source client` diff --git a/extras/controllerClient/x86/example_python.py b/extras/controllerClient/x86/example_python.py index e9db14cd21a..cdc15c56041 100644 --- a/extras/controllerClient/x86/example_python.py +++ b/extras/controllerClient/x86/example_python.py @@ -13,7 +13,7 @@ res = clientLib.nvdaController_testIfRunning() if res != 0: errorMessage = str(ctypes.WinError(res)) - ctypes.windll.user32.MessageBoxW(0, "Error: %s" % errorMessage, "Error communicating with NVDA", 0) + ctypes.windll.user32.MessageBoxW(0, f"Error: {errorMessage}", "Error communicating with NVDA", 0) # Speak and braille some messages for count in range(4): @@ -21,5 +21,32 @@ clientLib.nvdaController_brailleMessage("Time: %g seconds" % (0.75 * count)) time.sleep(0.625) clientLib.nvdaController_cancelSpeech() -clientLib.nvdaController_speakText("This is a test client for NVDA!") + + +# Test SSML output +@ctypes.WINFUNCTYPE(ctypes.c_ulong, ctypes.c_wchar_p) +def onMarkReached(name): + print(f"Reached SSML mark with name: {name}") + return 0 + + +ctypes.cast(clientLib._nvdaController_onSsmlMarkReached, ctypes.POINTER(ctypes.c_void_p)).contents.value = ( + ctypes.cast(onMarkReached, ctypes.c_void_p).value +) +ssml = ( + '' + 'This is one sentence. ' + '' + 'This sentense is pronounced with higher pitch.' + '' + 'This is a third sentence. ' + '' + 'This is a fourth sentence. We will stay silent for a second after this one.' + '' + '' + 'This is a fifth sentence. ' + '' + '' +) +clientLib.nvdaController_speakSsml(ssml, 0, 0, False) clientLib.nvdaController_brailleMessage("Test completed!") diff --git a/include/nvda-cldr b/include/nvda-cldr index 75233d0396e..e3af0878691 160000 --- a/include/nvda-cldr +++ b/include/nvda-cldr @@ -1 +1 @@ -Subproject commit 75233d0396e7df02c8dd591d0bb6ab831d71c538 +Subproject commit e3af0878691391cc019d2e1e7965aa5756ac653a diff --git a/nvdaHelper/client/client.cpp b/nvdaHelper/client/client.cpp index b96cc2e8d50..e5bd4384467 100644 --- a/nvdaHelper/client/client.cpp +++ b/nvdaHelper/client/client.cpp @@ -47,3 +47,12 @@ BOOL WINAPI DllMain(HINSTANCE hModule,DWORD reason,LPVOID lpReserved) { } return TRUE; } + +error_status_t(__stdcall *_nvdaController_onSsmlMarkReached)(const wchar_t*) = nullptr; + +error_status_t __stdcall nvdaController_onSsmlMarkReached(const wchar_t* mark) { + if (_nvdaController_onSsmlMarkReached == nullptr) { + return ERROR_CALL_NOT_IMPLEMENTED; + } + return _nvdaController_onSsmlMarkReached(mark); +} diff --git a/nvdaHelper/client/nvdaControllerClient.def b/nvdaHelper/client/nvdaControllerClient.def index 1e244283992..9b100842672 100644 --- a/nvdaHelper/client/nvdaControllerClient.def +++ b/nvdaHelper/client/nvdaControllerClient.def @@ -1,7 +1,7 @@ ;;; ;This file is a part of the NVDA project. ;URL: http://www.nvda-project.org/ -;Copyright 2006-2010 NVDA contributers. +;Copyright 2006-2023 NV Access Limited, Leonard de Ruijter. ;This program is free software: you can redistribute it and/or modify ;it under the terms of the GNU Lesser General Public License version 2.1, as published by ;the Free Software Foundation. @@ -17,3 +17,6 @@ EXPORTS nvdaController_speakText nvdaController_cancelSpeech nvdaController_brailleMessage + nvdaController_getProcessId + nvdaController_speakSsml + _nvdaController_onSsmlMarkReached diff --git a/nvdaHelper/interfaces/nvdaController/nvdaController.acf b/nvdaHelper/interfaces/nvdaController/nvdaController.acf index c3380a28af1..61e69287074 100644 --- a/nvdaHelper/interfaces/nvdaController/nvdaController.acf +++ b/nvdaHelper/interfaces/nvdaController/nvdaController.acf @@ -1,7 +1,7 @@ /* This file is a part of the NVDA project. URL: http://www.nvda-project.org/ -Copyright 2006-2010 NVDA contributers. +Copyright 2006-2023 NV Access Limited, Leonard de Ruijter. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 2.1, as published by the Free Software Foundation. @@ -20,4 +20,7 @@ interface NvdaController { [fault_status,comm_status] speakText(); [fault_status,comm_status] cancelSpeech(); [fault_status,comm_status] brailleMessage(); + [fault_status,comm_status] getProcessId(); + [fault_status,comm_status] speakSsml(); + [fault_status,comm_status] onSsmlMarkReached(); } diff --git a/nvdaHelper/interfaces/nvdaController/nvdaController.idl b/nvdaHelper/interfaces/nvdaController/nvdaController.idl index 72bda934719..a4d218cbd5c 100644 --- a/nvdaHelper/interfaces/nvdaController/nvdaController.idl +++ b/nvdaHelper/interfaces/nvdaController/nvdaController.idl @@ -1,7 +1,7 @@ /* This file is a part of the NVDA project. URL: http://www.nvda-project.org/ -Copyright 2006-2010 NVDA contributers. +Copyright 2006-2023 NV Access Limited, Leonard de Ruijter. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 2.1, as published by the Free Software Foundation. @@ -15,7 +15,7 @@ http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html cpp_quote("/*") cpp_quote("This file is a part of the NVDA project.") cpp_quote("URL: http://www.nvda-project.org/") -cpp_quote("Copyright 2006-2010 NVDA contributers.") +cpp_quote("Copyright 2006-2023 NV Access Limited, Leonard de Ruijter.") cpp_quote("This program is free software: you can redistribute it and/or modify") cpp_quote("it under the terms of the GNU Lesser General Public License version 2.1, as published by") cpp_quote("the Free Software Foundation.") @@ -26,12 +26,39 @@ cpp_quote("This license can be found at:") cpp_quote("http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html") cpp_quote("*/") +typedef enum { +/** + * Indicates that a speech sequence should have normal priority. + */ + SPEECH_PRIORITY_NORMAL = 0, +/** + * Indicates that a speech sequence should be spoken after the next utterance of lower priority is complete. + */ + SPEECH_PRIORITY_NEXT = 1, +/** + * Indicates that a speech sequence is very important and should be spoken right now, + * interrupting low priority speech. + * After it is spoken, interrupted speech will resume. + * Note that this does not interrupt previously queued speech at the same priority. + */ + SPEECH_PRIORITY_NOW = 2 +} SPEECH_PRIORITY; + +typedef enum { + SYMBOL_LEVEL_NONE = 0, + SYMBOL_LEVEL_SOME = 100, + SYMBOL_LEVEL_MOST = 200, + SYMBOL_LEVEL_ALL = 300, + SYMBOL_LEVEL_CHAR = 1000, + SYMBOL_LEVEL_UNCHANGED = -1 +} SYMBOL_LEVEL; + /** * Allows the controling of NVDA from a remote process */ [ uuid(DFF50B99-F7FD-4ca7-A82C-DAEB3E025295), - version(1.0), + version(1.1), ] interface NvdaController { @@ -41,7 +68,7 @@ interface NvdaController { error_status_t __stdcall testIfRunning(); /** - * Instructs NVDA to speak the given text. + * Instructs NVDA to speak the given text message. * @param text the text to speak. */ error_status_t __stdcall speakText([in,string] const wchar_t* text); @@ -57,4 +84,30 @@ interface NvdaController { */ error_status_t __stdcall brailleMessage([in,string] const wchar_t* message); +/** + * Retrieves the the process identifier (PID) of NVDA's process. + */ + error_status_t __stdcall getProcessId([out] unsigned long* pid); + +/** + * Instructs NVDA to speak the given Speech Synthesis Markup Language (SSML). + * @param ssml The ssml to speak. + * @param symbolLevel The symbol verbosity level. + * @param priority The priority of the speech sequence. + * @param asynchronous Whether SSML should be spoken asynchronously. + * If TRUE, returns instantly. + * If FALSE, returns either when the speech sequence is completed or canceled. + */ + error_status_t __stdcall speakSsml( + [in, string] const wchar_t* ssml, + [in, defaultvalue(SYMBOL_LEVEL_UNCHANGED)] const SYMBOL_LEVEL symbolLevel, + [in,defaultvalue(SPEECH_PRIORITY_NORMAL)] const SPEECH_PRIORITY priority, + [in, defaultvalue(TRUE)] const boolean asynchronous + ); + +/** + * Called by NVDA when a mark in provided SSML is reached. + * @param mark The name of the reached mark. + */ + [callback] error_status_t __stdcall onSsmlMarkReached([in, string] const wchar_t* mark); }; diff --git a/nvdaHelper/local/nvdaController.c b/nvdaHelper/local/nvdaController.cpp similarity index 51% rename from nvdaHelper/local/nvdaController.c rename to nvdaHelper/local/nvdaController.cpp index bdb7c3db60c..456035d7852 100644 --- a/nvdaHelper/local/nvdaController.c +++ b/nvdaHelper/local/nvdaController.cpp @@ -1,7 +1,7 @@ /* This file is a part of the NVDA project. URL: http://www.nvda-project.org/ -Copyright 2006-2010 NVDA contributers. +Copyright 2006-2023 NV Access Limited, Leonard de Ruijter. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 2.0, as published by the Free Software Foundation. @@ -13,22 +13,52 @@ This license can be found at: */ #include +#include + +error_status_t(__stdcall *_nvdaController_speakText)(const wchar_t*) = nullptr; -error_status_t(__stdcall *_nvdaController_speakText)(const wchar_t*); error_status_t __stdcall nvdaController_speakText(const wchar_t* text) { + if (_nvdaController_speakText == nullptr) { + return ERROR_CALL_NOT_IMPLEMENTED; + } return _nvdaController_speakText(text); } +error_status_t(__stdcall *_nvdaController_speakSsml)(const wchar_t*, const SYMBOL_LEVEL, const SPEECH_PRIORITY, const boolean) = nullptr; + +error_status_t __stdcall nvdaController_speakSsml(const wchar_t* ssml, const SYMBOL_LEVEL symbolLevel, const SPEECH_PRIORITY priority, const boolean asynchronous) { + if (_nvdaController_speakSsml == nullptr) { + return ERROR_CALL_NOT_IMPLEMENTED; + } + return _nvdaController_speakSsml(ssml, symbolLevel, priority, asynchronous); +} + error_status_t(__stdcall *_nvdaController_cancelSpeech)(); + error_status_t __stdcall nvdaController_cancelSpeech() { + if (_nvdaController_cancelSpeech == nullptr) { + return ERROR_CALL_NOT_IMPLEMENTED; + } return _nvdaController_cancelSpeech(); } error_status_t(__stdcall *_nvdaController_brailleMessage)(const wchar_t*); + error_status_t __stdcall nvdaController_brailleMessage(const wchar_t* text) { + if (_nvdaController_brailleMessage == nullptr) { + return ERROR_CALL_NOT_IMPLEMENTED; + } return _nvdaController_brailleMessage(text); } +error_status_t __stdcall nvdaController_getProcessId(unsigned long* pid) { + if (pid == nullptr) { + return ERROR_INVALID_PARAMETER; + } + *pid = GetCurrentProcessId(); + return ERROR_SUCCESS; +} + error_status_t __stdcall nvdaController_testIfRunning() { - return 0; + return ERROR_SUCCESS; } diff --git a/nvdaHelper/local/nvdaHelperLocal.def b/nvdaHelper/local/nvdaHelperLocal.def index c7d37a32c2f..a712dab31cd 100644 --- a/nvdaHelper/local/nvdaHelperLocal.def +++ b/nvdaHelper/local/nvdaHelperLocal.def @@ -50,6 +50,8 @@ EXPORTS _nvdaController_brailleMessage _nvdaController_cancelSpeech _nvdaController_speakText + _nvdaController_speakSsml + nvdaController_onSsmlMarkReached _nvdaControllerInternal_vbufChangeNotify displayModel_getWindowTextInRect displayModel_getFocusRect diff --git a/nvdaHelper/local/rpcSrv.cpp b/nvdaHelper/local/rpcSrv.cpp index 58904db5c12..bd28e9f1b6d 100644 --- a/nvdaHelper/local/rpcSrv.cpp +++ b/nvdaHelper/local/rpcSrv.cpp @@ -27,7 +27,7 @@ using namespace std; typedef RPC_STATUS(RPC_ENTRY *RpcServerRegisterIf3_functype)(RPC_IF_HANDLE,UUID __RPC_FAR*,RPC_MGR_EPV __RPC_FAR*,unsigned int,unsigned int,unsigned int,RPC_IF_CALLBACK_FN __RPC_FAR*,void __RPC_FAR*); RPC_IF_HANDLE availableInterfaces[]={ - nvdaController_NvdaController_v1_0_s_ifspec, + nvdaController_NvdaController_v1_1_s_ifspec, nvdaControllerInternal_NvdaControllerInternal_v1_0_s_ifspec }; diff --git a/nvdaHelper/local/sconscript b/nvdaHelper/local/sconscript index 4ff5a22e66b..53624842b40 100644 --- a/nvdaHelper/local/sconscript +++ b/nvdaHelper/local/sconscript @@ -80,7 +80,7 @@ localLib=env.SharedLibrary( nvdaInProcUtilsRPCClientSource, displayModelRPCClientSource, 'rpcSrv.cpp', - 'nvdaController.c', + 'nvdaController.cpp', winIPCUtilsObj, controllerRPCServerSource, 'nvdaControllerInternal.c', diff --git a/nvdaHelper/remote/nvdaController.cpp b/nvdaHelper/remote/nvdaController.cpp new file mode 100644 index 00000000000..6af4bb44119 --- /dev/null +++ b/nvdaHelper/remote/nvdaController.cpp @@ -0,0 +1,21 @@ +/* +This file is a part of the NVDA project. +Copyright 2023 NV Access Limited, Leonard de Ruijter. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2.0, as published by + the Free Software Foundation. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +This license can be found at: +http://www.gnu.org/licenses/old-licenses/gpl-2.0.html +*/ + +#define WIN32_LEAN_AND_MEAN + +#include +#include + +error_status_t __stdcall nvdaController_onSsmlMarkReached(const wchar_t* mark) { + return ERROR_SUCCESS; +} diff --git a/nvdaHelper/remote/sconscript b/nvdaHelper/remote/sconscript index 8d2022c9646..170501adfec 100644 --- a/nvdaHelper/remote/sconscript +++ b/nvdaHelper/remote/sconscript @@ -103,6 +103,7 @@ source = [ vbufRPCServerSource, winIPCUtilsObj, controllerRPCClientSource, + "nvdaController.cpp", controllerInternalRPCClientSource, "sysListView32.cpp", "winword.cpp", diff --git a/source/NVDAHelper.py b/source/NVDAHelper.py index 0ff7280b468..e0513d911ee 100755 --- a/source/NVDAHelper.py +++ b/source/NVDAHelper.py @@ -5,6 +5,7 @@ # See the file COPYING for more details. from typing import Optional +import typing import os import winreg import msvcrt @@ -16,9 +17,13 @@ from ctypes import * from ctypes import ( WINFUNCTYPE, + c_bool, + c_int, c_long, + c_ulong, c_wchar_p, c_wchar, + create_unicode_buffer, windll, ) from ctypes.wintypes import * @@ -31,6 +36,10 @@ from logHandler import log import NVDAState from utils.security import isLockScreenModeActive +from winAPI.constants import SystemErrorCodes + +if typing.TYPE_CHECKING: + from speech.priorities import SpeechPriority versionedLibPath = os.path.join(globalVars.appDir, 'lib') versionedLibARM64Path = os.path.join(globalVars.appDir, 'libArm64') @@ -49,6 +58,7 @@ _remoteLoaderARM64: "Optional[_RemoteLoader]" = None localLib=None generateBeep=None +onSsmlMarkReached = None VBuf_getTextInRange=None lastLanguageID=None lastLayoutString=None @@ -63,10 +73,79 @@ def nvdaController_speakText(text): focus=api.getFocusObject() if focus.sleepMode==focus.SLEEP_FULL: return -1 - import queueHandler import speech queueHandler.queueFunction(queueHandler.eventQueue,speech.speakText,text) - return 0 + return SystemErrorCodes.SUCCESS + + +@WINFUNCTYPE(c_long, c_wchar_p, c_int, c_int, c_bool) +def nvdaController_speakSsml( + ssml: str, + symbolLevel: int, + priority: "SpeechPriority", + asynchronous: bool, +): + focus = api.getFocusObject() + if focus.sleepMode == focus.SLEEP_FULL: + return -1 + import speech + from speech.speech import _getSpeakSsmlSpeech + + if asynchronous: + prefixSpeechCommand = None + markCallable = None + else: + from queue import SimpleQueue + + import synthDriverHandler + from speech.commands import CallbackCommand + + markQueue = SimpleQueue() + + def onDoneSpeaking(): + markQueue.put_nowait(None) + + def onSpeechCanceled(): + markQueue.put_nowait(False) + + def callback(): + synthDriverHandler.synthDoneSpeaking.register(onDoneSpeaking) + speech.speechCanceled.register(onSpeechCanceled) + + def markCallable(name: str): + markQueue.put_nowait(name) + + prefixSpeechCommand = CallbackCommand(callback) + + try: + sequence = _getSpeakSsmlSpeech(ssml, markCallable, prefixSpeechCommand) + except Exception: + log.error("Error parsing SSML", exc_info=True) + return SystemErrorCodes.INVALID_PARAMETER + queueHandler.queueFunction( + queueHandler.eventQueue, + speech.speak, + speechSequence=sequence, + symbolLevel=symbolLevel, + priority=priority + ) + if not asynchronous: + try: + while True: + match markQueue.get(): + case None: + break + case False: + return SystemErrorCodes.CANCELLED + case str() as name: + onSsmlMarkReached(name) + case _: + log.error(f"Unknown item in SSML mark queue: {_}") + finally: + speech.speechCanceled.unregister(onSpeechCanceled) + synthDriverHandler.synthDoneSpeaking.unregister(onDoneSpeaking) + return SystemErrorCodes.SUCCESS + @WINFUNCTYPE(c_long) def nvdaController_cancelSpeech(): @@ -76,7 +155,8 @@ def nvdaController_cancelSpeech(): import queueHandler import speech queueHandler.queueFunction(queueHandler.eventQueue,speech.cancelSpeech) - return 0 + return SystemErrorCodes.SUCCESS + @WINFUNCTYPE(c_long,c_wchar_p) def nvdaController_brailleMessage(text): @@ -87,7 +167,8 @@ def nvdaController_brailleMessage(text): import queueHandler import braille queueHandler.queueFunction(queueHandler.eventQueue, braille.handler.message, text) - return 0 + return SystemErrorCodes.SUCCESS + def _lookupKeyboardLayoutNameWithHexString(layoutString): buf=create_unicode_buffer(1024) @@ -537,7 +618,8 @@ def terminate(self): def initialize() -> None: global _remoteLib, _remoteLoaderAMD64, _remoteLoaderARM64 - global localLib, generateBeep, VBuf_getTextInRange, lastLanguageID, lastLayoutString + global localLib, generateBeep, onSsmlMarkReached, VBuf_getTextInRange + global lastLanguageID, lastLayoutString hkl=c_ulong(windll.User32.GetKeyboardLayout(0)).value lastLanguageID=winUser.LOWORD(hkl) KL_NAMELENGTH=9 @@ -548,6 +630,7 @@ def initialize() -> None: localLib=cdll.LoadLibrary(os.path.join(versionedLibPath,'nvdaHelperLocal.dll')) for name,func in [ ("nvdaController_speakText",nvdaController_speakText), + ("nvdaController_speakSsml", nvdaController_speakSsml), ("nvdaController_cancelSpeech",nvdaController_cancelSpeech), ("nvdaController_brailleMessage",nvdaController_brailleMessage), ("nvdaControllerInternal_requestRegistration",nvdaControllerInternal_requestRegistration), @@ -574,6 +657,9 @@ def initialize() -> None: generateBeep=localLib.generateBeep generateBeep.argtypes=[c_char_p,c_float,c_int,c_int,c_int] generateBeep.restype=c_int + onSsmlMarkReached = localLib.nvdaController_onSsmlMarkReached + onSsmlMarkReached.argtypes = [c_wchar_p] + onSsmlMarkReached.restype = c_ulong # The rest of this function (to do with injection) only applies if NVDA is not running as a Windows store application # Handle VBuf_getTextInRange's BSTR out parameter so that the BSTR will be freed automatically. VBuf_getTextInRange = CFUNCTYPE(c_int, c_int, c_int, c_int, POINTER(BSTR), c_int)( diff --git a/source/winAPI/constants.py b/source/winAPI/constants.py index 4c1f3e16e89..c9527e64f74 100644 --- a/source/winAPI/constants.py +++ b/source/winAPI/constants.py @@ -21,3 +21,4 @@ class SystemErrorCodes(enum.IntEnum): NOT_READY = 0x15 INVALID_PARAMETER = 0x57 MOD_NOT_FOUND = 0x7E + CANCELLED = 0x4C7 diff --git a/user_docs/en/changes.t2t b/user_docs/en/changes.t2t index e3172d24ae2..63aff25108d 100644 --- a/user_docs/en/changes.t2t +++ b/user_docs/en/changes.t2t @@ -70,7 +70,18 @@ Setting ``numCells`` is still supported for single line braille displays and ``n - Example: ``speech.speakSsml('helloJohn')`` - The SSML parsing capabilities are backed by the ``SsmlParser`` class in the ``speechXml`` module. - -- The file names of the NVDA Controller Library no longer contain a suffix denoting the architecture, i.e. ``nvdaControllerClient32/64.dll`` are now called ``nvdaControllerClient.dll``. (#15718, #15717, @LeonarddeR) +- Changes to the NVDA Controller Client library: + - The file names of the Library no longer contain a suffix denoting the architecture, i.e. ``nvdaControllerClient32/64.dll`` are now called ``nvdaControllerClient.dll``. (#15718, #15717, @LeonarddeR) + - Added the following functions to the controller client: + - ``nvdaController_getProcessId``: To get the process id (PID) of the current instance of NVDA the controller client is using. + - nvdaController_speakSsml: To instruct NVDA to speak according to the given SSML. This function also supports: + - Providing the symbol level. + - Providing the priority of speech to be spoken. + - speaking both synchronously (blockking) and asynchronously (instant return). + - The exported ``_nvdaController_onSsmlMarkReached`` pointer can be assigned to a callback function that is called in synchronous mode for every ```` tag encountered in the SSML sequence. + - + - Note: Version 1.1 of the controller client only supports NVDA 2024.1 and above. Version 1.0 supports all past and present versions of NVDA, including 2024.1. + - - Updated ``include`` dependencies: - detours to ``4b8c659f549b0ab21cf649377c7a84eb708f5e68``. (#15695) - ia2 to ``3d8c7f0b833453f761ded6b12d8be431507bfe0b``. (#15695) @@ -114,6 +125,7 @@ That method receives a ``DriverRegistrar`` object on which the ``addUsbDevices`` - ``languageHandler.makeNpgettext`` and ``languageHandler.makePgettext`` have been removed. ``npgettext`` and ``pgettext`` are supported natively now. (#15546) - The app module for [Poedit https://poedit.net] has been changed significantly. The ``fetchObject`` function has been removed. (#15313, #7303, @LeonarddeR) +- Version 1.1 of the NVDA controller client is unable to communicate with older versions of NVDA. If you don't rely on functionality in the new controller client (such as speaking SSML), you are encouraged to stick to version 1.0 of the client, available with NVDA 2023.3 and below. % Insert new list items here as the alias appModule table should be kept at the bottom of this list - The following app modules are removed. Code which imports from one of them, should instead import from the replacement module. (#15618, @lukaszgo1)