Attempt to speed up Steam even more. Sound for link added.
This commit is contained in:
@@ -914,6 +914,37 @@ class PluginSystemManager:
|
||||
inactive_plugins = [p.get_module_name() for p in self.plugins if not p.loaded]
|
||||
logger.info(f"Inactive plugins after sync: {inactive_plugins}")
|
||||
|
||||
@staticmethod
|
||||
def _lifecycle_accepts_plugin_instance(method: Any) -> bool:
|
||||
"""Return True if the bound lifecycle method expects a plugin argument."""
|
||||
|
||||
try:
|
||||
parameters = list(inspect.signature(method).parameters.values())
|
||||
except (TypeError, ValueError):
|
||||
return True
|
||||
|
||||
if any(parameter.kind == inspect.Parameter.VAR_POSITIONAL for parameter in parameters):
|
||||
return True
|
||||
|
||||
positional_parameters = [
|
||||
parameter for parameter in parameters
|
||||
if parameter.kind in (
|
||||
inspect.Parameter.POSITIONAL_ONLY,
|
||||
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
||||
)
|
||||
]
|
||||
return bool(positional_parameters)
|
||||
|
||||
def _callPluginLifecycle(self, pluginInstance: Any, methodName: str) -> None:
|
||||
"""Call a plugin lifecycle method without replaying it through pluggy."""
|
||||
|
||||
method = getattr(pluginInstance, methodName)
|
||||
if self._lifecycle_accepts_plugin_instance(method):
|
||||
method(pluginInstance)
|
||||
return
|
||||
|
||||
method()
|
||||
|
||||
def loadPlugin(self, pluginInfo):
|
||||
"""Load a plugin."""
|
||||
module_name = pluginInfo.get_module_name()
|
||||
@@ -998,7 +1029,7 @@ class PluginSystemManager:
|
||||
logger.info(f"Activating plugin: {module_name}")
|
||||
# Lifecycle is per-plugin. Broadcasting through pluggy replays
|
||||
# activate() on every previously-registered plugin.
|
||||
plugin_instance.activate(plugin_instance)
|
||||
self._callPluginLifecycle(plugin_instance, "activate")
|
||||
except Exception as e:
|
||||
logger.error(f"Error activating plugin {module_name}: {e}")
|
||||
import traceback
|
||||
@@ -1039,7 +1070,7 @@ class PluginSystemManager:
|
||||
try:
|
||||
# Mirror targeted activation and only deactivate the plugin
|
||||
# instance being unloaded.
|
||||
plugin_instance.deactivate(plugin_instance)
|
||||
self._callPluginLifecycle(plugin_instance, "deactivate")
|
||||
except Exception as e:
|
||||
logger.error(f"Error deactivating plugin {module_name}: {e}")
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@ import gi
|
||||
import locale
|
||||
import math
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
from difflib import SequenceMatcher
|
||||
from typing import Any, Callable, Generator, Optional, TYPE_CHECKING
|
||||
@@ -200,7 +199,8 @@ class Utilities:
|
||||
return ""
|
||||
|
||||
try:
|
||||
cmdline = subprocess.getoutput(f"cat /proc/{pid}/cmdline")
|
||||
with open(f"/proc/{pid}/cmdline", "rb") as cmdline_file:
|
||||
cmdline = cmdline_file.read().decode("utf-8", errors="replace")
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
@@ -211,7 +211,9 @@ class Utilities:
|
||||
return False
|
||||
|
||||
app = AXObject.get_application(window)
|
||||
tokens = ["SCRIPT UTILITIES: Looking at", window, "from", app, self._getAppCommandLine(app)]
|
||||
tokens = ["SCRIPT UTILITIES: Looking at", window, "from", app]
|
||||
if debug.debugLevel <= debug.LEVEL_INFO:
|
||||
tokens.append(self._getAppCommandLine(app))
|
||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||
|
||||
if clearCache:
|
||||
@@ -3082,6 +3084,56 @@ class Utilities:
|
||||
|
||||
return "".join(adjustedLine)
|
||||
|
||||
def getLinkIndicatorPresentation(self, obj: Atspi.Accessible, line: Any, startOffset: Any):
|
||||
"""Return the spoken text and any link icons for a line fragment."""
|
||||
|
||||
spoken = self.adjustForLinks(obj, line, startOffset)
|
||||
roleSoundPresentation = cthulhu.cthulhuApp.settingsManager.getSetting('roleSoundPresentation')
|
||||
if roleSoundPresentation == settings.ROLE_SOUND_PRESENTATION_SPEECH_ONLY:
|
||||
return spoken, []
|
||||
|
||||
if not cthulhu.cthulhuApp.settingsManager.getSetting('enableSound'):
|
||||
return spoken, []
|
||||
|
||||
if not AXObject.supports_hypertext(obj):
|
||||
return spoken, []
|
||||
|
||||
from . import sound_theme_manager
|
||||
|
||||
manager = sound_theme_manager.getManager()
|
||||
if manager is None:
|
||||
return spoken, []
|
||||
|
||||
endOffset = startOffset + len(line)
|
||||
icons = []
|
||||
missingIcon = False
|
||||
for link in AXHypertext.get_all_links(obj):
|
||||
start_index = AXHypertext.get_link_start_offset(link)
|
||||
end_index = AXHypertext.get_link_end_offset(link)
|
||||
if start_index < 0 or end_index < 0:
|
||||
continue
|
||||
|
||||
if startOffset < end_index <= endOffset:
|
||||
pass
|
||||
elif startOffset <= start_index < endOffset:
|
||||
pass
|
||||
else:
|
||||
continue
|
||||
|
||||
icon = manager.getLinkSoundIcon(visited=AXUtilities.is_visited(link))
|
||||
if icon:
|
||||
icons.append(icon)
|
||||
else:
|
||||
missingIcon = True
|
||||
|
||||
if not icons:
|
||||
return spoken, []
|
||||
|
||||
if roleSoundPresentation == settings.ROLE_SOUND_PRESENTATION_SOUND_ONLY and not missingIcon:
|
||||
return line, icons
|
||||
|
||||
return spoken, icons
|
||||
|
||||
def _processMultiCaseString(self, string: Any) -> Any:
|
||||
return re.sub(r'(?<=[a-z])(?=[A-Z])', ' ', string)
|
||||
|
||||
|
||||
@@ -2402,7 +2402,7 @@ class Script(script.Script):
|
||||
|
||||
voice = self.speechGenerator.voice(
|
||||
obj=obj, string=string, language=language, dialect=dialect)
|
||||
string = self.utilities.adjustForLinks(obj, string, start)
|
||||
string, linkIcons = self.utilities.getLinkIndicatorPresentation(obj, string, start)
|
||||
string = self.utilities.adjustForRepeats(string)
|
||||
if self.utilities.shouldVerbalizeAllPunctuation(obj):
|
||||
string = self.utilities.verbalizeAllPunctuation(string)
|
||||
@@ -2414,6 +2414,7 @@ class Script(script.Script):
|
||||
|
||||
result = [string]
|
||||
result.extend(voice)
|
||||
result.extend(linkIcons)
|
||||
utterance.append(result)
|
||||
speech.speak(utterance)
|
||||
else:
|
||||
|
||||
@@ -570,7 +570,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
|
||||
roleSoundIcon = None
|
||||
if roleSoundPresentation != settings.ROLE_SOUND_PRESENTATION_SPEECH_ONLY \
|
||||
and soundEnabled:
|
||||
roleSoundIcon = sound_theme_manager.getManager().getRoleSoundIcon(role)
|
||||
roleSoundIcon = self._getRoleSoundIcon(obj, role)
|
||||
if roleSoundPresentation == settings.ROLE_SOUND_PRESENTATION_SOUND_ONLY \
|
||||
and soundEnabled:
|
||||
# Stateful controls present their role via state sounds; suppress
|
||||
|
||||
@@ -303,6 +303,24 @@ class SoundThemeManager:
|
||||
|
||||
return None
|
||||
|
||||
def getLinkSoundIcon(self, visited=False, themeName=None):
|
||||
"""Return an Icon for a plain or visited link sound from the current theme."""
|
||||
|
||||
themeName = themeName or self.app.getSettingsManager().getSetting('soundTheme') or 'default'
|
||||
if themeName == THEME_NONE:
|
||||
return None
|
||||
|
||||
candidates = ["link"]
|
||||
if visited:
|
||||
candidates.insert(0, "link_visited")
|
||||
|
||||
for candidate in candidates:
|
||||
soundPath = self.getSoundPath(themeName, candidate)
|
||||
if soundPath:
|
||||
return Icon(os.path.dirname(soundPath), os.path.basename(soundPath))
|
||||
|
||||
return None
|
||||
|
||||
def getRoleStateSoundIcon(self, role, stateKey, themeName=None):
|
||||
"""Return an Icon for the role/state sound from the current theme, if any."""
|
||||
themeName = themeName or self.app.getSettingsManager().getSetting('soundTheme') or 'default'
|
||||
|
||||
@@ -118,6 +118,16 @@ class SpeechGenerator(generator.Generator):
|
||||
def __init__(self, script):
|
||||
generator.Generator.__init__(self, script, "speech")
|
||||
|
||||
def _getRoleSoundIcon(self, obj, role):
|
||||
"""Return the themed sound icon for obj's role, if any."""
|
||||
|
||||
if role == Atspi.Role.LINK or AXUtilities.is_link(obj):
|
||||
return sound_theme_manager.getManager().getLinkSoundIcon(
|
||||
visited=AXUtilities.is_visited(obj)
|
||||
)
|
||||
|
||||
return sound_theme_manager.getManager().getRoleSoundIcon(role)
|
||||
|
||||
def _addGlobals(self, globalsDict):
|
||||
"""Other things to make available from the formatting string.
|
||||
"""
|
||||
@@ -676,7 +686,7 @@ class SpeechGenerator(generator.Generator):
|
||||
roleSoundIcon = None
|
||||
if roleSoundPresentation != settings.ROLE_SOUND_PRESENTATION_SPEECH_ONLY \
|
||||
and soundEnabled:
|
||||
roleSoundIcon = sound_theme_manager.getManager().getRoleSoundIcon(role)
|
||||
roleSoundIcon = self._getRoleSoundIcon(obj, role)
|
||||
if roleSoundPresentation == settings.ROLE_SOUND_PRESENTATION_SOUND_ONLY \
|
||||
and soundEnabled:
|
||||
# Stateful controls present their role via state sounds; suppress
|
||||
@@ -1442,9 +1452,10 @@ class SpeechGenerator(generator.Generator):
|
||||
args.pop("string")
|
||||
|
||||
voice = self.voice(string=string, obj=obj, **args)
|
||||
string = self._script.utilities.adjustForLinks(obj, string, start)
|
||||
string, linkIcons = self._script.utilities.getLinkIndicatorPresentation(obj, string, start)
|
||||
rv = [self._script.utilities.adjustForRepeats(string)]
|
||||
rv.extend(voice)
|
||||
rv.extend(linkIcons)
|
||||
|
||||
# TODO - JD: speech.speak() has a bug which causes a list of utterances to
|
||||
# be presented before a string+voice pair that comes first. Until we can
|
||||
|
||||
Reference in New Issue
Block a user