Skip to content
12 changes: 9 additions & 3 deletions extras/controllerClient/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,18 @@ The client API is implemented as a dll (dynamic link library). The functions in

## Compatibility notice

Version 3.0 of the controller client was introduced in NVDA 2026.3 and provides the following new function:

* `nvdaController_isSpeaking`

On older versions, this returns `RPC_S_UNKNOWN_IF` (`1717`).

Version 2.0 of the controller client was introduced in NVDA 2024.1, offering the following additional functions compared to version 1.0:

* nvdaController_getProcessId
* nvdaController_speakSsml
* `nvdaController_getProcessId`
* `nvdaController_speakSsml`

These functions are supported in NVDA 2024.1 and newer. On older versions, they return error code 1717 (RPC_S_UNKNOWN_IF).
These functions are supported in NVDA 2024.1 and newer. On older versions, they return error code `1717` (`RPC_S_UNKNOWN_IF`).

## Security practices

Expand Down
12 changes: 10 additions & 2 deletions nvdaHelper/client/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,17 @@ BOOL WINAPI DllMain(HINSTANCE hModule,DWORD reason,LPVOID lpReserved) {
if (RPC_S_OK != status) {
Comment thread
ethindp marked this conversation as resolved.
return FALSE;
}
status = RpcBindingFromStringBinding(rpcWstr, &nvdaController3BindingHandle);
if (RPC_S_OK != status) {
return FALSE;
}
} else if(reason==DLL_PROCESS_DETACH) {
RpcBindingFree(&nvdaController2BindingHandle);
RpcBindingFree(&nvdaControllerBindingHandle);
if (nvdaController3BindingHandle)
RpcBindingFree(&nvdaController3BindingHandle);
if (nvdaController2BindingHandle)
RpcBindingFree(&nvdaController2BindingHandle);
if (nvdaControllerBindingHandle)
RpcBindingFree(&nvdaControllerBindingHandle);
}
return TRUE;
}
Expand Down
3 changes: 2 additions & 1 deletion nvdaHelper/client/nvdaControllerClient.def
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
;;;
;This file is a part of the NVDA project.
;URL: http://www.nvda-project.org/
;Copyright 2006-2023 NV Access Limited, Leonard de Ruijter.
;Copyright 2006-2026 NV Access Limited, Leonard de Ruijter, Ethin Probst.
;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.
Expand All @@ -20,3 +20,4 @@ EXPORTS
nvdaController_getProcessId
nvdaController_speakSsml
nvdaController_setOnSsmlMarkReachedCallback
nvdaController_isSpeaking
9 changes: 8 additions & 1 deletion nvdaHelper/interfaces/nvdaController/nvdaController.acf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This file is a part of the NVDA project.
URL: http://www.nvda-project.org/
Copyright 2006-2023 NV Access Limited, Leonard de Ruijter.
Copyright 2006-2026 NV Access Limited, Leonard de Ruijter, Ethin Probst.
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.
Expand Down Expand Up @@ -30,3 +30,10 @@ interface NvdaController2 {
[fault_status,comm_status] speakSsml();
[fault_status,comm_status] onSsmlMarkReached();
}

[
implicit_handle(handle_t nvdaController3BindingHandle)
]
interface NvdaController3 {
[fault_status,comm_status] isSpeaking();
}
14 changes: 13 additions & 1 deletion nvdaHelper/interfaces/nvdaController/nvdaController.idl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This file is a part of the NVDA project.
URL: http://www.nvda-project.org/
Copyright 2006-2023 NV Access Limited, Leonard de Ruijter.
Copyright 2006-2026 NV Access Limited, Leonard de Ruijter, Ethin Probst.
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.
Expand Down Expand Up @@ -146,3 +146,15 @@ interface NvdaController2 {
*/
[callback] error_status_t __stdcall onSsmlMarkReached([in, string] const wchar_t* mark);
};

[
uuid(019e4216-a9a1-7540-a217-e1a2d2793025),
version(1.0),
]
interface NvdaController3 {
/**
* Retrieves whether NVDA is speaking or not
* @param speaking Out parameter that receives the speaking status.
*/
error_status_t __stdcall isSpeaking([out] boolean* speaking);
};
10 changes: 9 additions & 1 deletion nvdaHelper/local/nvdaController.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This file is a part of the NVDA project.
URL: http://www.nvda-project.org/
Copyright 2006-2023 NV Access Limited, Leonard de Ruijter.
Copyright 2006-2026 NV Access Limited, Leonard de Ruijter, Ethin Probst.
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.
Expand Down Expand Up @@ -62,3 +62,11 @@ error_status_t __stdcall nvdaController_getProcessId(unsigned long* pid) {
error_status_t __stdcall nvdaController_testIfRunning() {
return ERROR_SUCCESS;
}

error_status_t(__stdcall *_nvdaController_isSpeaking)(boolean*) = nullptr;

error_status_t __stdcall nvdaController_isSpeaking(boolean* isSpeaking) {
if (isSpeaking == nullptr) return ERROR_INVALID_PARAMETER;
if (_nvdaController_isSpeaking == nullptr) return ERROR_CALL_NOT_IMPLEMENTED;
return _nvdaController_isSpeaking(isSpeaking);
Comment thread
ethindp marked this conversation as resolved.
}
1 change: 1 addition & 0 deletions nvdaHelper/local/nvdaHelperLocal.def
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ EXPORTS
_nvdaController_cancelSpeech DATA
_nvdaController_speakText DATA
_nvdaController_speakSsml DATA
_nvdaController_isSpeaking DATA
nvdaController_onSsmlMarkReached
_nvdaControllerInternal_vbufChangeNotify DATA
displayModel_getWindowTextInRect
Expand Down
3 changes: 2 additions & 1 deletion nvdaHelper/local/rpcSrv.cpp
Original file line number Diff line number Diff line change
@@ -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-2026 NVDA contributors.
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.
Expand Down Expand Up @@ -29,6 +29,7 @@ typedef RPC_STATUS(RPC_ENTRY *RpcServerRegisterIf3_functype)(RPC_IF_HANDLE,UUID
RPC_IF_HANDLE availableInterfaces[]={
nvdaController_NvdaController_v1_0_s_ifspec,
nvdaController_NvdaController2_v1_0_s_ifspec,
nvdaController_NvdaController3_v1_0_s_ifspec,
nvdaControllerInternal_NvdaControllerInternal_v1_0_s_ifspec
};

Expand Down
12 changes: 12 additions & 0 deletions source/NVDAHelper/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
create_unicode_buffer,
windll,
wstring_at,
_Pointer,
)

from winBindings import user32
Expand Down Expand Up @@ -196,6 +197,16 @@ def nvdaController_brailleMessage(text: str) -> SystemErrorCodes:
return SystemErrorCodes.SUCCESS


@WINFUNCTYPE(c_long, POINTER(c_bool))
def nvdaController_isSpeaking(pSpeaking: _Pointer[c_bool]) -> int:
if not pSpeaking:
return SystemErrorCodes.INVALID_PARAMETER.value
import speech

pSpeaking[0] = speech.isSpeaking()
return SystemErrorCodes.SUCCESS.value


def _lookupKeyboardLayoutNameWithHexString(layoutString):
buf = create_unicode_buffer(1024)
bufSize = c_ulong(2048)
Expand Down Expand Up @@ -787,6 +798,7 @@ def initialize() -> None:
("nvdaController_speakSsml", nvdaController_speakSsml),
("nvdaController_cancelSpeech", nvdaController_cancelSpeech),
("nvdaController_brailleMessage", nvdaController_brailleMessage),
("nvdaController_isSpeaking", nvdaController_isSpeaking),
("nvdaControllerInternal_requestRegistration", nvdaControllerInternal_requestRegistration),
("nvdaControllerInternal_reportLiveRegion", nvdaControllerInternal_reportLiveRegion),
("nvdaControllerInternal_inputLangChangeNotify", nvdaControllerInternal_inputLangChangeNotify),
Expand Down
2 changes: 2 additions & 0 deletions source/speech/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
IDT_MAX_SPACES,
getIndentToneDuration,
isBlank,
isSpeaking,
LANGS_WITH_CONJUNCT_CHARS,
pauseSpeech,
processText,
Expand Down Expand Up @@ -118,6 +119,7 @@
"IDT_MAX_SPACES",
"getIndentToneDuration",
"isBlank",
"isSpeaking",
"LANGS_WITH_CONJUNCT_CHARS",
"pauseSpeech",
"processText",
Expand Down
13 changes: 13 additions & 0 deletions source/speech/speech.py
Original file line number Diff line number Diff line change
Expand Up @@ -3179,3 +3179,16 @@ def clearTypedWordBuffer() -> None:
complete the word (such as a focus change or choosing to move the caret).
"""
_curWordChars.clear()


def isSpeaking() -> bool:
"""Whether NVDA is currently producing speech audio.
True if the synth driver has reported it is mid-utterance
and speech is neither paused nor disabled.
"""
state = getState()
if state.speechMode in (SpeechMode.off, SpeechMode.beeps):
return False
if state.isPaused:
return False
return _manager._synthStillSpeaking()
Comment thread
ethindp marked this conversation as resolved.
Comment thread
ethindp marked this conversation as resolved.
Loading