Detected screen reader now show in the menu instead of generic phrase screen reader.

This commit is contained in:
Storm Dragon
2026-02-27 11:37:19 -05:00
parent 7967c63684
commit 2cfb01549b
3 changed files with 51 additions and 19 deletions
-1
View File
@@ -741,7 +741,6 @@ class BookReader:
else: else:
message = f"Screen reader engine active: {self.readingEngine.get_active_reader_name()}" message = f"Screen reader engine active: {self.readingEngine.get_active_reader_name()}"
print(message) print(message)
self.speechEngine.speak(message)
else: else:
# Reload piper-tts with new voice (check if available first) # Reload piper-tts with new voice (check if available first)
if not TtsEngine.is_available(): if not TtsEngine.is_available():
+42 -12
View File
@@ -9,6 +9,7 @@ Inspired by soundstorm's hierarchical menu system.
from pathlib import Path from pathlib import Path
from src.tts_engine import TtsEngine from src.tts_engine import TtsEngine
from src.screen_reader_engine import ScreenReaderEngine
class OptionsMenu: class OptionsMenu:
@@ -32,6 +33,40 @@ class OptionsMenu:
self.ttsReloadCallback = ttsReloadCallback self.ttsReloadCallback = ttsReloadCallback
self.currentSelection = 0 self.currentSelection = 0
self.inMenu = False self.inMenu = False
self.screenReaderName = None
self._refresh_screen_reader_target()
def _refresh_screen_reader_target(self):
"""Detect active supported screen reader for optional engine entry."""
self.screenReaderName = None
try:
detectionEngine = ScreenReaderEngine(quiet=True)
if detectionEngine.is_available():
activeName = detectionEngine.get_active_reader_name()
if activeName and activeName != "Unavailable":
self.screenReaderName = activeName
else:
self.screenReaderName = "Screen Reader"
detectionEngine.close()
except Exception:
self.screenReaderName = None
def _get_engine_labels(self):
"""Return user-facing labels for available engine values."""
engineLabels = {
'piper': 'Piper-TTS',
'speechd': 'Speech-Dispatcher'
}
if self.screenReaderName:
engineLabels['screenreader'] = self.screenReaderName
return engineLabels
def _get_engine_cycle_order(self):
"""Return engine cycle order, skipping screen-reader mode when unavailable."""
engineOrder = ['piper', 'speechd']
if self.screenReaderName:
engineOrder.append('screenreader')
return engineOrder
def show_main_menu(self): def show_main_menu(self):
""" """
@@ -41,11 +76,7 @@ class OptionsMenu:
Menu items as list of dicts Menu items as list of dicts
""" """
readerEngine = self.config.get_reader_engine() readerEngine = self.config.get_reader_engine()
engineLabels = { engineLabels = self._get_engine_labels()
'piper': 'Piper-TTS',
'speechd': 'Speech-Dispatcher',
'screenreader': 'Screen Reader'
}
readerEngineText = engineLabels.get(readerEngine, 'Piper-TTS') readerEngineText = engineLabels.get(readerEngine, 'Piper-TTS')
menuItems = [ menuItems = [
@@ -147,13 +178,10 @@ class OptionsMenu:
def _toggle_reader_engine(self): def _toggle_reader_engine(self):
"""Cycle reader engine: piper-tts, speech-dispatcher, screen reader.""" """Cycle reader engine: piper-tts, speech-dispatcher, screen reader."""
self._refresh_screen_reader_target()
currentEngine = self.config.get_reader_engine() currentEngine = self.config.get_reader_engine()
engineOrder = ['piper', 'speechd', 'screenreader'] engineOrder = self._get_engine_cycle_order()
engineLabels = { engineLabels = self._get_engine_labels()
'piper': 'Piper-TTS',
'speechd': 'Speech-Dispatcher',
'screenreader': 'Screen Reader'
}
if currentEngine not in engineOrder: if currentEngine not in engineOrder:
currentEngine = 'piper' currentEngine = 'piper'
@@ -203,7 +231,8 @@ class OptionsMenu:
if readerEngine == 'speechd': if readerEngine == 'speechd':
return self._select_speechd_voice() return self._select_speechd_voice()
self.speechEngine.speak("Voice selection is managed by your active screen reader.") readerName = self.screenReaderName if self.screenReaderName else "your active screen reader"
self.speechEngine.speak(f"Voice selection is managed by {readerName}.")
return True return True
def _select_piper_voice(self): def _select_piper_voice(self):
@@ -481,6 +510,7 @@ class OptionsMenu:
def enter_menu(self): def enter_menu(self):
"""Enter the options menu""" """Enter the options menu"""
self._refresh_screen_reader_target()
self.inMenu = True self.inMenu = True
self.currentSelection = 0 self.currentSelection = 0
self.inVoiceMenu = False self.inVoiceMenu = False
+9 -6
View File
@@ -167,11 +167,12 @@ class ScreenReaderRemoteController:
class ScreenReaderEngine: class ScreenReaderEngine:
"""Book reading engine that speaks via active screen reader D-Bus APIs.""" """Book reading engine that speaks via active screen reader D-Bus APIs."""
def __init__(self): def __init__(self, quiet=False):
self.speechLock = threading.Lock() self.speechLock = threading.Lock()
self.isAvailable = False self.isAvailable = False
self.activeController = None self.activeController = None
self.availableControllers = [] self.availableControllers = []
self.quiet = quiet
self.isReading = False self.isReading = False
self.isPausedReading = False self.isPausedReading = False
@@ -185,7 +186,8 @@ class ScreenReaderEngine:
def _initialize_controllers(self): def _initialize_controllers(self):
"""Discover available screen reader remote controllers.""" """Discover available screen reader remote controllers."""
if not HAS_DASBUS: if not HAS_DASBUS:
print("Warning: python-dasbus not installed. Screen reader engine unavailable.") if not self.quiet:
print("Warning: python-dasbus not installed. Screen reader engine unavailable.")
return return
controllerCandidates = [ controllerCandidates = [
@@ -207,10 +209,11 @@ class ScreenReaderEngine:
self.activeController = self._select_active_controller() self.activeController = self._select_active_controller()
self.isAvailable = self.activeController is not None self.isAvailable = self.activeController is not None
if self.activeController: if not self.quiet:
print(f"Screen reader engine connected to {self.activeController.displayName}.") if self.activeController:
else: print(f"Screen reader engine connected to {self.activeController.displayName}.")
print("Warning: No supported screen reader D-Bus service detected (Orca/Cthulhu).") else:
print("Warning: No supported screen reader D-Bus service detected (Orca/Cthulhu).")
def _is_process_running(self, processName): def _is_process_running(self, processName):
"""Return True when the process name is running.""" """Return True when the process name is running."""