Compare commits
23 Commits
v0.3
...
5178a66a6f
Author | SHA1 | Date | |
---|---|---|---|
5178a66a6f | |||
89891f18af | |||
71add76898 | |||
1a9eb35418 | |||
ba6382c3cd | |||
17febf488c | |||
09718e5901 | |||
03bed6c5d4 | |||
d28e0086d9 | |||
515827e830 | |||
4f6260eb6f | |||
b6a44df82a | |||
0cadb12c00 | |||
2f6fde6896 | |||
89467b6988 | |||
17d2773344 | |||
0abe30791d | |||
087dcc3ab2 | |||
4eaa4fae86 | |||
ba0169b016 | |||
7605e7d60f | |||
9562c08130 | |||
8a9bbefeac |
@ -29,6 +29,8 @@ Cthulhu has the following dependencies:
|
||||
* liblouis - Liblouis (<http://liblouis.org/>) support for contracted braille (optional)
|
||||
* py-setproctitle - Python library to set the process title (optional)
|
||||
* gstreamer-1.0 - GStreamer - Streaming media framework (optional)
|
||||
* socat - Used for self-voicing functionality.
|
||||
* libpeas - For the plugin system.
|
||||
|
||||
You are strongly encouraged to also have the latest stable versions
|
||||
of AT-SPI2 and ATK.
|
||||
|
1
TODO
1
TODO
@ -1,4 +1,3 @@
|
||||
- Add Simple Cthulhu Plugin System as native code for Cthulhu
|
||||
- Merge in sleep mode.
|
||||
- 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.
|
||||
|
@ -1,4 +1,4 @@
|
||||
m4_define([cthulhu_version], [0.3])
|
||||
m4_define([cthulhu_version], [0.4])
|
||||
|
||||
m4_define(pygobject_required_version, 3.18)
|
||||
m4_define(atspi_required_version, 2.48)
|
||||
@ -28,6 +28,7 @@ PKG_CHECK_MODULES([PYGOBJECT], [pygobject-3.0 >= pygobject_required_version])
|
||||
PKG_CHECK_MODULES([ATSPI2], [atspi-2 >= atspi_required_version])
|
||||
PKG_CHECK_MODULES([ATKBRIDGE], [atk-bridge-2.0 >= atkbridge_required_version])
|
||||
PKG_CHECK_MODULES([GSTREAMER], [gstreamer-1.0], [gstreamer="yes"], [gstreamer="no"])
|
||||
PKG_CHECK_MODULES([LIBPEAS], [libpeas-1.0], [libpeas="yes"], [libpeas="no"])
|
||||
|
||||
dnl Needed programs
|
||||
AC_PROG_INSTALL
|
||||
@ -129,7 +130,7 @@ src/cthulhu/plugins/SelfVoice/Makefile
|
||||
src/cthulhu/plugins/Date/Makefile
|
||||
src/cthulhu/plugins/Time/Makefile
|
||||
src/cthulhu/plugins/MouseReview/Makefile
|
||||
src/cthulhu/plugins/ClassicPreferences/Makefile
|
||||
src/cthulhu/plugins/SimplePluginSystem/Makefile
|
||||
src/cthulhu/backends/Makefile
|
||||
src/cthulhu/cthulhu_bin.py
|
||||
src/cthulhu/cthulhu_i18n.py
|
||||
@ -157,6 +158,10 @@ echo
|
||||
echo "NOTE: Sound support requires gstreamer-1.0."
|
||||
fi
|
||||
|
||||
if test "$have_libpeas" = "no"; then
|
||||
AC_MSG_ERROR([libpeas-1.0 >= 1.20 is required])
|
||||
fi
|
||||
|
||||
echo
|
||||
echo Use speech-dispatcher: $speechd_available
|
||||
echo Use brltty: $brlapi_available
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Maintainer: Storm Dragon <storm_dragon@stormux.org>
|
||||
|
||||
pkgname=cthulhu
|
||||
pkgver=0.3
|
||||
pkgver=0.4
|
||||
pkgrel=1
|
||||
pkgdesc="Screen reader for individuals who are blind or visually impaired forked from Orca"
|
||||
url="https://git.stormux.org/storm/cthulhu"
|
||||
|
@ -530,6 +530,38 @@ def loadUserSettings(script=None, inputEvent=None, skipReloadMessage=False):
|
||||
|
||||
return True
|
||||
|
||||
def _showPreferencesUI(script, prefs):
|
||||
if cthulhu_state.cthulhuOS:
|
||||
cthulhu_state.cthulhuOS.showGUI()
|
||||
return
|
||||
|
||||
try:
|
||||
module = importlib.import_module('.cthulhu_gui_prefs', 'cthulhu')
|
||||
except Exception:
|
||||
debug.printException(debug.LEVEL_SEVERE)
|
||||
return
|
||||
|
||||
uiFile = os.path.join(cthulhu_platform.datadir,
|
||||
cthulhu_platform.package,
|
||||
"ui",
|
||||
"cthulhu-setup.ui")
|
||||
|
||||
cthulhu_state.cthulhuOS = module.CthulhuSetupGUI(uiFile, "cthulhuSetupWindow", prefs)
|
||||
cthulhu_state.cthulhuOS.init(script)
|
||||
cthulhu_state.cthulhuOS.showGUI()
|
||||
|
||||
def showPreferencesGUI(script=None, inputEvent=None):
|
||||
"""Displays the user interface to configure Cthulhu and set up
|
||||
user preferences using a GUI.
|
||||
|
||||
Returns True to indicate the input event has been consumed.
|
||||
"""
|
||||
|
||||
prefs = _settingsManager.getGeneralSettings(_settingsManager.profile)
|
||||
script = _scriptManager.getDefaultScript()
|
||||
_showPreferencesUI(script, prefs)
|
||||
|
||||
return True
|
||||
|
||||
def addKeyGrab(binding):
|
||||
""" Add a key grab for the given key binding."""
|
||||
|
@ -1,14 +0,0 @@
|
||||
[Plugin]
|
||||
Module=ClassicPreferences
|
||||
Loader=python3
|
||||
Name=Classic Preferences UI
|
||||
Description=The classic preferences dialog
|
||||
Authors=Chrys chrys@linux-a11y.org
|
||||
Website=
|
||||
Version=1.0
|
||||
Copyright=
|
||||
Builtin=true
|
||||
Hidden=true
|
||||
Depends=
|
||||
Icon=
|
||||
Help=
|
@ -1,84 +0,0 @@
|
||||
from cthulhu import plugin
|
||||
|
||||
import gi, time
|
||||
gi.require_version('Peas', '1.0')
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Peas
|
||||
import importlib, os
|
||||
import cthulhu_gui_prefs
|
||||
|
||||
class ClassicPreferences(GObject.Object, Peas.Activatable, plugin.Plugin):
|
||||
#__gtype_name__ = 'ClassicPreferences'
|
||||
|
||||
object = GObject.Property(type=GObject.Object)
|
||||
def __init__(self):
|
||||
plugin.Plugin.__init__(self)
|
||||
def do_activate(self):
|
||||
API = self.object
|
||||
self.connectSignal("setup-inputeventhandlers-completed", self.setupCompatBinding)
|
||||
#self.setupCompatBinding(API.app)
|
||||
def setupCompatBinding(self, app):
|
||||
cmdnames = app.getDynamicApiManager().getAPI('Cmdnames')
|
||||
inputEventHandlers = app.getDynamicApiManager().getAPI('inputEventHandlers')
|
||||
inputEventHandlers['preferencesSettingsHandler'] = app.getAPIHelper().createInputEventHandler(self.showPreferencesGUI, cmdnames.SHOW_PREFERENCES_GUI)
|
||||
inputEventHandlers['appPreferencesSettingsHandler'] = app.getAPIHelper().createInputEventHandler(self.showAppPreferencesGUI, cmdnames.SHOW_APP_PREFERENCES_GUI)
|
||||
def do_deactivate(self):
|
||||
API = self.object
|
||||
inputEventHandlers = API.app.getDynamicApiManager().getAPI('inputEventHandlers')
|
||||
del inputEventHandlers['preferencesSettingsHandler']
|
||||
del inputEventHandlers['appPreferencesSettingsHandler']
|
||||
def do_update_state(self):
|
||||
API = self.object
|
||||
def showAppPreferencesGUI(self, script=None, inputEvent=None):
|
||||
"""Displays the user interface to configure the settings for a
|
||||
specific applications within Cthulhu and set up those app-specific
|
||||
user preferences using a GUI.
|
||||
|
||||
Returns True to indicate the input event has been consumed.
|
||||
"""
|
||||
API = self.object
|
||||
cthulhu_state = API.app.getDynamicApiManager().getAPI('CthulhuState')
|
||||
settings = API.app.getDynamicApiManager().getAPI('Settings')
|
||||
_settingsManager = API.app.getDynamicApiManager().getAPI('SettingsManager').getManager()
|
||||
_scriptManager = API.app.getDynamicApiManager().getAPI('ScriptManager').getManager()
|
||||
|
||||
prefs = {}
|
||||
for key in settings.userCustomizableSettings:
|
||||
prefs[key] = _settingsManager.getSetting(key)
|
||||
|
||||
script = script or cthulhu_state.activeScript
|
||||
self._showPreferencesUI(script, prefs)
|
||||
return True
|
||||
def showPreferencesGUI(self, script=None, inputEvent=None):
|
||||
"""Displays the user interface to configure Cthulhu and set up
|
||||
user preferences using a GUI.
|
||||
|
||||
Returns True to indicate the input event has been consumed.
|
||||
"""
|
||||
API = self.object
|
||||
cthulhu_state = API.app.getDynamicApiManager().getAPI('CthulhuState')
|
||||
settings = API.app.getDynamicApiManager().getAPI('Settings')
|
||||
_settingsManager = API.app.getDynamicApiManager().getAPI('SettingsManager').getManager()
|
||||
_scriptManager = API.app.getDynamicApiManager().getAPI('ScriptManager').getManager()
|
||||
debug = API.app.getDynamicApiManager().getAPI('Debug')
|
||||
prefs = _settingsManager.getGeneralSettings(_settingsManager.profile)
|
||||
script = _scriptManager.getDefaultScript()
|
||||
self._showPreferencesUI(script, prefs)
|
||||
return True
|
||||
def _showPreferencesUI(self, script, prefs):
|
||||
API = self.object
|
||||
cthulhu_state = API.app.getDynamicApiManager().getAPI('CthulhuState')
|
||||
debug = API.app.getDynamicApiManager().getAPI('Debug')
|
||||
cthulhu_platform = API.app.getDynamicApiManager().getAPI('CthulhuPlatform')
|
||||
|
||||
if cthulhu_state.cthulhuOS:
|
||||
cthulhu_state.cthulhuOS.showGUI()
|
||||
return
|
||||
|
||||
uiFile = os.path.join(self.getModuleDir(),
|
||||
"cthulhu-setup.ui")
|
||||
|
||||
cthulhu_state.cthulhuOS = cthulhu_gui_prefs.CthulhuSetupGUI(uiFile, "cthulhuSetupWindow", prefs, API.app)
|
||||
cthulhu_state.cthulhuOS.init(script)
|
||||
cthulhu_state.cthulhuOS.setTranslationContext(self.getTranslationContext())
|
||||
cthulhu_state.cthulhuOS.showGUI()
|
@ -1,10 +0,0 @@
|
||||
cthulhu_python_PYTHON = \
|
||||
__init__.py \
|
||||
ClassicPreferences.plugin \
|
||||
ClassicPreferences.py \
|
||||
cthulhu_gui_prefs.py \
|
||||
cthulhu_gui_profile.py \
|
||||
cthulhu-setup.ui
|
||||
|
||||
cthulhu_pythondir=$(pkgpythondir)/plugins/ClassicPreferences
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,163 +0,0 @@
|
||||
# Cthulhu
|
||||
#
|
||||
# Copyright 2010 Consorcio Fernando de los Rios.
|
||||
# Author: Javier Hernandez Antunez <jhernandez@emergya.es>
|
||||
# Author: Alejandro Leiva <aleiva@emergya.es>
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
|
||||
# Boston MA 02110-1301 USA.
|
||||
|
||||
"""Displays the Save Profile As dialog."""
|
||||
|
||||
__id__ = "$Id$"
|
||||
__version__ = "$Revision$"
|
||||
__date__ = "$Date$"
|
||||
__copyright__ = "Copyright (c) 2010 Consorcio Fernando de los Rios."
|
||||
__license__ = "LGPL"
|
||||
|
||||
import locale
|
||||
import sys
|
||||
from gi.repository import Gtk
|
||||
|
||||
cthulhu_state = None
|
||||
guilabels = None
|
||||
|
||||
OS = None
|
||||
newProfile = None
|
||||
|
||||
app = None
|
||||
|
||||
class CthulhuProfileGUI(Gtk.Dialog):
|
||||
|
||||
def __init__(self, app):
|
||||
"""Initialize the Cthulhu profile configuration GUI."""
|
||||
self.app = app
|
||||
global guilabels
|
||||
global cthulhu_state
|
||||
|
||||
guilabels = self.app.getDynamicApiManager().getAPI('GuiLabels')
|
||||
cthulhu_state = self.app.getDynamicApiManager().getAPI('CthulhuState')
|
||||
|
||||
Gtk.Dialog.__init__(self)
|
||||
self.set_title(guilabels.PROFILE_SAVE_AS_TITLE)
|
||||
self.set_has_resize_grip(False)
|
||||
|
||||
self.add_button('gtk-cancel', Gtk.ResponseType.CANCEL)
|
||||
self.add_button('gtk-save', Gtk.ResponseType.ACCEPT)
|
||||
|
||||
grid = Gtk.Grid()
|
||||
grid.set_property('margin', 12)
|
||||
grid.set_row_spacing(10)
|
||||
grid.set_column_spacing(10)
|
||||
|
||||
# Right now the content area is a GtkBox. We'll need to update
|
||||
# this once GtkBox is fully deprecated.
|
||||
contentArea = self.get_content_area()
|
||||
contentArea.pack_start(grid, True, True, 0)
|
||||
|
||||
self.profileEntry = Gtk.Entry()
|
||||
self.profileEntry.set_property('hexpand', True)
|
||||
self.profileEntry.set_activates_default(True)
|
||||
grid.attach(self.profileEntry, 1, 0, 1, 1)
|
||||
|
||||
label = Gtk.Label(guilabels.PROFILE_NAME_LABEL)
|
||||
label.set_use_underline(True)
|
||||
label.set_mnemonic_widget(self.profileEntry)
|
||||
grid.attach(label, 0, 0, 1, 1)
|
||||
|
||||
defaultButton = self.get_widget_for_response(Gtk.ResponseType.ACCEPT)
|
||||
defaultButton.set_property('can-default', True)
|
||||
defaultButton.set_property('has-default', True)
|
||||
|
||||
self.connect('response', self.onResponse)
|
||||
self.connect('destroy', self.onDestroy)
|
||||
|
||||
self.searchString = None
|
||||
self.profileString = None
|
||||
self.prefsDialog = None
|
||||
self.translationContext = None
|
||||
|
||||
def init(self):
|
||||
self.profileString = ''
|
||||
|
||||
def showGUI(self, prefsDialog):
|
||||
"""Show the Save Profile As dialog."""
|
||||
|
||||
self.show_all()
|
||||
self.prefsDialog = prefsDialog
|
||||
self.profileEntry.set_text(self.profileString)
|
||||
ts = 0
|
||||
try:
|
||||
ts = cthulhu_state.lastInputEvent.timestamp
|
||||
except:
|
||||
pass
|
||||
if ts == 0:
|
||||
ts = Gtk.get_current_event_time()
|
||||
self.present_with_time(ts)
|
||||
|
||||
def onResponse(self, widget, response):
|
||||
"""Signal handler for the responses emitted by the dialog."""
|
||||
|
||||
if response in [Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT]:
|
||||
self.hide()
|
||||
return
|
||||
|
||||
if response == Gtk.ResponseType.ACCEPT:
|
||||
global newProfile
|
||||
|
||||
newProfile = self.profileEntry.get_text()
|
||||
if newProfile:
|
||||
self.destroy()
|
||||
if self.prefsDialog:
|
||||
self.prefsDialog.saveProfile(newProfile)
|
||||
|
||||
def onDestroy(self, widget):
|
||||
"""Signal handler for the 'destroy' signal of the dialog."""
|
||||
|
||||
global OS
|
||||
|
||||
OS = None
|
||||
|
||||
def setTranslationContext(newTranslationContext):
|
||||
global _, translationContext
|
||||
translationContext = newTranslationContext
|
||||
_ = newTranslationContext.gettext
|
||||
|
||||
def setApp(newApp):
|
||||
global app
|
||||
app = newApp
|
||||
|
||||
def showProfileUI(prefsDialog=None):
|
||||
global OS
|
||||
global newProfile
|
||||
|
||||
newProfile = None
|
||||
|
||||
if not OS:
|
||||
OS = CthulhuProfileGUI(app)
|
||||
OS.init()
|
||||
|
||||
OS.showGUI(prefsDialog)
|
||||
|
||||
def main():
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
|
||||
showProfileUI()
|
||||
|
||||
Gtk.main()
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,4 +1,4 @@
|
||||
SUBDIRS = Clipboard HelloWorld SelfVoice Time MouseReview Date ByeCthulhu HelloCthulhu PluginManager CapsLockHack ClassicPreferences
|
||||
SUBDIRS = Clipboard HelloWorld SelfVoice Time MouseReview Date ByeCthulhu HelloCthulhu PluginManager CapsLockHack SimplePluginSystem
|
||||
|
||||
cthulhu_pythondir=$(pkgpythondir)/plugins
|
||||
|
||||
|
7
src/cthulhu/plugins/SimplePluginSystem/Makefile.am
Normal file
7
src/cthulhu/plugins/SimplePluginSystem/Makefile.am
Normal file
@ -0,0 +1,7 @@
|
||||
cthulhu_python_PYTHON = \
|
||||
__init__.py \
|
||||
SimplePluginSystem.plugin \
|
||||
SimplePluginSystem.py
|
||||
|
||||
cthulhu_pythondir=$(pkgpythondir)/plugins/SimplePluginSystem
|
||||
|
@ -0,0 +1,11 @@
|
||||
[Plugin]
|
||||
Module=SimplePluginSystem
|
||||
Loader=python3
|
||||
Name=Simple Plugin System
|
||||
Description=Simple plugin system implementation for Cthulhu
|
||||
Authors=Chrys <chrys@linux-a11y.org>;Storm Dragon <storm_dragon@stormux.org>
|
||||
Copyright=Copyright Â2024 Chrys, Storm Dragon
|
||||
Website=https://git.stormux.org/storm/cthulhu
|
||||
Version=1.0
|
||||
Builtin=true
|
||||
|
266
src/cthulhu/plugins/SimplePluginSystem/SimplePluginSystem.py
Normal file
266
src/cthulhu/plugins/SimplePluginSystem/SimplePluginSystem.py
Normal file
@ -0,0 +1,266 @@
|
||||
from cthulhu import plugin
|
||||
|
||||
from gi.repository import GObject, Peas
|
||||
import glob
|
||||
import os
|
||||
import importlib.util
|
||||
import random
|
||||
import string
|
||||
import _thread
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
settings = None
|
||||
speech = None
|
||||
braille = None
|
||||
input_event = None
|
||||
|
||||
def outputMessage( Message):
|
||||
if (settings.enableSpeech):
|
||||
speech.speak(Message)
|
||||
if (settings.enableBraille):
|
||||
braille.displayMessage(Message)
|
||||
|
||||
class SimplePluginSystem(GObject.Object, Peas.Activatable, plugin.Plugin):
|
||||
__gtype_name__ = 'SimplePluginSystem'
|
||||
object = GObject.Property(type=GObject.Object)
|
||||
|
||||
def __init__(self):
|
||||
plugin.Plugin.__init__(self)
|
||||
self.plugin_list = []
|
||||
self.loaded = False
|
||||
self.plugin_repo = os.path.expanduser('~') + "/.local/share/cthulhu/simple-plugins-enabled/"
|
||||
|
||||
def do_activate(self):
|
||||
API = self.object
|
||||
global settings
|
||||
global speech
|
||||
global braille
|
||||
global input_event
|
||||
settings = API.app.getDynamicApiManager().getAPI('Settings')
|
||||
speech = API.app.getDynamicApiManager().getAPI('Speech')
|
||||
braille = API.app.getDynamicApiManager().getAPI('Braille')
|
||||
input_event = API.app.getDynamicApiManager().getAPI('InputEvent')
|
||||
"""Required method for plugins"""
|
||||
if not self.loaded:
|
||||
self.load_plugins()
|
||||
|
||||
def do_deactivate(self):
|
||||
"""Required method for plugins"""
|
||||
# Remove all registered keybindings
|
||||
for plugin in self.plugin_list:
|
||||
self.unregisterShortcut(plugin['function'], plugin['shortcut'])
|
||||
self.loaded = False
|
||||
self.plugin_list = []
|
||||
|
||||
def SetupShortcutAndHandle(self, currPluginSetting):
|
||||
shortcut = ''
|
||||
# just the modifier
|
||||
if not currPluginSetting['shiftkey'] and not currPluginSetting['ctrlkey'] and not currPluginSetting['altkey']:
|
||||
shortcut = 'kb:cthulhu+' + currPluginSetting['key']
|
||||
# cthulhu + alt
|
||||
if not currPluginSetting['shiftkey'] and not currPluginSetting['ctrlkey'] and currPluginSetting['altkey']:
|
||||
shortcut = 'kb:cthulhu+alt+' + currPluginSetting['key']
|
||||
# cthulhu + CTRL
|
||||
if not currPluginSetting['shiftkey'] and currPluginSetting['ctrlkey'] and not currPluginSetting['altkey']:
|
||||
shortcut = 'kb:cthulhu+control+' + currPluginSetting['key']
|
||||
# cthulhu + alt + CTRL
|
||||
if not currPluginSetting['shiftkey'] and currPluginSetting['ctrlkey'] and currPluginSetting['altkey']:
|
||||
shortcut = 'kb:cthulhu+alt+control+ ' + currPluginSetting['key']
|
||||
# cthulhu + shift
|
||||
if currPluginSetting['shiftkey'] and not currPluginSetting['ctrlkey'] and not currPluginSetting['altkey']:
|
||||
shortcut = 'kb:cthulhu+shift+' + currPluginSetting['key']
|
||||
# alt + shift
|
||||
if currPluginSetting['shiftkey'] and not currPluginSetting['ctrlkey'] and currPluginSetting['altkey']:
|
||||
shortcut = 'kb:alt+shift+' + currPluginSetting['key']
|
||||
if shortcut != '':
|
||||
print(shortcut)
|
||||
currPluginSetting['shortcut'] = shortcut
|
||||
self.registerGestureByString(currPluginSetting['function'], _(currPluginSetting['pluginname']), shortcut)
|
||||
return currPluginSetting
|
||||
|
||||
def id_generator(self, size=7, chars=string.ascii_letters):
|
||||
return ''.join(random.choice(chars) for _ in range(size))
|
||||
|
||||
def initSettings(self):
|
||||
currPluginSetting={
|
||||
'pluginname':'',
|
||||
'functionname':'',
|
||||
'key':'',
|
||||
'shiftkey':False,
|
||||
'ctrlkey':False,
|
||||
'altkey':False,
|
||||
'startnotify':False,
|
||||
'stopnotify':False,
|
||||
'blockcall':False,
|
||||
'error':False,
|
||||
'exec': False,
|
||||
'parameters':'',
|
||||
'function':None,
|
||||
'inputeventhandler':None,
|
||||
'valid':False,
|
||||
'supressoutput':False,
|
||||
'shortcut': ''
|
||||
}
|
||||
return currPluginSetting
|
||||
|
||||
def getPluginSettings(self, filepath, currPluginSetting):
|
||||
try:
|
||||
currPluginSetting['file'] = filepath
|
||||
fileName, fileExtension = os.path.splitext(filepath)
|
||||
if (fileExtension and (fileExtension != '')): #if there is an extension
|
||||
currPluginSetting['loadable'] = (fileExtension.lower() == '.py') # only python is loadable
|
||||
filename = os.path.basename(filepath) #filename
|
||||
filename = os.path.splitext(filename)[0] #remove extension if we have one
|
||||
#remove pluginname seperated by __-__
|
||||
filenamehelper = filename.split('__-__')
|
||||
filename = filenamehelper[len(filenamehelper) - 1 ]
|
||||
currPluginSetting['permission'] = os.access(filepath, os.X_OK )
|
||||
currPluginSetting['pluginname'] = 'NoNameAvailable'
|
||||
if len(filenamehelper) == 2:
|
||||
currPluginSetting['pluginname'] = filenamehelper[0]
|
||||
#now get shortcuts seperated by __+__
|
||||
filenamehelper = filename.split('__+__')
|
||||
if len([y for y in filenamehelper if 'parameters_' in y.lower()]) == 1 and\
|
||||
len([y for y in filenamehelper if 'parameters_' in y.lower()][0]) > 11:
|
||||
currPluginSetting['parameters'] = [y for y in filenamehelper if 'parameters_' in y.lower()][0][11:]
|
||||
if len([y for y in filenamehelper if 'key_' in y.lower()]) == 1 and\
|
||||
len([y for y in filenamehelper if 'key_' in y.lower()][0]) > 4 :
|
||||
currPluginSetting['key'] = [y for y in filenamehelper if 'key_' in y.lower()][0][4]
|
||||
if currPluginSetting['key'] == '':
|
||||
settcurrPluginSetting = 'shift' in map(str.lower, filenamehelper)
|
||||
currPluginSetting['ctrlkey'] = 'control' in map(str.lower, filenamehelper)
|
||||
currPluginSetting['altkey'] = 'alt' in map(str.lower, filenamehelper)
|
||||
currPluginSetting['startnotify'] = 'startnotify' in map(str.lower, filenamehelper)
|
||||
currPluginSetting['stopnotify'] = 'stopnotify' in map(str.lower, filenamehelper)
|
||||
currPluginSetting['blockcall'] = 'blockcall' in map(str.lower, filenamehelper)
|
||||
currPluginSetting['error'] = 'error' in map(str.lower, filenamehelper)
|
||||
currPluginSetting['supressoutput'] = 'supressoutput' in map(str.lower, filenamehelper)
|
||||
currPluginSetting['exec'] = 'exec' in map(str.lower, filenamehelper)
|
||||
currPluginSetting['loadmodule'] = 'loadmodule' in map(str.lower, filenamehelper)
|
||||
currPluginSetting = self.readSettingsFromPlugin(currPluginSetting)
|
||||
if not currPluginSetting['loadmodule']:
|
||||
if not currPluginSetting['permission']: #subprocessing only works with exec permission
|
||||
return self.initSettings()
|
||||
if currPluginSetting['loadmodule'] and not currPluginSetting['loadable']: #sorry.. its not loadable only .py is loadable
|
||||
return self.initSettings()
|
||||
if (len(currPluginSetting['key']) > 1): #no shortcut
|
||||
if not currPluginSetting['exec']: # and no exec -> the plugin make no sense because it isnt hooked anywhere
|
||||
return self.initSettings() #so not load it (sets valid = False)
|
||||
else:
|
||||
currPluginSetting['key'] = '' #there is a strange key, but exec? ignore the key..
|
||||
currPluginSetting['valid'] = True # we could load everything
|
||||
return currPluginSetting
|
||||
except:
|
||||
return self.initSettings()
|
||||
|
||||
def readSettingsFromPlugin(self, currPluginSetting):
|
||||
if not os.access(currPluginSetting['file'], os.R_OK ):
|
||||
return currPluginSetting
|
||||
fileName, fileExtension = os.path.splitext(currPluginSetting['file'])
|
||||
if (fileExtension and (fileExtension != '')): #if there is an extension
|
||||
if (fileExtension.lower() != '.py') and \
|
||||
(fileExtension.lower() != '.sh'):
|
||||
return currPluginSetting
|
||||
else:
|
||||
return currPluginSetting
|
||||
|
||||
with open(currPluginSetting['file'], "r") as pluginFile:
|
||||
for line in pluginFile:
|
||||
currPluginSetting['shiftkey'] = ('sopsproperty:shift' in line.lower().replace(" ", "")) or currPluginSetting['shiftkey']
|
||||
currPluginSetting['ctrlkey'] = ('sopsproperty:control' in line.lower().replace(" ", "")) or currPluginSetting['ctrlkey']
|
||||
currPluginSetting['altkey'] = ('sopsproperty:alt' in line.lower().replace(" ", "")) or currPluginSetting['altkey']
|
||||
currPluginSetting['startnotify'] = ('sopsproperty:startnotify' in line.lower().replace(" ", "")) or currPluginSetting['startnotify']
|
||||
currPluginSetting['stopnotify'] = ('sopsproperty:stopnotify' in line.lower().replace(" ", "")) or currPluginSetting['stopnotify']
|
||||
currPluginSetting['blockcall'] = ('sopsproperty:blockcall' in line.lower().replace(" ", "")) or currPluginSetting['blockcall']
|
||||
currPluginSetting['error'] = ('sopsproperty:error' in line.lower().replace(" ", "")) or currPluginSetting['error']
|
||||
currPluginSetting['supressoutput'] = ('sopsproperty:supressoutput' in line.lower().replace(" ", "")) or currPluginSetting['supressoutput']
|
||||
currPluginSetting['exec'] = ('sopsproperty:exec' in line.lower().replace(" ", "")) or currPluginSetting['exec']
|
||||
currPluginSetting['loadmodule'] = ('sopsproperty:loadmodule' in line.lower().replace(" ", "")) or currPluginSetting['loadmodule']
|
||||
return currPluginSetting
|
||||
|
||||
def buildPluginSubprocess(self, currPluginSetting):
|
||||
currplugin = "\'\"" + currPluginSetting['file'] + "\" " + currPluginSetting['parameters'] + "\'"
|
||||
pluginname = currPluginSetting['pluginname']
|
||||
if currPluginSetting['blockcall']:
|
||||
pluginname = "blocking " + pluginname
|
||||
fun_body = "global " + currPluginSetting['functionname']+"\n"
|
||||
fun_body += "def " + currPluginSetting['functionname'] + "(script=None, inputEvent=None):\n"
|
||||
if currPluginSetting['startnotify']:
|
||||
fun_body +=" outputMessage('start " + pluginname + "')\n"
|
||||
fun_body +=" p = Popen(" + currplugin + ", stdout=PIPE, stderr=PIPE, shell=True)\n"
|
||||
fun_body +=" stdout, stderr = p.communicate()\n"
|
||||
fun_body +=" message = ''\n"
|
||||
fun_body +=" if not " + str(currPluginSetting['supressoutput']) + " and stdout:\n"
|
||||
fun_body +=" message += str(stdout, \"utf-8\")\n"
|
||||
fun_body +=" if " + str(currPluginSetting['error']) + " and stderr:\n"
|
||||
fun_body +=" message += ' error: ' + str(stderr, \"utf-8\")\n"
|
||||
fun_body +=" outputMessage( message)\n"
|
||||
if currPluginSetting['stopnotify']:
|
||||
fun_body +=" outputMessage('finish " + pluginname + "')\n"
|
||||
fun_body +=" return True\n\n"
|
||||
fun_body += "global " + currPluginSetting['functionname']+"T\n"
|
||||
fun_body +="def " + currPluginSetting['functionname'] + "T(script=None, inputEvent=None):\n"
|
||||
fun_body +=" _thread.start_new_thread("+ currPluginSetting['functionname'] + ",(script, inputEvent))\n\n"
|
||||
return fun_body
|
||||
|
||||
def buildPluginExec(self, currPluginSetting):
|
||||
pluginname = currPluginSetting['pluginname']
|
||||
if currPluginSetting['blockcall']:
|
||||
pluginname = "blocking " + pluginname
|
||||
fun_body = "global " + currPluginSetting['functionname']+"\n"
|
||||
fun_body += "def " + currPluginSetting['functionname'] + "(script=None, inputEvent=None):\n"
|
||||
if currPluginSetting['startnotify']:
|
||||
fun_body +=" outputMessage('start " + pluginname + "')\n"
|
||||
fun_body += " try:\n"
|
||||
fun_body += " spec = importlib.util.spec_from_file_location(\"" + currPluginSetting['functionname'] + "\",\""+ currPluginSetting['file']+"\")\n"
|
||||
fun_body += " "+currPluginSetting['functionname'] + "Module = importlib.util.module_from_spec(spec)\n"
|
||||
fun_body += " spec.loader.exec_module(" + currPluginSetting['functionname'] + "Module)\n"
|
||||
fun_body += " except:\n"
|
||||
fun_body += " pass\n"
|
||||
if currPluginSetting['error']:
|
||||
fun_body += " outputMessage(\"Error while executing " + pluginname + "\")\n"
|
||||
if currPluginSetting['stopnotify']:
|
||||
fun_body +=" outputMessage('finish " + pluginname + "')\n"
|
||||
fun_body += " return True\n\n"
|
||||
fun_body += "global " + currPluginSetting['functionname']+"T\n"
|
||||
fun_body +="def " + currPluginSetting['functionname'] + "T(script=None, inputEvent=None):\n"
|
||||
fun_body +=" _thread.start_new_thread("+ currPluginSetting['functionname'] + ",(script, inputEvent))\n\n"
|
||||
return fun_body
|
||||
|
||||
def getFunctionName(self, currPluginSetting):
|
||||
currPluginSetting['functionname'] = ''
|
||||
while currPluginSetting['functionname'] == '' or currPluginSetting['functionname'] + 'T' in globals() or currPluginSetting['functionname'] in globals():
|
||||
currPluginSetting['functionname'] = self.id_generator()
|
||||
return currPluginSetting
|
||||
|
||||
def load_plugins(self):
|
||||
if not self.loaded:
|
||||
self.plugin_list = glob.glob(self.plugin_repo+'*')
|
||||
for currplugin in self.plugin_list:
|
||||
currPluginSetting = self.initSettings()
|
||||
currPluginSetting = self.getPluginSettings(currplugin, currPluginSetting)
|
||||
|
||||
if not currPluginSetting['valid']:
|
||||
continue
|
||||
|
||||
currPluginSetting = self.getFunctionName(currPluginSetting)
|
||||
|
||||
if currPluginSetting['loadmodule']:
|
||||
exec(self.buildPluginExec(currPluginSetting)) # load as python module
|
||||
else:
|
||||
exec(self.buildPluginSubprocess(currPluginSetting)) # run as subprocess
|
||||
|
||||
if currPluginSetting['blockcall']:
|
||||
currPluginSetting['function'] = globals()[currPluginSetting['functionname']] # non threaded
|
||||
else:
|
||||
currPluginSetting['function'] = globals()[currPluginSetting['functionname']+"T"] # T = Threaded
|
||||
|
||||
|
||||
if currPluginSetting['exec']: # exec on load if we want
|
||||
currPluginSetting['function']()
|
||||
|
||||
if not currPluginSetting['key'] == '':
|
||||
currPluginSetting = self.SetupShortcutAndHandle(currPluginSetting)
|
||||
print(currPluginSetting)
|
||||
self.plugin_list.append(currPluginSetting) # store in a list
|
||||
self.loaded = True
|
@ -407,4 +407,4 @@ presentChatRoomLast = False
|
||||
presentLiveRegionFromInactiveTab = False
|
||||
|
||||
# Plugins
|
||||
activePlugins = ['Clipboard', 'MouseReview', 'Date', 'ByeCthulhu', 'Time', 'HelloCthulhu', 'HelloWorld', 'SelfVoice', 'PluginManager', 'ClassicPreferences']
|
||||
activePlugins = ['Clipboard', 'MouseReview', 'Date', 'ByeCthulhu', 'Time', 'HelloCthulhu', 'HelloWorld', 'SelfVoice', 'PluginManager', 'SimplePluginSystem']
|
||||
|
Reference in New Issue
Block a user