File indexing completed on 2024-05-19 04:59:18
0001 /* ============================================================ 0002 * Falkon - Qt web browser 0003 * Copyright (C) 2018 David Rosca <nowrep@gmail.com> 0004 * 0005 * This program is free software: you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation, either version 3 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU General Public License 0016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0017 * ============================================================ */ 0018 #include "pythonplugin.h" 0019 0020 #include "datapaths.h" 0021 #include "desktopfile.h" 0022 0023 #include <QDir> 0024 #include <QCoreApplication> 0025 0026 #include <PyFalkon/pyfalkon_python.h> 0027 0028 extern "C" PyObject *PyInit_PyFalkon(); 0029 0030 enum State 0031 { 0032 PythonUninitialized, 0033 PythonInitialized, 0034 PythonError 0035 }; 0036 0037 State state = PythonUninitialized; 0038 0039 PluginInterface *pluginInterface = nullptr; 0040 QHash<PyObject*, PluginInterface*> pluginInstances; 0041 0042 static void cleanup() 0043 { 0044 if (state > PythonUninitialized) { 0045 Py_Finalize(); 0046 state = PythonUninitialized; 0047 } 0048 } 0049 0050 static void set_path(const QStringList &scriptPaths) 0051 { 0052 const QString originalPath = QString::fromLocal8Bit(qgetenv("PYTHONPATH")); 0053 0054 QStringList paths = scriptPaths; 0055 paths.append(originalPath); 0056 0057 qputenv("PYTHONPATH", paths.join(QL1C(':')).toLocal8Bit()); 0058 } 0059 0060 static State init() 0061 { 0062 if (state > PythonUninitialized) { 0063 return state; 0064 } 0065 0066 set_path(DataPaths::allPaths(DataPaths::Plugins)); 0067 0068 if (PyImport_AppendInittab("Falkon", PyInit_PyFalkon) != 0) { 0069 PyErr_Print(); 0070 qWarning() << "Failed to initialize Falkon module!"; 0071 return state = PythonError; 0072 } 0073 0074 Py_Initialize(); 0075 qAddPostRoutine(cleanup); 0076 return state = PythonInitialized; 0077 } 0078 0079 void pyfalkon_register_plugin(PluginInterface *plugin) 0080 { 0081 pluginInterface = plugin; 0082 } 0083 0084 void *pyfalkon_load_plugin(const QString &name) 0085 { 0086 QString fullPath; 0087 if (QFileInfo(name).isAbsolute()) { 0088 fullPath = name; 0089 } else { 0090 fullPath = DataPaths::locate(DataPaths::Plugins, name); 0091 if (fullPath.isEmpty()) { 0092 qWarning() << "Python plugin" << name << "not found"; 0093 return nullptr; 0094 } 0095 } 0096 0097 Plugins::Plugin *plugin = new Plugins::Plugin; 0098 plugin->type = Plugins::Plugin::PythonPlugin; 0099 plugin->pluginId = QSL("python:%1").arg(QFileInfo(name).fileName()); 0100 plugin->pluginPath = fullPath; 0101 plugin->pluginSpec = Plugins::createSpec(DesktopFile(fullPath + QSL("/metadata.desktop"))); 0102 return plugin; 0103 } 0104 0105 void pyfalkon_init_plugin(Plugins::Plugin *plugin) 0106 { 0107 if (init() != PythonInitialized) { 0108 return; 0109 } 0110 0111 PyObject *module = static_cast<PyObject*>(plugin->data.value<void*>()); 0112 if (module) { 0113 plugin->instance = pluginInstances.value(module); 0114 return; 0115 } 0116 0117 const QString moduleName = plugin->pluginId.mid(7); 0118 0119 pluginInterface = nullptr; 0120 0121 module = PyImport_ImportModule(qPrintable(moduleName)); 0122 if (!module) { 0123 PyErr_Print(); 0124 qWarning() << "Failed to import module" << moduleName; 0125 return; 0126 } 0127 if (!pluginInterface) { 0128 qWarning() << "No plugin registered! Falkon.registerPlugin() must be called from script."; 0129 return; 0130 } 0131 0132 pluginInstances[module] = pluginInterface; 0133 plugin->instance = pluginInterface; 0134 plugin->data = QVariant::fromValue(static_cast<void*>(module)); 0135 }