-
Notifications
You must be signed in to change notification settings - Fork 11
New SRAL_GetTTSEngines and SRAL_GetAssistiveTechEngines bit mask functions #36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,6 +31,8 @@ class SRALEngine(IntEnum): | |
| VOICE_OVER = 1 << 8 | ||
| NS_SPEECH = 1 << 9 | ||
| AV_SPEECH = 1 << 10 | ||
| ANDROID_ACCESSIBILITY_MANAGER = 1 << 11 | ||
| ANDROID_TEXT_TO_SPEECH = 1 << 12 | ||
|
Comment on lines
+34
to
+35
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as above, missing engines from the Android PRs |
||
|
|
||
| class SRALFeature(IntEnum): | ||
| """ | ||
|
|
@@ -217,6 +219,12 @@ def ptr(self): | |
| _sral_lib.SRAL_GetActiveEngines.argtypes = [] | ||
| _sral_lib.SRAL_GetActiveEngines.restype = ctypes.c_int | ||
|
|
||
| _sral_lib.SRAL_GetTTSEngines.argtypes = [] | ||
| _sral_lib.SRAL_GetTTSEngines.restype = ctypes.c_int | ||
|
|
||
| _sral_lib.SRAL_GetAssistiveTechEngines.argtypes = [] | ||
| _sral_lib.SRAL_GetAssistiveTechEngines.restype = ctypes.c_int | ||
|
|
||
| _sral_lib.SRAL_GetEngineName.argtypes = [ctypes.c_int] | ||
| _sral_lib.SRAL_GetEngineName.restype = ctypes.c_char_p | ||
|
|
||
|
|
@@ -772,6 +780,31 @@ def get_active_engines(self) -> int: | |
| if not _sral_lib: return 0 | ||
| return _sral_lib.SRAL_GetActiveEngines() | ||
|
|
||
| def get_tts_engines(self) -> int: | ||
| """ | ||
| Get the bitmask of engines that are pure text-to-speech synthesizers. | ||
|
|
||
| Intended use: pass to set_engines_exclude when the application wants | ||
| to opt out of TTS output (e.g., only speak through assistive tech | ||
| unless the user has enabled an in-app TTS option). | ||
|
|
||
| Returns: | ||
| A bitmask of SRALEngine enums representing TTS engines. | ||
| """ | ||
| if not _sral_lib: return 0 | ||
| return _sral_lib.SRAL_GetTTSEngines() | ||
|
|
||
| def get_assistive_tech_engines(self) -> int: | ||
| """ | ||
| Get the bitmask of engines that represent assistive technology | ||
| (screen readers and the accessibility frameworks that drive them). | ||
|
|
||
| Returns: | ||
| A bitmask of SRALEngine enums representing assistive-tech engines. | ||
| """ | ||
| if not _sral_lib: return 0 | ||
| return _sral_lib.SRAL_GetAssistiveTechEngines() | ||
|
|
||
| def set_engines_exclude(self, engines_exclude: int) -> bool: | ||
| """ | ||
| Exclude certain engines from auto-update. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,8 +27,12 @@ const ( | |
| NSSpeechEngine | ||
| // AVSpeechEngine — AVFoundation Speech Synthesizer (AVSpeechSynthesizer) for Apple platforms. | ||
| AVSpeechEngine | ||
| // AndroidAccessibilityManagerEngine — Android AccessibilityManager, drives the active screen reader (typically TalkBack). | ||
| AndroidAccessibilityManagerEngine | ||
| // AndroidTextToSpeechEngine — Android TextToSpeech synthesizer. | ||
| AndroidTextToSpeechEngine | ||
| // AllEngines is a bitmask of all supported engines. | ||
| AllEngines Engine = NVDAEngine | JAWSEngine | ZDSREngine | NarratorEngine | UIAEngine | SAPIEngine | SpeechDispatcherEngine | NSSpeechEngine | VoiceOverEngine | AVSpeechEngine | ||
| AllEngines Engine = NVDAEngine | JAWSEngine | ZDSREngine | NarratorEngine | UIAEngine | SAPIEngine | SpeechDispatcherEngine | NSSpeechEngine | VoiceOverEngine | AVSpeechEngine | AndroidAccessibilityManagerEngine | AndroidTextToSpeechEngine | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing engines from the Android PRs 😅 |
||
| // InvalidEngine represents an error or uninitialized engine state. | ||
| InvalidEngine Engine = -1 | ||
| // NoSpecifiedEngine is used for auto-selection of the engine. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,7 +40,7 @@ void PrintEngineNames(int engineBitmask, const char* title) { | |
| return; | ||
| } | ||
| bool found = false; | ||
| for (int engine_val = SRAL_ENGINE_NVDA; engine_val <= SRAL_ENGINE_AV_SPEECH; engine_val <<= 1) { | ||
| for (int engine_val = SRAL_ENGINE_NVDA; engine_val <= SRAL_ENGINE_ANDROID_TEXT_TO_SPEECH; engine_val <<= 1) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. throughout this file we reference |
||
| if (engineBitmask & engine_val) { | ||
| const char* name = SRAL_GetEngineName(engine_val); | ||
| printf(" - %s (0x%X)\n", name ? name : "Unknown Engine", engine_val); | ||
|
|
@@ -122,26 +122,39 @@ int main(void) { | |
| int active_engines = SRAL_GetActiveEngines(); | ||
| PrintEngineNames(active_engines, "Currently Active/Usable Engines"); | ||
|
|
||
| int tts_engines = SRAL_GetTTSEngines(); | ||
| PrintEngineNames(tts_engines, "TTS Engines (category)"); | ||
|
|
||
| int at_engines = SRAL_GetAssistiveTechEngines(); | ||
| PrintEngineNames(at_engines, "Assistive-Tech Engines (category)"); | ||
|
|
||
| bool at_active = (active_engines & at_engines) != 0; | ||
| printf("Assistive tech currently active: %s\n\n", at_active ? "yes" : "no"); | ||
|
|
||
| CHECK((tts_engines & at_engines) == 0, | ||
| "TTS and assistive-tech masks are disjoint.", | ||
| "TTS and assistive-tech masks overlap!"); | ||
|
|
||
| int current_engine_id = SRAL_GetCurrentEngine(); | ||
| printf("Current Default Engine: %s (0x%X)\n", SRAL_GetEngineName(current_engine_id) ? SRAL_GetEngineName(current_engine_id) : "None/Unknown", current_engine_id); | ||
|
|
||
| printf("\nNames of all SRAL_Engines enum members (as per SRAL_GetEngineName):\n"); | ||
| for (int e_val = SRAL_ENGINE_NVDA; e_val <= SRAL_ENGINE_AV_SPEECH; e_val <<= 1) { | ||
| for (int e_val = SRAL_ENGINE_NVDA; e_val <= SRAL_ENGINE_ANDROID_TEXT_TO_SPEECH; e_val <<= 1) { | ||
| const char* name = SRAL_GetEngineName(e_val); | ||
| printf(" Engine ID 0x%X: %s\n", e_val, name ? name : "(Name not defined or not a single engine ID)"); | ||
| } | ||
|
|
||
|
|
||
| int specific_engine_for_ex_tests = SRAL_ENGINE_NONE; | ||
| if (active_engines != SRAL_ENGINE_NONE) { | ||
| for (int e_val = SRAL_ENGINE_NVDA; e_val <= SRAL_ENGINE_AV_SPEECH; e_val <<= 1) { | ||
| for (int e_val = SRAL_ENGINE_NVDA; e_val <= SRAL_ENGINE_ANDROID_TEXT_TO_SPEECH; e_val <<= 1) { | ||
| if ((active_engines & e_val) && e_val != current_engine_id) { | ||
| specific_engine_for_ex_tests = e_val; | ||
| break; | ||
| } | ||
| } | ||
| if (specific_engine_for_ex_tests == SRAL_ENGINE_NONE) { | ||
| for (int e_val = SRAL_ENGINE_NVDA; e_val <= SRAL_ENGINE_AV_SPEECH; e_val <<= 1) { | ||
| for (int e_val = SRAL_ENGINE_NVDA; e_val <= SRAL_ENGINE_ANDROID_TEXT_TO_SPEECH; e_val <<= 1) { | ||
| if (active_engines & e_val) { | ||
| specific_engine_for_ex_tests = e_val; | ||
| break; | ||
|
|
@@ -616,6 +629,19 @@ int main(void) { | |
|
|
||
| CHECK(engines_to_exclude == new_engines_to_exclude, "Engines exclude set/get matches", "Engines exclude set/get mismatch"); | ||
|
|
||
| // Regression check: excluding the whole TTS category must never leave a TTS | ||
| // engine selected as current. Previously a sticky fallback engine (e.g. NS | ||
| // Speech) could keep speaking through TTS after it had been excluded. | ||
| CHECK_SRAL(SRAL_SetEnginesExclude(SRAL_GetTTSEngines()), "Excluded the TTS engine category."); | ||
| int current_with_tts_excluded = SRAL_GetCurrentEngine(); | ||
| printf(" Current engine with TTS excluded: %s (0x%X)\n", | ||
| SRAL_GetEngineName(current_with_tts_excluded) ? SRAL_GetEngineName(current_with_tts_excluded) : "None", | ||
| current_with_tts_excluded); | ||
| CHECK((current_with_tts_excluded & SRAL_GetTTSEngines()) == 0, | ||
| "No TTS engine is current while the TTS category is excluded.", | ||
| "A TTS engine is still current despite the TTS category being excluded!"); | ||
| SRAL_SetEnginesExclude(engines_to_exclude); // Restore the prior exclude state. | ||
|
|
||
|
|
||
| TEST_SECTION("Unregister Keyboard Hooks"); | ||
| SRAL_UnregisterKeyboardHooks(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -336,14 +336,26 @@ static BOOL FindProcess(const wchar_t* name) { | |
|
|
||
| #endif | ||
| static void speech_engine_update() { | ||
| if (!g_currentEngine || !g_currentEngine->GetActive() || g_currentEngine->GetNumber() == SRAL_ENGINE_SAPI || g_currentEngine->GetNumber() == SRAL_ENGINE_UIA || g_currentEngine->GetNumber() == SRAL_ENGINE_AV_SPEECH || g_currentEngine->GetNumber() == SRAL_ENGINE_ANDROID_TEXT_TO_SPEECH) { | ||
| // Re-evaluate the current engine whenever it is unset, no longer active, a | ||
| // TTS engine, or UIA. Screen readers are sticky once chosen; the lower | ||
| // priority engines (TTS / UIA) must keep yielding to a screen reader that | ||
| // appears, and must also react to changes in g_excludes. Deriving the TTS | ||
| // set from SRAL_GetTTSEngines() keeps this in sync as engines are added. | ||
| if (!g_currentEngine | ||
| || !g_currentEngine->GetActive() | ||
| || (g_currentEngine->GetNumber() & SRAL_GetTTSEngines()) | ||
| || g_currentEngine->GetNumber() == SRAL_ENGINE_UIA) { | ||
| #if defined(_WIN32) && !defined(SRAL_NO_UIA) | ||
| if (FindProcess(L"narrator.exe") == TRUE) { | ||
| g_currentEngine = get_engine(SRAL_ENGINE_UIA); | ||
| return; | ||
| } | ||
| else { | ||
| #endif | ||
| // Clear first: if no active, non-excluded engine qualifies there is | ||
| // genuinely nothing to speak through, and g_currentEngine must not | ||
| // keep pointing at a stale (e.g. excluded) engine. | ||
| g_currentEngine = nullptr; | ||
| for (const auto& [value, ptr] : g_engines) { | ||
| if (ptr->GetActive() && !(g_excludes & value)) { | ||
| g_currentEngine = ptr.get(); | ||
|
|
@@ -622,6 +634,24 @@ extern "C" SRAL_API int SRAL_GetActiveEngines(void) { | |
| return mask; | ||
| } | ||
|
|
||
| extern "C" SRAL_API int SRAL_GetTTSEngines(void) { | ||
| return SRAL_ENGINE_SAPI | ||
| | SRAL_ENGINE_SPEECH_DISPATCHER | ||
| | SRAL_ENGINE_NS_SPEECH | ||
| | SRAL_ENGINE_AV_SPEECH | ||
| | SRAL_ENGINE_ANDROID_TEXT_TO_SPEECH; | ||
| } | ||
|
|
||
| extern "C" SRAL_API int SRAL_GetAssistiveTechEngines(void) { | ||
| return SRAL_ENGINE_NVDA | ||
| | SRAL_ENGINE_JAWS | ||
| | SRAL_ENGINE_ZDSR | ||
| | SRAL_ENGINE_NARRATOR | ||
| | SRAL_ENGINE_UIA | ||
| | SRAL_ENGINE_VOICE_OVER | ||
| | SRAL_ENGINE_ANDROID_ACCESSIBILITY_MANAGER; | ||
| } | ||
|
|
||
|
Comment on lines
+637
to
+654
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't hardcode engines here. Instead, consider making engine feature or Sral::Engine interface method with engine category (either TTS or screen reader). |
||
|
|
||
| extern "C" SRAL_API const char* SRAL_GetEngineName(int engine) { | ||
| switch (static_cast<SRAL_Engines>(engine)) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
noticed that we were missing the Apple Silicone build targets, so including that here as well