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

This commit is contained in:
Storm Dragon
2025-08-05 23:57:23 -04:00
parent 37bd89ab87
commit c9cfe3e0f4
6 changed files with 137 additions and 107 deletions

View File

@@ -76,7 +76,8 @@ class ActionList(Gtk.Window):
self.set_skip_taskbar_hint(True)
self.set_skip_pager_hint(True)
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)
# Create scrolled window
@@ -158,9 +159,9 @@ class ActionPresenter:
self._obj = None
self._window = None
# Initialize handlers and bindings
# Initialize handlers
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:
"""Returns a dictionary of input event handlers."""
@@ -207,7 +208,7 @@ class ActionPresenter:
self._bindings = keybindings.KeyBindings()
self._bindings.add(keybindings.KeyBinding(
"a",
keybindings.DEFAULT_MODIFIER_MASK,
keybindings.defaultModifierMask,
keybindings.CTHULHU_SHIFT_MODIFIER_MASK,
self._handlers["show_actions_list"]))
@@ -221,7 +222,7 @@ class ActionPresenter:
debug.printTokens(debug.LEVEL_INFO, tokens, True)
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_manager.getManager().setActiveScript(script, reason)
@@ -247,48 +248,77 @@ class ActionPresenter:
result = AXObject.do_named_action(self._obj, action)
tokens = ["ACTION PRESENTER: Performing", action, "on", self._obj, "succeeded:", result]
debug.printTokens(debug.LEVEL_INFO, tokens, True)
if not result:
full_message = messages.ACTION_NOT_PERFORMED % action
cthulhu.presentMessage(full_message)
self._gui.destroy()
# Use idle_add for asynchronous destruction to allow action to complete
GLib.idle_add(self._gui.destroy)
def present_with_time(self, obj, start_time: float) -> bool:
"""Presents accessible actions for the given object with timing."""
if self._gui is not None:
msg = "ACTION PRESENTER: GUI already exists."
debug.printMessage(debug.LEVEL_INFO, msg, True)
try:
debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: present_with_time called with obj: {obj}", 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
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:
"""Shows the accessible actions list."""
start_time = time.time()
return self.present_with_time(input_event.get_object(), start_time)
try:
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()

View File

@@ -142,14 +142,14 @@ class APIHelper:
bindings.add(binding)
# 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}")
# 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, "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)
logger.info("=== APIHelper.registerGestureByString completed ===")
@@ -674,17 +674,19 @@ def loadUserSettings(script=None, inputEvent=None, skipReloadMessage=False):
_storeXmodmap(_cthulhuModifiers)
_createCthulhuXmodmap()
activePlugins = list(_settingsManager.getSetting('activePlugins'))
cthulhuApp.getPluginSystemManager().setActivePlugins(activePlugins)
# Activate core systems FIRST before loading plugins
_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()
# 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')

View File

@@ -135,7 +135,8 @@ class PluginSystemManager:
active_script = cthulhu_state.activeScript
for binding in bindings.keyBindings:
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

View File

@@ -12,6 +12,7 @@
import logging
from cthulhu.plugin import Plugin, cthulhu_hookimpl
from cthulhu import cthulhuVersion
from cthulhu import debug
logger = logging.getLogger(__name__)
@@ -21,8 +22,9 @@ class DisplayVersion(Plugin):
def __init__(self, *args, **kwargs):
"""Initialize the plugin."""
super().__init__(*args, **kwargs)
logger.info("DisplayVersion plugin initialized")
debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Plugin initialized", True)
self._kb_binding = None
self._activated = False
@cthulhu_hookimpl
def activate(self, plugin=None):
@@ -31,32 +33,24 @@ class DisplayVersion(Plugin):
if plugin is not None and plugin is not self:
return
try:
logger.info("=== DisplayVersion plugin activation starting ===")
logger.info(f"Plugin name: {self.name}")
logger.info(f"App reference: {self.app}")
# Prevent duplicate activation
if self._activated:
debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Already activated, skipping", True)
return
# 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")
try:
# Use debug.printMessage to ensure it goes to the main debug log
debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Plugin activation starting", True)
debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: Plugin name: {self.name}", True)
# Check if we have access to the API helper
if not self.app:
logger.error("DisplayVersion: No app reference available!")
debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: ERROR - No app reference available!", True)
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}")
debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: Registering gesture: {gesture_string}", True)
self._kb_binding = self.registerGestureByString(
self.speakText,
@@ -64,30 +58,17 @@ class DisplayVersion(Plugin):
gesture_string
)
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")
debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: Keybinding registered successfully", True)
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")
debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: ERROR - Failed to create keybinding!", True)
logger.info("=== DisplayVersion plugin activation completed ===")
with open('/tmp/displayversion_debug.log', 'a') as f:
f.write("=== DisplayVersion plugin activation completed ===\n\n")
self._activated = True
debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Plugin activation completed", True)
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
logger.error(traceback.format_exc())
debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: {traceback.format_exc()}", True)
@cthulhu_hookimpl
def deactivate(self, plugin=None):
@@ -96,12 +77,13 @@ class DisplayVersion(Plugin):
if plugin is not None and plugin is not self:
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):
"""Speak the Cthulhu version when shortcut is pressed."""
try:
logger.info("DisplayVersion plugin: speakText called")
debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: speakText called", True)
if self.app:
state = self.app.getDynamicApiManager().getAPI('CthulhuState')
if state.activeScript:
@@ -111,5 +93,5 @@ class DisplayVersion(Plugin):
)
return True
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

View File

@@ -44,7 +44,8 @@ class IndentationAudio(Plugin):
self._tone_duration = 0.15 # Seconds
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
def activate(self, plugin=None):
@@ -52,8 +53,13 @@ class IndentationAudio(Plugin):
if plugin is not None and plugin is not self:
return
# Prevent duplicate activation
if self._activated:
debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Already activated, skipping", True)
return
try:
logger.info("=== IndentationAudio plugin activation starting ===")
debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin activation starting", True)
# Initialize sound player
if SOUND_AVAILABLE:
@@ -69,11 +75,12 @@ class IndentationAudio(Plugin):
# Connect to text caret movement 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
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
@cthulhu_hookimpl
@@ -83,7 +90,7 @@ class IndentationAudio(Plugin):
return
try:
logger.info("=== IndentationAudio plugin deactivation starting ===")
debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin deactivation starting", True)
# Disconnect from events
self._disconnect_from_events()
@@ -91,11 +98,12 @@ class IndentationAudio(Plugin):
# Clear tracking data
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
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
def _register_keybinding(self):

View File

@@ -35,8 +35,9 @@ class PluginManager(Plugin):
self._kb_binding = None
self._dialog = None
self._plugin_checkboxes = {}
self._activated = False
logger.info("PluginManager plugin initialized")
debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin initialized", True)
@cthulhu_hookimpl
def activate(self, plugin=None):
@@ -44,17 +45,23 @@ class PluginManager(Plugin):
if plugin is not None and plugin is not self:
return
# Prevent duplicate activation
if self._activated:
debug.printMessage(debug.LEVEL_INFO, "PluginManager: Already activated, skipping", True)
return
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)
self._register_keybinding()
logger.info("PluginManager plugin activated successfully")
self._activated = True
debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin activated successfully", True)
return True
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
@cthulhu_hookimpl
@@ -64,25 +71,26 @@ class PluginManager(Plugin):
return
try:
logger.info("=== PluginManager plugin deactivation starting ===")
debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin deactivation starting", True)
# Close dialog if open
if self._dialog:
self._dialog.destroy()
self._dialog = None
logger.info("PluginManager plugin deactivated successfully")
self._activated = False
debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin deactivated successfully", True)
return True
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
def _register_keybinding(self):
"""Register the Cthulhu+Shift+P keybinding for opening plugin manager."""
try:
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
# Register Cthulhu+Shift+P keybinding
@@ -96,13 +104,12 @@ class PluginManager(Plugin):
)
if self._kb_binding:
logger.info(f"PluginManager: Registered keybinding {gesture_string}")
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Registered keybinding {gesture_string}", True)
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:
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):
"""Open the plugin manager dialog."""