diff --git a/README.md b/README.md
index ddf0ba2..77ee4a4 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,7 @@ Cthulhu also has the following dependencies:
* Python 3 - Python platform
* pygobject-3.0 - Python bindings for the GObject library
+* libpeas - GObject based Plugin engine
* gtk+-3.0 - GTK+ toolkit
* json-py - a JSON () reader and writer in Python
* python-speechd - Python bindings for Speech Dispatcher (optional)
@@ -71,4 +72,4 @@ within Cthulhu as well as at:
So, you want to write a script for Cthulhu? The best thing to do is
start by looking at other scripts under the src/cthulhu/scripts/ hierarchy
-of the source tree.
\ No newline at end of file
+of the source tree.
diff --git a/TODO b/TODO
index 5994764..46c2025 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,4 @@
- Add Simple Orca Plugin System as native code for Cthulhu
-- Merge Chrys' plugin work.
- Merge in sleep mode.
-- Add in system for Cthulhu to have self voicing option. Probably based on socat.
- Add in ocrdesktop code so that Cthulhu has native ocr support.
- Get rid of build systems. My hope is git clone and run so long as requirements are satisfied.
diff --git a/configure.ac b/configure.ac
index f5b046a..f3c5ed7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -118,6 +118,17 @@ src/cthulhu/scripts/toolkits/GAIL/Makefile
src/cthulhu/scripts/toolkits/Qt/Makefile
src/cthulhu/scripts/toolkits/WebKitGtk/Makefile
src/cthulhu/scripts/toolkits/gtk/Makefile
+src/cthulhu/plugins/Makefile
+src/cthulhu/plugins/ByeCthulhu/Makefile
+src/cthulhu/plugins/HelloCthulhu/Makefile
+src/cthulhu/plugins/PluginManager/Makefile
+src/cthulhu/plugins/Clipboard/Makefile
+src/cthulhu/plugins/HelloWorld/Makefile
+src/cthulhu/plugins/CapsLockHack/Makefile
+src/cthulhu/plugins/SelfVoice/Makefile
+src/cthulhu/plugins/Date/Makefile
+src/cthulhu/plugins/Time/Makefile
+src/cthulhu/plugins/MouseReview/Makefile
src/cthulhu/backends/Makefile
src/cthulhu/cthulhu_bin.py
src/cthulhu/cthulhu_i18n.py
diff --git a/src/cthulhu/Makefile.am b/src/cthulhu/Makefile.am
index 8196786..c413610 100644
--- a/src/cthulhu/Makefile.am
+++ b/src/cthulhu/Makefile.am
@@ -32,6 +32,7 @@ cthulhu_python_PYTHON = \
date_and_time_presenter.py \
debug.py \
desktop_keyboardmap.py \
+ dynamic_api_manager.py \
event_manager.py \
find.py \
flat_review.py \
@@ -63,14 +64,18 @@ cthulhu_python_PYTHON = \
cthulhu_i18n.py \
cthulhu_state.py \
phonnames.py \
+ plugin.py \
cthulhu_platform.py \
+ plugin_system_manager.py \
pronunciation_dict.py \
punctuation_settings.py \
+ resource_manager.py \
script.py \
script_manager.py \
script_utilities.py \
settings.py \
settings_manager.py \
+ signal_manager.py \
sound.py \
sound_generator.py \
speech_and_verbosity_manager.py \
@@ -81,14 +86,17 @@ cthulhu_python_PYTHON = \
speechserver.py \
structural_navigation.py \
text_attribute_names.py \
+ translation_context.py \
+ translation_manager.py \
tutorialgenerator.py \
where_am_i_presenter.py
cthulhu_pythondir=$(pkgpythondir)
SUBDIRS = \
- scripts \
- backends
+ backends \
+ scripts \
+ plugins
ui_DATA = \
cthulhu-find.ui \
diff --git a/src/cthulhu/cthulhu.py b/src/cthulhu/cthulhu.py
index 9166754..8804e86 100644
--- a/src/cthulhu/cthulhu.py
+++ b/src/cthulhu/cthulhu.py
@@ -42,6 +42,7 @@ gi.require_version("Atspi", "2.0")
gi.require_version("Gdk", "3.0")
from gi.repository import Atspi
from gi.repository import Gdk
+from gi.repository import GObject
try:
from gi.repository.Gio import Settings
@@ -52,10 +53,11 @@ except Exception:
from . import braille
from . import debug
from . import event_manager
+from . import keybindings
from . import learn_mode_presenter
from . import logger
from . import messages
-from . import mouse_review
+from . import notification_presenter
from . import cthulhu_state
from . import cthulhu_platform
from . import script_manager
@@ -63,9 +65,24 @@ from . import settings
from . import settings_manager
from . import speech
from . import sound
+from . import mouse_review
from .ax_object import AXObject
from .ax_utilities import AXUtilities
from .input_event import BrailleEvent
+from . import cmdnames
+from . import plugin_system_manager
+from . import guilabels
+from . import acss
+from . import text_attribute_names
+from . import speechserver
+from . import input_event
+from . import pronunciation_dict
+from . import cthulhu_gtkbuilder
+from . import signal_manager
+from . import dynamic_api_manager
+from . import translation_manager
+from . import resource_manager
+
_eventManager = event_manager.getManager()
_scriptManager = script_manager.getManager()
@@ -428,6 +445,7 @@ def loadUserSettings(script=None, inputEvent=None, skipReloadMessage=False):
braille.shutdown()
_scriptManager.deactivate()
+ cthulhuApp.getSignalManager().emitSignal('load-setting-begin')
reloaded = False
if _userSettings:
@@ -500,9 +518,14 @@ def loadUserSettings(script=None, inputEvent=None, skipReloadMessage=False):
_storeXmodmap(_cthulhuModifiers)
_createCthulhuXmodmap()
+ activePlugins = list(_settingsManager.getSetting('activePlugins'))
+ cthulhuApp.getPluginSystemManager().setActivePlugins(activePlugins)
+
_scriptManager.activate()
_eventManager.activate()
+ cthulhuApp.getSignalManager().emitSignal('load-setting-completed')
+
debug.printMessage(debug.LEVEL_INFO, 'ORCA: User Settings Loaded', True)
return True
@@ -721,7 +744,9 @@ def shutdown(script=None, inputEvent=None):
signal.alarm(settings.timeoutTime)
cthulhu_state.activeScript.presentationInterrupt()
- cthulhu_state.activeScript.presentMessage(messages.STOP_ORCA, resetStyles=False)
+
+ cthulhuApp.getSignalManager().emitSignal('stop-application-completed')
+ cthulhuApp.getPluginSystemManager().unloadAllPlugins(ForceAllPlugins=True)
# Deactivate the event manager first so that it clears its queue and will not
# accept new events. Then let the script manager unregister script event listeners.
@@ -797,11 +822,6 @@ def crashOnSignal(signum, frame):
debug.printMessage(debug.LEVEL_SEVERE, msg, True)
debug.printStack(debug.LEVEL_SEVERE)
_restoreXmodmap(_cthulhuModifiers)
- try:
- cthulhu_state.activeScript.presentationInterrupt()
- cthulhu_state.activeScript.presentMessage(messages.STOP_ORCA, resetStyles=False)
- except Exception:
- pass
sys.exit(1)
def main():
@@ -847,14 +867,8 @@ def main():
init()
debug.printMessage(debug.LEVEL_INFO, "ORCA: Initialized.", True)
- try:
- message = messages.START_ORCA
- script = _scriptManager.getDefaultScript()
- script.presentMessage(message)
- except Exception:
- debug.printException(debug.LEVEL_SEVERE)
-
script = cthulhu_state.activeScript
+ cthulhuApp.getSignalManager().emitSignal('start-application-completed')
if script:
window = script.utilities.activeWindow()
@@ -887,5 +901,94 @@ def main():
die(EXIT_CODE_HANG)
return 0
+class Cthulhu(GObject.Object):
+ # basic signals
+ __gsignals__ = {
+ "start-application-completed": (GObject.SignalFlags.RUN_LAST, None, ()),
+ "stop-application-completed": (GObject.SignalFlags.RUN_LAST, None, ()),
+ "load-setting-begin": (GObject.SignalFlags.RUN_LAST, None, ()),
+ "load-setting-completed": (GObject.SignalFlags.RUN_LAST, None, ()),
+ "setup-inputeventhandlers-completed": (GObject.SignalFlags.RUN_LAST, None, ()), # compat signal for register input event handlers
+ "request-cthulhu-preferences": (GObject.SignalFlags.RUN_LAST, None, ()),
+ "request-application-preferences": (GObject.SignalFlags.RUN_LAST, None, ()),
+ }
+ def __init__(self):
+ GObject.Object.__init__(self)
+ # add members
+ self.resourceManager = resource_manager.ResourceManager(self)
+ self.APIHelper = plugin_system_manager.APIHelper(self)
+ self.eventManager = _eventManager
+ self.settingsManager = _settingsManager
+ self.scriptManager = _scriptManager
+ self.signalManager = signal_manager.SignalManager(self)
+ self.dynamicApiManager = dynamic_api_manager.DynamicApiManager(self)
+ self.translationManager = translation_manager.TranslationManager(self)
+ self.debugManager = debug
+ self.createCompatAPI()
+ self.pluginSystemManager = plugin_system_manager.PluginSystemManager(self)
+ def getAPIHelper(self):
+ return self.APIHelper
+ def getPluginSystemManager(self):
+ return self.pluginSystemManager
+ def getDynamicApiManager(self):
+ return self.dynamicApiManager
+ def getSignalManager(self):
+ return self.signalManager
+ def getEventManager(self):
+ return self.eventManager
+ def getSettingsManager(self):
+ return self.settingsManager
+ def getScriptManager(self):
+ return self.scriptManager
+ def getDebugManager(self):
+ return self.debugManager
+ def getTranslationManager(self):
+ return self.translationManager
+ def getResourceManager(self):
+ return self.resourceManager
+ def run(self, cacheValues=True):
+ return main(cacheValues)
+ def stop(self):
+ pass
+ def createCompatAPI(self):
+ # for now add compatibility layer using Dynamic API
+ # should be removed step by step
+ # use clean objects, getters and setters instead
+
+ self.getDynamicApiManager().registerAPI('Logger', _logger)
+ self.getDynamicApiManager().registerAPI('SettingsManager', settings_manager)
+ self.getDynamicApiManager().registerAPI('ScriptManager', script_manager)
+ self.getDynamicApiManager().registerAPI('EventManager', event_manager)
+ self.getDynamicApiManager().registerAPI('Speech', speech)
+ self.getDynamicApiManager().registerAPI('Sound', sound)
+ self.getDynamicApiManager().registerAPI('Braille', braille)
+ self.getDynamicApiManager().registerAPI('Debug', debug)
+ self.getDynamicApiManager().registerAPI('Messages', messages)
+ self.getDynamicApiManager().registerAPI('Cmdnames', cmdnames)
+ self.getDynamicApiManager().registerAPI('NotificationPresenter', notification_presenter)
+ self.getDynamicApiManager().registerAPI('CthulhuState', cthulhu_state)
+ self.getDynamicApiManager().registerAPI('CthulhuPlatform', cthulhu_platform)
+ self.getDynamicApiManager().registerAPI('Settings', settings)
+ self.getDynamicApiManager().registerAPI('Keybindings', keybindings)
+ self.getDynamicApiManager().registerAPI('GuiLabels', guilabels)
+ self.getDynamicApiManager().registerAPI('Acss', acss)
+ self.getDynamicApiManager().registerAPI('TextAttributeNames', text_attribute_names)
+ self.getDynamicApiManager().registerAPI('PronunciationDict', pronunciation_dict)
+ self.getDynamicApiManager().registerAPI('InputEvent', input_event)
+ self.getDynamicApiManager().registerAPI('SpeechServer', speechserver)
+ self.getDynamicApiManager().registerAPI('CthulhuGtkbuilder', cthulhu_gtkbuilder)
+ self.getDynamicApiManager().registerAPI('AXObject', AXObject)
+ self.getDynamicApiManager().registerAPI('AXUtilities', AXUtilities)
+ self.getDynamicApiManager().registerAPI('LearnModePresenter', learn_mode_presenter)
+ # cthulhu lets say, special compat handling....
+ self.getDynamicApiManager().registerAPI('EmitRegionChanged', emitRegionChanged)
+ self.getDynamicApiManager().registerAPI('LoadUserSettings', loadUserSettings)
+
+cthulhuApp = Cthulhu()
+
+def getManager():
+ return cthulhuApp
+
if __name__ == "__main__":
- sys.exit(main())
+ GObject.threads_init()
+ sys.exit(cthulhuApp.run())
diff --git a/src/cthulhu/settings.py b/src/cthulhu/settings.py
index 1dd59a9..68f22d6 100644
--- a/src/cthulhu/settings.py
+++ b/src/cthulhu/settings.py
@@ -115,6 +115,7 @@ userCustomizableSettings = [
"presentDateFormat",
"presentTimeFormat",
"activeProfile",
+ "activePlugins",
"startingProfile",
"spellcheckSpellError",
"spellcheckSpellSuggestion",
@@ -404,3 +405,6 @@ structNavInSayAll = False
enableSadPidginHack = False
presentChatRoomLast = False
presentLiveRegionFromInactiveTab = False
+
+# Plugins
+activePlugins = ['Clipboard', 'MouseReview', 'Date', 'ByeCthulhu', 'Time', 'HelloCthulhu', 'HelloWorld', 'SelfVoice', 'PluginManager']