From 0580dda131b3a4ea01f36e4a86f26d2c26ae7e16 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Fri, 11 Apr 2025 13:17:26 -0400 Subject: [PATCH 01/37] A few documentation updates. --- QUICKSTART | 1 - README.md | 8 ++++++-- src/cthulhu/cthulhuVersion.py | 2 +- src/cthulhu/plugins/self_voice/plugin.py | 5 ----- 4 files changed, 7 insertions(+), 9 deletions(-) delete mode 100644 QUICKSTART diff --git a/QUICKSTART b/QUICKSTART deleted file mode 100644 index f616c62..0000000 --- a/QUICKSTART +++ /dev/null @@ -1 +0,0 @@ -See http://wiki.gnome.org/Projects/Cthulhu diff --git a/README.md b/README.md index bcb98e8..7c9b215 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # Cthulhu +## Note + +If you somehow stumbled across this while looking for a desktop screen reader for Linux, you most likely want [Orca](https://orca.gnome.org/) instead. Cthulhu is currently a supplemental screen reader that fills a nitch for some advanced users. E.g. some older QT based programs may work with Cthulhu, and if you use certain window managers like i3, Mozilla applications like Firefox and Thunderbird may work better. + + ## Introduction Cthulhu is a free, open source, flexible, and extensible screen reader @@ -20,7 +25,7 @@ Cthulhu has the following dependencies: * Python 3 - Python platform * pygobject-3.0 - Python bindings for the GObject library -* libpeas - GObject based Plugin engine +* pluggy - Plugin and hook calling mechanisms for python * gtk+-3.0 - GTK+ toolkit * json-py - a JSON () reader and writer in Python * python-speechd - Python bindings for Speech Dispatcher (optional) @@ -30,7 +35,6 @@ Cthulhu has the following dependencies: * py-setproctitle - Python library to set the process title (optional) * gstreamer-1.0 - GStreamer - Streaming media framework (optional) * socat - Used for self-voicing functionality. -* libpeas - For the plugin system. You are strongly encouraged to also have the latest stable versions of AT-SPI2 and ATK. diff --git a/src/cthulhu/cthulhuVersion.py b/src/cthulhu/cthulhuVersion.py index 030b4bd..83cd4eb 100644 --- a/src/cthulhu/cthulhuVersion.py +++ b/src/cthulhu/cthulhuVersion.py @@ -23,5 +23,5 @@ # Fork of Orca Screen Reader (GNOME) # Original source: https://gitlab.gnome.org/GNOME/orca -version = "2025.04.04" +version = "2025.04.11" codeName = "testing" diff --git a/src/cthulhu/plugins/self_voice/plugin.py b/src/cthulhu/plugins/self_voice/plugin.py index 324f15b..5a52d55 100644 --- a/src/cthulhu/plugins/self_voice/plugin.py +++ b/src/cthulhu/plugins/self_voice/plugin.py @@ -1,9 +1,6 @@ #!/usr/bin/env python3 # # 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 # modify it under the terms of the GNU Lesser General Public @@ -20,8 +17,6 @@ # Free Software Foundation, Inc., Franklin Street, Fifth Floor, # Boston MA 02110-1301 USA. # -# Fork of Orca Screen Reader (GNOME) -# Original source: https://gitlab.gnome.org/GNOME/orca """Self Voice plugin for Cthulhu screen reader.""" From 0347b7feea35cc489883a00a5619cda551a43ca2 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Mon, 14 Apr 2025 04:54:48 -0400 Subject: [PATCH 02/37] Another attempt at fixing plugin keyboard shortcuts. --- src/cthulhu/cthulhu.py | 57 +++++++++++++++++++++++------------ src/cthulhu/cthulhuVersion.py | 2 +- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/cthulhu/cthulhu.py b/src/cthulhu/cthulhu.py index ed26293..ff3844e 100644 --- a/src/cthulhu/cthulhu.py +++ b/src/cthulhu/cthulhu.py @@ -47,11 +47,9 @@ class APIHelper: self.app = app self._gestureBindings = {} - def registerGestureByString(self, function, name, gestureString, - inputEventType='default', normalizer='cthulhu', - learnModeEnabled=True, contextName=None): + def registerGestureByString(self, function, name, gestureString, inputEventType='default', normalizer='cthulhu', learnModeEnabled=True, contextName=None): """Register a gesture by string. - + Arguments: - function: the function to call when the gesture is performed - name: a human-readable name for this gesture @@ -60,23 +58,23 @@ class APIHelper: - normalizer: the normalizer to use - learnModeEnabled: whether this should be available in learn mode - contextName: the context for this gesture (e.g., plugin name) - + Returns the binding ID or None if registration failed """ if not gestureString.startswith("kb:"): return None - + # Extract the key portion from the gesture string key = gestureString.split(":", 1)[1] - + # Handle Cthulhu modifier specially if "cthulhu+" in key.lower(): from . import keybindings key_parts = key.lower().split("+") - + # Determine appropriate modifier mask modifiers = keybindings.CTHULHU_MODIFIER_MASK - + # Extract the final key (without modifiers) final_key = key_parts[-1] @@ -87,18 +85,23 @@ class APIHelper: modifiers = keybindings.CTHULHU_CTRL_MODIFIER_MASK elif "alt" in key_parts: modifiers = keybindings.CTHULHU_ALT_MODIFIER_MASK - + # Create a keybinding handler class GestureHandler: def __init__(self, function, description): self.function = function self.description = description - + def __call__(self, script, inputEvent): - return self.function(script, inputEvent) - + try: + return function(script, inputEvent) + except Exception as e: + import logging + logging.getLogger(__name__).error(f"Error in keybinding handler: {e}") + return True + handler = GestureHandler(function, name) - + # Register the binding with the active script from . import cthulhu_state if cthulhu_state.activeScript: @@ -108,20 +111,29 @@ class APIHelper: keybindings.defaultModifierMask, modifiers, handler) - bindings.add(binding) + # Add the binding to the active script + bindings.add(binding) + # Store binding for later reference if contextName not in self._gestureBindings: self._gestureBindings[contextName] = [] self._gestureBindings[contextName].append(binding) - + + # Register key grab at the system level + grab_ids = self.app.addKeyGrab(binding) + + # For later removal + if grab_ids: + binding._grab_ids = grab_ids + return binding - + return None def unregisterShortcut(self, binding, contextName=None): """Unregister a previously registered shortcut. - + Arguments: - binding: the binding to unregister - contextName: the context for this gesture @@ -131,11 +143,18 @@ class APIHelper: if cthulhu_state.activeScript: bindings = cthulhu_state.activeScript.getKeyBindings() bindings.remove(binding) + + # Remove key grab at system level + if hasattr(binding, '_grab_ids'): + for grab_id in binding._grab_ids: + self.app.removeKeyGrab(grab_id) - # Remove from our tracking + # Remove from tracking if contextName in self._gestureBindings: if binding in self._gestureBindings[contextName]: self._gestureBindings[contextName].remove(binding) + + import gi import importlib import os diff --git a/src/cthulhu/cthulhuVersion.py b/src/cthulhu/cthulhuVersion.py index 83cd4eb..f0a14c3 100644 --- a/src/cthulhu/cthulhuVersion.py +++ b/src/cthulhu/cthulhuVersion.py @@ -23,5 +23,5 @@ # Fork of Orca Screen Reader (GNOME) # Original source: https://gitlab.gnome.org/GNOME/orca -version = "2025.04.11" +version = "2025.04.14" codeName = "testing" From f01374d15e3ed64ea7a337a14f39f88796068608 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Mon, 14 Apr 2025 05:04:59 -0400 Subject: [PATCH 03/37] One more try before sleep. --- src/cthulhu/cthulhu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cthulhu/cthulhu.py b/src/cthulhu/cthulhu.py index ff3844e..a3a97be 100644 --- a/src/cthulhu/cthulhu.py +++ b/src/cthulhu/cthulhu.py @@ -121,7 +121,7 @@ class APIHelper: self._gestureBindings[contextName].append(binding) # Register key grab at the system level - grab_ids = self.app.addKeyGrab(binding) + grab_ids = cthulhu.addKeyGrab(binding) # For later removal if grab_ids: From ec9090605222ba6029052b97c5cff679538a6665 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Fri, 18 Apr 2025 13:00:31 -0400 Subject: [PATCH 04/37] Maybe finally solved the plugin keybinding issue... --- src/cthulhu/cthulhu.py | 2 +- src/cthulhu/cthulhuVersion.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cthulhu/cthulhu.py b/src/cthulhu/cthulhu.py index a3a97be..ff3844e 100644 --- a/src/cthulhu/cthulhu.py +++ b/src/cthulhu/cthulhu.py @@ -121,7 +121,7 @@ class APIHelper: self._gestureBindings[contextName].append(binding) # Register key grab at the system level - grab_ids = cthulhu.addKeyGrab(binding) + grab_ids = self.app.addKeyGrab(binding) # For later removal if grab_ids: diff --git a/src/cthulhu/cthulhuVersion.py b/src/cthulhu/cthulhuVersion.py index f0a14c3..794f76f 100644 --- a/src/cthulhu/cthulhuVersion.py +++ b/src/cthulhu/cthulhuVersion.py @@ -23,5 +23,5 @@ # Fork of Orca Screen Reader (GNOME) # Original source: https://gitlab.gnome.org/GNOME/orca -version = "2025.04.14" +version = "2025.04.18" codeName = "testing" From 9790a8d49463705631a482243ead751d92161cc9 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Fri, 18 Apr 2025 14:28:16 -0400 Subject: [PATCH 05/37] More attempts to fix keyboard. --- src/cthulhu/cthulhu.py | 58 ++++------ src/cthulhu/cthulhuVersion.py | 2 +- src/cthulhu/plugin.py | 19 +++- src/cthulhu/plugin_system_manager.py | 109 +++++++++++++++---- src/cthulhu/plugins/DisplayVersion/plugin.py | 6 +- 5 files changed, 133 insertions(+), 61 deletions(-) diff --git a/src/cthulhu/cthulhu.py b/src/cthulhu/cthulhu.py index ff3844e..d88e58c 100644 --- a/src/cthulhu/cthulhu.py +++ b/src/cthulhu/cthulhu.py @@ -37,47 +37,35 @@ import faulthandler class APIHelper: """Helper class for plugin API interactions, including keybindings.""" - + def __init__(self, app): """Initialize the APIHelper. - + Arguments: - app: the Cthulhu application """ self.app = app self._gestureBindings = {} - + def registerGestureByString(self, function, name, gestureString, inputEventType='default', normalizer='cthulhu', learnModeEnabled=True, contextName=None): - """Register a gesture by string. - - Arguments: - - function: the function to call when the gesture is performed - - name: a human-readable name for this gesture - - gestureString: string representation of the gesture (e.g., 'kb:cthulhu+z') - - inputEventType: the type of input event - - normalizer: the normalizer to use - - learnModeEnabled: whether this should be available in learn mode - - contextName: the context for this gesture (e.g., plugin name) - - Returns the binding ID or None if registration failed - """ + """Register a gesture by string.""" if not gestureString.startswith("kb:"): return None - + # Extract the key portion from the gesture string key = gestureString.split(":", 1)[1] - + # Handle Cthulhu modifier specially if "cthulhu+" in key.lower(): from . import keybindings key_parts = key.lower().split("+") - + # Determine appropriate modifier mask modifiers = keybindings.CTHULHU_MODIFIER_MASK - + # Extract the final key (without modifiers) final_key = key_parts[-1] - + # Check for additional modifiers if "shift" in key_parts: modifiers = keybindings.CTHULHU_SHIFT_MODIFIER_MASK @@ -85,13 +73,13 @@ class APIHelper: modifiers = keybindings.CTHULHU_CTRL_MODIFIER_MASK elif "alt" in key_parts: modifiers = keybindings.CTHULHU_ALT_MODIFIER_MASK - + # Create a keybinding handler class GestureHandler: def __init__(self, function, description): self.function = function self.description = description - + def __call__(self, script, inputEvent): try: return function(script, inputEvent) @@ -99,9 +87,9 @@ class APIHelper: import logging logging.getLogger(__name__).error(f"Error in keybinding handler: {e}") return True - + handler = GestureHandler(function, name) - + # Register the binding with the active script from . import cthulhu_state if cthulhu_state.activeScript: @@ -111,29 +99,29 @@ class APIHelper: keybindings.defaultModifierMask, modifiers, handler) - + # Add the binding to the active script bindings.add(binding) - + # Store binding for later reference if contextName not in self._gestureBindings: self._gestureBindings[contextName] = [] self._gestureBindings[contextName].append(binding) - + # Register key grab at the system level grab_ids = self.app.addKeyGrab(binding) - + # For later removal if grab_ids: binding._grab_ids = grab_ids - + return binding - + return None - + def unregisterShortcut(self, binding, contextName=None): """Unregister a previously registered shortcut. - + Arguments: - binding: the binding to unregister - contextName: the context for this gesture @@ -143,12 +131,12 @@ class APIHelper: if cthulhu_state.activeScript: bindings = cthulhu_state.activeScript.getKeyBindings() bindings.remove(binding) - + # Remove key grab at system level if hasattr(binding, '_grab_ids'): for grab_id in binding._grab_ids: self.app.removeKeyGrab(grab_id) - + # Remove from tracking if contextName in self._gestureBindings: if binding in self._gestureBindings[contextName]: diff --git a/src/cthulhu/cthulhuVersion.py b/src/cthulhu/cthulhuVersion.py index 794f76f..2f306d4 100644 --- a/src/cthulhu/cthulhuVersion.py +++ b/src/cthulhu/cthulhuVersion.py @@ -24,4 +24,4 @@ # Original source: https://gitlab.gnome.org/GNOME/orca version = "2025.04.18" -codeName = "testing" +codeName = "plugins" diff --git a/src/cthulhu/plugin.py b/src/cthulhu/plugin.py index 88a3080..6f7f7fb 100644 --- a/src/cthulhu/plugin.py +++ b/src/cthulhu/plugin.py @@ -21,7 +21,7 @@ except ImportError: # Fallback if pluggy is not available def cthulhu_hookimpl(func=None, **kwargs): """Fallback decorator when pluggy is not available. - + This is a no-op decorator that returns the original function. It allows the code to continue working without pluggy, though plugins will be disabled. @@ -47,6 +47,8 @@ class Plugin: self.name = '' self.version = '' self.description = '' + self._bindings = None + self._gestureBindings = {} def set_app(self, app): """Set the application reference.""" @@ -75,12 +77,16 @@ class Plugin: return logger.info(f"Deactivating plugin: {self.name}") + def get_bindings(self): + """Get keybindings for this plugin. Override in subclasses.""" + return self._bindings + def registerGestureByString(self, function, name, gestureString, learnModeEnabled=True): """Register a gesture by string.""" if self.app: api_helper = self.app.getAPIHelper() if api_helper: - return api_helper.registerGestureByString( + binding = api_helper.registerGestureByString( function, name, gestureString, @@ -89,4 +95,13 @@ class Plugin: learnModeEnabled, contextName=self.module_name ) + + # Also store the binding locally so get_bindings() can use it + if binding: + if not self._bindings: + from . import keybindings + self._bindings = keybindings.KeyBindings() + self._bindings.add(binding) + + return binding return None diff --git a/src/cthulhu/plugin_system_manager.py b/src/cthulhu/plugin_system_manager.py index 2d6d5cd..80fe9c5 100644 --- a/src/cthulhu/plugin_system_manager.py +++ b/src/cthulhu/plugin_system_manager.py @@ -112,11 +112,72 @@ class PluginSystemManager: # Create plugin directories self._setup_plugin_dirs() - + # Log available plugins directory paths logger.info(f"System plugins directory: {PluginType.SYSTEM.get_root_dir()}") logger.info(f"User plugins directory: {PluginType.USER.get_root_dir()}") + def register_plugin_global_keybindings(self, plugin): + """Register a plugin's keybindings with all scripts.""" + if not hasattr(plugin, 'get_bindings'): + return + + try: + bindings = plugin.get_bindings() + if not bindings or not bindings.keyBindings: + return + + logger.info(f"Registering global keybindings for plugin: {plugin.name}") + + # First register with the active script + from . import cthulhu_state + if cthulhu_state.activeScript: + active_script = cthulhu_state.activeScript + for binding in bindings.keyBindings: + active_script.getKeyBindings().add(binding) + grab_ids = self.app.addKeyGrab(binding) + if grab_ids: + binding._grab_ids = grab_ids + + # Store these bindings for future script changes + plugin_name = plugin.name or plugin.module_name + if not hasattr(self, '_plugin_global_bindings'): + self._plugin_global_bindings = {} + self._plugin_global_bindings[plugin_name] = bindings + + # Connect to script changes to ensure bindings work with all scripts + if not hasattr(self, '_connected_to_script_changes'): + signal_manager = self.app.getSignalManager() + if signal_manager: + signal_manager.connect('load-setting-completed', self._on_settings_changed) + self._connected_to_script_changes = True + except Exception as e: + logger.error(f"Error registering global keybindings for plugin {plugin.name}: {e}") + import traceback + logger.error(traceback.format_exc()) + + def _on_settings_changed(self): + """Re-register all plugin keybindings when settings change.""" + if not hasattr(self, '_plugin_global_bindings'): + return + + from . import cthulhu_state + if not cthulhu_state.activeScript: + return + + active_script = cthulhu_state.activeScript + for plugin_name, bindings in self._plugin_global_bindings.items(): + logger.info(f"Re-registering keybindings for plugin: {plugin_name}") + for binding in bindings.keyBindings: + # Check if binding already exists + if active_script.getKeyBindings().hasKeyBinding(binding, "keysNoMask"): + continue + + active_script.getKeyBindings().add(binding) + grab_ids = self.app.addKeyGrab(binding) + if grab_ids: + binding._grab_ids = grab_ids + def _setup_plugin_dirs(self): """Ensure plugin directories exist.""" os.makedirs(PluginType.SYSTEM.get_root_dir(), exist_ok=True) @@ -219,24 +280,24 @@ class PluginSystemManager: def setActivePlugins(self, activePlugins): """Set active plugins and sync their state.""" logger.info(f"Setting active plugins: {activePlugins}") - + # Make sure we have scanned for plugins first if not self._plugins: logger.info("No plugins found, rescanning...") self.rescanPlugins() - + self._active_plugins = activePlugins - + # Log active vs available plugins available_plugins = [p.get_module_name() for p in self.plugins] logger.info(f"Available plugins: {available_plugins}") logger.info(f"Active plugins: {self._active_plugins}") - + # Find missing plugins missing_plugins = [p for p in self._active_plugins if p not in available_plugins] if missing_plugins: logger.warning(f"Active plugins not found: {missing_plugins}") - + self.syncAllPluginsActive() def setPluginActive(self, pluginInfo, active): @@ -258,7 +319,7 @@ class PluginSystemManager: def isPluginActive(self, pluginInfo): """Check if a plugin is active.""" module_name = pluginInfo.get_module_name() - + # Builtin plugins are always active if pluginInfo.builtin: logger.debug(f"Plugin {module_name} is builtin, active by default") @@ -271,34 +332,34 @@ class PluginSystemManager: # Check case-insensitive match in active plugins list active_plugins = self.getActivePlugins() - + # Try exact match first if module_name in active_plugins: logger.debug(f"Plugin {module_name} found in active plugins list") return True - + # Try case-insensitive match module_name_lower = module_name.lower() is_active = any(plugin.lower() == module_name_lower for plugin in active_plugins) - + if is_active: logger.debug(f"Plugin {module_name} found in active plugins list (case-insensitive match)") else: logger.debug(f"Plugin {module_name} not found in active plugins list") - + return is_active def syncAllPluginsActive(self): """Sync the active state of all plugins.""" logger.info("Syncing active state of all plugins") - + # Log plugin status before syncing if PLUGIN_DEBUG: for pluginInfo in self.plugins: is_active = self.isPluginActive(pluginInfo) is_loaded = pluginInfo.loaded logger.debug(f"Plugin {pluginInfo.get_module_name()}: active={is_active}, loaded={is_loaded}") - + # First unload inactive plugins for pluginInfo in self.plugins: if not self.isPluginActive(pluginInfo) and pluginInfo.loaded: @@ -311,7 +372,7 @@ class PluginSystemManager: logger.info(f"Loading active plugin: {pluginInfo.get_module_name()}") result = self.loadPlugin(pluginInfo) logger.info(f"Plugin {pluginInfo.get_module_name()} load result: {result}") - + # Log final plugin status active_plugins = [p.get_module_name() for p in self.plugins if p.loaded] logger.info(f"Active plugins after sync: {active_plugins}") @@ -337,27 +398,27 @@ class PluginSystemManager: # Try to find the plugin file module_name = pluginInfo.get_module_name() plugin_dir = pluginInfo.get_module_dir() - + # Check for plugin.py first (standard format) plugin_file = os.path.join(plugin_dir, "plugin.py") - + # Fall back to [PluginName].py if plugin.py doesn't exist if not os.path.exists(plugin_file): alternative_plugin_file = os.path.join(plugin_dir, f"{module_name}.py") if os.path.exists(alternative_plugin_file): plugin_file = alternative_plugin_file logger.info(f"Using alternative plugin file: {alternative_plugin_file}") - + if not os.path.exists(plugin_file): logger.error(f"Plugin file not found: {plugin_file}") return False - + logger.info(f"Loading plugin from: {plugin_file}") spec = importlib.util.spec_from_file_location(module_name, plugin_file) if spec is None: logger.error(f"Failed to create spec for plugin: {module_name}") return False - + module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) pluginInfo.module = module @@ -385,7 +446,7 @@ class PluginSystemManager: # Ensure plugins have a reference to the app plugin_instance.app = self.getApp() logger.info(f"Set app reference for plugin: {module_name}") - + if hasattr(plugin_instance, 'set_app'): plugin_instance.set_app(self.getApp()) logger.info(f"Called set_app() for plugin: {module_name}") @@ -398,10 +459,10 @@ class PluginSystemManager: if self.plugin_manager is None: logger.error(f"Plugin manager is None when loading {module_name}") return False - + logger.info(f"Registering plugin with pluggy: {module_name}") self.plugin_manager.register(plugin_instance) - + try: logger.info(f"Activating plugin: {module_name}") self.plugin_manager.hook.activate(plugin=plugin_instance) @@ -413,6 +474,10 @@ class PluginSystemManager: pluginInfo.loaded = True logger.info(f"Successfully loaded plugin: {module_name}") + + # Register any global keybindings from the plugin + self.register_plugin_global_keybindings(pluginInfo.instance) + return True except Exception as e: diff --git a/src/cthulhu/plugins/DisplayVersion/plugin.py b/src/cthulhu/plugins/DisplayVersion/plugin.py index 9f7017b..75ab90a 100644 --- a/src/cthulhu/plugins/DisplayVersion/plugin.py +++ b/src/cthulhu/plugins/DisplayVersion/plugin.py @@ -40,8 +40,11 @@ class DisplayVersion(Plugin): f'Cthulhu screen reader version {cthulhuVersion.version}-{cthulhuVersion.codeName}', 'kb:cthulhu+shift+v' ) + logger.info(f"Registered keybinding: {self._kb_binding}") except Exception as e: logger.error(f"Error activating DisplayVersion plugin: {e}") + import traceback + logger.error(traceback.format_exc()) @cthulhu_hookimpl def deactivate(self, plugin=None): @@ -55,6 +58,7 @@ class DisplayVersion(Plugin): def speakText(self, script=None, inputEvent=None): """Speak the Cthulhu version when shortcut is pressed.""" try: + logger.info("DisplayVersion plugin: speakText called") if self.app: state = self.app.getDynamicApiManager().getAPI('CthulhuState') if state.activeScript: @@ -65,4 +69,4 @@ class DisplayVersion(Plugin): return True except Exception as e: logger.error(f"Error in DisplayVersion speakText: {e}") - return False \ No newline at end of file + return False From 80212d616f5bf34d56e0c7786bdff462775e2ec5 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Fri, 18 Apr 2025 14:42:02 -0400 Subject: [PATCH 06/37] Added some logging to try and figure out what's going on. --- src/cthulhu/cthulhu.py | 1 + src/cthulhu/keybindings.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/cthulhu/cthulhu.py b/src/cthulhu/cthulhu.py index d88e58c..997fb12 100644 --- a/src/cthulhu/cthulhu.py +++ b/src/cthulhu/cthulhu.py @@ -115,6 +115,7 @@ class APIHelper: if grab_ids: binding._grab_ids = grab_ids + logger.info(f"Created binding: {binding.keysymstring} with modifiers {binding.modifiers}") return binding return None diff --git a/src/cthulhu/keybindings.py b/src/cthulhu/keybindings.py index ec578a2..f7ba3b0 100644 --- a/src/cthulhu/keybindings.py +++ b/src/cthulhu/keybindings.py @@ -470,6 +470,8 @@ class KeyBindings: given keycode and modifiers, or None if no match exists. """ + logger.info(f"Looking for handler for key: {keyboardEvent.hw_code} with modifiers {keyboardEvent.modifiers}") + return binding matches = [] candidates = [] clickCount = keyboardEvent.getClickCount() From c64591a16230c21991b2f7f9902afa54b84c330e Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Fri, 18 Apr 2025 14:45:33 -0400 Subject: [PATCH 07/37] Fixed indentation error. --- src/cthulhu/keybindings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cthulhu/keybindings.py b/src/cthulhu/keybindings.py index f7ba3b0..eecd6a5 100644 --- a/src/cthulhu/keybindings.py +++ b/src/cthulhu/keybindings.py @@ -470,7 +470,7 @@ class KeyBindings: given keycode and modifiers, or None if no match exists. """ - logger.info(f"Looking for handler for key: {keyboardEvent.hw_code} with modifiers {keyboardEvent.modifiers}") + logger.info(f"Looking for handler for key: {keyboardEvent.hw_code} with modifiers {keyboardEvent.modifiers}") return binding matches = [] candidates = [] From 04b8592ed3bb3497a4f553c8392e8686476956d2 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Fri, 18 Apr 2025 14:53:52 -0400 Subject: [PATCH 08/37] Indentation error was worse than I thought. --- src/cthulhu/keybindings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cthulhu/keybindings.py b/src/cthulhu/keybindings.py index eecd6a5..3e6506b 100644 --- a/src/cthulhu/keybindings.py +++ b/src/cthulhu/keybindings.py @@ -470,8 +470,8 @@ class KeyBindings: given keycode and modifiers, or None if no match exists. """ - logger.info(f"Looking for handler for key: {keyboardEvent.hw_code} with modifiers {keyboardEvent.modifiers}") - return binding + logger.info(f"Looking for handler for key: {keyboardEvent.hw_code} with modifiers {keyboardEvent.modifiers}") + return binding matches = [] candidates = [] clickCount = keyboardEvent.getClickCount() From 331b1c3ad5d9f8b9ab61d72e645c37acd2d10431 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sat, 19 Apr 2025 14:01:20 -0400 Subject: [PATCH 09/37] More debugging efforts. --- src/cthulhu/cthulhuVersion.py | 2 +- src/cthulhu/keybindings.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cthulhu/cthulhuVersion.py b/src/cthulhu/cthulhuVersion.py index 2f306d4..c5d1f93 100644 --- a/src/cthulhu/cthulhuVersion.py +++ b/src/cthulhu/cthulhuVersion.py @@ -23,5 +23,5 @@ # Fork of Orca Screen Reader (GNOME) # Original source: https://gitlab.gnome.org/GNOME/orca -version = "2025.04.18" +version = "2025.04.19" codeName = "plugins" diff --git a/src/cthulhu/keybindings.py b/src/cthulhu/keybindings.py index 3e6506b..ef3c17b 100644 --- a/src/cthulhu/keybindings.py +++ b/src/cthulhu/keybindings.py @@ -45,6 +45,7 @@ from . import settings from . import cthulhu_state from .cthulhu_i18n import _ +logger = logging.getLogger(__name__) _keysymsCache = {} _keycodeCache = {} From 1e6f4b8913f1c5caa14ee64e8b66079e52216926 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sat, 19 Apr 2025 14:06:48 -0400 Subject: [PATCH 10/37] fixed import error. --- src/cthulhu/keybindings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cthulhu/keybindings.py b/src/cthulhu/keybindings.py index ef3c17b..fdb709c 100644 --- a/src/cthulhu/keybindings.py +++ b/src/cthulhu/keybindings.py @@ -45,6 +45,7 @@ from . import settings from . import cthulhu_state from .cthulhu_i18n import _ +import logging logger = logging.getLogger(__name__) _keysymsCache = {} From 3296e5d57168143590e06a7708423d89f4caef28 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sat, 19 Apr 2025 14:15:30 -0400 Subject: [PATCH 11/37] Fix broken method. --- src/cthulhu/keybindings.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cthulhu/keybindings.py b/src/cthulhu/keybindings.py index fdb709c..b8fae42 100644 --- a/src/cthulhu/keybindings.py +++ b/src/cthulhu/keybindings.py @@ -45,8 +45,6 @@ from . import settings from . import cthulhu_state from .cthulhu_i18n import _ -import logging -logger = logging.getLogger(__name__) _keysymsCache = {} _keycodeCache = {} @@ -472,15 +470,17 @@ class KeyBindings: given keycode and modifiers, or None if no match exists. """ + import logging + logger = logging.getLogger(__name__) logger.info(f"Looking for handler for key: {keyboardEvent.hw_code} with modifiers {keyboardEvent.modifiers}") - return binding + matches = [] candidates = [] clickCount = keyboardEvent.getClickCount() for keyBinding in self.keyBindings: if keyBinding.matches(keyboardEvent.hw_code, keyboardEvent.modifiers): if keyBinding.modifier_mask == keyboardEvent.modifiers and \ - keyBinding.click_count == clickCount: + keyBinding.click_count == clickCount: matches.append(keyBinding) # If there's no keysymstring, it's unbound and cannot be # a match. From 51984a65408d163b41e0164899e5b587e6afb4cc Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sat, 19 Apr 2025 14:41:17 -0400 Subject: [PATCH 12/37] Hopefully got this keybinding thing once and for all... Fingers crossed. --- src/cthulhu/cthulhu.py | 3 +++ src/cthulhu/plugin_system_manager.py | 28 ++++++++++++++++++++++++++++ src/cthulhu/script_manager.py | 8 ++++++++ 3 files changed, 39 insertions(+) diff --git a/src/cthulhu/cthulhu.py b/src/cthulhu/cthulhu.py index 997fb12..e22e693 100644 --- a/src/cthulhu/cthulhu.py +++ b/src/cthulhu/cthulhu.py @@ -638,6 +638,9 @@ def loadUserSettings(script=None, inputEvent=None, skipReloadMessage=False): _scriptManager.activate() _eventManager.activate() + cthulhuApp.getSignalManager().emitSignal('load-setting-begin') + cthulhuApp.getPluginSystemManager().register_plugin_keybindings_with_active_script() + cthulhuApp.getSignalManager().emitSignal('load-setting-completed') debug.printMessage(debug.LEVEL_INFO, 'CTHULHU: User Settings Loaded', True) diff --git a/src/cthulhu/plugin_system_manager.py b/src/cthulhu/plugin_system_manager.py index 80fe9c5..a0f756e 100644 --- a/src/cthulhu/plugin_system_manager.py +++ b/src/cthulhu/plugin_system_manager.py @@ -156,6 +156,34 @@ class PluginSystemManager: import traceback logger.error(traceback.format_exc()) + def register_plugin_keybindings_with_active_script(self): + """Register all plugin keybindings with the active script.""" + + if not PLUGGY_AVAILABLE: + return + + from . import cthulhu_state + if not cthulhu_state.activeScript: + logger.warning("No active script available to register plugin keybindings") + return + + active_script = cthulhu_state.activeScript + logger.info(f"Registering plugin keybindings with active script: {active_script}") + + for pluginInfo in self._plugins.values(): + if not pluginInfo.loaded or not pluginInfo.instance: + continue + + plugin = pluginInfo.instance + if not hasattr(plugin, 'get_bindings') or not plugin.get_bindings(): + continue + + logger.info(f"Registering keybindings for plugin: {plugin.name}") + bindings = plugin.get_bindings() + for binding in bindings.keyBindings: + logger.info(f"Adding binding: {binding.keysymstring} with modifiers {binding.modifiers}") + active_script.getKeyBindings().add(binding) + def _on_settings_changed(self): """Re-register all plugin keybindings when settings change.""" if not hasattr(self, '_plugin_global_bindings'): diff --git a/src/cthulhu/script_manager.py b/src/cthulhu/script_manager.py index c55fb23..0f184ef 100644 --- a/src/cthulhu/script_manager.py +++ b/src/cthulhu/script_manager.py @@ -319,6 +319,14 @@ class ScriptManager: newScript.activate() tokens = ["SCRIPT MANAGER: Setting active script to", newScript, "reason:", reason] + self._activeScript = newScript + + # Register plugin keybindings with the new active script + from . import cthulhu + plugin_manager = cthulhu.getPluginSystemManager() + if plugin_manager: + plugin_manager.register_plugin_keybindings_with_active_script() + debug.printTokens(debug.LEVEL_INFO, tokens, True) def _getScriptForAppReplicant(self, app): From 96335baf5d0817bb8d9fe68a5dd4bbf3e4aacc75 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sat, 19 Apr 2025 14:48:38 -0400 Subject: [PATCH 13/37] Fixed indentation issues. --- src/cthulhu/script_manager.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/cthulhu/script_manager.py b/src/cthulhu/script_manager.py index 0f184ef..b672166 100644 --- a/src/cthulhu/script_manager.py +++ b/src/cthulhu/script_manager.py @@ -318,15 +318,14 @@ class ScriptManager: return newScript.activate() + + # Register plugin keybindings with the new active script + from . import cthulhu + plugin_manager = cthulhu.getPluginSystemManager() + if plugin_manager: + plugin_manager.register_plugin_keybindings_with_active_script() + tokens = ["SCRIPT MANAGER: Setting active script to", newScript, "reason:", reason] - self._activeScript = newScript - - # Register plugin keybindings with the new active script - from . import cthulhu - plugin_manager = cthulhu.getPluginSystemManager() - if plugin_manager: - plugin_manager.register_plugin_keybindings_with_active_script() - debug.printTokens(debug.LEVEL_INFO, tokens, True) def _getScriptForAppReplicant(self, app): From 8b1f501fe781c4b1cccedbcc66f06d494c99f479 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sat, 19 Apr 2025 14:54:32 -0400 Subject: [PATCH 14/37] fixed an error. --- src/cthulhu/script_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cthulhu/script_manager.py b/src/cthulhu/script_manager.py index b672166..92fb5fc 100644 --- a/src/cthulhu/script_manager.py +++ b/src/cthulhu/script_manager.py @@ -321,7 +321,7 @@ class ScriptManager: # Register plugin keybindings with the new active script from . import cthulhu - plugin_manager = cthulhu.getPluginSystemManager() + plugin_manager = cthulhu.cthulhuApp.getPluginSystemManager() if plugin_manager: plugin_manager.register_plugin_keybindings_with_active_script() From 39dca0574a4aea19d8b063b630f4094ed00e2cba Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sat, 19 Apr 2025 15:39:43 -0400 Subject: [PATCH 15/37] Improve key detection in registerGestureByString --- src/cthulhu/cthulhu.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/cthulhu/cthulhu.py b/src/cthulhu/cthulhu.py index e22e693..d927d69 100644 --- a/src/cthulhu/cthulhu.py +++ b/src/cthulhu/cthulhu.py @@ -60,19 +60,16 @@ class APIHelper: from . import keybindings key_parts = key.lower().split("+") - # Determine appropriate modifier mask + # Start with the base Cthulhu modifier modifiers = keybindings.CTHULHU_MODIFIER_MASK # Extract the final key (without modifiers) final_key = key_parts[-1] - # Check for additional modifiers + # Check for additional modifiers and combine them properly if "shift" in key_parts: + # Use the pre-defined combined mask rather than trying to OR them modifiers = keybindings.CTHULHU_SHIFT_MODIFIER_MASK - elif "ctrl" in key_parts or "control" in key_parts: - modifiers = keybindings.CTHULHU_CTRL_MODIFIER_MASK - elif "alt" in key_parts: - modifiers = keybindings.CTHULHU_ALT_MODIFIER_MASK # Create a keybinding handler class GestureHandler: @@ -104,8 +101,8 @@ class APIHelper: bindings.add(binding) # Store binding for later reference - if contextName not in self._gestureBindings: - self._gestureBindings[contextName] = [] + if contextName not in self._gestureBindings: + self._gestureBindings[contextName] = [] self._gestureBindings[contextName].append(binding) # Register key grab at the system level From c376b2489a913f39c7f05f507e7cad3a6f6a509a Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 20 Apr 2025 02:30:04 -0400 Subject: [PATCH 16/37] Getting closer to working bindings. --- src/cthulhu/cthulhu.py | 16 ++++++++-------- src/cthulhu/cthulhuVersion.py | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cthulhu/cthulhu.py b/src/cthulhu/cthulhu.py index d927d69..e7e2420 100644 --- a/src/cthulhu/cthulhu.py +++ b/src/cthulhu/cthulhu.py @@ -103,17 +103,17 @@ class APIHelper: # Store binding for later reference if contextName not in self._gestureBindings: self._gestureBindings[contextName] = [] - self._gestureBindings[contextName].append(binding) + self._gestureBindings[contextName].append(binding) # This line should be outside the if block - # Register key grab at the system level - grab_ids = self.app.addKeyGrab(binding) + # Register key grab at the system level + grab_ids = self.app.addKeyGrab(binding) - # For later removal - if grab_ids: - binding._grab_ids = grab_ids + # For later removal + if grab_ids: + binding._grab_ids = grab_ids - logger.info(f"Created binding: {binding.keysymstring} with modifiers {binding.modifiers}") - return binding + logger.info(f"Created binding: {binding.keysymstring} with modifiers {binding.modifiers}") + return binding return None diff --git a/src/cthulhu/cthulhuVersion.py b/src/cthulhu/cthulhuVersion.py index c5d1f93..770781d 100644 --- a/src/cthulhu/cthulhuVersion.py +++ b/src/cthulhu/cthulhuVersion.py @@ -23,5 +23,5 @@ # Fork of Orca Screen Reader (GNOME) # Original source: https://gitlab.gnome.org/GNOME/orca -version = "2025.04.19" +version = "2025.04.20" codeName = "plugins" From d8df2ed757f0f6ffad6f96100578d4fba28ca4a5 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 20 Apr 2025 02:43:59 -0400 Subject: [PATCH 17/37] Another try at getting keybindings working. --- src/cthulhu/cthulhu.py | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/cthulhu/cthulhu.py b/src/cthulhu/cthulhu.py index e7e2420..af8bbc3 100644 --- a/src/cthulhu/cthulhu.py +++ b/src/cthulhu/cthulhu.py @@ -87,30 +87,32 @@ class APIHelper: handler = GestureHandler(function, name) - # Register the binding with the active script + # Create the binding object regardless of whether there's an active script + # This allows plugins to define bindings that will work when a script becomes active from . import cthulhu_state - if cthulhu_state.activeScript: - bindings = cthulhu_state.activeScript.getKeyBindings() - binding = keybindings.KeyBinding( - final_key, - keybindings.defaultModifierMask, - modifiers, - handler) + from . import keybindings + binding = keybindings.KeyBinding( + final_key, + keybindings.defaultModifierMask, + modifiers, + handler) - # Add the binding to the active script - bindings.add(binding) - - # Store binding for later reference + # Store binding for later reference if contextName not in self._gestureBindings: self._gestureBindings[contextName] = [] - self._gestureBindings[contextName].append(binding) # This line should be outside the if block - - # Register key grab at the system level - grab_ids = self.app.addKeyGrab(binding) - - # For later removal - if grab_ids: - binding._grab_ids = grab_ids + self._gestureBindings[contextName].append(binding) + + # Only add to active script if one exists + if cthulhu_state.activeScript: + bindings = cthulhu_state.activeScript.getKeyBindings() + bindings.add(binding) + + # Register key grab at the system level + grab_ids = self.app.addKeyGrab(binding) + + # For later removal + if grab_ids: + binding._grab_ids = grab_ids logger.info(f"Created binding: {binding.keysymstring} with modifiers {binding.modifiers}") return binding From 01273618a73780184ec413a62fa743bf65c0a612 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 20 Apr 2025 02:50:32 -0400 Subject: [PATCH 18/37] Fixed an error with logging. --- src/cthulhu/cthulhu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cthulhu/cthulhu.py b/src/cthulhu/cthulhu.py index af8bbc3..4f99ad5 100644 --- a/src/cthulhu/cthulhu.py +++ b/src/cthulhu/cthulhu.py @@ -114,7 +114,7 @@ class APIHelper: if grab_ids: binding._grab_ids = grab_ids - logger.info(f"Created binding: {binding.keysymstring} with modifiers {binding.modifiers}") + _logger.info(f"Created binding: {binding.keysymstring} with modifiers {binding.modifiers}") return binding return None From ecd122786f815eed0d72bdf61eb27d58cef9f7d9 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 20 Apr 2025 02:58:12 -0400 Subject: [PATCH 19/37] Fixed an error with logging, for real this time. --- src/cthulhu/cthulhu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cthulhu/cthulhu.py b/src/cthulhu/cthulhu.py index 4f99ad5..c963877 100644 --- a/src/cthulhu/cthulhu.py +++ b/src/cthulhu/cthulhu.py @@ -114,7 +114,7 @@ class APIHelper: if grab_ids: binding._grab_ids = grab_ids - _logger.info(f"Created binding: {binding.keysymstring} with modifiers {binding.modifiers}") + debug.printMessage(debug.LEVEL_INFO, f"Created binding: {binding.keysymstring} with modifiers {binding.modifiers}", True) return binding return None From 399f44948497d12cf84d3a80f0787ada4ee5dee7 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 20 Apr 2025 03:07:37 -0400 Subject: [PATCH 20/37] Fixed method call. --- src/cthulhu/plugin_system_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cthulhu/plugin_system_manager.py b/src/cthulhu/plugin_system_manager.py index a0f756e..d6a6b0c 100644 --- a/src/cthulhu/plugin_system_manager.py +++ b/src/cthulhu/plugin_system_manager.py @@ -149,7 +149,7 @@ class PluginSystemManager: if not hasattr(self, '_connected_to_script_changes'): signal_manager = self.app.getSignalManager() if signal_manager: - signal_manager.connect('load-setting-completed', self._on_settings_changed) + signal_manager.connectSignal('load-setting-completed', self._on_settings_changed, None) self._connected_to_script_changes = True except Exception as e: logger.error(f"Error registering global keybindings for plugin {plugin.name}: {e}") From d94ba1accbe82fcd12f48bfe0b136ca89e302018 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 20 Apr 2025 03:13:43 -0400 Subject: [PATCH 21/37] Add optional parameter to _on_settings_changed() --- src/cthulhu/plugin_system_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cthulhu/plugin_system_manager.py b/src/cthulhu/plugin_system_manager.py index d6a6b0c..aa143ce 100644 --- a/src/cthulhu/plugin_system_manager.py +++ b/src/cthulhu/plugin_system_manager.py @@ -184,7 +184,7 @@ class PluginSystemManager: logger.info(f"Adding binding: {binding.keysymstring} with modifiers {binding.modifiers}") active_script.getKeyBindings().add(binding) - def _on_settings_changed(self): + def _on_settings_changed(self, app=None): """Re-register all plugin keybindings when settings change.""" if not hasattr(self, '_plugin_global_bindings'): return From 0a8bb684ecb32d35fa25fe92893efdcfb77f7434 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 20 Apr 2025 03:20:12 -0400 Subject: [PATCH 22/37] Fixed an error in a call to a cthulhu module. --- src/cthulhu/plugin_system_manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cthulhu/plugin_system_manager.py b/src/cthulhu/plugin_system_manager.py index aa143ce..81e291b 100644 --- a/src/cthulhu/plugin_system_manager.py +++ b/src/cthulhu/plugin_system_manager.py @@ -202,7 +202,8 @@ class PluginSystemManager: continue active_script.getKeyBindings().add(binding) - grab_ids = self.app.addKeyGrab(binding) + from . import cthulhu + grab_ids = cthulhu.addKeyGrab(binding) if grab_ids: binding._grab_ids = grab_ids From 5181944de02a2db728ecf6ff313be54b12c43e10 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 20 Apr 2025 15:27:59 -0400 Subject: [PATCH 23/37] Move timestamps to the end of the log message instead of the beginning. Makes debugging much less of a PITA. --- src/cthulhu/debug.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cthulhu/debug.py b/src/cthulhu/debug.py index e6de6de..47a2566 100644 --- a/src/cthulhu/debug.py +++ b/src/cthulhu/debug.py @@ -298,7 +298,7 @@ def println(level, text="", timestamp=False, stack=False): text = text.replace("\ufffc", "[OBJ]") if timestamp: text = text.replace("\n", f"\n{' ' * 18}") - text = f"{datetime.now().strftime('%H:%M:%S.%f')} - {text}" + text = f"{text} - {datetime.now().strftime('%H:%M:%S.%f')}" if stack: text += f" {_stackAsString()}" From a21f1aa13b0bd1101cc451cf594a36bbf63dd852 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 5 Jun 2025 03:32:01 -0400 Subject: [PATCH 24/37] another attempt to fix the keybinding problem for plugins. --- src/cthulhu/cthulhuVersion.py | 2 +- src/cthulhu/plugin_system_manager.py | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/cthulhu/cthulhuVersion.py b/src/cthulhu/cthulhuVersion.py index 770781d..5b7ac94 100644 --- a/src/cthulhu/cthulhuVersion.py +++ b/src/cthulhu/cthulhuVersion.py @@ -23,5 +23,5 @@ # Fork of Orca Screen Reader (GNOME) # Original source: https://gitlab.gnome.org/GNOME/orca -version = "2025.04.20" +version = "2025.06.05" codeName = "plugins" diff --git a/src/cthulhu/plugin_system_manager.py b/src/cthulhu/plugin_system_manager.py index 81e291b..60326d6 100644 --- a/src/cthulhu/plugin_system_manager.py +++ b/src/cthulhu/plugin_system_manager.py @@ -182,7 +182,14 @@ class PluginSystemManager: bindings = plugin.get_bindings() for binding in bindings.keyBindings: logger.info(f"Adding binding: {binding.keysymstring} with modifiers {binding.modifiers}") - active_script.getKeyBindings().add(binding) + # Check if binding already exists to avoid duplicates + if not active_script.getKeyBindings().hasKeyBinding(binding, "keysNoMask"): + active_script.getKeyBindings().add(binding) + # Register key grab at system level - this was missing! + from . import cthulhu + grab_ids = cthulhu.addKeyGrab(binding) + if grab_ids: + binding._grab_ids = grab_ids def _on_settings_changed(self, app=None): """Re-register all plugin keybindings when settings change.""" From 314aa18a1be3d918e2d661a259a977620010edfd Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 5 Jun 2025 03:42:02 -0400 Subject: [PATCH 25/37] Another another attempt to fix the plugin keybindings. --- src/cthulhu/event_manager.py | 7 +++++++ src/cthulhu/plugin_system_manager.py | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/src/cthulhu/event_manager.py b/src/cthulhu/event_manager.py index a507ab9..2e43e21 100644 --- a/src/cthulhu/event_manager.py +++ b/src/cthulhu/event_manager.py @@ -112,6 +112,13 @@ class EventManager: cthulhu_state.device.key_watcher = cthulhu_state.device.add_key_watcher( self._processNewKeyboardEvent) self.newKeyHandlingActive = True + + # Notify plugin system that device is now available for keybinding registration + from . import cthulhu + if hasattr(cthulhu, 'cthulhuApp') and cthulhu.cthulhuApp: + plugin_manager = cthulhu.cthulhuApp.getPluginSystemManager() + if plugin_manager: + plugin_manager.register_plugin_keybindings_with_active_script() def activateLegacyKeyHandling(self): if not self.legacyKeyHandlingActive: diff --git a/src/cthulhu/plugin_system_manager.py b/src/cthulhu/plugin_system_manager.py index 60326d6..04407fd 100644 --- a/src/cthulhu/plugin_system_manager.py +++ b/src/cthulhu/plugin_system_manager.py @@ -185,11 +185,17 @@ class PluginSystemManager: # Check if binding already exists to avoid duplicates if not active_script.getKeyBindings().hasKeyBinding(binding, "keysNoMask"): active_script.getKeyBindings().add(binding) + # Force recalculation of keycode if it wasn't set when device was None + if not binding.keycode and binding.keysymstring: + from . import keybindings + binding.keycode = keybindings.getKeycode(binding.keysymstring) # Register key grab at system level - this was missing! from . import cthulhu grab_ids = cthulhu.addKeyGrab(binding) if grab_ids: binding._grab_ids = grab_ids + else: + logger.warning(f"Failed to create key grab for {binding.keysymstring} - device may not be available") def _on_settings_changed(self, app=None): """Re-register all plugin keybindings when settings change.""" From 48d99e8813e3c3276b592d8938aa4d41c2ce546c Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 5 Jun 2025 04:02:28 -0400 Subject: [PATCH 26/37] Improved debugging to help track down this bug. --- src/cthulhu/cthulhu.py | 27 +++++++++++ src/cthulhu/keybindings.py | 39 +++++++++++++++- src/cthulhu/plugin_system_manager.py | 10 +++- src/cthulhu/plugins/DisplayVersion/plugin.py | 49 ++++++++++++++++++-- 4 files changed, 120 insertions(+), 5 deletions(-) diff --git a/src/cthulhu/cthulhu.py b/src/cthulhu/cthulhu.py index c963877..28025f9 100644 --- a/src/cthulhu/cthulhu.py +++ b/src/cthulhu/cthulhu.py @@ -49,27 +49,42 @@ class APIHelper: def registerGestureByString(self, function, name, gestureString, inputEventType='default', normalizer='cthulhu', learnModeEnabled=True, contextName=None): """Register a gesture by string.""" + import logging + logger = logging.getLogger(__name__) + + logger.info(f"=== APIHelper.registerGestureByString called ===") + logger.info(f"gestureString: {gestureString}") + logger.info(f"name: {name}") + logger.info(f"contextName: {contextName}") + if not gestureString.startswith("kb:"): + logger.warning(f"Gesture string doesn't start with 'kb:': {gestureString}") return None # Extract the key portion from the gesture string key = gestureString.split(":", 1)[1] + logger.info(f"Extracted key: {key}") # Handle Cthulhu modifier specially if "cthulhu+" in key.lower(): from . import keybindings key_parts = key.lower().split("+") + logger.info(f"Key parts: {key_parts}") # Start with the base Cthulhu modifier modifiers = keybindings.CTHULHU_MODIFIER_MASK # Extract the final key (without modifiers) final_key = key_parts[-1] + logger.info(f"Final key: {final_key}") # Check for additional modifiers and combine them properly if "shift" in key_parts: # Use the pre-defined combined mask rather than trying to OR them modifiers = keybindings.CTHULHU_SHIFT_MODIFIER_MASK + logger.info(f"Using CTHULHU_SHIFT_MODIFIER_MASK: {modifiers}") + else: + logger.info(f"Using CTHULHU_MODIFIER_MASK: {modifiers}") # Create a keybinding handler class GestureHandler: @@ -79,6 +94,7 @@ class APIHelper: def __call__(self, script, inputEvent): try: + logger.info(f"=== DisplayVersion keybinding handler called! ===") return function(script, inputEvent) except Exception as e: import logging @@ -86,6 +102,7 @@ class APIHelper: return True handler = GestureHandler(function, name) + logger.info(f"Created handler: {handler}") # Create the binding object regardless of whether there's an active script # This allows plugins to define bindings that will work when a script becomes active @@ -97,25 +114,35 @@ class APIHelper: modifiers, handler) + logger.info(f"Created binding: keysym={binding.keysymstring}, modifiers={binding.modifiers}, mask={binding.modifier_mask}") + # Store binding for later reference if contextName not in self._gestureBindings: self._gestureBindings[contextName] = [] self._gestureBindings[contextName].append(binding) + logger.info(f"Stored binding in context '{contextName}'") # Only add to active script if one exists if cthulhu_state.activeScript: + logger.info(f"Adding binding to active script: {cthulhu_state.activeScript}") bindings = cthulhu_state.activeScript.getKeyBindings() bindings.add(binding) # Register key grab at the system level grab_ids = self.app.addKeyGrab(binding) + logger.info(f"Key grab IDs: {grab_ids}") # For later removal if grab_ids: binding._grab_ids = grab_ids + else: + logger.warning("No active script available - binding stored for later registration") debug.printMessage(debug.LEVEL_INFO, f"Created binding: {binding.keysymstring} with modifiers {binding.modifiers}", True) + logger.info("=== APIHelper.registerGestureByString completed ===") return binding + else: + logger.warning(f"Key doesn't contain 'cthulhu+': {key}") return None diff --git a/src/cthulhu/keybindings.py b/src/cthulhu/keybindings.py index b8fae42..3819439 100644 --- a/src/cthulhu/keybindings.py +++ b/src/cthulhu/keybindings.py @@ -472,13 +472,37 @@ class KeyBindings: import logging logger = logging.getLogger(__name__) - logger.info(f"Looking for handler for key: {keyboardEvent.hw_code} with modifiers {keyboardEvent.modifiers}") + + # Check if this might be the DisplayVersion key combination + event_str = keyboardEvent.event_string if hasattr(keyboardEvent, 'event_string') else 'unknown' + if event_str.lower() == 'v': + logger.info(f"=== KeyBindings.getInputHandler: Looking for handler ===") + logger.info(f"Event string: {event_str}") + logger.info(f"Hardware code: {keyboardEvent.hw_code}") + logger.info(f"Modifiers: {keyboardEvent.modifiers}") + logger.info(f"Total keybindings to check: {len(self.keyBindings)}") + + with open('/tmp/keybinding_lookup.log', 'a') as f: + f.write(f"=== Looking for 'v' key handler ===\n") + f.write(f"Event string: {event_str}\n") + f.write(f"Hardware code: {keyboardEvent.hw_code}\n") + f.write(f"Modifiers: {keyboardEvent.modifiers}\n") + f.write(f"Total keybindings: {len(self.keyBindings)}\n") + + # Log all keybindings for comparison + for i, kb in enumerate(self.keyBindings): + if 'v' in kb.keysymstring.lower() or 'version' in kb.handler.description.lower(): + logger.info(f"Binding {i}: keysym={kb.keysymstring}, modifiers={kb.modifiers}, mask={kb.modifier_mask}, desc={kb.handler.description}") + with open('/tmp/keybinding_lookup.log', 'a') as f: + f.write(f"Found V-related binding {i}: keysym={kb.keysymstring}, modifiers={kb.modifiers}, mask={kb.modifier_mask}, desc={kb.handler.description}\n") matches = [] candidates = [] clickCount = keyboardEvent.getClickCount() for keyBinding in self.keyBindings: if keyBinding.matches(keyboardEvent.hw_code, keyboardEvent.modifiers): + if event_str.lower() == 'v': + logger.info(f"MATCH found! keysym={keyBinding.keysymstring}, desc={keyBinding.handler.description}") if keyBinding.modifier_mask == keyboardEvent.modifiers and \ keyBinding.click_count == clickCount: matches.append(keyBinding) @@ -488,8 +512,17 @@ class KeyBindings: if keyBinding.keysymstring: candidates.append(keyBinding) + if event_str.lower() == 'v': + logger.info(f"Exact matches: {len(matches)}") + logger.info(f"Candidates: {len(candidates)}") + with open('/tmp/keybinding_lookup.log', 'a') as f: + f.write(f"Exact matches: {len(matches)}\n") + f.write(f"Candidates: {len(candidates)}\n") + self._checkMatchingBindings(keyboardEvent, matches) if matches: + if event_str.lower() == 'v': + logger.info(f"Returning exact match handler: {matches[0].handler.description}") return matches[0].handler if keyboardEvent.isKeyPadKeyWithNumlockOn(): @@ -503,8 +536,12 @@ class KeyBindings: self._checkMatchingBindings(keyboardEvent, candidates) for candidate in candidates: if candidate.click_count <= clickCount: + if event_str.lower() == 'v': + logger.info(f"Returning candidate handler: {candidate.handler.description}") return candidate.handler + if event_str.lower() == 'v': + logger.info("No handler found!") return None def load(self, keymap, handlers): diff --git a/src/cthulhu/plugin_system_manager.py b/src/cthulhu/plugin_system_manager.py index 04407fd..7f3f2d1 100644 --- a/src/cthulhu/plugin_system_manager.py +++ b/src/cthulhu/plugin_system_manager.py @@ -321,6 +321,7 @@ class PluginSystemManager: def setActivePlugins(self, activePlugins): """Set active plugins and sync their state.""" + logger.info(f"=== PluginSystemManager.setActivePlugins called ===") logger.info(f"Setting active plugins: {activePlugins}") # Make sure we have scanned for plugins first @@ -334,6 +335,12 @@ class PluginSystemManager: available_plugins = [p.get_module_name() for p in self.plugins] logger.info(f"Available plugins: {available_plugins}") logger.info(f"Active plugins: {self._active_plugins}") + + # Check specifically for DisplayVersion + if 'DisplayVersion' in self._active_plugins: + logger.info("DisplayVersion is in active plugins list!") + else: + logger.warning("DisplayVersion is NOT in active plugins list!") # Find missing plugins missing_plugins = [p for p in self._active_plugins if p not in available_plugins] @@ -341,6 +348,7 @@ class PluginSystemManager: logger.warning(f"Active plugins not found: {missing_plugins}") self.syncAllPluginsActive() + logger.info("=== PluginSystemManager.setActivePlugins completed ===") def setPluginActive(self, pluginInfo, active): """Set the active state of a plugin.""" @@ -429,7 +437,7 @@ class PluginSystemManager: return False module_name = pluginInfo.get_module_name() - logger.info(f"Attempting to load plugin: {module_name}") + logger.info(f"=== PluginSystemManager.loadPlugin starting for: {module_name} ===") try: # Already loaded? diff --git a/src/cthulhu/plugins/DisplayVersion/plugin.py b/src/cthulhu/plugins/DisplayVersion/plugin.py index 75ab90a..e9aea2d 100644 --- a/src/cthulhu/plugins/DisplayVersion/plugin.py +++ b/src/cthulhu/plugins/DisplayVersion/plugin.py @@ -32,15 +32,58 @@ class DisplayVersion(Plugin): return try: - logger.info("Activating DisplayVersion plugin") + logger.info("=== DisplayVersion plugin activation starting ===") + logger.info(f"Plugin name: {self.name}") + logger.info(f"App reference: {self.app}") + + # Also write to a debug file + with open('/tmp/displayversion_debug.log', 'a') as f: + f.write("=== DisplayVersion plugin activation starting ===\n") + f.write(f"Plugin name: {self.name}\n") + f.write(f"App reference: {self.app}\n") + + # Check if we have access to the API helper + if not self.app: + logger.error("DisplayVersion: No app reference available!") + return + + api_helper = self.app.getAPIHelper() + if not api_helper: + logger.error("DisplayVersion: No API helper available!") + return + + logger.info(f"API helper: {api_helper}") # Register keyboard shortcut + gesture_string = 'kb:cthulhu+shift+v' + logger.info(f"DisplayVersion: Attempting to register gesture: {gesture_string}") + self._kb_binding = self.registerGestureByString( self.speakText, f'Cthulhu screen reader version {cthulhuVersion.version}-{cthulhuVersion.codeName}', - 'kb:cthulhu+shift+v' + gesture_string ) - logger.info(f"Registered keybinding: {self._kb_binding}") + + logger.info(f"DisplayVersion: Registered keybinding result: {self._kb_binding}") + if self._kb_binding: + logger.info(f"Binding keysymstring: {self._kb_binding.keysymstring}") + logger.info(f"Binding modifiers: {self._kb_binding.modifiers}") + logger.info(f"Binding modifier_mask: {self._kb_binding.modifier_mask}") + logger.info(f"Binding handler: {self._kb_binding.handler}") + + with open('/tmp/displayversion_debug.log', 'a') as f: + f.write(f"SUCCESS: Keybinding created!\n") + f.write(f" keysymstring: {self._kb_binding.keysymstring}\n") + f.write(f" modifiers: {self._kb_binding.modifiers}\n") + f.write(f" modifier_mask: {self._kb_binding.modifier_mask}\n") + else: + logger.error("DisplayVersion: Failed to create keybinding!") + with open('/tmp/displayversion_debug.log', 'a') as f: + f.write("ERROR: Failed to create keybinding!\n") + + logger.info("=== DisplayVersion plugin activation completed ===") + with open('/tmp/displayversion_debug.log', 'a') as f: + f.write("=== DisplayVersion plugin activation completed ===\n\n") except Exception as e: logger.error(f"Error activating DisplayVersion plugin: {e}") import traceback From 90aecf805566bb2f3dee5ca84614e66913f57af7 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 5 Jun 2025 04:13:24 -0400 Subject: [PATCH 27/37] Changes to the plugin manager. --- src/cthulhu/plugin_system_manager.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/cthulhu/plugin_system_manager.py b/src/cthulhu/plugin_system_manager.py index 7f3f2d1..bb47ff7 100644 --- a/src/cthulhu/plugin_system_manager.py +++ b/src/cthulhu/plugin_system_manager.py @@ -170,6 +170,33 @@ class PluginSystemManager: active_script = cthulhu_state.activeScript logger.info(f"Registering plugin keybindings with active script: {active_script}") + # First, register keybindings from APIHelper's stored bindings + # This is where plugin keybindings actually get stored + from . import cthulhu + api_helper = cthulhu.cthulhuApp.getAPIHelper() + if api_helper and hasattr(api_helper, '_gestureBindings'): + logger.info("Registering stored gesture bindings from APIHelper") + for context_name, bindings_list in api_helper._gestureBindings.items(): + logger.info(f"Processing context '{context_name}' with {len(bindings_list)} bindings") + for binding in bindings_list: + logger.info(f"Adding stored binding: {binding.keysymstring} with modifiers {binding.modifiers}") + # Check if binding already exists to avoid duplicates + if not active_script.getKeyBindings().hasKeyBinding(binding, "keysNoMask"): + active_script.getKeyBindings().add(binding) + # Force recalculation of keycode if it wasn't set when device was None + if not binding.keycode and binding.keysymstring: + from . import keybindings + binding.keycode = keybindings.getKeycode(binding.keysymstring) + # Register key grab at system level - this was missing! + grab_ids = cthulhu.addKeyGrab(binding) + if grab_ids: + binding._grab_ids = grab_ids + else: + logger.warning(f"Failed to create key grab for {binding.keysymstring} - device may not be available") + else: + logger.info(f"Binding already exists: {binding.keysymstring}") + + # Also check the old method for any plugins that use get_bindings() for pluginInfo in self._plugins.values(): if not pluginInfo.loaded or not pluginInfo.instance: continue @@ -190,7 +217,6 @@ class PluginSystemManager: from . import keybindings binding.keycode = keybindings.getKeycode(binding.keysymstring) # Register key grab at system level - this was missing! - from . import cthulhu grab_ids = cthulhu.addKeyGrab(binding) if grab_ids: binding._grab_ids = grab_ids From 0edbefac47e87d92afd83cbec71824ffd5a874ee Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 5 Jun 2025 13:11:47 -0400 Subject: [PATCH 28/37] more debugging. --- src/cthulhu/plugin_system_manager.py | 36 +++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/cthulhu/plugin_system_manager.py b/src/cthulhu/plugin_system_manager.py index bb47ff7..053fa15 100644 --- a/src/cthulhu/plugin_system_manager.py +++ b/src/cthulhu/plugin_system_manager.py @@ -159,30 +159,54 @@ class PluginSystemManager: def register_plugin_keybindings_with_active_script(self): """Register all plugin keybindings with the active script.""" + logger.info("=== register_plugin_keybindings_with_active_script() CALLED ===") + with open('/tmp/plugin_registration.log', 'a') as f: + f.write("=== register_plugin_keybindings_with_active_script() CALLED ===\n") + if not PLUGGY_AVAILABLE: + logger.warning("PLUGGY not available") + with open('/tmp/plugin_registration.log', 'a') as f: + f.write("ERROR: PLUGGY not available\n") return from . import cthulhu_state if not cthulhu_state.activeScript: logger.warning("No active script available to register plugin keybindings") + with open('/tmp/plugin_registration.log', 'a') as f: + f.write("ERROR: No active script available\n") return active_script = cthulhu_state.activeScript logger.info(f"Registering plugin keybindings with active script: {active_script}") + with open('/tmp/plugin_registration.log', 'a') as f: + f.write(f"Active script: {active_script}\n") # First, register keybindings from APIHelper's stored bindings # This is where plugin keybindings actually get stored from . import cthulhu api_helper = cthulhu.cthulhuApp.getAPIHelper() if api_helper and hasattr(api_helper, '_gestureBindings'): - logger.info("Registering stored gesture bindings from APIHelper") + logger.info("=== FOUND APIHelper with _gestureBindings ===") + with open('/tmp/plugin_registration.log', 'a') as f: + f.write("=== Registering stored gesture bindings from APIHelper ===\n") + f.write(f"Total contexts: {len(api_helper._gestureBindings)}\n") + for context_name, bindings_list in api_helper._gestureBindings.items(): logger.info(f"Processing context '{context_name}' with {len(bindings_list)} bindings") + with open('/tmp/plugin_registration.log', 'a') as f: + f.write(f"Context '{context_name}': {len(bindings_list)} bindings\n") + for binding in bindings_list: logger.info(f"Adding stored binding: {binding.keysymstring} with modifiers {binding.modifiers}") + with open('/tmp/plugin_registration.log', 'a') as f: + f.write(f" Binding: {binding.keysymstring} modifiers={binding.modifiers} desc={binding.handler.description}\n") + # Check if binding already exists to avoid duplicates if not active_script.getKeyBindings().hasKeyBinding(binding, "keysNoMask"): active_script.getKeyBindings().add(binding) + with open('/tmp/plugin_registration.log', 'a') as f: + f.write(f" ADDED to active script!\n") + # Force recalculation of keycode if it wasn't set when device was None if not binding.keycode and binding.keysymstring: from . import keybindings @@ -195,6 +219,16 @@ class PluginSystemManager: logger.warning(f"Failed to create key grab for {binding.keysymstring} - device may not be available") else: logger.info(f"Binding already exists: {binding.keysymstring}") + with open('/tmp/plugin_registration.log', 'a') as f: + f.write(f" Already exists, skipped\n") + else: + logger.warning("=== NO APIHelper or no _gestureBindings found ===") + with open('/tmp/plugin_registration.log', 'a') as f: + f.write("ERROR: No APIHelper or no _gestureBindings found!\n") + if api_helper: + f.write(f"APIHelper exists but _gestureBindings: {hasattr(api_helper, '_gestureBindings')}\n") + else: + f.write("No APIHelper found!\n") # Also check the old method for any plugins that use get_bindings() for pluginInfo in self._plugins.values(): From 2eb6d3c7dd10e609bcc5ee4991887d8e837e3089 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 5 Jun 2025 13:20:27 -0400 Subject: [PATCH 29/37] updated keybindings.py --- src/cthulhu/keybindings.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/cthulhu/keybindings.py b/src/cthulhu/keybindings.py index 3819439..3a2dd05 100644 --- a/src/cthulhu/keybindings.py +++ b/src/cthulhu/keybindings.py @@ -251,6 +251,23 @@ class KeyBinding: if not self.keycode: self.keycode = getKeycode(self.keysymstring) + # Debug logging for DisplayVersion plugin specifically + if self.keysymstring == 'v' and self.modifiers == 257: + with open('/tmp/displayversion_matches.log', 'a') as f: + f.write(f"=== DisplayVersion matches() debug ===\n") + f.write(f"Self keycode: {self.keycode}\n") + f.write(f"Self keysymstring: {self.keysymstring}\n") + f.write(f"Self modifiers: {self.modifiers}\n") + f.write(f"Self modifier_mask: {self.modifier_mask}\n") + f.write(f"Input keycode: {keycode}\n") + f.write(f"Input modifiers: {modifiers}\n") + f.write(f"Keycode match: {self.keycode == keycode}\n") + if self.keycode == keycode: + result = modifiers & self.modifier_mask + f.write(f"Modifier calculation: {modifiers} & {self.modifier_mask} = {result}\n") + f.write(f"Modifier match: {result == self.modifiers}\n") + f.write(f"Overall match: {self.keycode == keycode and (modifiers & self.modifier_mask) == self.modifiers}\n") + if self.keycode == keycode: result = modifiers & self.modifier_mask return result == self.modifiers From a1d90a72453895150c45d648e9b058d346a6bf36 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 5 Jun 2025 13:27:25 -0400 Subject: [PATCH 30/37] more work on keybindings. --- src/cthulhu/cthulhu.py | 2 +- src/cthulhu/event_manager.py | 2 +- src/cthulhu/script_manager.py | 2 +- src/cthulhu/scripts/default.py | 18 ++++++++++++++++++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/cthulhu/cthulhu.py b/src/cthulhu/cthulhu.py index 28025f9..e9507fd 100644 --- a/src/cthulhu/cthulhu.py +++ b/src/cthulhu/cthulhu.py @@ -665,7 +665,7 @@ def loadUserSettings(script=None, inputEvent=None, skipReloadMessage=False): _eventManager.activate() cthulhuApp.getSignalManager().emitSignal('load-setting-begin') - cthulhuApp.getPluginSystemManager().register_plugin_keybindings_with_active_script() + # cthulhuApp.getPluginSystemManager().register_plugin_keybindings_with_active_script() cthulhuApp.getSignalManager().emitSignal('load-setting-completed') diff --git a/src/cthulhu/event_manager.py b/src/cthulhu/event_manager.py index 2e43e21..cf35061 100644 --- a/src/cthulhu/event_manager.py +++ b/src/cthulhu/event_manager.py @@ -118,7 +118,7 @@ class EventManager: if hasattr(cthulhu, 'cthulhuApp') and cthulhu.cthulhuApp: plugin_manager = cthulhu.cthulhuApp.getPluginSystemManager() if plugin_manager: - plugin_manager.register_plugin_keybindings_with_active_script() + # plugin_manager.register_plugin_keybindings_with_active_script() def activateLegacyKeyHandling(self): if not self.legacyKeyHandlingActive: diff --git a/src/cthulhu/script_manager.py b/src/cthulhu/script_manager.py index 92fb5fc..e5daeb9 100644 --- a/src/cthulhu/script_manager.py +++ b/src/cthulhu/script_manager.py @@ -323,7 +323,7 @@ class ScriptManager: from . import cthulhu plugin_manager = cthulhu.cthulhuApp.getPluginSystemManager() if plugin_manager: - plugin_manager.register_plugin_keybindings_with_active_script() + # plugin_manager.register_plugin_keybindings_with_active_script() tokens = ["SCRIPT MANAGER: Setting active script to", newScript, "reason:", reason] debug.printTokens(debug.LEVEL_INFO, tokens, True) diff --git a/src/cthulhu/scripts/default.py b/src/cthulhu/scripts/default.py index 2b119fa..a0cca9a 100644 --- a/src/cthulhu/scripts/default.py +++ b/src/cthulhu/scripts/default.py @@ -407,6 +407,24 @@ class Script(script.Script): for keyBinding in bindings.keyBindings: keyBindings.add(keyBinding) + # Add plugin keybindings from APIHelper storage + try: + import cthulhu.cthulhu as cthulhu + api_helper = cthulhu.getCthulhu() + if api_helper and hasattr(api_helper, '_gestureBindings'): + with open('/tmp/extension_bindings_debug.log', 'a') as f: + f.write(f"=== Adding plugin bindings in getExtensionBindings() ===\n") + f.write(f"Available contexts: {list(api_helper._gestureBindings.keys())}\n") + + for context_name, context_bindings in api_helper._gestureBindings.items(): + for binding in context_bindings: + keyBindings.add(binding) + with open('/tmp/extension_bindings_debug.log', 'a') as f: + f.write(f"Added plugin binding: {binding.keysymstring} modifiers={binding.modifiers} desc={binding.handler.description}\n") + except Exception as e: + import cthulhu.debug as debug + debug.printMessage(debug.LEVEL_WARNING, f"Failed to add plugin bindings: {e}", True) + return keyBindings def getKeyBindings(self): From ea50d8b0247bf81782e74b3031bb3217c7f39470 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 5 Jun 2025 13:31:11 -0400 Subject: [PATCH 31/37] Fix error in script manager. --- src/cthulhu/script_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cthulhu/script_manager.py b/src/cthulhu/script_manager.py index e5daeb9..a8508d2 100644 --- a/src/cthulhu/script_manager.py +++ b/src/cthulhu/script_manager.py @@ -323,7 +323,7 @@ class ScriptManager: from . import cthulhu plugin_manager = cthulhu.cthulhuApp.getPluginSystemManager() if plugin_manager: - # plugin_manager.register_plugin_keybindings_with_active_script() + pass # plugin_manager.register_plugin_keybindings_with_active_script() tokens = ["SCRIPT MANAGER: Setting active script to", newScript, "reason:", reason] debug.printTokens(debug.LEVEL_INFO, tokens, True) From 20907677949d4d4c85dbab68f1d9e5a7caa67d74 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 5 Jun 2025 13:34:23 -0400 Subject: [PATCH 32/37] Fixed error in event manager. --- src/cthulhu/event_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cthulhu/event_manager.py b/src/cthulhu/event_manager.py index cf35061..46897a6 100644 --- a/src/cthulhu/event_manager.py +++ b/src/cthulhu/event_manager.py @@ -118,7 +118,7 @@ class EventManager: if hasattr(cthulhu, 'cthulhuApp') and cthulhu.cthulhuApp: plugin_manager = cthulhu.cthulhuApp.getPluginSystemManager() if plugin_manager: - # plugin_manager.register_plugin_keybindings_with_active_script() + pass # plugin_manager.register_plugin_keybindings_with_active_script() def activateLegacyKeyHandling(self): if not self.legacyKeyHandlingActive: From e2364a154aa81e390fdea6e8989582a64949cbac Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 5 Jun 2025 13:40:44 -0400 Subject: [PATCH 33/37] Another try to get keybindings working. --- src/cthulhu/cthulhu.py | 3 +++ src/cthulhu/plugin_system_manager.py | 16 ++++++++++++++++ src/cthulhu/scripts/default.py | 2 ++ 3 files changed, 21 insertions(+) diff --git a/src/cthulhu/cthulhu.py b/src/cthulhu/cthulhu.py index e9507fd..b177a31 100644 --- a/src/cthulhu/cthulhu.py +++ b/src/cthulhu/cthulhu.py @@ -663,6 +663,9 @@ def loadUserSettings(script=None, inputEvent=None, skipReloadMessage=False): _scriptManager.activate() _eventManager.activate() + + # Refresh keybindings to include plugin bindings (after script manager is active) + cthulhuApp.getPluginSystemManager().refresh_active_script_keybindings() cthulhuApp.getSignalManager().emitSignal('load-setting-begin') # cthulhuApp.getPluginSystemManager().register_plugin_keybindings_with_active_script() diff --git a/src/cthulhu/plugin_system_manager.py b/src/cthulhu/plugin_system_manager.py index 053fa15..623cafd 100644 --- a/src/cthulhu/plugin_system_manager.py +++ b/src/cthulhu/plugin_system_manager.py @@ -156,6 +156,22 @@ class PluginSystemManager: import traceback logger.error(traceback.format_exc()) + def refresh_active_script_keybindings(self): + """Force active script to refresh its keybindings to include plugin bindings.""" + from . import cthulhu_state + if cthulhu_state.activeScript: + active_script = cthulhu_state.activeScript + with open('/tmp/plugin_registration.log', 'a') as f: + f.write(f"=== refresh_active_script_keybindings() CALLED ===\n") + f.write(f"Active script: {active_script.name}\n") + + # Force the script to recreate its keybindings to include plugin bindings + old_keybindings = active_script.keyBindings + active_script.keyBindings = active_script.getKeyBindings() + + with open('/tmp/plugin_registration.log', 'a') as f: + f.write(f"Keybindings refreshed: old={len(old_keybindings.keyBindings) if old_keybindings else 0}, new={len(active_script.keyBindings.keyBindings)}\n") + def register_plugin_keybindings_with_active_script(self): """Register all plugin keybindings with the active script.""" diff --git a/src/cthulhu/scripts/default.py b/src/cthulhu/scripts/default.py index a0cca9a..5ea8e31 100644 --- a/src/cthulhu/scripts/default.py +++ b/src/cthulhu/scripts/default.py @@ -363,6 +363,8 @@ class Script(script.Script): return keyBindings def getExtensionBindings(self): + with open('/tmp/extension_bindings_debug.log', 'a') as f: + f.write(f"=== getExtensionBindings() called ===\n") keyBindings = keybindings.KeyBindings() bindings = self.notificationPresenter.get_bindings() From 62f46c0eb7670ebd2823f004eafebd1afc267f3b Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 5 Jun 2025 13:45:12 -0400 Subject: [PATCH 34/37] Maybe getting closer. --- src/cthulhu/scripts/default.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/cthulhu/scripts/default.py b/src/cthulhu/scripts/default.py index 5ea8e31..4360443 100644 --- a/src/cthulhu/scripts/default.py +++ b/src/cthulhu/scripts/default.py @@ -413,19 +413,32 @@ class Script(script.Script): try: import cthulhu.cthulhu as cthulhu api_helper = cthulhu.getCthulhu() + with open('/tmp/extension_bindings_debug.log', 'a') as f: + f.write(f"=== Checking for plugin bindings ===\n") + f.write(f"api_helper exists: {api_helper is not None}\n") + if api_helper: + f.write(f"api_helper has _gestureBindings: {hasattr(api_helper, '_gestureBindings')}\n") + if hasattr(api_helper, '_gestureBindings'): + f.write(f"_gestureBindings content: {api_helper._gestureBindings}\n") + f.write(f"Available contexts: {list(api_helper._gestureBindings.keys())}\n") + if api_helper and hasattr(api_helper, '_gestureBindings'): with open('/tmp/extension_bindings_debug.log', 'a') as f: f.write(f"=== Adding plugin bindings in getExtensionBindings() ===\n") - f.write(f"Available contexts: {list(api_helper._gestureBindings.keys())}\n") for context_name, context_bindings in api_helper._gestureBindings.items(): for binding in context_bindings: keyBindings.add(binding) with open('/tmp/extension_bindings_debug.log', 'a') as f: f.write(f"Added plugin binding: {binding.keysymstring} modifiers={binding.modifiers} desc={binding.handler.description}\n") + else: + with open('/tmp/extension_bindings_debug.log', 'a') as f: + f.write(f"=== No plugin bindings available ===\n") except Exception as e: import cthulhu.debug as debug debug.printMessage(debug.LEVEL_WARNING, f"Failed to add plugin bindings: {e}", True) + with open('/tmp/extension_bindings_debug.log', 'a') as f: + f.write(f"Exception in plugin binding addition: {e}\n") return keyBindings From 13f110ab344606e0efb8a89af4a622f76227f577 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 5 Jun 2025 13:50:36 -0400 Subject: [PATCH 35/37] Updated bindings code. --- src/cthulhu/scripts/default.py | 40 +++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/cthulhu/scripts/default.py b/src/cthulhu/scripts/default.py index 4360443..b754d18 100644 --- a/src/cthulhu/scripts/default.py +++ b/src/cthulhu/scripts/default.py @@ -412,28 +412,32 @@ class Script(script.Script): # Add plugin keybindings from APIHelper storage try: import cthulhu.cthulhu as cthulhu - api_helper = cthulhu.getCthulhu() - with open('/tmp/extension_bindings_debug.log', 'a') as f: - f.write(f"=== Checking for plugin bindings ===\n") - f.write(f"api_helper exists: {api_helper is not None}\n") - if api_helper: - f.write(f"api_helper has _gestureBindings: {hasattr(api_helper, '_gestureBindings')}\n") - if hasattr(api_helper, '_gestureBindings'): - f.write(f"_gestureBindings content: {api_helper._gestureBindings}\n") - f.write(f"Available contexts: {list(api_helper._gestureBindings.keys())}\n") - - if api_helper and hasattr(api_helper, '_gestureBindings'): + if hasattr(cthulhu, 'cthulhuApp') and cthulhu.cthulhuApp: + api_helper = cthulhu.cthulhuApp.getAPIHelper() with open('/tmp/extension_bindings_debug.log', 'a') as f: - f.write(f"=== Adding plugin bindings in getExtensionBindings() ===\n") + f.write(f"=== Checking for plugin bindings ===\n") + f.write(f"api_helper exists: {api_helper is not None}\n") + if api_helper: + f.write(f"api_helper has _gestureBindings: {hasattr(api_helper, '_gestureBindings')}\n") + if hasattr(api_helper, '_gestureBindings'): + f.write(f"_gestureBindings content: {api_helper._gestureBindings}\n") + f.write(f"Available contexts: {list(api_helper._gestureBindings.keys())}\n") - for context_name, context_bindings in api_helper._gestureBindings.items(): - for binding in context_bindings: - keyBindings.add(binding) - with open('/tmp/extension_bindings_debug.log', 'a') as f: - f.write(f"Added plugin binding: {binding.keysymstring} modifiers={binding.modifiers} desc={binding.handler.description}\n") + if api_helper and hasattr(api_helper, '_gestureBindings'): + with open('/tmp/extension_bindings_debug.log', 'a') as f: + f.write(f"=== Adding plugin bindings in getExtensionBindings() ===\n") + + for context_name, context_bindings in api_helper._gestureBindings.items(): + for binding in context_bindings: + keyBindings.add(binding) + with open('/tmp/extension_bindings_debug.log', 'a') as f: + f.write(f"Added plugin binding: {binding.keysymstring} modifiers={binding.modifiers} desc={binding.handler.description}\n") + else: + with open('/tmp/extension_bindings_debug.log', 'a') as f: + f.write(f"=== No plugin bindings available ===\n") else: with open('/tmp/extension_bindings_debug.log', 'a') as f: - f.write(f"=== No plugin bindings available ===\n") + f.write(f"=== cthulhuApp not available ===\n") except Exception as e: import cthulhu.debug as debug debug.printMessage(debug.LEVEL_WARNING, f"Failed to add plugin bindings: {e}", True) From 0f25245d3db310439041c7dce39ad9a24867a36a Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 5 Jun 2025 13:55:30 -0400 Subject: [PATCH 36/37] OMG it actually works! Just some finishing touches. --- src/cthulhu/keybindings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cthulhu/keybindings.py b/src/cthulhu/keybindings.py index 3a2dd05..d85b453 100644 --- a/src/cthulhu/keybindings.py +++ b/src/cthulhu/keybindings.py @@ -520,7 +520,7 @@ class KeyBindings: if keyBinding.matches(keyboardEvent.hw_code, keyboardEvent.modifiers): if event_str.lower() == 'v': logger.info(f"MATCH found! keysym={keyBinding.keysymstring}, desc={keyBinding.handler.description}") - if keyBinding.modifier_mask == keyboardEvent.modifiers and \ + if (keyboardEvent.modifiers & keyBinding.modifier_mask) == keyBinding.modifiers and \ keyBinding.click_count == clickCount: matches.append(keyBinding) # If there's no keysymstring, it's unbound and cannot be From 408fb85730d3e72c3095c8ed558a1b62f2116584 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 5 Jun 2025 14:05:23 -0400 Subject: [PATCH 37/37] Updated clipboard plugin to work with the now fixed plugin system. --- src/cthulhu/plugins/Clipboard/__init__.py | 5 +++ src/cthulhu/plugins/Clipboard/plugin.py | 37 +---------------------- 2 files changed, 6 insertions(+), 36 deletions(-) diff --git a/src/cthulhu/plugins/Clipboard/__init__.py b/src/cthulhu/plugins/Clipboard/__init__.py index 782103c..9531613 100644 --- a/src/cthulhu/plugins/Clipboard/__init__.py +++ b/src/cthulhu/plugins/Clipboard/__init__.py @@ -23,3 +23,8 @@ # Fork of Orca Screen Reader (GNOME) # Original source: https://gitlab.gnome.org/GNOME/orca +"""Clipboard plugin package.""" + +from .plugin import Clipboard + +__all__ = ['Clipboard'] diff --git a/src/cthulhu/plugins/Clipboard/plugin.py b/src/cthulhu/plugins/Clipboard/plugin.py index 5388d87..27e2f4e 100644 --- a/src/cthulhu/plugins/Clipboard/plugin.py +++ b/src/cthulhu/plugins/Clipboard/plugin.py @@ -72,42 +72,7 @@ class Clipboard(Plugin): return logger.info("Deactivating Clipboard plugin") - try: - # Unregister keyboard shortcut - if self.app: - api_helper = self.app.getAPIHelper() - if api_helper and hasattr(api_helper, 'unregisterShortcut'): - api_helper.unregisterShortcut('kb:cthulhu+shift+c') - logger.debug("Unregistered clipboard shortcut") - except Exception as e: - logger.error(f"Error deactivating Clipboard plugin: {e}") - """Activate the plugin.""" - # Skip if this activation call isn't for us - if plugin is not None and plugin is not self: - return - - logger.info("Activating Clipboard plugin") - try: - # Register keyboard shortcut - self.registerGestureByString(self.speakClipboard, _('clipboard'), 'kb:cthulhu+shift+c') - logger.debug("Registered shortcut for clipboard") - except Exception as e: - logger.error(f"Error activating Clipboard plugin: {e}") - - @cthulhu_hookimpl - def deactivate(self, plugin=None): - """Deactivate the plugin.""" - # Skip if this deactivation call isn't for us - if plugin is not None and plugin is not self: - return - - logger.info("Deactivating Clipboard plugin") - try: - # Unregister keyboard shortcut - self.unregisterGestureByString('kb:cthulhu+shift+c') - logger.debug("Unregistered clipboard shortcut") - except Exception as e: - logger.error(f"Error deactivating Clipboard plugin: {e}") + # Note: Currently no unregister method needed as keybindings are managed by APIHelper def speakClipboard(self, script=None, inputEvent=None): """Present the contents of the clipboard."""