Fixed some errors with plugins.

This commit is contained in:
Storm Dragon 2025-04-03 20:56:48 -04:00
parent d6a373c726
commit df7f4c5e62
8 changed files with 198 additions and 86 deletions

View File

@ -18,9 +18,15 @@ from enum import IntEnum
try: try:
import pluggy import pluggy
PLUGGY_AVAILABLE = True PLUGGY_AVAILABLE = True
logging.getLogger(__name__).info("Pluggy loaded successfully")
except ImportError: except ImportError:
PLUGGY_AVAILABLE = False PLUGGY_AVAILABLE = False
logging.getLogger(__name__).info("Pluggy not available, plugins will be disabled") logging.getLogger(__name__).warning("Pluggy not available (ImportError), plugins will be disabled")
except Exception as e:
PLUGGY_AVAILABLE = False
logging.getLogger(__name__).warning(f"Error loading pluggy: {str(e)}, plugins will be disabled")
import traceback
logging.getLogger(__name__).debug(traceback.format_exc())
# Set to True for more detailed plugin loading debug info # Set to True for more detailed plugin loading debug info
PLUGIN_DEBUG = True PLUGIN_DEBUG = True
@ -292,6 +298,11 @@ class PluginSystemManager:
"""Sync the active state of all plugins.""" """Sync the active state of all plugins."""
logger.info("Syncing active state of all plugins") logger.info("Syncing active state of all plugins")
# Skip if pluggy is not available
if not PLUGGY_AVAILABLE:
logger.warning("Pluggy not available, skipping plugin sync")
return
# Log plugin status before syncing # Log plugin status before syncing
if PLUGIN_DEBUG: if PLUGIN_DEBUG:
for pluginInfo in self.plugins: for pluginInfo in self.plugins:
@ -299,24 +310,29 @@ class PluginSystemManager:
is_loaded = pluginInfo.loaded is_loaded = pluginInfo.loaded
logger.debug(f"Plugin {pluginInfo.get_module_name()}: active={is_active}, loaded={is_loaded}") logger.debug(f"Plugin {pluginInfo.get_module_name()}: active={is_active}, loaded={is_loaded}")
# First unload inactive plugins try:
for pluginInfo in self.plugins: # First unload inactive plugins
if not self.isPluginActive(pluginInfo) and pluginInfo.loaded: for pluginInfo in self.plugins:
logger.info(f"Unloading inactive plugin: {pluginInfo.get_module_name()}") if not self.isPluginActive(pluginInfo) and pluginInfo.loaded:
self.unloadPlugin(pluginInfo) logger.info(f"Unloading inactive plugin: {pluginInfo.get_module_name()}")
self.unloadPlugin(pluginInfo)
# Then load active plugins # Then load active plugins
for pluginInfo in self.plugins: for pluginInfo in self.plugins:
if self.isPluginActive(pluginInfo) and not pluginInfo.loaded: if self.isPluginActive(pluginInfo) and not pluginInfo.loaded:
logger.info(f"Loading active plugin: {pluginInfo.get_module_name()}") logger.info(f"Loading active plugin: {pluginInfo.get_module_name()}")
result = self.loadPlugin(pluginInfo) result = self.loadPlugin(pluginInfo)
logger.info(f"Plugin {pluginInfo.get_module_name()} load result: {result}") logger.info(f"Plugin {pluginInfo.get_module_name()} load result: {result}")
# Log final plugin status # Log final plugin status
active_plugins = [p.get_module_name() for p in self.plugins if p.loaded] active_plugins = [p.get_module_name() for p in self.plugins if p.loaded]
logger.info(f"Active plugins after sync: {active_plugins}") logger.info(f"Active plugins after sync: {active_plugins}")
inactive_plugins = [p.get_module_name() for p in self.plugins if not p.loaded] inactive_plugins = [p.get_module_name() for p in self.plugins if not p.loaded]
logger.info(f"Inactive plugins after sync: {inactive_plugins}") logger.info(f"Inactive plugins after sync: {inactive_plugins}")
except Exception as e:
logger.error(f"Error syncing plugins: {e}")
import traceback
logger.error(traceback.format_exc())
def loadPlugin(self, pluginInfo): def loadPlugin(self, pluginInfo):
"""Load a plugin.""" """Load a plugin."""

View File

@ -26,13 +26,14 @@
from cthulhu import plugin from cthulhu import plugin
import gi, os import gi, os
gi.require_version('Peas', '1.0')
from gi.repository import GObject from gi.repository import GObject
from gi.repository import Peas import logging
logger = logging.getLogger(__name__)
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk from gi.repository import Gtk, Gdk
class Clipboard(GObject.Object, Peas.Activatable, plugin.Plugin): class Clipboard(GObject.Object, plugin.Plugin):
#__gtype_name__ = 'Clipboard' #__gtype_name__ = 'Clipboard'
object = GObject.Property(type=GObject.Object) object = GObject.Property(type=GObject.Object)

View File

@ -26,11 +26,11 @@
from cthulhu import plugin from cthulhu import plugin
import gi, time import gi, time
gi.require_version('Peas', '1.0')
from gi.repository import GObject from gi.repository import GObject
from gi.repository import Peas import logging
class Date(GObject.Object, Peas.Activatable, plugin.Plugin): logger = logging.getLogger(__name__)
class Date(GObject.Object, plugin.Plugin):
#__gtype_name__ = 'Date' #__gtype_name__ = 'Date'
object = GObject.Property(type=GObject.Object) object = GObject.Property(type=GObject.Object)

View File

@ -26,23 +26,41 @@
from cthulhu import plugin from cthulhu import plugin
import gi import gi
gi.require_version('Peas', '1.0')
from gi.repository import GObject from gi.repository import GObject
from gi.repository import Peas import logging
class HelloCthulhu(GObject.Object, Peas.Activatable, plugin.Plugin): logger = logging.getLogger(__name__)
class HelloCthulhu(GObject.Object, plugin.Plugin):
#__gtype_name__ = 'HelloCthulhu' #__gtype_name__ = 'HelloCthulhu'
object = GObject.Property(type=GObject.Object) object = GObject.Property(type=GObject.Object)
def __init__(self): def __init__(self):
plugin.Plugin.__init__(self) plugin.Plugin.__init__(self)
def do_activate(self): @plugin.cthulhu_hookimpl
API = self.object def activate(self, plugin=None):
self.connectSignal("start-application-completed", self.process) # Skip if this activation call isn't for us
def do_deactivate(self): if plugin is not None and plugin is not self:
API = self.object return
def do_update_state(self):
API = self.object logger.info("Activating HelloCthulhu plugin")
try:
if self.app:
signal_manager = self.app.getSignalManager()
signal_manager.connectSignal("start-application-completed", self.process, "default")
else:
logger.error("No app reference available")
except Exception as e:
logger.error(f"Error activating HelloCthulhu plugin: {e}")
@plugin.cthulhu_hookimpl
def deactivate(self, plugin=None):
# Skip if this deactivation call isn't for us
if plugin is not None and plugin is not self:
return
logger.info("Deactivating HelloCthulhu plugin")
# No specific cleanup needed
def process(self, app): def process(self, app):
messages = app.getDynamicApiManager().getAPI('Messages') messages = app.getDynamicApiManager().getAPI('Messages')
app.getDynamicApiManager().getAPI('CthulhuState').activeScript.presentMessage(messages.START_CTHULHU, resetStyles=False) app.getDynamicApiManager().getAPI('CthulhuState').activeScript.presentMessage(messages.START_CTHULHU, resetStyles=False)

View File

@ -35,9 +35,10 @@ __license__ = "LGPL"
from cthulhu import plugin from cthulhu import plugin
import gi, math, time import gi, math, time
gi.require_version('Peas', '1.0')
from gi.repository import GObject from gi.repository import GObject
from gi.repository import Peas import logging
logger = logging.getLogger(__name__)
gi.require_version("Atspi", "2.0") gi.require_version("Atspi", "2.0")
from gi.repository import Atspi from gi.repository import Atspi
@ -68,7 +69,7 @@ AXUtilities = None
keybindings = None keybindings = None
input_event = None input_event = None
class MouseReview(GObject.Object, Peas.Activatable, plugin.Plugin): class MouseReview(GObject.Object, plugin.Plugin):
#__gtype_name__ = 'MouseReview' #__gtype_name__ = 'MouseReview'
object = GObject.Property(type=GObject.Object) object = GObject.Property(type=GObject.Object)

View File

@ -26,25 +26,43 @@
from cthulhu import plugin from cthulhu import plugin
import gi import gi
gi.require_version('Peas', '1.0')
from gi.repository import GObject from gi.repository import GObject
from gi.repository import Peas import logging
import PluginManagerUi import PluginManagerUi
class PluginManager(GObject.Object, Peas.Activatable, plugin.Plugin): logger = logging.getLogger(__name__)
class PluginManager(GObject.Object, plugin.Plugin):
#__gtype_name__ = 'PluginManager' #__gtype_name__ = 'PluginManager'
object = GObject.Property(type=GObject.Object) object = GObject.Property(type=GObject.Object)
def __init__(self): def __init__(self):
plugin.Plugin.__init__(self) plugin.Plugin.__init__(self)
self.pluginManagerUi = None self.pluginManagerUi = None
def do_activate(self): @plugin.cthulhu_hookimpl
API = self.object def activate(self, plugin=None):
self.registerGestureByString(self.startPluginManagerUi, _('plugin manager'), 'kb:cthulhu+e') # Skip if this activation call isn't for us
if plugin is not None and plugin is not self:
def do_deactivate(self): return
API = self.object
logger.info("Activating PluginManager plugin")
try:
if self.app:
self.registerGestureByString(self.startPluginManagerUi, 'plugin manager', 'kb:cthulhu+e')
else:
logger.error("No app reference available")
except Exception as e:
logger.error(f"Error activating PluginManager plugin: {e}")
@plugin.cthulhu_hookimpl
def deactivate(self, plugin=None):
# Skip if this deactivation call isn't for us
if plugin is not None and plugin is not self:
return
logger.info("Deactivating PluginManager plugin")
# No specific cleanup needed
def startPluginManagerUi(self, script=None, inputEvent=None): def startPluginManagerUi(self, script=None, inputEvent=None):
self.showUI() self.showUI()

View File

@ -25,7 +25,7 @@
from cthulhu import plugin from cthulhu import plugin
from gi.repository import GObject, Peas from gi.repository import GObject
import glob import glob
import os import os
import importlib.util import importlib.util
@ -33,21 +33,23 @@ import random
import string import string
import _thread import _thread
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
import logging
logger = logging.getLogger(__name__)
settings = None settings = None
speech = None speech = None
braille = None braille = None
input_event = None input_event = None
def outputMessage( Message): def outputMessage(Message):
if (settings.enableSpeech): if (settings and settings.enableSpeech and speech):
speech.speak(Message) speech.speak(Message)
if (settings.enableBraille): if (settings and settings.enableBraille and braille):
braille.displayMessage(Message) braille.displayMessage(Message)
class SimplePluginSystem(GObject.Object, Peas.Activatable, plugin.Plugin): class SimplePluginSystem(GObject.Object, plugin.Plugin):
__gtype_name__ = 'SimplePluginSystem' __gtype_name__ = 'SimplePluginSystem'
object = GObject.Property(type=GObject.Object)
def __init__(self): def __init__(self):
plugin.Plugin.__init__(self) plugin.Plugin.__init__(self)
@ -55,27 +57,56 @@ class SimplePluginSystem(GObject.Object, Peas.Activatable, plugin.Plugin):
self.loaded = False self.loaded = False
self.plugin_repo = os.path.expanduser('~') + "/.local/share/cthulhu/simple-plugins-enabled/" self.plugin_repo = os.path.expanduser('~') + "/.local/share/cthulhu/simple-plugins-enabled/"
def do_activate(self): @plugin.cthulhu_hookimpl
API = self.object def activate(self, plugin=None):
global settings """Activate the plugin."""
global speech # Skip if this activation call isn't for us
global braille if plugin is not None and plugin is not self:
global input_event return
settings = API.app.getDynamicApiManager().getAPI('Settings')
speech = API.app.getDynamicApiManager().getAPI('Speech') logger.info("Activating SimplePluginSystem plugin")
braille = API.app.getDynamicApiManager().getAPI('Braille')
input_event = API.app.getDynamicApiManager().getAPI('InputEvent') try:
"""Required method for plugins""" global settings
if not self.loaded: global speech
self.load_plugins() global braille
global input_event
if self.app:
settings = self.app.getDynamicApiManager().getAPI('Settings')
speech = self.app.getDynamicApiManager().getAPI('Speech')
braille = self.app.getDynamicApiManager().getAPI('Braille')
input_event = self.app.getDynamicApiManager().getAPI('InputEvent')
if not self.loaded:
self.load_plugins()
else:
logger.error("SimplePluginSystem: No app reference available")
except Exception as e:
logger.error(f"Error activating SimplePluginSystem plugin: {e}")
import traceback
logger.error(traceback.format_exc())
def do_deactivate(self): @plugin.cthulhu_hookimpl
"""Required method for plugins""" def deactivate(self, plugin=None):
# Remove all registered keybindings """Deactivate the plugin."""
for plugin in self.plugin_list: # Skip if this deactivation call isn't for us
self.unregisterShortcut(plugin['function'], plugin['shortcut']) if plugin is not None and plugin is not self:
self.loaded = False return
self.plugin_list = []
logger.info("Deactivating SimplePluginSystem plugin")
try:
# Remove all registered keybindings
for plugin in self.plugin_list:
if 'function' in plugin and 'shortcut' in plugin:
self.unregisterShortcut(plugin['function'], plugin['shortcut'])
self.loaded = False
self.plugin_list = []
except Exception as e:
logger.error(f"Error deactivating SimplePluginSystem plugin: {e}")
import traceback
logger.error(traceback.format_exc())
def SetupShortcutAndHandle(self, currPluginSetting): def SetupShortcutAndHandle(self, currPluginSetting):
shortcut = '' shortcut = ''

View File

@ -24,30 +24,57 @@
# Original source: https://gitlab.gnome.org/GNOME/orca # Original source: https://gitlab.gnome.org/GNOME/orca
import gi, time import gi, time
gi.require_version('Peas', '1.0')
from gi.repository import GObject from gi.repository import GObject
from gi.repository import Peas import logging
from cthulhu import plugin from cthulhu import plugin
class Time(GObject.Object, Peas.Activatable, plugin.Plugin): logger = logging.getLogger(__name__)
class Time(GObject.Object, plugin.Plugin):
#__gtype_name__ = 'Time' #__gtype_name__ = 'Time'
object = GObject.Property(type=GObject.Object) object = GObject.Property(type=GObject.Object)
def __init__(self): def __init__(self):
plugin.Plugin.__init__(self) plugin.Plugin.__init__(self)
def do_activate(self): @plugin.cthulhu_hookimpl
API = self.object def activate(self, plugin=None):
self.connectSignal("setup-inputeventhandlers-completed", self.setupCompatBinding) # Skip if this activation call isn't for us
if plugin is not None and plugin is not self:
return
logger.info("Activating Time plugin")
try:
if self.app:
signal_manager = self.app.getSignalManager()
signal_manager.connectSignal("setup-inputeventhandlers-completed", self.setupCompatBinding, "default")
else:
logger.error("No app reference available")
except Exception as e:
logger.error(f"Error activating Time plugin: {e}")
def setupCompatBinding(self, app): def setupCompatBinding(self, app):
cmdnames = app.getDynamicApiManager().getAPI('Cmdnames') try:
inputEventHandlers = app.getDynamicApiManager().getAPI('inputEventHandlers') cmdnames = app.getDynamicApiManager().getAPI('Cmdnames')
inputEventHandlers['presentTimeHandler'] = app.getAPIHelper().createInputEventHandler(self.presentTime, cmdnames.PRESENT_CURRENT_TIME) inputEventHandlers = app.getDynamicApiManager().getAPI('inputEventHandlers')
def do_deactivate(self): inputEventHandlers['presentTimeHandler'] = app.getAPIHelper().createInputEventHandler(self.presentTime, cmdnames.PRESENT_CURRENT_TIME)
API = self.object except Exception as e:
inputEventHandlers = API.app.getDynamicApiManager().getAPI('inputEventHandlers') logger.error(f"Error setting up Time plugin: {e}")
del inputEventHandlers['presentTimeHandler']
def do_update_state(self): @plugin.cthulhu_hookimpl
def deactivate(self, plugin=None):
# Skip if this deactivation call isn't for us
if plugin is not None and plugin is not self:
return
logger.info("Deactivating Time plugin")
try:
if self.app:
inputEventHandlers = self.app.getDynamicApiManager().getAPI('inputEventHandlers')
if 'presentTimeHandler' in inputEventHandlers:
del inputEventHandlers['presentTimeHandler']
except Exception as e:
logger.error(f"Error deactivating Time plugin: {e}")
API = self.object API = self.object
def presentTime(self, script=None, inputEvent=None): def presentTime(self, script=None, inputEvent=None):
""" Presents the current time. """ """ Presents the current time. """