Skip to content

Commit fd7a608

Browse files
authored
Merge 991a1dd into 4c673fd
2 parents 4c673fd + 991a1dd commit fd7a608

19 files changed

Lines changed: 366 additions & 42 deletions

File tree

extras/controllerClient/readme.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
1-
# NVDA Controller Client API 1.0 Documentation
1+
# NVDA Controller Client API 2.0 Documentation
22

33
## Introduction
44

5-
This client API allows an application to communicate with NVDA, in order to do such things as speak text or braille a message.
5+
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.
66

77
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++.
88

9+
## Compatibility notice
10+
11+
Version 2.0 of the controller client was introduced in NVDA 2024.1, offering the following additional functions compared to version 1.0:
12+
- nvdaController_getProcessId
13+
- nvdaController_speakSsml
14+
15+
These functions are supported in NVDA 2024.1 and newer. On older versions, they return error code 1717 (RPC_S_UNKNOWN_IF).
16+
917
## Security practices
18+
1019
Developers should be aware that NVDA runs on the lock screen and [secure screens](https://www.nvaccess.org/files/nvda/documentation/userGuide.html#SecureScreens).
1120
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.
1221

extras/controllerClient/x86/example_python.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,38 @@
1313
res = clientLib.nvdaController_testIfRunning()
1414
if res != 0:
1515
errorMessage = str(ctypes.WinError(res))
16-
ctypes.windll.user32.MessageBoxW(0, "Error: %s" % errorMessage, "Error communicating with NVDA", 0)
16+
ctypes.windll.user32.MessageBoxW(0, f"Error: {errorMessage}", "Error communicating with NVDA", 0)
1717

1818
# Speak and braille some messages
1919
for count in range(4):
2020
clientLib.nvdaController_speakText("This is a test client for NVDA")
2121
clientLib.nvdaController_brailleMessage("Time: %g seconds" % (0.75 * count))
2222
time.sleep(0.625)
2323
clientLib.nvdaController_cancelSpeech()
24-
clientLib.nvdaController_speakText("This is a test client for NVDA!")
24+
25+
26+
# Test SSML output
27+
@ctypes.WINFUNCTYPE(ctypes.c_ulong, ctypes.c_wchar_p)
28+
def onMarkReached(name: str) -> int:
29+
print(f"Reached SSML mark with name: {name}")
30+
return 0
31+
32+
ssml = (
33+
'<speak>'
34+
'This is one sentence. '
35+
'<mark name="test" />'
36+
'<prosody pitch="200%">This sentence is pronounced with higher pitch.</prosody>'
37+
'<mark name="test2" />'
38+
'This is a third sentence. '
39+
'<mark name="test3" />'
40+
'This is a fourth sentence. We will stay silent for a second after this one.'
41+
'<break time="1000ms" />'
42+
'<mark name="test4" />'
43+
'This is a fifth sentence. '
44+
'<mark name="test5" />'
45+
'</speak>'
46+
)
47+
clientLib.nvdaController_setOnSsmlMarkReachedCallback(onMarkReached);
48+
clientLib.nvdaController_speakSsml(ssml, -1, 0, False)
49+
clientLib.nvdaController_setOnSsmlMarkReachedCallback(None);
2550
clientLib.nvdaController_brailleMessage("Test completed!")

nvdaHelper/client/client.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This file is a part of the NVDA project.
33
URL: http://www.nvda-project.org/
4-
Copyright 2006-2010 NVDA contributers.
4+
Copyright 2006-2023 NV Access Limited, Leonard de Ruijter.
55
This program is free software: you can redistribute it and/or modify
66
it under the terms of the GNU Lesser General Public License version 2.1, as published by
77
the Free Software Foundation.
@@ -42,8 +42,27 @@ BOOL WINAPI DllMain(HINSTANCE hModule,DWORD reason,LPVOID lpReserved) {
4242
if (RPC_S_OK != status) {
4343
return FALSE;
4444
}
45+
status = RpcBindingFromStringBinding(rpcWstr, &nvdaController2BindingHandle);
46+
if (RPC_S_OK != status) {
47+
return FALSE;
48+
}
4549
} else if(reason==DLL_PROCESS_DETACH) {
50+
RpcBindingFree(&nvdaController2BindingHandle);
4651
RpcBindingFree(&nvdaControllerBindingHandle);
4752
}
4853
return TRUE;
4954
}
55+
56+
onSsmlMarkReachedFuncType _onSsmlMarkReached = nullptr;
57+
58+
error_status_t __stdcall nvdaController_onSsmlMarkReached(const wchar_t* mark) {
59+
if (_onSsmlMarkReached == nullptr) {
60+
return ERROR_CALL_NOT_IMPLEMENTED;
61+
}
62+
return _onSsmlMarkReached(mark);
63+
}
64+
65+
error_status_t __stdcall nvdaController_setOnSsmlMarkReachedCallback(onSsmlMarkReachedFuncType callback) {
66+
_onSsmlMarkReached = callback;
67+
return ERROR_SUCCESS;
68+
}

nvdaHelper/client/nvdaControllerClient.def

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
;;;
22
;This file is a part of the NVDA project.
33
;URL: http://www.nvda-project.org/
4-
;Copyright 2006-2010 NVDA contributers.
4+
;Copyright 2006-2023 NV Access Limited, Leonard de Ruijter.
55
;This program is free software: you can redistribute it and/or modify
66
;it under the terms of the GNU Lesser General Public License version 2.1, as published by
77
;the Free Software Foundation.
@@ -17,3 +17,6 @@ EXPORTS
1717
nvdaController_speakText
1818
nvdaController_cancelSpeech
1919
nvdaController_brailleMessage
20+
nvdaController_getProcessId
21+
nvdaController_speakSsml
22+
nvdaController_setOnSsmlMarkReachedCallback

nvdaHelper/interfaces/nvdaController/nvdaController.acf

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This file is a part of the NVDA project.
33
URL: http://www.nvda-project.org/
4-
Copyright 2006-2010 NVDA contributers.
4+
Copyright 2006-2023 NV Access Limited, Leonard de Ruijter.
55
This program is free software: you can redistribute it and/or modify
66
it under the terms of the GNU Lesser General Public License version 2.1, as published by
77
the Free Software Foundation.
@@ -21,3 +21,12 @@ interface NvdaController {
2121
[fault_status,comm_status] cancelSpeech();
2222
[fault_status,comm_status] brailleMessage();
2323
}
24+
25+
[
26+
implicit_handle(handle_t nvdaController2BindingHandle)
27+
]
28+
interface NvdaController2 {
29+
[fault_status,comm_status] getProcessId();
30+
[fault_status,comm_status] speakSsml();
31+
[fault_status,comm_status] onSsmlMarkReached();
32+
}

nvdaHelper/interfaces/nvdaController/nvdaController.idl

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This file is a part of the NVDA project.
33
URL: http://www.nvda-project.org/
4-
Copyright 2006-2010 NVDA contributers.
4+
Copyright 2006-2023 NV Access Limited, Leonard de Ruijter.
55
This program is free software: you can redistribute it and/or modify
66
it under the terms of the GNU Lesser General Public License version 2.1, as published by
77
the Free Software Foundation.
@@ -15,7 +15,7 @@ http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
1515
cpp_quote("/*")
1616
cpp_quote("This file is a part of the NVDA project.")
1717
cpp_quote("URL: http://www.nvda-project.org/")
18-
cpp_quote("Copyright 2006-2010 NVDA contributers.")
18+
cpp_quote("Copyright 2006-2023 NV Access Limited, Leonard de Ruijter.")
1919
cpp_quote("This program is free software: you can redistribute it and/or modify")
2020
cpp_quote("it under the terms of the GNU Lesser General Public License version 2.1, as published by")
2121
cpp_quote("the Free Software Foundation.")
@@ -27,7 +27,56 @@ cpp_quote("http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html")
2727
cpp_quote("*/")
2828

2929
/**
30-
* Allows the controling of NVDA from a remote process
30+
* Facilitates the ability to prioritize speech.
31+
* These values ​​correspond to the values ​​of speech.priorities.SpeechPriority in NVDA.
32+
*/
33+
typedef [v1_enum] enum tagSPEECH_PRIORITY{
34+
/**
35+
* Indicates that a speech sequence should have normal priority.
36+
*/
37+
SPEECH_PRIORITY_NORMAL = 0,
38+
/**
39+
* Indicates that a speech sequence should be spoken after the next utterance of lower priority is complete.
40+
*/
41+
SPEECH_PRIORITY_NEXT = 1,
42+
/**
43+
* Indicates that a speech sequence is very important and should be spoken right now,
44+
* interrupting low priority speech.
45+
* After it is spoken, interrupted speech will resume.
46+
* Note that this does not interrupt previously queued speech at the same priority.
47+
*/
48+
SPEECH_PRIORITY_NOW = 2
49+
} SPEECH_PRIORITY;
50+
51+
/**
52+
* The desired symbol level in a speech sequence.
53+
* These values ​​correspond to the values ​​of characterProcessing.SymbolLevel in NVDA.
54+
*/
55+
typedef [v1_enum] enum tagSYMBOL_LEVEL {
56+
SYMBOL_LEVEL_NONE = 0,
57+
SYMBOL_LEVEL_SOME = 100,
58+
SYMBOL_LEVEL_MOST = 200,
59+
SYMBOL_LEVEL_ALL = 300,
60+
SYMBOL_LEVEL_CHAR = 1000,
61+
SYMBOL_LEVEL_UNCHANGED = -1
62+
} SYMBOL_LEVEL;
63+
64+
/**
65+
* Type signature for a callback that may be implemented by a client.
66+
* The callback is called for every mark in the SSML as received by speakSsml when called synchronously.
67+
* @param mark The name of the reached mark.
68+
*/
69+
typedef error_status_t(__stdcall *onSsmlMarkReachedFuncType)([in, string] const wchar_t* mark);
70+
71+
/**
72+
* Setts the callback used when marks are reached within SSML.
73+
* The callback is called for every mark in the SSML as received by speakSsml when called synchronously.
74+
* Param callback The callback to use, NULL to reset.
75+
*/
76+
error_status_t __stdcall setOnSsmlMarkReachedCallback(onSsmlMarkReachedFuncType callback);
77+
78+
/**
79+
* Allows controling of NVDA from a remote process
3180
*/
3281
[
3382
uuid(DFF50B99-F7FD-4ca7-A82C-DAEB3E025295),
@@ -41,7 +90,7 @@ interface NvdaController {
4190
error_status_t __stdcall testIfRunning();
4291

4392
/**
44-
* Instructs NVDA to speak the given text.
93+
* Instructs NVDA to speak the given text message.
4594
* @param text the text to speak.
4695
*/
4796
error_status_t __stdcall speakText([in,string] const wchar_t* text);
@@ -56,5 +105,42 @@ interface NvdaController {
56105
* @param message the message that will be temporarily shown on the display
57106
*/
58107
error_status_t __stdcall brailleMessage([in,string] const wchar_t* message);
108+
};
109+
110+
/**
111+
* Adds additional methods to control NVDA from a remote process
112+
*/
113+
[
114+
uuid(3D168D45-CB58-4270-8257-4E0BE515D557),
115+
version(1.0),
116+
]
117+
interface NvdaController2 {
118+
/**
119+
* Retrieves the process identifier (PID) of NVDA's process.
120+
*/
121+
error_status_t __stdcall getProcessId([out] unsigned long* pid);
122+
123+
/**
124+
* Instructs NVDA to speak the given Speech Synthesis Markup Language (SSML).
125+
* @param ssml The ssml to speak.
126+
* @param symbolLevel The symbol verbosity level.
127+
* @param priority The priority of the speech sequence.
128+
* @param asynchronous Whether SSML should be spoken asynchronously.
129+
* If TRUE, returns instantly.
130+
* If FALSE, returns either when the speech sequence is completed or canceled.
131+
*/
132+
error_status_t __stdcall speakSsml(
133+
[in, string] const wchar_t* ssml,
134+
[in, defaultvalue(SYMBOL_LEVEL_UNCHANGED)] const SYMBOL_LEVEL symbolLevel,
135+
[in,defaultvalue(SPEECH_PRIORITY_NORMAL)] const SPEECH_PRIORITY priority,
136+
[in, defaultvalue(TRUE)] const boolean asynchronous
137+
);
59138

139+
/**
140+
* Called by NVDA when a mark in provided SSML is reached after calling speakSsml synchronously.
141+
* This callback is implemented by the controller client library.
142+
* It forwards the call to the callback registered with
143+
* @param mark The name of the reached mark.
144+
*/
145+
[callback] error_status_t __stdcall onSsmlMarkReached([in, string] const wchar_t* mark);
60146
};
Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This file is a part of the NVDA project.
33
URL: http://www.nvda-project.org/
4-
Copyright 2006-2010 NVDA contributers.
4+
Copyright 2006-2023 NV Access Limited, Leonard de Ruijter.
55
This program is free software: you can redistribute it and/or modify
66
it under the terms of the GNU General Public License version 2.0, as published by
77
the Free Software Foundation.
@@ -13,22 +13,52 @@ This license can be found at:
1313
*/
1414

1515
#include <local/nvdaController.h>
16+
#include <windows.h>
17+
18+
error_status_t(__stdcall *_nvdaController_speakText)(const wchar_t*) = nullptr;
1619

17-
error_status_t(__stdcall *_nvdaController_speakText)(const wchar_t*);
1820
error_status_t __stdcall nvdaController_speakText(const wchar_t* text) {
21+
if (_nvdaController_speakText == nullptr) {
22+
return ERROR_CALL_NOT_IMPLEMENTED;
23+
}
1924
return _nvdaController_speakText(text);
2025
}
2126

27+
error_status_t(__stdcall *_nvdaController_speakSsml)(const wchar_t*, const SYMBOL_LEVEL, const SPEECH_PRIORITY, const boolean) = nullptr;
28+
29+
error_status_t __stdcall nvdaController_speakSsml(const wchar_t* ssml, const SYMBOL_LEVEL symbolLevel, const SPEECH_PRIORITY priority, const boolean asynchronous) {
30+
if (_nvdaController_speakSsml == nullptr) {
31+
return ERROR_CALL_NOT_IMPLEMENTED;
32+
}
33+
return _nvdaController_speakSsml(ssml, symbolLevel, priority, asynchronous);
34+
}
35+
2236
error_status_t(__stdcall *_nvdaController_cancelSpeech)();
37+
2338
error_status_t __stdcall nvdaController_cancelSpeech() {
39+
if (_nvdaController_cancelSpeech == nullptr) {
40+
return ERROR_CALL_NOT_IMPLEMENTED;
41+
}
2442
return _nvdaController_cancelSpeech();
2543
}
2644

2745
error_status_t(__stdcall *_nvdaController_brailleMessage)(const wchar_t*);
46+
2847
error_status_t __stdcall nvdaController_brailleMessage(const wchar_t* text) {
48+
if (_nvdaController_brailleMessage == nullptr) {
49+
return ERROR_CALL_NOT_IMPLEMENTED;
50+
}
2951
return _nvdaController_brailleMessage(text);
3052
}
3153

54+
error_status_t __stdcall nvdaController_getProcessId(unsigned long* pid) {
55+
if (pid == nullptr) {
56+
return ERROR_INVALID_PARAMETER;
57+
}
58+
*pid = GetCurrentProcessId();
59+
return ERROR_SUCCESS;
60+
}
61+
3262
error_status_t __stdcall nvdaController_testIfRunning() {
33-
return 0;
63+
return ERROR_SUCCESS;
3464
}

nvdaHelper/local/nvdaHelperLocal.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ EXPORTS
5050
_nvdaController_brailleMessage
5151
_nvdaController_cancelSpeech
5252
_nvdaController_speakText
53+
_nvdaController_speakSsml
54+
nvdaController_onSsmlMarkReached
5355
_nvdaControllerInternal_vbufChangeNotify
5456
displayModel_getWindowTextInRect
5557
displayModel_getFocusRect

nvdaHelper/local/rpcSrv.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ typedef RPC_STATUS(RPC_ENTRY *RpcServerRegisterIf3_functype)(RPC_IF_HANDLE,UUID
2828

2929
RPC_IF_HANDLE availableInterfaces[]={
3030
nvdaController_NvdaController_v1_0_s_ifspec,
31+
nvdaController_NvdaController2_v1_0_s_ifspec,
3132
nvdaControllerInternal_NvdaControllerInternal_v1_0_s_ifspec
3233
};
3334

nvdaHelper/local/sconscript

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ localLib=env.SharedLibrary(
8080
nvdaInProcUtilsRPCClientSource,
8181
displayModelRPCClientSource,
8282
'rpcSrv.cpp',
83-
'nvdaController.c',
83+
'nvdaController.cpp',
8484
winIPCUtilsObj,
8585
controllerRPCServerSource,
8686
'nvdaControllerInternal.c',

0 commit comments

Comments
 (0)