Compare commits
33 Commits
ec90906052
...
408fb85730
Author | SHA1 | Date | |
---|---|---|---|
|
408fb85730 | ||
|
0f25245d3d | ||
|
13f110ab34 | ||
|
62f46c0eb7 | ||
|
e2364a154a | ||
|
2090767794 | ||
|
ea50d8b024 | ||
|
a1d90a7245 | ||
|
2eb6d3c7dd | ||
|
0edbefac47 | ||
|
90aecf8055 | ||
|
48d99e8813 | ||
|
314aa18a1b | ||
|
a21f1aa13b | ||
|
5181944de0 | ||
|
0a8bb684ec | ||
|
d94ba1accb | ||
|
399f449484 | ||
|
ecd122786f | ||
|
01273618a7 | ||
|
d8df2ed757 | ||
|
c376b2489a | ||
|
39dca0574a | ||
|
8b1f501fe7 | ||
|
96335baf5d | ||
|
51984a6540 | ||
|
3296e5d571 | ||
|
1e6f4b8913 | ||
|
331b1c3ad5 | ||
|
04b8592ed3 | ||
|
c64591a162 | ||
|
80212d616f | ||
|
9790a8d494 |
@ -37,103 +37,118 @@ import faulthandler
|
|||||||
|
|
||||||
class APIHelper:
|
class APIHelper:
|
||||||
"""Helper class for plugin API interactions, including keybindings."""
|
"""Helper class for plugin API interactions, including keybindings."""
|
||||||
|
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
"""Initialize the APIHelper.
|
"""Initialize the APIHelper.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
- app: the Cthulhu application
|
- app: the Cthulhu application
|
||||||
"""
|
"""
|
||||||
self.app = app
|
self.app = app
|
||||||
self._gestureBindings = {}
|
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.
|
"""Register a gesture by string."""
|
||||||
|
import logging
|
||||||
Arguments:
|
logger = logging.getLogger(__name__)
|
||||||
- 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
|
|
||||||
"""
|
|
||||||
if not gestureString.startswith("kb:"):
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
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
|
# Extract the key portion from the gesture string
|
||||||
key = gestureString.split(":", 1)[1]
|
key = gestureString.split(":", 1)[1]
|
||||||
|
logger.info(f"Extracted key: {key}")
|
||||||
|
|
||||||
# Handle Cthulhu modifier specially
|
# Handle Cthulhu modifier specially
|
||||||
if "cthulhu+" in key.lower():
|
if "cthulhu+" in key.lower():
|
||||||
from . import keybindings
|
from . import keybindings
|
||||||
key_parts = key.lower().split("+")
|
key_parts = key.lower().split("+")
|
||||||
|
logger.info(f"Key parts: {key_parts}")
|
||||||
# Determine appropriate modifier mask
|
|
||||||
|
# Start with the base Cthulhu modifier
|
||||||
modifiers = keybindings.CTHULHU_MODIFIER_MASK
|
modifiers = keybindings.CTHULHU_MODIFIER_MASK
|
||||||
|
|
||||||
# Extract the final key (without modifiers)
|
# Extract the final key (without modifiers)
|
||||||
final_key = key_parts[-1]
|
final_key = key_parts[-1]
|
||||||
|
logger.info(f"Final key: {final_key}")
|
||||||
# Check for additional modifiers
|
|
||||||
|
# Check for additional modifiers and combine them properly
|
||||||
if "shift" in key_parts:
|
if "shift" in key_parts:
|
||||||
|
# Use the pre-defined combined mask rather than trying to OR them
|
||||||
modifiers = keybindings.CTHULHU_SHIFT_MODIFIER_MASK
|
modifiers = keybindings.CTHULHU_SHIFT_MODIFIER_MASK
|
||||||
elif "ctrl" in key_parts or "control" in key_parts:
|
logger.info(f"Using CTHULHU_SHIFT_MODIFIER_MASK: {modifiers}")
|
||||||
modifiers = keybindings.CTHULHU_CTRL_MODIFIER_MASK
|
else:
|
||||||
elif "alt" in key_parts:
|
logger.info(f"Using CTHULHU_MODIFIER_MASK: {modifiers}")
|
||||||
modifiers = keybindings.CTHULHU_ALT_MODIFIER_MASK
|
|
||||||
|
|
||||||
# Create a keybinding handler
|
# Create a keybinding handler
|
||||||
class GestureHandler:
|
class GestureHandler:
|
||||||
def __init__(self, function, description):
|
def __init__(self, function, description):
|
||||||
self.function = function
|
self.function = function
|
||||||
self.description = description
|
self.description = description
|
||||||
|
|
||||||
def __call__(self, script, inputEvent):
|
def __call__(self, script, inputEvent):
|
||||||
try:
|
try:
|
||||||
|
logger.info(f"=== DisplayVersion keybinding handler called! ===")
|
||||||
return function(script, inputEvent)
|
return function(script, inputEvent)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
import logging
|
import logging
|
||||||
logging.getLogger(__name__).error(f"Error in keybinding handler: {e}")
|
logging.getLogger(__name__).error(f"Error in keybinding handler: {e}")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
handler = GestureHandler(function, name)
|
handler = GestureHandler(function, name)
|
||||||
|
logger.info(f"Created handler: {handler}")
|
||||||
# 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
|
from . import cthulhu_state
|
||||||
|
from . import keybindings
|
||||||
|
binding = keybindings.KeyBinding(
|
||||||
|
final_key,
|
||||||
|
keybindings.defaultModifierMask,
|
||||||
|
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:
|
if cthulhu_state.activeScript:
|
||||||
|
logger.info(f"Adding binding to active script: {cthulhu_state.activeScript}")
|
||||||
bindings = cthulhu_state.activeScript.getKeyBindings()
|
bindings = cthulhu_state.activeScript.getKeyBindings()
|
||||||
binding = keybindings.KeyBinding(
|
|
||||||
final_key,
|
|
||||||
keybindings.defaultModifierMask,
|
|
||||||
modifiers,
|
|
||||||
handler)
|
|
||||||
|
|
||||||
# Add the binding to the active script
|
|
||||||
bindings.add(binding)
|
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
|
# Register key grab at the system level
|
||||||
grab_ids = self.app.addKeyGrab(binding)
|
grab_ids = self.app.addKeyGrab(binding)
|
||||||
|
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:
|
||||||
return binding
|
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
|
return None
|
||||||
|
|
||||||
def unregisterShortcut(self, binding, contextName=None):
|
def unregisterShortcut(self, binding, contextName=None):
|
||||||
"""Unregister a previously registered shortcut.
|
"""Unregister a previously registered shortcut.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
- binding: the binding to unregister
|
- binding: the binding to unregister
|
||||||
- contextName: the context for this gesture
|
- contextName: the context for this gesture
|
||||||
@ -143,12 +158,12 @@ class APIHelper:
|
|||||||
if cthulhu_state.activeScript:
|
if cthulhu_state.activeScript:
|
||||||
bindings = cthulhu_state.activeScript.getKeyBindings()
|
bindings = cthulhu_state.activeScript.getKeyBindings()
|
||||||
bindings.remove(binding)
|
bindings.remove(binding)
|
||||||
|
|
||||||
# Remove key grab at system level
|
# Remove key grab at system level
|
||||||
if hasattr(binding, '_grab_ids'):
|
if hasattr(binding, '_grab_ids'):
|
||||||
for grab_id in binding._grab_ids:
|
for grab_id in binding._grab_ids:
|
||||||
self.app.removeKeyGrab(grab_id)
|
self.app.removeKeyGrab(grab_id)
|
||||||
|
|
||||||
# Remove from tracking
|
# Remove from tracking
|
||||||
if contextName in self._gestureBindings:
|
if contextName in self._gestureBindings:
|
||||||
if binding in self._gestureBindings[contextName]:
|
if binding in self._gestureBindings[contextName]:
|
||||||
@ -648,6 +663,12 @@ def loadUserSettings(script=None, inputEvent=None, skipReloadMessage=False):
|
|||||||
|
|
||||||
_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.getPluginSystemManager().register_plugin_keybindings_with_active_script()
|
||||||
|
|
||||||
cthulhuApp.getSignalManager().emitSignal('load-setting-completed')
|
cthulhuApp.getSignalManager().emitSignal('load-setting-completed')
|
||||||
|
|
||||||
|
@ -23,5 +23,5 @@
|
|||||||
# Fork of Orca Screen Reader (GNOME)
|
# Fork of Orca Screen Reader (GNOME)
|
||||||
# Original source: https://gitlab.gnome.org/GNOME/orca
|
# Original source: https://gitlab.gnome.org/GNOME/orca
|
||||||
|
|
||||||
version = "2025.04.18"
|
version = "2025.06.05"
|
||||||
codeName = "testing"
|
codeName = "plugins"
|
||||||
|
@ -298,7 +298,7 @@ def println(level, text="", timestamp=False, stack=False):
|
|||||||
text = text.replace("\ufffc", "[OBJ]")
|
text = text.replace("\ufffc", "[OBJ]")
|
||||||
if timestamp:
|
if timestamp:
|
||||||
text = text.replace("\n", f"\n{' ' * 18}")
|
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:
|
if stack:
|
||||||
text += f" {_stackAsString()}"
|
text += f" {_stackAsString()}"
|
||||||
|
|
||||||
|
@ -112,6 +112,13 @@ class EventManager:
|
|||||||
cthulhu_state.device.key_watcher = cthulhu_state.device.add_key_watcher(
|
cthulhu_state.device.key_watcher = cthulhu_state.device.add_key_watcher(
|
||||||
self._processNewKeyboardEvent)
|
self._processNewKeyboardEvent)
|
||||||
self.newKeyHandlingActive = True
|
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:
|
||||||
|
pass # plugin_manager.register_plugin_keybindings_with_active_script()
|
||||||
|
|
||||||
def activateLegacyKeyHandling(self):
|
def activateLegacyKeyHandling(self):
|
||||||
if not self.legacyKeyHandlingActive:
|
if not self.legacyKeyHandlingActive:
|
||||||
|
@ -251,6 +251,23 @@ class KeyBinding:
|
|||||||
if not self.keycode:
|
if not self.keycode:
|
||||||
self.keycode = getKeycode(self.keysymstring)
|
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:
|
if self.keycode == keycode:
|
||||||
result = modifiers & self.modifier_mask
|
result = modifiers & self.modifier_mask
|
||||||
return result == self.modifiers
|
return result == self.modifiers
|
||||||
@ -470,13 +487,41 @@ class KeyBindings:
|
|||||||
given keycode and modifiers, or None if no match exists.
|
given keycode and modifiers, or None if no match exists.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# 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 = []
|
matches = []
|
||||||
candidates = []
|
candidates = []
|
||||||
clickCount = keyboardEvent.getClickCount()
|
clickCount = keyboardEvent.getClickCount()
|
||||||
for keyBinding in self.keyBindings:
|
for keyBinding in self.keyBindings:
|
||||||
if keyBinding.matches(keyboardEvent.hw_code, keyboardEvent.modifiers):
|
if keyBinding.matches(keyboardEvent.hw_code, keyboardEvent.modifiers):
|
||||||
if keyBinding.modifier_mask == keyboardEvent.modifiers and \
|
if event_str.lower() == 'v':
|
||||||
keyBinding.click_count == clickCount:
|
logger.info(f"MATCH found! keysym={keyBinding.keysymstring}, desc={keyBinding.handler.description}")
|
||||||
|
if (keyboardEvent.modifiers & keyBinding.modifier_mask) == keyBinding.modifiers and \
|
||||||
|
keyBinding.click_count == clickCount:
|
||||||
matches.append(keyBinding)
|
matches.append(keyBinding)
|
||||||
# If there's no keysymstring, it's unbound and cannot be
|
# If there's no keysymstring, it's unbound and cannot be
|
||||||
# a match.
|
# a match.
|
||||||
@ -484,8 +529,17 @@ class KeyBindings:
|
|||||||
if keyBinding.keysymstring:
|
if keyBinding.keysymstring:
|
||||||
candidates.append(keyBinding)
|
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)
|
self._checkMatchingBindings(keyboardEvent, matches)
|
||||||
if matches:
|
if matches:
|
||||||
|
if event_str.lower() == 'v':
|
||||||
|
logger.info(f"Returning exact match handler: {matches[0].handler.description}")
|
||||||
return matches[0].handler
|
return matches[0].handler
|
||||||
|
|
||||||
if keyboardEvent.isKeyPadKeyWithNumlockOn():
|
if keyboardEvent.isKeyPadKeyWithNumlockOn():
|
||||||
@ -499,8 +553,12 @@ class KeyBindings:
|
|||||||
self._checkMatchingBindings(keyboardEvent, candidates)
|
self._checkMatchingBindings(keyboardEvent, candidates)
|
||||||
for candidate in candidates:
|
for candidate in candidates:
|
||||||
if candidate.click_count <= clickCount:
|
if candidate.click_count <= clickCount:
|
||||||
|
if event_str.lower() == 'v':
|
||||||
|
logger.info(f"Returning candidate handler: {candidate.handler.description}")
|
||||||
return candidate.handler
|
return candidate.handler
|
||||||
|
|
||||||
|
if event_str.lower() == 'v':
|
||||||
|
logger.info("No handler found!")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def load(self, keymap, handlers):
|
def load(self, keymap, handlers):
|
||||||
|
@ -21,7 +21,7 @@ except ImportError:
|
|||||||
# Fallback if pluggy is not available
|
# Fallback if pluggy is not available
|
||||||
def cthulhu_hookimpl(func=None, **kwargs):
|
def cthulhu_hookimpl(func=None, **kwargs):
|
||||||
"""Fallback decorator when pluggy is not available.
|
"""Fallback decorator when pluggy is not available.
|
||||||
|
|
||||||
This is a no-op decorator that returns the original function.
|
This is a no-op decorator that returns the original function.
|
||||||
It allows the code to continue working without pluggy, though
|
It allows the code to continue working without pluggy, though
|
||||||
plugins will be disabled.
|
plugins will be disabled.
|
||||||
@ -47,6 +47,8 @@ class Plugin:
|
|||||||
self.name = ''
|
self.name = ''
|
||||||
self.version = ''
|
self.version = ''
|
||||||
self.description = ''
|
self.description = ''
|
||||||
|
self._bindings = None
|
||||||
|
self._gestureBindings = {}
|
||||||
|
|
||||||
def set_app(self, app):
|
def set_app(self, app):
|
||||||
"""Set the application reference."""
|
"""Set the application reference."""
|
||||||
@ -75,12 +77,16 @@ class Plugin:
|
|||||||
return
|
return
|
||||||
logger.info(f"Deactivating plugin: {self.name}")
|
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):
|
def registerGestureByString(self, function, name, gestureString, learnModeEnabled=True):
|
||||||
"""Register a gesture by string."""
|
"""Register a gesture by string."""
|
||||||
if self.app:
|
if self.app:
|
||||||
api_helper = self.app.getAPIHelper()
|
api_helper = self.app.getAPIHelper()
|
||||||
if api_helper:
|
if api_helper:
|
||||||
return api_helper.registerGestureByString(
|
binding = api_helper.registerGestureByString(
|
||||||
function,
|
function,
|
||||||
name,
|
name,
|
||||||
gestureString,
|
gestureString,
|
||||||
@ -89,4 +95,13 @@ class Plugin:
|
|||||||
learnModeEnabled,
|
learnModeEnabled,
|
||||||
contextName=self.module_name
|
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
|
return None
|
||||||
|
@ -112,11 +112,190 @@ class PluginSystemManager:
|
|||||||
|
|
||||||
# Create plugin directories
|
# Create plugin directories
|
||||||
self._setup_plugin_dirs()
|
self._setup_plugin_dirs()
|
||||||
|
|
||||||
# Log available plugins directory paths
|
# Log available plugins directory paths
|
||||||
logger.info(f"System plugins directory: {PluginType.SYSTEM.get_root_dir()}")
|
logger.info(f"System plugins directory: {PluginType.SYSTEM.get_root_dir()}")
|
||||||
logger.info(f"User plugins directory: {PluginType.USER.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.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}")
|
||||||
|
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."""
|
||||||
|
|
||||||
|
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("=== 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
|
||||||
|
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}")
|
||||||
|
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():
|
||||||
|
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}")
|
||||||
|
# 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")
|
||||||
|
|
||||||
|
def _on_settings_changed(self, app=None):
|
||||||
|
"""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)
|
||||||
|
from . import cthulhu
|
||||||
|
grab_ids = cthulhu.addKeyGrab(binding)
|
||||||
|
if grab_ids:
|
||||||
|
binding._grab_ids = grab_ids
|
||||||
|
|
||||||
def _setup_plugin_dirs(self):
|
def _setup_plugin_dirs(self):
|
||||||
"""Ensure plugin directories exist."""
|
"""Ensure plugin directories exist."""
|
||||||
os.makedirs(PluginType.SYSTEM.get_root_dir(), exist_ok=True)
|
os.makedirs(PluginType.SYSTEM.get_root_dir(), exist_ok=True)
|
||||||
@ -218,26 +397,34 @@ class PluginSystemManager:
|
|||||||
|
|
||||||
def setActivePlugins(self, activePlugins):
|
def setActivePlugins(self, activePlugins):
|
||||||
"""Set active plugins and sync their state."""
|
"""Set active plugins and sync their state."""
|
||||||
|
logger.info(f"=== PluginSystemManager.setActivePlugins called ===")
|
||||||
logger.info(f"Setting active plugins: {activePlugins}")
|
logger.info(f"Setting active plugins: {activePlugins}")
|
||||||
|
|
||||||
# Make sure we have scanned for plugins first
|
# Make sure we have scanned for plugins first
|
||||||
if not self._plugins:
|
if not self._plugins:
|
||||||
logger.info("No plugins found, rescanning...")
|
logger.info("No plugins found, rescanning...")
|
||||||
self.rescanPlugins()
|
self.rescanPlugins()
|
||||||
|
|
||||||
self._active_plugins = activePlugins
|
self._active_plugins = activePlugins
|
||||||
|
|
||||||
# Log active vs available plugins
|
# Log active vs available plugins
|
||||||
available_plugins = [p.get_module_name() for p in self.plugins]
|
available_plugins = [p.get_module_name() for p in self.plugins]
|
||||||
logger.info(f"Available plugins: {available_plugins}")
|
logger.info(f"Available plugins: {available_plugins}")
|
||||||
logger.info(f"Active plugins: {self._active_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
|
# Find missing plugins
|
||||||
missing_plugins = [p for p in self._active_plugins if p not in available_plugins]
|
missing_plugins = [p for p in self._active_plugins if p not in available_plugins]
|
||||||
if missing_plugins:
|
if missing_plugins:
|
||||||
logger.warning(f"Active plugins not found: {missing_plugins}")
|
logger.warning(f"Active plugins not found: {missing_plugins}")
|
||||||
|
|
||||||
self.syncAllPluginsActive()
|
self.syncAllPluginsActive()
|
||||||
|
logger.info("=== PluginSystemManager.setActivePlugins completed ===")
|
||||||
|
|
||||||
def setPluginActive(self, pluginInfo, active):
|
def setPluginActive(self, pluginInfo, active):
|
||||||
"""Set the active state of a plugin."""
|
"""Set the active state of a plugin."""
|
||||||
@ -258,7 +445,7 @@ class PluginSystemManager:
|
|||||||
def isPluginActive(self, pluginInfo):
|
def isPluginActive(self, pluginInfo):
|
||||||
"""Check if a plugin is active."""
|
"""Check if a plugin is active."""
|
||||||
module_name = pluginInfo.get_module_name()
|
module_name = pluginInfo.get_module_name()
|
||||||
|
|
||||||
# Builtin plugins are always active
|
# Builtin plugins are always active
|
||||||
if pluginInfo.builtin:
|
if pluginInfo.builtin:
|
||||||
logger.debug(f"Plugin {module_name} is builtin, active by default")
|
logger.debug(f"Plugin {module_name} is builtin, active by default")
|
||||||
@ -271,34 +458,34 @@ class PluginSystemManager:
|
|||||||
|
|
||||||
# Check case-insensitive match in active plugins list
|
# Check case-insensitive match in active plugins list
|
||||||
active_plugins = self.getActivePlugins()
|
active_plugins = self.getActivePlugins()
|
||||||
|
|
||||||
# Try exact match first
|
# Try exact match first
|
||||||
if module_name in active_plugins:
|
if module_name in active_plugins:
|
||||||
logger.debug(f"Plugin {module_name} found in active plugins list")
|
logger.debug(f"Plugin {module_name} found in active plugins list")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Try case-insensitive match
|
# Try case-insensitive match
|
||||||
module_name_lower = module_name.lower()
|
module_name_lower = module_name.lower()
|
||||||
is_active = any(plugin.lower() == module_name_lower for plugin in active_plugins)
|
is_active = any(plugin.lower() == module_name_lower for plugin in active_plugins)
|
||||||
|
|
||||||
if is_active:
|
if is_active:
|
||||||
logger.debug(f"Plugin {module_name} found in active plugins list (case-insensitive match)")
|
logger.debug(f"Plugin {module_name} found in active plugins list (case-insensitive match)")
|
||||||
else:
|
else:
|
||||||
logger.debug(f"Plugin {module_name} not found in active plugins list")
|
logger.debug(f"Plugin {module_name} not found in active plugins list")
|
||||||
|
|
||||||
return is_active
|
return is_active
|
||||||
|
|
||||||
def syncAllPluginsActive(self):
|
def syncAllPluginsActive(self):
|
||||||
"""Sync the active state of all plugins."""
|
"""Sync the active state of all plugins."""
|
||||||
logger.info("Syncing active state of all plugins")
|
logger.info("Syncing active state of all plugins")
|
||||||
|
|
||||||
# Log plugin status before syncing
|
# Log plugin status before syncing
|
||||||
if PLUGIN_DEBUG:
|
if PLUGIN_DEBUG:
|
||||||
for pluginInfo in self.plugins:
|
for pluginInfo in self.plugins:
|
||||||
is_active = self.isPluginActive(pluginInfo)
|
is_active = self.isPluginActive(pluginInfo)
|
||||||
is_loaded = pluginInfo.loaded
|
is_loaded = pluginInfo.loaded
|
||||||
logger.debug(f"Plugin {pluginInfo.get_module_name()}: active={is_active}, loaded={is_loaded}")
|
logger.debug(f"Plugin {pluginInfo.get_module_name()}: active={is_active}, loaded={is_loaded}")
|
||||||
|
|
||||||
# First unload inactive plugins
|
# First unload inactive plugins
|
||||||
for pluginInfo in self.plugins:
|
for pluginInfo in self.plugins:
|
||||||
if not self.isPluginActive(pluginInfo) and pluginInfo.loaded:
|
if not self.isPluginActive(pluginInfo) and pluginInfo.loaded:
|
||||||
@ -311,7 +498,7 @@ class PluginSystemManager:
|
|||||||
logger.info(f"Loading active plugin: {pluginInfo.get_module_name()}")
|
logger.info(f"Loading active plugin: {pluginInfo.get_module_name()}")
|
||||||
result = self.loadPlugin(pluginInfo)
|
result = self.loadPlugin(pluginInfo)
|
||||||
logger.info(f"Plugin {pluginInfo.get_module_name()} load result: {result}")
|
logger.info(f"Plugin {pluginInfo.get_module_name()} load result: {result}")
|
||||||
|
|
||||||
# Log final plugin status
|
# Log final plugin status
|
||||||
active_plugins = [p.get_module_name() for p in self.plugins if p.loaded]
|
active_plugins = [p.get_module_name() for p in self.plugins if p.loaded]
|
||||||
logger.info(f"Active plugins after sync: {active_plugins}")
|
logger.info(f"Active plugins after sync: {active_plugins}")
|
||||||
@ -326,7 +513,7 @@ class PluginSystemManager:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
module_name = pluginInfo.get_module_name()
|
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:
|
try:
|
||||||
# Already loaded?
|
# Already loaded?
|
||||||
@ -337,27 +524,27 @@ class PluginSystemManager:
|
|||||||
# Try to find the plugin file
|
# Try to find the plugin file
|
||||||
module_name = pluginInfo.get_module_name()
|
module_name = pluginInfo.get_module_name()
|
||||||
plugin_dir = pluginInfo.get_module_dir()
|
plugin_dir = pluginInfo.get_module_dir()
|
||||||
|
|
||||||
# Check for plugin.py first (standard format)
|
# Check for plugin.py first (standard format)
|
||||||
plugin_file = os.path.join(plugin_dir, "plugin.py")
|
plugin_file = os.path.join(plugin_dir, "plugin.py")
|
||||||
|
|
||||||
# Fall back to [PluginName].py if plugin.py doesn't exist
|
# Fall back to [PluginName].py if plugin.py doesn't exist
|
||||||
if not os.path.exists(plugin_file):
|
if not os.path.exists(plugin_file):
|
||||||
alternative_plugin_file = os.path.join(plugin_dir, f"{module_name}.py")
|
alternative_plugin_file = os.path.join(plugin_dir, f"{module_name}.py")
|
||||||
if os.path.exists(alternative_plugin_file):
|
if os.path.exists(alternative_plugin_file):
|
||||||
plugin_file = alternative_plugin_file
|
plugin_file = alternative_plugin_file
|
||||||
logger.info(f"Using alternative plugin file: {alternative_plugin_file}")
|
logger.info(f"Using alternative plugin file: {alternative_plugin_file}")
|
||||||
|
|
||||||
if not os.path.exists(plugin_file):
|
if not os.path.exists(plugin_file):
|
||||||
logger.error(f"Plugin file not found: {plugin_file}")
|
logger.error(f"Plugin file not found: {plugin_file}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logger.info(f"Loading plugin from: {plugin_file}")
|
logger.info(f"Loading plugin from: {plugin_file}")
|
||||||
spec = importlib.util.spec_from_file_location(module_name, plugin_file)
|
spec = importlib.util.spec_from_file_location(module_name, plugin_file)
|
||||||
if spec is None:
|
if spec is None:
|
||||||
logger.error(f"Failed to create spec for plugin: {module_name}")
|
logger.error(f"Failed to create spec for plugin: {module_name}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
module = importlib.util.module_from_spec(spec)
|
module = importlib.util.module_from_spec(spec)
|
||||||
spec.loader.exec_module(module)
|
spec.loader.exec_module(module)
|
||||||
pluginInfo.module = module
|
pluginInfo.module = module
|
||||||
@ -385,7 +572,7 @@ class PluginSystemManager:
|
|||||||
# Ensure plugins have a reference to the app
|
# Ensure plugins have a reference to the app
|
||||||
plugin_instance.app = self.getApp()
|
plugin_instance.app = self.getApp()
|
||||||
logger.info(f"Set app reference for plugin: {module_name}")
|
logger.info(f"Set app reference for plugin: {module_name}")
|
||||||
|
|
||||||
if hasattr(plugin_instance, 'set_app'):
|
if hasattr(plugin_instance, 'set_app'):
|
||||||
plugin_instance.set_app(self.getApp())
|
plugin_instance.set_app(self.getApp())
|
||||||
logger.info(f"Called set_app() for plugin: {module_name}")
|
logger.info(f"Called set_app() for plugin: {module_name}")
|
||||||
@ -398,10 +585,10 @@ class PluginSystemManager:
|
|||||||
if self.plugin_manager is None:
|
if self.plugin_manager is None:
|
||||||
logger.error(f"Plugin manager is None when loading {module_name}")
|
logger.error(f"Plugin manager is None when loading {module_name}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logger.info(f"Registering plugin with pluggy: {module_name}")
|
logger.info(f"Registering plugin with pluggy: {module_name}")
|
||||||
self.plugin_manager.register(plugin_instance)
|
self.plugin_manager.register(plugin_instance)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.info(f"Activating plugin: {module_name}")
|
logger.info(f"Activating plugin: {module_name}")
|
||||||
self.plugin_manager.hook.activate(plugin=plugin_instance)
|
self.plugin_manager.hook.activate(plugin=plugin_instance)
|
||||||
@ -413,6 +600,10 @@ class PluginSystemManager:
|
|||||||
|
|
||||||
pluginInfo.loaded = True
|
pluginInfo.loaded = True
|
||||||
logger.info(f"Successfully loaded plugin: {module_name}")
|
logger.info(f"Successfully loaded plugin: {module_name}")
|
||||||
|
|
||||||
|
# Register any global keybindings from the plugin
|
||||||
|
self.register_plugin_global_keybindings(pluginInfo.instance)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -23,3 +23,8 @@
|
|||||||
# Fork of Orca Screen Reader (GNOME)
|
# Fork of Orca Screen Reader (GNOME)
|
||||||
# Original source: https://gitlab.gnome.org/GNOME/orca
|
# Original source: https://gitlab.gnome.org/GNOME/orca
|
||||||
|
|
||||||
|
"""Clipboard plugin package."""
|
||||||
|
|
||||||
|
from .plugin import Clipboard
|
||||||
|
|
||||||
|
__all__ = ['Clipboard']
|
||||||
|
@ -72,42 +72,7 @@ class Clipboard(Plugin):
|
|||||||
return
|
return
|
||||||
|
|
||||||
logger.info("Deactivating Clipboard plugin")
|
logger.info("Deactivating Clipboard plugin")
|
||||||
try:
|
# Note: Currently no unregister method needed as keybindings are managed by APIHelper
|
||||||
# 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}")
|
|
||||||
|
|
||||||
def speakClipboard(self, script=None, inputEvent=None):
|
def speakClipboard(self, script=None, inputEvent=None):
|
||||||
"""Present the contents of the clipboard."""
|
"""Present the contents of the clipboard."""
|
||||||
|
@ -32,16 +32,62 @@ class DisplayVersion(Plugin):
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
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
|
# 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._kb_binding = self.registerGestureByString(
|
||||||
self.speakText,
|
self.speakText,
|
||||||
f'Cthulhu screen reader version {cthulhuVersion.version}-{cthulhuVersion.codeName}',
|
f'Cthulhu screen reader version {cthulhuVersion.version}-{cthulhuVersion.codeName}',
|
||||||
'kb:cthulhu+shift+v'
|
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")
|
||||||
|
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:
|
except Exception as e:
|
||||||
logger.error(f"Error activating DisplayVersion plugin: {e}")
|
logger.error(f"Error activating DisplayVersion plugin: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
|
||||||
@cthulhu_hookimpl
|
@cthulhu_hookimpl
|
||||||
def deactivate(self, plugin=None):
|
def deactivate(self, plugin=None):
|
||||||
@ -55,6 +101,7 @@ class DisplayVersion(Plugin):
|
|||||||
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")
|
||||||
if self.app:
|
if self.app:
|
||||||
state = self.app.getDynamicApiManager().getAPI('CthulhuState')
|
state = self.app.getDynamicApiManager().getAPI('CthulhuState')
|
||||||
if state.activeScript:
|
if state.activeScript:
|
||||||
@ -65,4 +112,4 @@ class DisplayVersion(Plugin):
|
|||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error in DisplayVersion speakText: {e}")
|
logger.error(f"Error in DisplayVersion speakText: {e}")
|
||||||
return False
|
return False
|
||||||
|
@ -318,6 +318,13 @@ class ScriptManager:
|
|||||||
return
|
return
|
||||||
|
|
||||||
newScript.activate()
|
newScript.activate()
|
||||||
|
|
||||||
|
# Register plugin keybindings with the new active script
|
||||||
|
from . import cthulhu
|
||||||
|
plugin_manager = cthulhu.cthulhuApp.getPluginSystemManager()
|
||||||
|
if plugin_manager:
|
||||||
|
pass # plugin_manager.register_plugin_keybindings_with_active_script()
|
||||||
|
|
||||||
tokens = ["SCRIPT MANAGER: Setting active script to", newScript, "reason:", reason]
|
tokens = ["SCRIPT MANAGER: Setting active script to", newScript, "reason:", reason]
|
||||||
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
debug.printTokens(debug.LEVEL_INFO, tokens, True)
|
||||||
|
|
||||||
|
@ -363,6 +363,8 @@ class Script(script.Script):
|
|||||||
return keyBindings
|
return keyBindings
|
||||||
|
|
||||||
def getExtensionBindings(self):
|
def getExtensionBindings(self):
|
||||||
|
with open('/tmp/extension_bindings_debug.log', 'a') as f:
|
||||||
|
f.write(f"=== getExtensionBindings() called ===\n")
|
||||||
keyBindings = keybindings.KeyBindings()
|
keyBindings = keybindings.KeyBindings()
|
||||||
|
|
||||||
bindings = self.notificationPresenter.get_bindings()
|
bindings = self.notificationPresenter.get_bindings()
|
||||||
@ -407,6 +409,41 @@ class Script(script.Script):
|
|||||||
for keyBinding in bindings.keyBindings:
|
for keyBinding in bindings.keyBindings:
|
||||||
keyBindings.add(keyBinding)
|
keyBindings.add(keyBinding)
|
||||||
|
|
||||||
|
# Add plugin keybindings from APIHelper storage
|
||||||
|
try:
|
||||||
|
import cthulhu.cthulhu as cthulhu
|
||||||
|
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"=== 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")
|
||||||
|
|
||||||
|
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"=== 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)
|
||||||
|
with open('/tmp/extension_bindings_debug.log', 'a') as f:
|
||||||
|
f.write(f"Exception in plugin binding addition: {e}\n")
|
||||||
|
|
||||||
return keyBindings
|
return keyBindings
|
||||||
|
|
||||||
def getKeyBindings(self):
|
def getKeyBindings(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user