Hopefully fixed an error in simple plugin system.

This commit is contained in:
Storm Dragon 2025-04-04 16:33:53 -04:00
parent 35a83327ac
commit 1b4c4916e3

View File

@ -1,6 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# #
# Copyright (c) 2024 Stormux # Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
# #
# This library is free software; you can redistribute it and/or # This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public # modify it under the terms of the GNU Lesser General Public
@ -17,6 +20,8 @@
# Free Software Foundation, Inc., Franklin Street, Fifth Floor, # Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA. # Boston MA 02110-1301 USA.
# #
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Simple Plugin System for Cthulhu.""" """Simple Plugin System for Cthulhu."""
@ -28,9 +33,13 @@ import string
import _thread import _thread
import logging import logging
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
import gettext
from cthulhu.plugin import Plugin, cthulhu_hookimpl from cthulhu.plugin import Plugin, cthulhu_hookimpl
# Set up translation function
_ = gettext.gettext
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Global variables for API access # Global variables for API access
@ -48,11 +57,11 @@ def outputMessage(Message):
class SimplePluginSystem(Plugin): class SimplePluginSystem(Plugin):
"""Simple plugin system implementation for Cthulhu. """Simple plugin system implementation for Cthulhu.
This plugin allows loading and managing simple script-based plugins This plugin allows loading and managing simple script-based plugins
from a designated directory. from a designated directory.
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Initialize the plugin system.""" """Initialize the plugin system."""
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -68,22 +77,22 @@ class SimplePluginSystem(Plugin):
# Skip if this activation call isn't for us # Skip if this activation call isn't for us
if plugin is not None and plugin is not self: if plugin is not None and plugin is not self:
return return
logger.info("Activating SimplePluginSystem plugin") logger.info("Activating SimplePluginSystem plugin")
try: try:
global settings global settings
global speech global speech
global braille global braille
global input_event global input_event
settings = self.app.getDynamicApiManager().getAPI('Settings') settings = self.app.getDynamicApiManager().getAPI('Settings')
speech = self.app.getDynamicApiManager().getAPI('Speech') speech = self.app.getDynamicApiManager().getAPI('Speech')
braille = self.app.getDynamicApiManager().getAPI('Braille') braille = self.app.getDynamicApiManager().getAPI('Braille')
input_event = self.app.getDynamicApiManager().getAPI('InputEvent') input_event = self.app.getDynamicApiManager().getAPI('InputEvent')
if not self.loaded: if not self.loaded:
self.load_plugins() self.load_plugins()
except Exception as e: except Exception as e:
logger.error(f"Error activating SimplePluginSystem plugin: {e}") logger.error(f"Error activating SimplePluginSystem plugin: {e}")
@ -93,7 +102,7 @@ class SimplePluginSystem(Plugin):
# Skip if this deactivation call isn't for us # Skip if this deactivation call isn't for us
if plugin is not None and plugin is not self: if plugin is not None and plugin is not self:
return return
logger.info("Deactivating SimplePluginSystem plugin") logger.info("Deactivating SimplePluginSystem plugin")
try: try:
# Remove all registered keybindings # Remove all registered keybindings
@ -128,7 +137,15 @@ class SimplePluginSystem(Plugin):
if shortcut != '': if shortcut != '':
logger.debug(f"Registering shortcut: {shortcut}") logger.debug(f"Registering shortcut: {shortcut}")
currPluginSetting['shortcut'] = shortcut currPluginSetting['shortcut'] = shortcut
self.registerGestureByString(currPluginSetting['function'], _(currPluginSetting['pluginname']), shortcut) try:
# Try to use the translation function, fall back to plain text if it fails
plugin_name = _(currPluginSetting['pluginname'])
except Exception:
# If translation fails, use the original name
plugin_name = currPluginSetting['pluginname']
logger.warning(f"Translation failed for plugin: {currPluginSetting['pluginname']}")
self.registerGestureByString(currPluginSetting['function'], plugin_name, shortcut)
return currPluginSetting return currPluginSetting
def id_generator(self, size=7, chars=string.ascii_letters): def id_generator(self, size=7, chars=string.ascii_letters):
@ -237,7 +254,7 @@ class SimplePluginSystem(Plugin):
currPluginSetting['loadmodule'] = ('sopsproperty:loadmodule' in line.lower().replace(" ", "")) or currPluginSetting['loadmodule'] currPluginSetting['loadmodule'] = ('sopsproperty:loadmodule' in line.lower().replace(" ", "")) or currPluginSetting['loadmodule']
except Exception as e: except Exception as e:
logger.error(f"Error reading plugin file: {e}") logger.error(f"Error reading plugin file: {e}")
return currPluginSetting return currPluginSetting
def buildPluginSubprocess(self, currPluginSetting): def buildPluginSubprocess(self, currPluginSetting):
@ -298,13 +315,45 @@ class SimplePluginSystem(Plugin):
currPluginSetting['functionname'] = self.id_generator() currPluginSetting['functionname'] = self.id_generator()
return currPluginSetting return currPluginSetting
def registerGestureByString(self, function, description, shortcut):
"""Register a keyboard shortcut for a function.
This is a compatibility wrapper for the new plugin system.
"""
try:
# Try to get the InputEventManager and register the shortcut
input_manager = self.app.getDynamicApiManager().getAPI('InputEventManager')
if input_manager:
input_manager.registerGestureByString(function, description, shortcut)
logger.debug(f"Registered shortcut {shortcut} for {description}")
else:
logger.error("Could not get InputEventManager API")
except Exception as e:
logger.error(f"Error registering shortcut {shortcut}: {e}")
def unregisterShortcut(self, function, shortcut):
"""Unregister a keyboard shortcut for a function.
This is a compatibility wrapper for the new plugin system.
"""
try:
# Try to get the InputEventManager and unregister the shortcut
input_manager = self.app.getDynamicApiManager().getAPI('InputEventManager')
if input_manager:
input_manager.unregisterGestureByString(shortcut)
logger.debug(f"Unregistered shortcut {shortcut}")
else:
logger.error("Could not get InputEventManager API")
except Exception as e:
logger.error(f"Error unregistering shortcut {shortcut}: {e}")
def load_plugins(self): def load_plugins(self):
"""Load and setup all plugins in the plugin repository.""" """Load and setup all plugins in the plugin repository."""
if not self.loaded: if not self.loaded:
try: try:
logger.info(f"Loading plugins from {self.plugin_repo}") logger.info(f"Loading plugins from {self.plugin_repo}")
self.plugin_list = glob.glob(self.plugin_repo + '*') self.plugin_list = glob.glob(self.plugin_repo + '*')
for currplugin in self.plugin_list: for currplugin in self.plugin_list:
try: try:
currPluginSetting = self.initSettings() currPluginSetting = self.initSettings()
@ -313,30 +362,30 @@ class SimplePluginSystem(Plugin):
if not currPluginSetting['valid']: if not currPluginSetting['valid']:
logger.debug(f"Skipping invalid plugin: {currplugin}") logger.debug(f"Skipping invalid plugin: {currplugin}")
continue continue
currPluginSetting = self.getFunctionName(currPluginSetting) currPluginSetting = self.getFunctionName(currPluginSetting)
if currPluginSetting['loadmodule']: if currPluginSetting['loadmodule']:
exec(self.buildPluginExec(currPluginSetting)) # load as python module exec(self.buildPluginExec(currPluginSetting)) # load as python module
else: else:
exec(self.buildPluginSubprocess(currPluginSetting)) # run as subprocess exec(self.buildPluginSubprocess(currPluginSetting)) # run as subprocess
if currPluginSetting['blockcall']: if currPluginSetting['blockcall']:
currPluginSetting['function'] = globals()[currPluginSetting['functionname']] # non threaded currPluginSetting['function'] = globals()[currPluginSetting['functionname']] # non threaded
else: else:
currPluginSetting['function'] = globals()[currPluginSetting['functionname'] + "T"] # T = Threaded currPluginSetting['function'] = globals()[currPluginSetting['functionname'] + "T"] # T = Threaded
if currPluginSetting['exec']: # exec on load if we want if currPluginSetting['exec']: # exec on load if we want
currPluginSetting['function']() currPluginSetting['function']()
if not currPluginSetting['key'] == '': if not currPluginSetting['key'] == '':
currPluginSetting = self.SetupShortcutAndHandle(currPluginSetting) currPluginSetting = self.SetupShortcutAndHandle(currPluginSetting)
logger.debug(f"Loaded plugin: {currPluginSetting['pluginname']}") logger.debug(f"Loaded plugin: {currPluginSetting['pluginname']}")
self.plugin_list.append(currPluginSetting) # store in a list self.plugin_list.append(currPluginSetting) # store in a list
except Exception as e: except Exception as e:
logger.error(f"Error loading plugin {currplugin}: {e}") logger.error(f"Error loading plugin {currplugin}: {e}")
self.loaded = True self.loaded = True
logger.info(f"Loaded {len(self.plugin_list)} plugins") logger.info(f"Loaded {len(self.plugin_list)} plugins")
except Exception as e: except Exception as e: