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 }