Went bug huntin' Squished a bunch of 'em little critters.

This commit is contained in:
Storm Dragon committed 2025-08-05 23:57:23 -04:00
1 parent 37bd89ab87
commit c9cfe3e0f4
6 files changed
+137 -107

No files matched your search

+68 -38
View File
@@ -76,7 +76,8 @@ class ActionList(Gtk.Window):
self.set_skip_taskbar_hint(True) self.set_skip_taskbar_hint(True)
self.set_skip_pager_hint(True) self.set_skip_pager_hint(True)
self.set_type_hint(Gdk.WindowTypeHint.DIALOG) self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
self.set_window_position(Gtk.WindowPosition.MOUSE) # Note: set_window_position is deprecated, using move() instead
self.move(100, 100) # Position window at reasonable location
self.set_default_size(400, 300) self.set_default_size(400, 300)
# Create scrolled window # Create scrolled window
@@ -158,9 +159,9 @@ class ActionPresenter:
self._obj = None self._obj = None
self._window = None self._window = None
# Initialize handlers and bindings # Initialize handlers
self._handlers = self.get_handlers(True) self._handlers = self.get_handlers(True)
self._bindings = keybindings.KeyBindings() # _bindings will be initialized lazily in get_bindings()
def get_handlers(self, refresh: bool = False) -> dict: def get_handlers(self, refresh: bool = False) -> dict:
"""Returns a dictionary of input event handlers.""" """Returns a dictionary of input event handlers."""
@@ -207,7 +208,7 @@ class ActionPresenter:
self._bindings = keybindings.KeyBindings() self._bindings = keybindings.KeyBindings()
self._bindings.add(keybindings.KeyBinding( self._bindings.add(keybindings.KeyBinding(
"a", "a",
keybindings.DEFAULT_MODIFIER_MASK, keybindings.defaultModifierMask,
keybindings.CTHULHU_SHIFT_MODIFIER_MASK, keybindings.CTHULHU_SHIFT_MODIFIER_MASK,
self._handlers["show_actions_list"])) self._handlers["show_actions_list"]))
@@ -221,7 +222,7 @@ class ActionPresenter:
debug.printTokens(debug.LEVEL_INFO, tokens, True) debug.printTokens(debug.LEVEL_INFO, tokens, True)
reason = "Action Presenter list is being destroyed" reason = "Action Presenter list is being destroyed"
app = AXUtilities.get_application(self._obj) app = AXObject.get_application(self._obj)
script = script_manager.getManager().getScript(app, self._obj) script = script_manager.getManager().getScript(app, self._obj)
script_manager.getManager().setActiveScript(script, reason) script_manager.getManager().setActiveScript(script, reason)
@@ -247,48 +248,77 @@ class ActionPresenter:
result = AXObject.do_named_action(self._obj, action) result = AXObject.do_named_action(self._obj, action)
tokens = ["ACTION PRESENTER: Performing", action, "on", self._obj, "succeeded:", result] tokens = ["ACTION PRESENTER: Performing", action, "on", self._obj, "succeeded:", result]
debug.printTokens(debug.LEVEL_INFO, tokens, True) debug.printTokens(debug.LEVEL_INFO, tokens, True)
if not result: # Use idle_add for asynchronous destruction to allow action to complete
full_message = messages.ACTION_NOT_PERFORMED % action GLib.idle_add(self._gui.destroy)
cthulhu.presentMessage(full_message)
self._gui.destroy()
def present_with_time(self, obj, start_time: float) -> bool: def present_with_time(self, obj, start_time: float) -> bool:
"""Presents accessible actions for the given object with timing.""" """Presents accessible actions for the given object with timing."""
if self._gui is not None: try:
msg = "ACTION PRESENTER: GUI already exists." debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: present_with_time called with obj: {obj}", True)
debug.printMessage(debug.LEVEL_INFO, msg, True)
if self._gui is not None:
msg = "ACTION PRESENTER: GUI already exists."
debug.printMessage(debug.LEVEL_INFO, msg, True)
return False
if obj is None:
debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: obj is None, using locusOfFocus", True)
obj = cthulhu_state.locusOfFocus
if obj is None:
debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: No object found, presenting NO_ACCESSIBLE_ACTIONS", True)
full_message = messages.NO_ACCESSIBLE_ACTIONS
cthulhu.presentMessage(full_message)
return False
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Getting actions for object: {obj}", True)
actions = AXObject.get_action_names(obj)
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Found {len(actions) if actions else 0} actions: {actions}", True)
if not actions:
debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: No actions found, presenting NO_ACCESSIBLE_ACTIONS", True)
full_message = messages.NO_ACCESSIBLE_ACTIONS
cthulhu.presentMessage(full_message)
return False
debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: Creating GUI", True)
self._obj = obj
self._window = cthulhu_state.activeWindow
self._gui = ActionList(self)
self._gui.populate_actions(actions)
debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: GUI created successfully", True)
return True
except Exception as e:
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: ERROR in present_with_time: {e}", True)
import traceback
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Traceback: {traceback.format_exc()}", True)
return False return False
if obj is None:
obj = cthulhu_state.locusOfFocus
if obj is None:
full_message = messages.NO_ACCESSIBLE_ACTIONS
cthulhu.presentMessage(full_message)
return False
actions = AXObject.get_action_names(obj)
if not actions:
full_message = messages.NO_ACCESSIBLE_ACTIONS
cthulhu.presentMessage(full_message)
return False
self._obj = obj
self._window = cthulhu_state.activeWindow
self._gui = ActionList(self)
self._gui.populate_actions(actions)
return True
def show_actions_list(self, script: script.Script = None, input_event: input_event.InputEvent = None) -> bool: def show_actions_list(self, script: script.Script = None, input_event: input_event.InputEvent = None) -> bool:
"""Shows the accessible actions list.""" """Shows the accessible actions list."""
start_time = time.time() try:
return self.present_with_time(input_event.get_object(), start_time) msg = f"ACTION PRESENTER: show_actions_list called! input_event: {input_event}"
debug.printMessage(debug.LEVEL_INFO, msg, True)
start_time = time.time()
# Get object from input event if available, otherwise let present_with_time handle it
obj = input_event.getObject() if input_event else None
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Object from input_event: {obj}", True)
result = self.present_with_time(obj, start_time)
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: show_actions_list returning: {result}", True)
return result
except Exception as e:
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: ERROR in show_actions_list: {e}", True)
import traceback
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Traceback: {traceback.format_exc()}", True)
return False
_presenter = ActionPresenter() _presenter = ActionPresenter()
+11 -9
View File
@@ -142,14 +142,14 @@ class APIHelper:
bindings.add(binding) bindings.add(binding)
# Register key grab at the system level # Register key grab at the system level
grab_ids = self.app.addKeyGrab(binding) grab_ids = addKeyGrab(binding)
logger.info(f"Key grab IDs: {grab_ids}") logger.info(f"Key grab IDs: {grab_ids}")
# For later removal # For later removal
if grab_ids: if grab_ids:
binding._grab_ids = grab_ids binding._grab_ids = grab_ids
else: else:
logger.warning("No active script available - binding stored for later registration") debug.printMessage(debug.LEVEL_INFO, "No active script available - binding stored for later registration", True)
debug.printMessage(debug.LEVEL_INFO, f"Created binding: {binding.keysymstring} with modifiers {binding.modifiers}", True) debug.printMessage(debug.LEVEL_INFO, f"Created binding: {binding.keysymstring} with modifiers {binding.modifiers}", True)
logger.info("=== APIHelper.registerGestureByString completed ===") logger.info("=== APIHelper.registerGestureByString completed ===")
@@ -674,17 +674,19 @@ def loadUserSettings(script=None, inputEvent=None, skipReloadMessage=False):
_storeXmodmap(_cthulhuModifiers) _storeXmodmap(_cthulhuModifiers)
_createCthulhuXmodmap() _createCthulhuXmodmap()
activePlugins = list(_settingsManager.getSetting('activePlugins')) # Activate core systems FIRST before loading plugins
cthulhuApp.getPluginSystemManager().setActivePlugins(activePlugins)
_scriptManager.activate() _scriptManager.activate()
_eventManager.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.getSignalManager().emitSignal('load-setting-begin')
# cthulhuApp.getPluginSystemManager().register_plugin_keybindings_with_active_script()
# NOW load plugins after script system is ready
activePlugins = list(_settingsManager.getSetting('activePlugins'))
debug.printMessage(debug.LEVEL_INFO, f'CTHULHU: Loading active plugins: {activePlugins}', True)
cthulhuApp.getPluginSystemManager().setActivePlugins(activePlugins)
# Refresh keybindings to include plugin bindings (after script manager is active)
cthulhuApp.getPluginSystemManager().refresh_active_script_keybindings()
cthulhuApp.getSignalManager().emitSignal('load-setting-completed') cthulhuApp.getSignalManager().emitSignal('load-setting-completed')
+2 -1
View File
@@ -135,7 +135,8 @@ class PluginSystemManager:
active_script = cthulhu_state.activeScript active_script = cthulhu_state.activeScript
for binding in bindings.keyBindings: for binding in bindings.keyBindings:
active_script.getKeyBindings().add(binding) active_script.getKeyBindings().add(binding)
grab_ids = self.app.addKeyGrab(binding) from . import cthulhu
grab_ids = cthulhu.addKeyGrab(binding)
if grab_ids: if grab_ids:
binding._grab_ids = grab_ids binding._grab_ids = grab_ids
+23 -41
View File
@@ -12,6 +12,7 @@
import logging import logging
from cthulhu.plugin import Plugin, cthulhu_hookimpl from cthulhu.plugin import Plugin, cthulhu_hookimpl
from cthulhu import cthulhuVersion from cthulhu import cthulhuVersion
from cthulhu import debug
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -21,8 +22,9 @@ class DisplayVersion(Plugin):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Initialize the plugin.""" """Initialize the plugin."""
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
logger.info("DisplayVersion plugin initialized") debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Plugin initialized", True)
self._kb_binding = None self._kb_binding = None
self._activated = False
@cthulhu_hookimpl @cthulhu_hookimpl
def activate(self, plugin=None): def activate(self, plugin=None):
@@ -31,32 +33,24 @@ class DisplayVersion(Plugin):
if plugin is not None and plugin is not self: if plugin is not None and plugin is not self:
return return
try: # Prevent duplicate activation
logger.info("=== DisplayVersion plugin activation starting ===") if self._activated:
logger.info(f"Plugin name: {self.name}") debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Already activated, skipping", True)
logger.info(f"App reference: {self.app}") return
# Also write to a debug file try:
with open('/tmp/displayversion_debug.log', 'a') as f: # Use debug.printMessage to ensure it goes to the main debug log
f.write("=== DisplayVersion plugin activation starting ===\n") debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Plugin activation starting", True)
f.write(f"Plugin name: {self.name}\n") debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: Plugin name: {self.name}", True)
f.write(f"App reference: {self.app}\n")
# Check if we have access to the API helper # Check if we have access to the API helper
if not self.app: if not self.app:
logger.error("DisplayVersion: No app reference available!") debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: ERROR - No app reference available!", True)
return 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 # Register keyboard shortcut
gesture_string = 'kb:cthulhu+shift+v' gesture_string = 'kb:cthulhu+shift+v'
logger.info(f"DisplayVersion: Attempting to register gesture: {gesture_string}") debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: Registering gesture: {gesture_string}", True)
self._kb_binding = self.registerGestureByString( self._kb_binding = self.registerGestureByString(
self.speakText, self.speakText,
@@ -64,30 +58,17 @@ class DisplayVersion(Plugin):
gesture_string gesture_string
) )
logger.info(f"DisplayVersion: Registered keybinding result: {self._kb_binding}")
if self._kb_binding: if self._kb_binding:
logger.info(f"Binding keysymstring: {self._kb_binding.keysymstring}") debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: Keybinding registered successfully", True)
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: else:
logger.error("DisplayVersion: Failed to create keybinding!") debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: ERROR - Failed to create keybinding!", True)
with open('/tmp/displayversion_debug.log', 'a') as f:
f.write("ERROR: Failed to create keybinding!\n")
logger.info("=== DisplayVersion plugin activation completed ===") self._activated = True
with open('/tmp/displayversion_debug.log', 'a') as f: debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Plugin activation completed", True)
f.write("=== DisplayVersion plugin activation completed ===\n\n")
except Exception as e: except Exception as e:
logger.error(f"Error activating DisplayVersion plugin: {e}") debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: ERROR activating plugin: {e}", True)
import traceback import traceback
logger.error(traceback.format_exc()) debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: {traceback.format_exc()}", True)
@cthulhu_hookimpl @cthulhu_hookimpl
def deactivate(self, plugin=None): def deactivate(self, plugin=None):
@@ -96,12 +77,13 @@ class DisplayVersion(Plugin):
if plugin is not None and plugin is not self: if plugin is not None and plugin is not self:
return return
logger.info("Deactivating DisplayVersion plugin") self._activated = False
debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Plugin deactivated", True)
def speakText(self, script=None, inputEvent=None): def speakText(self, script=None, inputEvent=None):
"""Speak the Cthulhu version when shortcut is pressed.""" """Speak the Cthulhu version when shortcut is pressed."""
try: try:
logger.info("DisplayVersion plugin: speakText called") debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: speakText called", True)
if self.app: if self.app:
state = self.app.getDynamicApiManager().getAPI('CthulhuState') state = self.app.getDynamicApiManager().getAPI('CthulhuState')
if state.activeScript: if state.activeScript:
@@ -111,5 +93,5 @@ class DisplayVersion(Plugin):
) )
return True return True
except Exception as e: except Exception as e:
logger.error(f"Error in DisplayVersion speakText: {e}") debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: ERROR in speakText: {e}", True)
return False return False
+15 -7
View File
@@ -44,7 +44,8 @@ class IndentationAudio(Plugin):
self._tone_duration = 0.15 # Seconds self._tone_duration = 0.15 # Seconds
self._max_frequency = 1200 # Cap frequency to avoid harsh sounds self._max_frequency = 1200 # Cap frequency to avoid harsh sounds
logger.info("IndentationAudio plugin initialized") self._activated = False
debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin initialized", True)
@cthulhu_hookimpl @cthulhu_hookimpl
def activate(self, plugin=None): def activate(self, plugin=None):
@@ -52,8 +53,13 @@ class IndentationAudio(Plugin):
if plugin is not None and plugin is not self: if plugin is not None and plugin is not self:
return return
# Prevent duplicate activation
if self._activated:
debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Already activated, skipping", True)
return
try: try:
logger.info("=== IndentationAudio plugin activation starting ===") debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin activation starting", True)
# Initialize sound player # Initialize sound player
if SOUND_AVAILABLE: if SOUND_AVAILABLE:
@@ -69,11 +75,12 @@ class IndentationAudio(Plugin):
# Connect to text caret movement events # Connect to text caret movement events
self._connect_to_events() self._connect_to_events()
logger.info("IndentationAudio plugin activated successfully") self._activated = True
debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin activated successfully", True)
return True return True
except Exception as e: except Exception as e:
logger.error(f"Failed to activate IndentationAudio plugin: {e}") debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: ERROR activating plugin: {e}", True)
return False return False
@cthulhu_hookimpl @cthulhu_hookimpl
@@ -83,7 +90,7 @@ class IndentationAudio(Plugin):
return return
try: try:
logger.info("=== IndentationAudio plugin deactivation starting ===") debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin deactivation starting", True)
# Disconnect from events # Disconnect from events
self._disconnect_from_events() self._disconnect_from_events()
@@ -91,11 +98,12 @@ class IndentationAudio(Plugin):
# Clear tracking data # Clear tracking data
self._last_indentation_level.clear() self._last_indentation_level.clear()
logger.info("IndentationAudio plugin deactivated successfully") self._activated = False
debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin deactivated successfully", True)
return True return True
except Exception as e: except Exception as e:
logger.error(f"Failed to deactivate IndentationAudio plugin: {e}") debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: ERROR deactivating plugin: {e}", True)
return False return False
def _register_keybinding(self): def _register_keybinding(self):
+18 -11
View File
@@ -35,8 +35,9 @@ class PluginManager(Plugin):
self._kb_binding = None self._kb_binding = None
self._dialog = None self._dialog = None
self._plugin_checkboxes = {} self._plugin_checkboxes = {}
self._activated = False
logger.info("PluginManager plugin initialized") debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin initialized", True)
@cthulhu_hookimpl @cthulhu_hookimpl
def activate(self, plugin=None): def activate(self, plugin=None):
@@ -44,17 +45,23 @@ class PluginManager(Plugin):
if plugin is not None and plugin is not self: if plugin is not None and plugin is not self:
return return
# Prevent duplicate activation
if self._activated:
debug.printMessage(debug.LEVEL_INFO, "PluginManager: Already activated, skipping", True)
return
try: try:
logger.info("=== PluginManager plugin activation starting ===") debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin activation starting", True)
# Register keybinding for opening plugin manager (Cthulhu+Shift+P) # Register keybinding for opening plugin manager (Cthulhu+Shift+P)
self._register_keybinding() self._register_keybinding()
logger.info("PluginManager plugin activated successfully") self._activated = True
debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin activated successfully", True)
return True return True
except Exception as e: except Exception as e:
logger.error(f"Failed to activate PluginManager plugin: {e}") debug.printMessage(debug.LEVEL_INFO, f"PluginManager: ERROR activating plugin: {e}", True)
return False return False
@cthulhu_hookimpl @cthulhu_hookimpl
@@ -64,25 +71,26 @@ class PluginManager(Plugin):
return return
try: try:
logger.info("=== PluginManager plugin deactivation starting ===") debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin deactivation starting", True)
# Close dialog if open # Close dialog if open
if self._dialog: if self._dialog:
self._dialog.destroy() self._dialog.destroy()
self._dialog = None self._dialog = None
logger.info("PluginManager plugin deactivated successfully") self._activated = False
debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin deactivated successfully", True)
return True return True
except Exception as e: except Exception as e:
logger.error(f"Failed to deactivate PluginManager plugin: {e}") debug.printMessage(debug.LEVEL_INFO, f"PluginManager: ERROR deactivating plugin: {e}", True)
return False return False
def _register_keybinding(self): def _register_keybinding(self):
"""Register the Cthulhu+Shift+P keybinding for opening plugin manager.""" """Register the Cthulhu+Shift+P keybinding for opening plugin manager."""
try: try:
if not self.app: if not self.app:
logger.error("PluginManager: No app reference available for keybinding") debug.printMessage(debug.LEVEL_INFO, "PluginManager: ERROR - No app reference available for keybinding", True)
return return
# Register Cthulhu+Shift+P keybinding # Register Cthulhu+Shift+P keybinding
@@ -96,13 +104,12 @@ class PluginManager(Plugin):
) )
if self._kb_binding: if self._kb_binding:
logger.info(f"PluginManager: Registered keybinding {gesture_string}")
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Registered keybinding {gesture_string}", True) debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Registered keybinding {gesture_string}", True)
else: else:
logger.error(f"PluginManager: Failed to register keybinding {gesture_string}") debug.printMessage(debug.LEVEL_INFO, f"PluginManager: ERROR - Failed to register keybinding {gesture_string}", True)
except Exception as e: except Exception as e:
logger.error(f"PluginManager: Error registering keybinding: {e}") debug.printMessage(debug.LEVEL_INFO, f"PluginManager: ERROR registering keybinding: {e}", True)
def _open_plugin_manager(self, script, inputEvent=None): def _open_plugin_manager(self, script, inputEvent=None):
"""Open the plugin manager dialog.""" """Open the plugin manager dialog."""