Another dirty hack to bypass libpeas.
This commit is contained in:
parent
8cbe387e0b
commit
262c954f02
@ -1,185 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
#
|
|
||||||
# Copyright (c) 2024 Stormux
|
|
||||||
# Copyright (c) 2010-2012 The Orca Team
|
|
||||||
# Copyright (c) 2012 Igalia, S.L.
|
|
||||||
# Copyright (c) 2005-2010 Sun Microsystems Inc.
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
# Fork of Orca Screen Reader (GNOME)
|
|
||||||
# Original source: https://gitlab.gnome.org/GNOME/orca
|
|
||||||
|
|
||||||
import os, inspect
|
|
||||||
import gi
|
|
||||||
from gi.repository import GObject
|
|
||||||
|
|
||||||
class Plugin():
|
|
||||||
#__gtype_name__ = 'BasePlugin'
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.API = None
|
|
||||||
self.pluginInfo = None
|
|
||||||
self.moduleDir = ''
|
|
||||||
self.hidden = False
|
|
||||||
self.moduleName = ''
|
|
||||||
self.name = ''
|
|
||||||
self.version = ''
|
|
||||||
self.website = ''
|
|
||||||
self.authors = []
|
|
||||||
self.buildIn = False
|
|
||||||
self.description = ''
|
|
||||||
self.iconName = ''
|
|
||||||
self.copyright = ''
|
|
||||||
self.dependencies = False
|
|
||||||
self.helpUri = ''
|
|
||||||
self.dataDir = ''
|
|
||||||
self.translationContext = None
|
|
||||||
def setApp(self, app):
|
|
||||||
self.app = app
|
|
||||||
self.dynamicApiManager = app.getDynamicApiManager()
|
|
||||||
self.signalManager = app.getSignalManager()
|
|
||||||
|
|
||||||
def getApp(self):
|
|
||||||
return self.app
|
|
||||||
def setPluginInfo(self, pluginInfo):
|
|
||||||
self.pluginInfo = pluginInfo
|
|
||||||
self.updatePluginInfoAttributes()
|
|
||||||
def getPluginInfo(self):
|
|
||||||
return self.pluginInfo
|
|
||||||
def updatePluginInfoAttributes(self):
|
|
||||||
self.moduleDir = ''
|
|
||||||
self.hidden = False
|
|
||||||
self.moduleName = ''
|
|
||||||
self.name = ''
|
|
||||||
self.version = ''
|
|
||||||
self.website = ''
|
|
||||||
self.authors = []
|
|
||||||
self.buildIn = False
|
|
||||||
self.description = ''
|
|
||||||
self.iconName = ''
|
|
||||||
self.copyright = ''
|
|
||||||
self.dependencies = False
|
|
||||||
self.helpUri = ''
|
|
||||||
self.dataDir = ''
|
|
||||||
pluginInfo = self.getPluginInfo()
|
|
||||||
if pluginInfo == None:
|
|
||||||
return
|
|
||||||
self.moduleName = self.getApp().getPluginSystemManager().getPluginModuleName(pluginInfo)
|
|
||||||
self.name = self.getApp().getPluginSystemManager().getPluginName(pluginInfo)
|
|
||||||
self.version = self.getApp().getPluginSystemManager().getPluginVersion(pluginInfo)
|
|
||||||
self.moduleDir = self.getApp().getPluginSystemManager().getPluginModuleDir(pluginInfo)
|
|
||||||
self.buildIn = self.getApp().getPluginSystemManager().isPluginBuildIn(pluginInfo)
|
|
||||||
self.description = self.getApp().getPluginSystemManager().getPluginDescription(pluginInfo)
|
|
||||||
self.hidden = self.getApp().getPluginSystemManager().isPluginHidden(pluginInfo)
|
|
||||||
self.website = self.getApp().getPluginSystemManager().getPluginWebsite(pluginInfo)
|
|
||||||
self.authors = self.getApp().getPluginSystemManager().getPluginAuthors(pluginInfo)
|
|
||||||
self.iconName = self.getApp().getPluginSystemManager().getPluginIconName(pluginInfo)
|
|
||||||
self.copyright = self.getApp().getPluginSystemManager().getPluginCopyright(pluginInfo)
|
|
||||||
self.dependencies = self.getApp().getPluginSystemManager().getPluginDependencies(pluginInfo)
|
|
||||||
|
|
||||||
#settings = self.getApp().getPluginSystemManager().getPluginSettings(pluginInfo)
|
|
||||||
#hasDependencies = self.getApp().getPluginSystemManager().hasPluginDependency(pluginInfo)
|
|
||||||
|
|
||||||
#externalData = self.getApp().getPluginSystemManager().getPluginExternalData(pluginInfo)
|
|
||||||
self.helpUri = self.getApp().getPluginSystemManager().getPlugingetHelpUri(pluginInfo)
|
|
||||||
self.dataDir = self.getApp().getPluginSystemManager().getPluginDataDir(pluginInfo)
|
|
||||||
self.updateTranslationContext()
|
|
||||||
|
|
||||||
def updateTranslationContext(self, domain = None, localeDir = None, language = None, fallbackToCthulhuTranslation = True):
|
|
||||||
self.translationContext = None
|
|
||||||
useLocaleDir = '{}/locale/'.format(self.getModuleDir())
|
|
||||||
if localeDir:
|
|
||||||
if os.path.isdir(localeDir):
|
|
||||||
useLocaleDir = localeDir
|
|
||||||
useName = self.getModuleName()
|
|
||||||
useDomain = useName
|
|
||||||
if domain:
|
|
||||||
useDomain = domain
|
|
||||||
useLanguage = None
|
|
||||||
if language:
|
|
||||||
useLanguage = language
|
|
||||||
self.translationContext = self.getApp().getTranslationManager().initTranslation(useName, domain=useDomain, localeDir=useLocaleDir, language=useLanguage, fallbackToCthulhuTranslation=fallbackToCthulhuTranslation)
|
|
||||||
# Point _ to the translation object in the globals namespace of the caller frame
|
|
||||||
try:
|
|
||||||
callerFrame = inspect.currentframe().f_back
|
|
||||||
# Install our gettext and ngettext function to the upper frame
|
|
||||||
callerFrame.f_globals['_'] = self.translationContext.gettext
|
|
||||||
callerFrame.f_globals['ngettext'] = self.translationContext.ngettext
|
|
||||||
finally:
|
|
||||||
del callerFrame # Avoid reference problems with frames (per python docs)
|
|
||||||
def getTranslationContext(self):
|
|
||||||
return self.translationContext
|
|
||||||
def isPluginBuildIn(self):
|
|
||||||
return self.buildIn
|
|
||||||
def isPluginHidden(self):
|
|
||||||
return self.hidden
|
|
||||||
def getAuthors(self):
|
|
||||||
return self.authors
|
|
||||||
def getCopyright(self):
|
|
||||||
return self.copyright
|
|
||||||
def getDataDir(self):
|
|
||||||
return self.dataDir
|
|
||||||
def getDependencies(self):
|
|
||||||
return self.dependencies
|
|
||||||
def getDescription(self):
|
|
||||||
return self.description
|
|
||||||
def getgetHelpUri(self):
|
|
||||||
return self.helpUri
|
|
||||||
def getIconName(self):
|
|
||||||
return self.iconName
|
|
||||||
def getModuleDir(self):
|
|
||||||
return self.moduleDir
|
|
||||||
def getModuleName(self):
|
|
||||||
return self.moduleName
|
|
||||||
def getName(self):
|
|
||||||
return self.name
|
|
||||||
def getVersion(self):
|
|
||||||
return self.version
|
|
||||||
def getWebsite(self):
|
|
||||||
return self.website
|
|
||||||
def getSetting(key):
|
|
||||||
#self.getModuleName())
|
|
||||||
return None
|
|
||||||
def setSetting(key, value):
|
|
||||||
#self.getModuleName())
|
|
||||||
pass
|
|
||||||
def registerGestureByString(self, function, name, gestureString, learnModeEnabled = True):
|
|
||||||
keybinding = self.getApp().getAPIHelper().registerGestureByString(function, name, gestureString, 'default', 'cthulhu', learnModeEnabled, contextName = self.getModuleName())
|
|
||||||
return keybinding
|
|
||||||
def unregisterShortcut(self, function, name, gestureString, learnModeEnabled = True):
|
|
||||||
ok = self.getApp().getAPIHelper().unregisterShortcut(keybinding, contextName = self.getModuleName())
|
|
||||||
return ok
|
|
||||||
def registerSignal(self, signalName, signalFlag = GObject.SignalFlags.RUN_LAST, closure = GObject.TYPE_NONE, accumulator=()):
|
|
||||||
ok = self.signalManager.registerSignal(signalName, signalFlag, closure, accumulator, contextName = self.getModuleName())
|
|
||||||
return ok
|
|
||||||
def unregisterSignal(self, signalName):
|
|
||||||
# how to unregister?
|
|
||||||
pass
|
|
||||||
|
|
||||||
def connectSignal(self, signalName, function, param = None):
|
|
||||||
signalID = self.signalManager.connectSignal(signalName, function, param, contextName = self.getModuleName())
|
|
||||||
return signalID
|
|
||||||
def disconnectSignalByFunction(self, function):
|
|
||||||
# need get mapped function
|
|
||||||
mappedFunction = function
|
|
||||||
self.signalManager.disconnectSignalByFunction(mappedFunction, contextName = self.getModuleName())
|
|
||||||
|
|
||||||
def registerAPI(self, key, value, application = ''):
|
|
||||||
ok = self.dynamicApiManager.registerAPI(key, value, application = application, contextName = self.getModuleName())
|
|
||||||
return ok
|
|
||||||
def unregisterAPI(self, key, application = ''):
|
|
||||||
self.dynamicApiManager.unregisterAPI(key, application = application, contextName = self.getModuleName())
|
|
@ -1,544 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
#
|
|
||||||
# Copyright (c) 2024 Stormux
|
|
||||||
# Copyright (c) 2010-2012 The Orca Team
|
|
||||||
# Copyright (c) 2012 Igalia, S.L.
|
|
||||||
# Copyright (c) 2005-2010 Sun Microsystems Inc.
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
# Fork of Orca Screen Reader (GNOME)
|
|
||||||
# Original source: https://gitlab.gnome.org/GNOME/orca
|
|
||||||
|
|
||||||
"""PluginManager for loading cthulhu plugins."""
|
|
||||||
import os, inspect, sys, tarfile, shutil
|
|
||||||
|
|
||||||
from enum import IntEnum
|
|
||||||
|
|
||||||
version = sys.version[:3] # we only need major.minor version.
|
|
||||||
if version in ["3.3","3.4"]:
|
|
||||||
from importlib.machinery import SourceFileLoader
|
|
||||||
else: # Python 3.5+, no support for python < 3.3.
|
|
||||||
import importlib.util
|
|
||||||
|
|
||||||
import gi
|
|
||||||
gi.require_version('Peas', '1.0')
|
|
||||||
from gi.repository import GObject
|
|
||||||
from gi.repository import Peas
|
|
||||||
|
|
||||||
gi.require_version('Atspi', '2.0')
|
|
||||||
from gi.repository import Atspi
|
|
||||||
|
|
||||||
from cthulhu import resource_manager
|
|
||||||
|
|
||||||
class API(GObject.GObject):
|
|
||||||
"""Interface that gives access to all the objects of Cthulhu."""
|
|
||||||
def __init__(self, app):
|
|
||||||
GObject.GObject.__init__(self)
|
|
||||||
self.app = app
|
|
||||||
|
|
||||||
class PluginType(IntEnum):
|
|
||||||
"""Types of plugins we support, depending on their directory location."""
|
|
||||||
# pylint: disable=comparison-with-callable,inconsistent-return-statements,no-else-return
|
|
||||||
# SYSTEM: provides system wide plugins
|
|
||||||
SYSTEM = 1
|
|
||||||
# USER: provides per user plugin
|
|
||||||
USER = 2
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
if self.value == PluginType.SYSTEM:
|
|
||||||
return _("System plugin")
|
|
||||||
elif self.value == PluginType.USER:
|
|
||||||
return _("User plugin")
|
|
||||||
|
|
||||||
def get_root_dir(self):
|
|
||||||
"""Returns the directory where this type of plugins can be found."""
|
|
||||||
if self.value == PluginType.SYSTEM:
|
|
||||||
return os.path.dirname(os.path.realpath(os.path.abspath(inspect.getfile(inspect.currentframe())))) + '/plugins'
|
|
||||||
elif self.value == PluginType.USER:
|
|
||||||
return os.path.expanduser('~') + '/.local/share/cthulhu/plugins'
|
|
||||||
|
|
||||||
|
|
||||||
class PluginSystemManager():
|
|
||||||
"""Cthulhu Plugin Manager to handle a set of plugins.
|
|
||||||
Attributes:
|
|
||||||
DEFAULT_LOADERS (tuple): Default loaders used by the plugin manager. For
|
|
||||||
possible values see
|
|
||||||
https://developer.gnome.org/libpeas/stable/PeasEngine.html#peas-engine-enable-loader
|
|
||||||
"""
|
|
||||||
DEFAULT_LOADERS = ("python3", )
|
|
||||||
|
|
||||||
def __init__(self, app):
|
|
||||||
self.app = app
|
|
||||||
self.engine = Peas.Engine.get_default()
|
|
||||||
|
|
||||||
for loader in self.DEFAULT_LOADERS:
|
|
||||||
self.engine.enable_loader(loader)
|
|
||||||
|
|
||||||
self._setupPluginsDir()
|
|
||||||
self._setupExtensionSet()
|
|
||||||
|
|
||||||
if self.app:
|
|
||||||
self.gsettingsManager = self.app.getSettingsManager()
|
|
||||||
# settings else:
|
|
||||||
# settings self.gsettingsManager = gsettings_manager.getSettingsManager(self.app)
|
|
||||||
self._activePlugins = []
|
|
||||||
self._ignorePluginModulePath = []
|
|
||||||
@property
|
|
||||||
def plugins(self):
|
|
||||||
"""Gets the engine's plugin list."""
|
|
||||||
return self.engine.get_plugin_list()
|
|
||||||
@classmethod
|
|
||||||
def getPluginType(cls, pluginInfo):
|
|
||||||
"""Gets the PluginType for the specified Peas.PluginInfo."""
|
|
||||||
paths = [pluginInfo.get_data_dir(), PluginType.SYSTEM.get_root_dir()]
|
|
||||||
if os.path.commonprefix(paths) == PluginType.SYSTEM.get_root_dir():
|
|
||||||
return PluginType.SYSTEM
|
|
||||||
return PluginType.USER
|
|
||||||
|
|
||||||
def getExtension(self, pluginInfo):
|
|
||||||
if not pluginInfo:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return self.extension_set.get_extension(pluginInfo)
|
|
||||||
def rescanPlugins(self):
|
|
||||||
self.engine.garbage_collect()
|
|
||||||
self.engine.rescan_plugins()
|
|
||||||
def getApp(self):
|
|
||||||
return self.app
|
|
||||||
def getPluginInfoByName(self, pluginName, pluginType=PluginType.USER):
|
|
||||||
"""Gets the plugin info for the specified plugin name.
|
|
||||||
Args:
|
|
||||||
pluginName (str): The name from the .plugin file of the module.
|
|
||||||
Returns:
|
|
||||||
Peas.PluginInfo: The plugin info if it exists. Otherwise, `None`.
|
|
||||||
"""
|
|
||||||
for pluginInfo in self.plugins:
|
|
||||||
if pluginInfo.get_module_name() == pluginName and PluginSystemManager.getPluginType(pluginInfo) == pluginType:
|
|
||||||
return pluginInfo
|
|
||||||
return None
|
|
||||||
def getActivePlugins(self):
|
|
||||||
return self._activePlugins
|
|
||||||
def setActivePlugins(self, activePlugins):
|
|
||||||
self._activePlugins = activePlugins
|
|
||||||
self.syncAllPluginsActive()
|
|
||||||
def isPluginBuildIn(self, pluginInfo):
|
|
||||||
return pluginInfo.is_builtin()
|
|
||||||
def isPluginHidden(self, pluginInfo):
|
|
||||||
return pluginInfo.is_hidden()
|
|
||||||
def getPluginAuthors(self, pluginInfo):
|
|
||||||
return pluginInfo.get_authors()
|
|
||||||
def getPluginCopyright(self, pluginInfo):
|
|
||||||
return pluginInfo.get_copyright()
|
|
||||||
def getPluginDataDir(self, pluginInfo):
|
|
||||||
return pluginInfo.get_data_dir()
|
|
||||||
def getPluginDependencies(self, pluginInfo):
|
|
||||||
return pluginInfo.get_dependencies()
|
|
||||||
def getPluginDescription(self, pluginInfo):
|
|
||||||
return pluginInfo.get_description()
|
|
||||||
def getPlugingetHelpUri(self, pluginInfo):
|
|
||||||
return pluginInfo.get_help_uri()
|
|
||||||
def getPluginIconName(self, pluginInfo):
|
|
||||||
return pluginInfo.get_icon_name()
|
|
||||||
def getPluginModuleDir(self, pluginInfo):
|
|
||||||
return pluginInfo.get_module_dir()
|
|
||||||
def getPluginModuleName(self, pluginInfo):
|
|
||||||
return pluginInfo.get_module_name()
|
|
||||||
def getPluginName(self, pluginInfo):
|
|
||||||
return pluginInfo.get_name()
|
|
||||||
def getPluginSettings(self, pluginInfo):
|
|
||||||
return pluginInfo.get_settings()
|
|
||||||
def getPluginVersion(self, pluginInfo):
|
|
||||||
return pluginInfo.get_version()
|
|
||||||
def getPluginWebsite(self, pluginInfo):
|
|
||||||
return pluginInfo.get_website()
|
|
||||||
# has_dependency and get_external_data seems broken-> takes exactly 2 arguments (1 given) but documentation doesnt say any parameter
|
|
||||||
#def hasPluginDependency(self, pluginInfo):
|
|
||||||
# return pluginInfo.has_dependency()
|
|
||||||
#def getPluginExternalData(self, pluginInfo):
|
|
||||||
# return pluginInfo.get_external_data()
|
|
||||||
def isPluginAvailable(self, pluginInfo):
|
|
||||||
try:
|
|
||||||
return pluginInfo.is_available()
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
def isPluginLoaded(self, pluginInfo):
|
|
||||||
try:
|
|
||||||
return pluginInfo.is_loaded()
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def getIgnoredPlugins(self):
|
|
||||||
return self._ignorePluginModulePath
|
|
||||||
def setIgnoredPlugins(self, pluginModulePath, ignored):
|
|
||||||
if pluginModulePath.endswith('/'):
|
|
||||||
pluginModulePath = pluginModulePath[:-1]
|
|
||||||
if ignored:
|
|
||||||
if not pluginModulePath in self.getIgnoredPlugins():
|
|
||||||
self._ignorePluginModulePath.append(pluginModulePath)
|
|
||||||
else:
|
|
||||||
if pluginModulePath in self.getIgnoredPlugins():
|
|
||||||
self._ignorePluginModulePath.remove(pluginModulePath)
|
|
||||||
|
|
||||||
def setPluginActive(self, pluginInfo, active):
|
|
||||||
if self.isPluginBuildIn(pluginInfo):
|
|
||||||
active = True
|
|
||||||
pluginName = self.getPluginModuleName(pluginInfo)
|
|
||||||
if active:
|
|
||||||
if not pluginName in self.getActivePlugins():
|
|
||||||
if self.loadPlugin(pluginInfo):
|
|
||||||
self._activePlugins.append(pluginName )
|
|
||||||
else:
|
|
||||||
if pluginName in self.getActivePlugins():
|
|
||||||
if self.unloadPlugin(pluginInfo):
|
|
||||||
self._activePlugins.remove(pluginName )
|
|
||||||
def isPluginActive(self, pluginInfo):
|
|
||||||
if self.isPluginBuildIn(pluginInfo):
|
|
||||||
return True
|
|
||||||
if self.isPluginLoaded(pluginInfo):
|
|
||||||
return True
|
|
||||||
active_plugin_names = self.getActivePlugins()
|
|
||||||
return self.getPluginModuleName(pluginInfo) in active_plugin_names
|
|
||||||
def syncAllPluginsActive(self, ForceAllPlugins=False):
|
|
||||||
self.unloadAllPlugins(ForceAllPlugins)
|
|
||||||
self.loadAllPlugins(ForceAllPlugins)
|
|
||||||
|
|
||||||
def loadAllPlugins(self, ForceAllPlugins=False):
|
|
||||||
"""Loads plugins from settings."""
|
|
||||||
for pluginInfo in self.plugins:
|
|
||||||
if self.isPluginActive(pluginInfo) or ForceAllPlugins:
|
|
||||||
self.loadPlugin(pluginInfo)
|
|
||||||
|
|
||||||
def loadPlugin(self, pluginInfo):
|
|
||||||
resourceManager = self.getApp().getResourceManager()
|
|
||||||
moduleName = pluginInfo.get_module_name()
|
|
||||||
try:
|
|
||||||
if pluginInfo not in self.plugins:
|
|
||||||
print("Plugin missing: {}".format(moduleName))
|
|
||||||
return False
|
|
||||||
resourceManager.addResourceContext(moduleName)
|
|
||||||
self.engine.load_plugin(pluginInfo)
|
|
||||||
except Exception as e:
|
|
||||||
print('loadPlugin:',e)
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def unloadAllPlugins(self, ForceAllPlugins=False):
|
|
||||||
"""Loads plugins from settings."""
|
|
||||||
for pluginInfo in self.plugins:
|
|
||||||
if not self.isPluginActive(pluginInfo) or ForceAllPlugins:
|
|
||||||
self.unloadPlugin(pluginInfo)
|
|
||||||
|
|
||||||
def unloadPlugin(self, pluginInfo):
|
|
||||||
resourceManager = self.getApp().getResourceManager()
|
|
||||||
moduleName = pluginInfo.get_module_name()
|
|
||||||
try:
|
|
||||||
if pluginInfo not in self.plugins:
|
|
||||||
print("Plugin missing: {}".format(moduleName))
|
|
||||||
return False
|
|
||||||
if self.isPluginBuildIn(pluginInfo):
|
|
||||||
return False
|
|
||||||
self.engine.unload_plugin(pluginInfo)
|
|
||||||
self.getApp().getResourceManager().removeResourceContext(moduleName)
|
|
||||||
self.engine.garbage_collect()
|
|
||||||
except Exception as e:
|
|
||||||
print('unloadPlugin:',e)
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
def installPlugin(self, pluginFilePath, pluginType=PluginType.USER):
|
|
||||||
if not self.isValidPluginFile(pluginFilePath):
|
|
||||||
return False
|
|
||||||
pluginFolder = pluginType.get_root_dir()
|
|
||||||
if not pluginFolder.endswith('/'):
|
|
||||||
pluginFolder += '/'
|
|
||||||
if not os.path.exists(pluginFolder):
|
|
||||||
os.mkdir(pluginFolder)
|
|
||||||
else:
|
|
||||||
if not os.path.isdir(pluginFolder):
|
|
||||||
return False
|
|
||||||
try:
|
|
||||||
with tarfile.open(pluginFilePath) as tar:
|
|
||||||
tar.extractall(path=pluginFolder)
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
|
|
||||||
pluginModulePath = self.getModuleDirByPluginFile(pluginFilePath)
|
|
||||||
if pluginModulePath != '':
|
|
||||||
pluginModulePath = pluginFolder + pluginModulePath
|
|
||||||
self.setIgnoredPlugins(pluginModulePath[:-1], False) # without ending /
|
|
||||||
print('install', pluginFilePath)
|
|
||||||
self.callPackageTriggers(pluginModulePath, 'onPostInstall')
|
|
||||||
self.rescanPlugins()
|
|
||||||
|
|
||||||
return True
|
|
||||||
def getModuleDirByPluginFile(self, pluginFilePath):
|
|
||||||
if not isinstance(pluginFilePath, str):
|
|
||||||
return ''
|
|
||||||
if pluginFilePath == '':
|
|
||||||
return ''
|
|
||||||
if not os.path.exists(pluginFilePath):
|
|
||||||
return ''
|
|
||||||
try:
|
|
||||||
with tarfile.open(pluginFilePath) as tar:
|
|
||||||
tarMembers = tar.getmembers()
|
|
||||||
for tarMember in tarMembers:
|
|
||||||
if tarMember.isdir():
|
|
||||||
return tarMember.name
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
return ''
|
|
||||||
def isValidPluginFile(self, pluginFilePath):
|
|
||||||
if not isinstance(pluginFilePath, str):
|
|
||||||
return False
|
|
||||||
if pluginFilePath == '':
|
|
||||||
return False
|
|
||||||
if not os.path.exists(pluginFilePath):
|
|
||||||
return False
|
|
||||||
pluginFolder = ''
|
|
||||||
pluginFileExists = False
|
|
||||||
packageFileExists = False
|
|
||||||
try:
|
|
||||||
with tarfile.open(pluginFilePath) as tar:
|
|
||||||
tarMembers = tar.getmembers()
|
|
||||||
for tarMember in tarMembers:
|
|
||||||
if tarMember.isdir():
|
|
||||||
if pluginFolder == '':
|
|
||||||
pluginFolder = tarMember.name
|
|
||||||
if tarMember.isfile():
|
|
||||||
if tarMember.name.endswith('.plugin'):
|
|
||||||
pluginFileExists = True
|
|
||||||
if tarMember.name.endswith('package.py'):
|
|
||||||
pluginFileExists = True
|
|
||||||
if not tarMember.name.startswith(pluginFolder):
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
return False
|
|
||||||
return pluginFileExists
|
|
||||||
def uninstallPlugin(self, pluginInfo):
|
|
||||||
if self.isPluginBuildIn(pluginInfo):
|
|
||||||
return False
|
|
||||||
# do we want to allow removing system plugins?
|
|
||||||
if PluginSystemManager.getPluginType(pluginInfo) == PluginType.SYSTEM:
|
|
||||||
return False
|
|
||||||
pluginFolder = pluginInfo.get_data_dir()
|
|
||||||
if not pluginFolder.endswith('/'):
|
|
||||||
pluginFolder += '/'
|
|
||||||
if not os.path.isdir(pluginFolder):
|
|
||||||
return False
|
|
||||||
if self.isPluginActive(pluginInfo):
|
|
||||||
self.setPluginActive(pluginInfo, False)
|
|
||||||
SettingsManager = self.app.getSettingsManager()
|
|
||||||
# TODO SettingsManager.set_settings_value_list('active-plugins', self.getActivePlugins())
|
|
||||||
self.callPackageTriggers(pluginFolder, 'onPreUninstall')
|
|
||||||
|
|
||||||
try:
|
|
||||||
shutil.rmtree(pluginFolder, ignore_errors=True)
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
return False
|
|
||||||
self.setIgnoredPlugins(pluginFolder, True)
|
|
||||||
self.rescanPlugins()
|
|
||||||
|
|
||||||
return True
|
|
||||||
def callPackageTriggers(self, pluginPath, trigger):
|
|
||||||
if not os.path.exists(pluginPath):
|
|
||||||
return
|
|
||||||
if not pluginPath.endswith('/'):
|
|
||||||
pluginPath += '/'
|
|
||||||
packageModulePath = pluginPath + 'package.py'
|
|
||||||
if not os.path.isfile(packageModulePath):
|
|
||||||
return
|
|
||||||
if not os.access(packageModulePath, os.R_OK):
|
|
||||||
return
|
|
||||||
package = self.getApp().getAPIHelper().importModule('package', packageModulePath)
|
|
||||||
|
|
||||||
if trigger == 'onPostInstall':
|
|
||||||
try:
|
|
||||||
package.onPostInstall(pluginPath, self.getApp())
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
elif trigger == 'onPreUninstall':
|
|
||||||
try:
|
|
||||||
package.onPreUninstall(pluginPath, self.getApp())
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
|
|
||||||
def _setupExtensionSet(self):
|
|
||||||
plugin_iface = API(self.getApp())
|
|
||||||
self.extension_set = Peas.ExtensionSet.new(self.engine,
|
|
||||||
Peas.Activatable,
|
|
||||||
["object"],
|
|
||||||
[plugin_iface])
|
|
||||||
self.extension_set.connect("extension-removed",
|
|
||||||
self.__extensionRemoved)
|
|
||||||
self.extension_set.connect("extension-added",
|
|
||||||
self.__extensionAdded)
|
|
||||||
|
|
||||||
def _setupPluginsDir(self):
|
|
||||||
system_plugins_dir = PluginType.SYSTEM.get_root_dir()
|
|
||||||
user_plugins_dir = PluginType.USER.get_root_dir()
|
|
||||||
if os.path.exists(user_plugins_dir):
|
|
||||||
self.engine.add_search_path(user_plugins_dir)
|
|
||||||
if os.path.exists(system_plugins_dir):
|
|
||||||
self.engine.add_search_path(system_plugins_dir)
|
|
||||||
|
|
||||||
def __extensionRemoved(self, unusedSet, pluginInfo, extension):
|
|
||||||
extension.deactivate()
|
|
||||||
|
|
||||||
def __extensionAdded(self, unusedSet, pluginInfo, extension):
|
|
||||||
extension.setApp(self.getApp())
|
|
||||||
extension.setPluginInfo(pluginInfo)
|
|
||||||
extension.activate()
|
|
||||||
def __loadedPlugins(self, engine, unusedSet):
|
|
||||||
"""Handles the changing of the loaded plugin list."""
|
|
||||||
self.getApp().settings.ActivePlugins = engine.get_property("loaded-plugins")
|
|
||||||
|
|
||||||
class APIHelper():
|
|
||||||
def __init__(self, app):
|
|
||||||
self.app = app
|
|
||||||
self.cthulhuKeyBindings = None
|
|
||||||
|
|
||||||
'''
|
|
||||||
_pluginAPIManager.seCthulhuAPI('Logger', _logger)
|
|
||||||
_pluginAPIManager.setCthulhuAPI('SettingsManager', _settingsManager)
|
|
||||||
_pluginAPIManager.setCthulhuAPI('ScriptManager', _scriptManager)
|
|
||||||
_pluginAPIManager.setCthulhuAPI('EventManager', _eventManager)
|
|
||||||
_pluginAPIManager.setCthulhuAPI('Speech', speech)
|
|
||||||
_pluginAPIManager.setCthulhuAPI('Sound', sound)
|
|
||||||
_pluginAPIManager.setCthulhuAPI('Braille', braille)
|
|
||||||
_pluginAPIManager.setCthulhuAPI('Debug', debug)
|
|
||||||
_pluginAPIManager.setCthulhuAPI('Messages', messages)
|
|
||||||
_pluginAPIManager.setCthulhuAPI('MouseReview', mouse_review)
|
|
||||||
_pluginAPIManager.setCthulhuAPI('NotificationMessages', notification_messages)
|
|
||||||
_pluginAPIManager.setCthulhuAPI('CthulhuState', cthulhu_state)
|
|
||||||
_pluginAPIManager.setCthulhuAPI('CthulhuPlatform', cthulhu_platform)
|
|
||||||
_pluginAPIManager.setCthulhuAPI('Settings', settings)
|
|
||||||
_pluginAPIManager.setCthulhuAPI('Keybindings', keybindings)
|
|
||||||
'''
|
|
||||||
def outputMessage(self, Message, interrupt=False):
|
|
||||||
settings = self.app.getDynamicApiManager().getAPI('Settings')
|
|
||||||
braille = self.app.getDynamicApiManager().getAPI('Braille')
|
|
||||||
speech = self.app.getDynamicApiManager().getAPI('Speech')
|
|
||||||
if speech != None:
|
|
||||||
if (settings.enableSpeech):
|
|
||||||
if interrupt:
|
|
||||||
speech.cancel()
|
|
||||||
if Message != '':
|
|
||||||
speech.speak(Message)
|
|
||||||
if braille != None:
|
|
||||||
if (settings.enableBraille):
|
|
||||||
braille.displayMessage(Message)
|
|
||||||
def createInputEventHandler(self, function, name, learnModeEnabled=True):
|
|
||||||
EventManager = self.app.getDynamicApiManager().getAPI('EventManager')
|
|
||||||
newInputEventHandler = EventManager.input_event.InputEventHandler(function, name, learnModeEnabled)
|
|
||||||
return newInputEventHandler
|
|
||||||
def registerGestureByString(self, function, name, gestureString, profile, application, learnModeEnabled = True, contextName = None):
|
|
||||||
gestureList = gestureString.split(',')
|
|
||||||
registeredGestures = []
|
|
||||||
for gesture in gestureList:
|
|
||||||
if gesture.startswith('kb:'):
|
|
||||||
shortcutString = gesture[3:]
|
|
||||||
registuredGesture = self.registerShortcutByString(function, name, shortcutString, profile, application, learnModeEnabled, contextName=contextName)
|
|
||||||
if registuredGesture:
|
|
||||||
registeredGestures.append(registuredGesture)
|
|
||||||
return registeredGestures
|
|
||||||
def registerShortcutByString(self, function, name, shortcutString, profile, application, learnModeEnabled = True, contextName = None):
|
|
||||||
keybindings = self.app.getDynamicApiManager().getAPI('Keybindings')
|
|
||||||
settings = self.app.getDynamicApiManager().getAPI('Settings')
|
|
||||||
resourceManager = self.app.getResourceManager()
|
|
||||||
|
|
||||||
clickCount = 0
|
|
||||||
cthulhuKey = False
|
|
||||||
shiftKey = False
|
|
||||||
ctrlKey = False
|
|
||||||
altKey = False
|
|
||||||
key = ''
|
|
||||||
shortcutList = shortcutString.split('+')
|
|
||||||
for shortcutElement in shortcutList:
|
|
||||||
shortcutElementLower = shortcutElement.lower()
|
|
||||||
if shortcutElementLower == 'press':
|
|
||||||
clickCount += 1
|
|
||||||
elif shortcutElement == 'cthulhu':
|
|
||||||
cthulhuKey = True
|
|
||||||
elif shortcutElementLower == 'shift':
|
|
||||||
shiftKey = True
|
|
||||||
elif shortcutElementLower == 'control':
|
|
||||||
ctrlKey = True
|
|
||||||
elif shortcutElementLower == 'alt':
|
|
||||||
altKey = True
|
|
||||||
else:
|
|
||||||
key = shortcutElementLower
|
|
||||||
if clickCount == 0:
|
|
||||||
clickCount = 1
|
|
||||||
|
|
||||||
if self.cthulhuKeyBindings == None:
|
|
||||||
self.cthulhuKeyBindings = keybindings.KeyBindings()
|
|
||||||
|
|
||||||
tryFunction = resource_manager.TryFunction(function)
|
|
||||||
newInputEventHandler = self.createInputEventHandler(tryFunction.runInputEvent, name, learnModeEnabled)
|
|
||||||
|
|
||||||
currModifierMask = keybindings.NO_MODIFIER_MASK
|
|
||||||
if cthulhuKey:
|
|
||||||
currModifierMask = currModifierMask | 1 << keybindings.MODIFIER_CTHULHU
|
|
||||||
if shiftKey:
|
|
||||||
currModifierMask = currModifierMask | 1 << Atspi.ModifierType.SHIFT
|
|
||||||
if altKey:
|
|
||||||
currModifierMask = currModifierMask | 1 << Atspi.ModifierType.ALT
|
|
||||||
if ctrlKey:
|
|
||||||
currModifierMask = currModifierMask | 1 << Atspi.ModifierType.CONTROL
|
|
||||||
|
|
||||||
newKeyBinding = keybindings.KeyBinding(key, keybindings.defaultModifierMask, currModifierMask, newInputEventHandler, clickCount)
|
|
||||||
self.cthulhuKeyBindings.add(newKeyBinding)
|
|
||||||
|
|
||||||
settings.keyBindingsMap["default"] = self.cthulhuKeyBindings
|
|
||||||
|
|
||||||
if contextName:
|
|
||||||
resourceContext = resourceManager.getResourceContext(contextName)
|
|
||||||
if resourceContext:
|
|
||||||
resourceEntry = resource_manager.ResourceEntry('keyboard', newKeyBinding, function, tryFunction, shortcutString)
|
|
||||||
resourceContext.addGesture(profile, application, newKeyBinding, resourceEntry)
|
|
||||||
|
|
||||||
return newKeyBinding
|
|
||||||
|
|
||||||
def unregisterShortcut(self, KeyBindingToRemove, contextName = None):
|
|
||||||
ok = False
|
|
||||||
keybindings = self.app.getDynamicApiManager().getAPI('Keybindings')
|
|
||||||
settings = self.app.getDynamicApiManager().getAPI('Settings')
|
|
||||||
resourceManager = self.app.getResourceManager()
|
|
||||||
|
|
||||||
if self.cthulhuKeyBindings == None:
|
|
||||||
self.cthulhuKeyBindings = keybindings.KeyBindings()
|
|
||||||
try:
|
|
||||||
self.cthulhuKeyBindings.remove(KeyBindingToRemove)
|
|
||||||
settings.keyBindingsMap["default"] = self.cthulhuKeyBindings
|
|
||||||
ok = True
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
if contextName:
|
|
||||||
resourceContext = resourceManager.getResourceContext(contextName)
|
|
||||||
if resourceContext:
|
|
||||||
resourceContext.removeGesture(KeyBindingToRemove)
|
|
||||||
|
|
||||||
return ok
|
|
||||||
def importModule(self, moduleName, moduleLocation):
|
|
||||||
if version in ["3.3","3.4"]:
|
|
||||||
return SourceFileLoader(moduleName, moduleLocation).load_module()
|
|
||||||
else:
|
|
||||||
spec = importlib.util.spec_from_file_location(moduleName, moduleLocation)
|
|
||||||
driver_mod = importlib.util.module_from_spec(spec)
|
|
||||||
spec.loader.exec_module(driver_mod)
|
|
||||||
return driver_mod
|
|
Loading…
x
Reference in New Issue
Block a user