diff options
-rw-r--r-- | src/remmina.c | 3 | ||||
-rw-r--r-- | src/remmina_plugin_manager.c | 59 | ||||
-rw-r--r-- | src/remmina_plugin_python.c | 124 | ||||
-rw-r--r-- | src/remmina_plugin_python_module.c | 402 |
4 files changed, 355 insertions, 233 deletions
diff --git a/src/remmina.c b/src/remmina.c index f1f28df43..4cb3f5d56 100644 --- a/src/remmina.c +++ b/src/remmina.c @@ -356,9 +356,6 @@ int main(int argc, char *argv[]) /* Initialize some Remmina parts needed also on a local instance for correct handle-local-options */ remmina_pref_init(); remmina_file_manager_init(); - - remmina_plugin_manager_add_loader("so", remmina_plugin_native_load); - remmina_plugin_manager_add_loader("py", remmina_plugin_python_load); remmina_plugin_manager_init(); diff --git a/src/remmina_plugin_manager.c b/src/remmina_plugin_manager.c index 3f51beb85..df762abb2 100644 --- a/src/remmina_plugin_manager.c +++ b/src/remmina_plugin_manager.c @@ -53,6 +53,8 @@ #include "remmina_widget_pool.h" #include "rcw.h" #include "remmina_plugin_manager.h" +#include "remmina_plugin_native.h" +#include "remmina_plugin_python.h" #include "remmina_public.h" #include "remmina_masterthread_exec.h" #include "remmina/remmina_trace_calls.h" @@ -68,9 +70,6 @@ static RemminaSecretPlugin *remmina_secret_plugin = NULL; static const gchar *remmina_plugin_type_name[] = { N_("Protocol"), N_("Entry"), N_("File"), N_("Tool"), N_("Preference"), N_("Secret"), NULL }; - -GPtrArray* remmina_plugin_loaders = NULL; - static gint remmina_plugin_manager_compare_func(RemminaPlugin **a, RemminaPlugin **b) { TRACE_CALL(__func__); @@ -251,30 +250,24 @@ RemminaPluginService remmina_plugin_manager_service = remmina_protocol_widget_get_profile_remote_height }; -typedef struct { - const gchar* filetype; - const gchar* name; -} RemminaPluginLoaderArgs; - const char *get_filename_ext(const char *filename) { - const char *dot = strrchr(filename, '.'); + const char* last = strrchr(filename, '/'); + const char *dot = strrchr(last, '.'); if(!dot || dot == filename) return ""; return dot + 1; } -static void remmina_plugin_manager_use_loader(const RemminaPluginLoader* loader, const RemminaPluginLoaderArgs* args) { - if (g_str_equal(loader->filetype, args->filetype)) { - loader->func(&remmina_plugin_manager_service, args->name); - } -} - -static void remmina_plugin_manager_load_plugin(char *name) +static void remmina_plugin_manager_load_plugin(const gchar *name) { const char* ext = get_filename_ext(name); - RemminaPluginLoaderArgs args; - args.filetype = ext; - args.name = name; - g_ptr_array_foreach(remmina_plugin_loaders, (GFunc)remmina_plugin_manager_use_loader, &args); + + if (g_str_equal(G_MODULE_SUFFIX, ext)) { + remmina_plugin_native_load(&remmina_plugin_manager_service, name); + } else if (g_str_equal("py", ext)) { + remmina_plugin_python_load(&remmina_plugin_manager_service, name); + } else { + g_print("%s: Skip unsupported file type '%s'\n", name, ext); + } } static gint compare_secret_plugin_init_order(gconstpointer a, gconstpointer b) @@ -321,7 +314,7 @@ void remmina_plugin_manager_init() continue; ptr++; // TODO: Iterate plugin loaders to find out at runtime if we support it - if (g_strcmp0(ptr, G_MODULE_SUFFIX) != 0 && !remmina_plugin_manager_supported(ptr)) + if (!remmina_plugin_manager_loader_supported(ptr)) continue; fullpath = g_strdup_printf(REMMINA_RUNTIME_PLUGINDIR "/%s", name); remmina_plugin_manager_load_plugin(fullpath); @@ -359,29 +352,9 @@ void remmina_plugin_manager_init() g_slist_free(secret_plugins); } -gboolean remmina_plugin_manager_supported(const char *filetype) { +gboolean remmina_plugin_manager_loader_supported(const char *filetype) { TRACE_CALL(__func__); - RemminaPluginLoader *loader; - gint i; - - for (i = 0; i < remmina_plugin_loaders->len; i++) { - loader = (RemminaPluginLoader*)g_ptr_array_index(remmina_plugin_loaders, i); - if (g_strcmp0(loader->filetype, filetype) == 0) { - g_print("Plugin type %s for %s is supported!\n", filetype, loader->filetype); - return TRUE; - } - } - return FALSE; -} - -void remmina_plugin_manager_add_loader(char *filetype, RemminaPluginLoaderFunc func) { - if (!remmina_plugin_loaders) - remmina_plugin_loaders = g_ptr_array_new(); - - RemminaPluginLoader* loader = (RemminaPluginLoader*)malloc(sizeof(RemminaPluginLoader*)); - loader->filetype = filetype; - loader->func = func; - g_ptr_array_add(remmina_plugin_loaders, loader); + return g_str_equal("py", filetype) || g_str_equal(G_MODULE_SUFFIX, filetype); } RemminaPlugin* remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name) diff --git a/src/remmina_plugin_python.c b/src/remmina_plugin_python.c index 1a1273cbd..5e619d761 100644 --- a/src/remmina_plugin_python.c +++ b/src/remmina_plugin_python.c @@ -1,71 +1,111 @@ -#include "config.h" - -#include <gtk/gtk.h> - -#include "remmina/remmina_trace_calls.h" +/* + * Remmina - The GTK+ Remote Desktop Client + * Copyright (C) 2009-2011 Vic Lee + * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo + * Copyright (C) 2016-2020 Antenore Gatta, Giovanni Panozzo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. * If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. * If you + * do not wish to do so, delete this exception statement from your + * version. * If you delete this exception statement from all source + * files in the program, then also delete it here. + * + */ +/** + * @file remmina_plugin_python.c + * @brief Remmina Python plugin loader. + * @author Mathias Winterhalter + * @date 14.10.2020 + * + * When Remmina discovers Python scripts in the plugin root folder the plugin manager + * passes the path to the Python plugin loader. There it gets exexuted and the plugin + * classes get mapped to "real" Remmina plugin instances. + * + * For the communication between Remmina and Python the python module called 'remmina' + * is initialized and loaded into the environment of the plugin script + * (@see remmina_plugin_python_module.c). + * + * @see http://www.remmina.org/wp for more information. + */ +#include <gtk/gtk.h> #define PY_SSIZE_T_CLEAN #include <Python.h> +#include <structmember.h> + #include "pygobject.h" -#include "structmember.h" +#include "config.h" +#include "remmina/remmina_trace_calls.h" #include "remmina_plugin_python.h" #include "remmina_plugin_python_module.h" +static int basename_no_ext(const char* in, char** out) { + const char* base = strrchr(in, '/'); + if (base) base++; + + const char* base_end = strrchr(base, '.'); + if (!base_end) base_end = base + strlen(base); + + int length = base_end - base; + int memsize = sizeof(char*) * ((length) + 1); + *out = (char*)malloc(memsize); + memset(*out, 0, memsize); + strncpy(*out, base, length); + (*out)[length] = '\0'; + + return length; +} void remmina_plugin_python_init(void) { TRACE_CALL(__FUNC__); - // Initialize Python environment + remmina_plugin_python_module_init(); Py_Initialize(); + PyRun_SimpleString("import sys"); PyRun_SimpleString("sys.path.append('" REMMINA_RUNTIME_PLUGINDIR "')"); pygobject_init(-1, -1, -1); } -int basename_no_ext(const char* in, char** out) { - // Find last segment of path... - const char* end = in + strlen(in); - const char* from = in; - const char* to = end; - for (const char* ptr = in; ptr != end; ++ptr) { - if (*ptr == '/') { - while (*ptr == '/' && ptr != end) - ++ptr; - if (ptr == end) - return NULL; - from = ptr; - } else if (*ptr == '.') { - to = ptr; - } - } - - int length = to - from; - *out = (char*)malloc(sizeof(char*) * ((length) + 1)); - strncpy(*out, from, to-from); - (*out)[length] = '\0'; - return length; -} - -gboolean -remmina_plugin_python_load(RemminaPluginService* service, const char* name) { +gboolean remmina_plugin_python_load(RemminaPluginService* service, const char* name) { TRACE_CALL(__FUNC__); - PyObject *plugin_name, *plugin_file; - PyObject *pArgs, *pValue; - int i; + char* filename = NULL; basename_no_ext(name, &filename); - plugin_name = PyUnicode_DecodeFSDefault(filename); - plugin_file = PyImport_Import(plugin_name); - if (plugin_file != NULL) { - return TRUE; - } else { - g_print("Failed to load \"%s\"\n", name); + PyObject *plugin_name = PyUnicode_DecodeFSDefault(filename); + free(filename); + + if (!PyImport_Import(plugin_name)) { + g_print("Failed to load python plugin file: \"%s\"\n", name); PyErr_Print(); return FALSE; } + return TRUE; } diff --git a/src/remmina_plugin_python_module.c b/src/remmina_plugin_python_module.c index 34100f40a..4d1c1598c 100644 --- a/src/remmina_plugin_python_module.c +++ b/src/remmina_plugin_python_module.c @@ -1,69 +1,190 @@ -#include "config.h" +/* + * Remmina - The GTK+ Remote Desktop Client + * Copyright (C) 2009-2011 Vic Lee + * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo + * Copyright (C) 2016-2020 Antenore Gatta, Giovanni Panozzo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. * If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. * If you + * do not wish to do so, delete this exception statement from your + * version. * If you delete this exception statement from all source + * files in the program, then also delete it here. + * + */ -#define PY_SSIZE_T_CLEAN -#include <Python.h> -#include "pygobject.h" -#include "structmember.h" +/** + * @file remmina_plugin_python_module.c + * @brief Implementation of the Python module 'remmina'. + * @author Mathias Winterhalter + * @date 14.10.2020 + * + * The RemminaPluginService provides an API for plugins to interact with Remmina. The + * module called 'remmina' forwards this interface to make it accessible for Python + * scripts. + * + * This is an example of a minimal protocol plugin: + * + * @code + * import remmina + * + * class MyProtocol: + * def __init__(self): + * self.name = "MyProtocol" + * self.description = "Example protocol plugin to explain how Python plugins work." + * self.version = "0.1" + * self.icon_name = "" + * self.icon_name_ssh = "" + * + * def init(self, handle): + * print("This is getting logged to the standard output of Remmina.") + * remmina.log_print("For debugging purposes it would be better to log the output to the %s window %s!" % ("debug", ":)")) + * self.init_your_stuff(handle) + * + * def open_connection(self, handle): + * if not self.connect(): + * remmina.log_print("Error! Can not connect...") + * return False + * + * remmina.remmina_signal_connected(handle) + * remmina.log_print("Connection established!") + * return True + * + * + * def close_connection(self, handle): + * self.disconnect() + * return True + * + * plugin = MyProtocol() + * remmina.register_plugin(plugin) + * @endcode + * + * + * + * @see http://www.remmina.org/wp for more information. + */ #include <glib.h> #include <gtk/gtk.h> +#define PY_SSIZE_T_CLEAN +#include <Python.h> +#include <structmember.h> + +#include "config.h" +#include "pygobject.h" #include "remmina_plugin_manager.h" #include "remmina/plugin.h" #include "remmina_protocol_widget.h" -GPtrArray* remmina_plugin_registry = NULL; +/** + * @brief Holds pairs of Python and Remmina plugin instances (PyPlugin). + */ +GPtrArray *remmina_plugin_registry = NULL; typedef struct { - PyObject* pythonInstance; - RemminaProtocolPlugin* protocolPlugin; -} PyPlugin; + PyObject_HEAD + RemminaProtocolWidget* gp; +} PyRemminaProtocolWidget; /** - * A list of objects initialized and provided by the Remmina Python module. + * @brief Maps an instance of a Python plugin to a Remmina one. */ -static GPtrArray* remmina_python_module_object_table = NULL; -static GPtrArray* remmina_python_module_member_table = NULL; - -static void remmina_protocol_plugin_wrapper_init(RemminaProtocolWidget *gp); -static gboolean remmina_protocol_plugin_wrapper_open_connection(RemminaProtocolWidget *gp); -static gboolean remmina_protocol_plugin_wrapper_close_connection(RemminaProtocolWidget *gp); -static gboolean remmina_protocol_plugin_wrapper_query_feature(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature); -static void remmina_protocol_plugin_wrapper_call_feature(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature); -static void remmina_protocol_plugin_wrapper_send_keystrokes(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen); -static gboolean remmina_protocol_plugin_wrapper_get_plugin_screenshot(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd); - - -static void remmina_protocol_plugin_wrapper_init(RemminaProtocolWidget *gp) { +typedef struct { + PyObject *pythonInstance; -} - -static gboolean remmina_protocol_plugin_wrapper_open_connection(RemminaProtocolWidget *gp) { - return FALSE; -} - -static gboolean remmina_protocol_plugin_wrapper_close_connection(RemminaProtocolWidget *gp) { - return FALSE; -} - -static gboolean remmina_protocol_plugin_wrapper_query_feature(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature) { - return TRUE; -} + RemminaProtocolPlugin *protocolPlugin; + //@TODO: Add more plugin types + PyRemminaProtocolWidget *gp; +} PyPlugin; -static void remmina_protocol_plugin_wrapper_call_feature(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature) { -} +/** + * @brief Wraps the log_printf function of RemminaPluginService. + * + * @details This function is only called by the Python engine! There is only one argument + * since Python offers an inline string formatting which renders any formatting capabilities + * redundant. + * + * @param self Is always NULL since it is a static function of the 'remmina' module + * @param msg The message to print. + */ +static PyObject* remmina_plugin_python_log_printf_wrapper(PyObject* self, PyObject* msg); -static void remmina_protocol_plugin_wrapper_send_keystrokes(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen) { +/** + * @brief Accepts an instance of a Python class representing a Remmina plugin. + * + * @details This function is only called by the Python engine! + * + * @param self Is always NULL since it is a static function of the 'remmina' module + * @param plugin An instance of a Python plugin class. + */ +static PyObject* remmina_plugin_python_register_plugin(PyObject* self, PyObject* plugin); -} +/** + * @brief Retrieves the viewport of the current connection window if available. + * + * @details This function is only called by the Python engine! + * + * @param self Is always NULL since it is a static function of the 'remmina' module + * @param plugin The handle to retrieve the RemminaConnectionWidget + */ +static PyObject* remmina_plugin_python_get_viewport(PyObject* self, PyObject* handle); -static gboolean remmina_protocol_plugin_wrapper_get_plugin_screenshot(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd) { - return TRUE; -} +/** + * @brief Initializes the 'remmina' module + * @details This function is only called by the Python engine! + */ +PyMODINIT_FUNC remmina_plugin_python_module_initialize(void); +/** + * @brief The functions of the remmina module. + * + * @details If any function has to be added this is the place to do it. This list is referenced + * by the PyModuleDef (describing the module) instance below. + */ +static PyMethodDef remmina_python_module_type_methods[] = { + // @TODO: Add all functions from RemminaPluginService + {"register_plugin", (PyCFunction)remmina_plugin_python_register_plugin, METH_O, NULL }, + {"log_print", (PyCFunction)remmina_plugin_python_log_printf_wrapper, METH_VARARGS, NULL }, + {"get_viewport", (PyCFunction)remmina_plugin_python_get_viewport, METH_O, NULL }, + {NULL} /* Sentinel */ +}; +/** + * @brief The definition of the Python module 'remmina'. + */ +static PyModuleDef remmina_python_module_type = { + PyModuleDef_HEAD_INIT, + .m_name = "remmina", + .m_doc = "Remmina API.", + .m_size = -1, + .m_methods = remmina_python_module_type_methods +}; -static PyObject* remmina_plugin_python_log_printf_wrapper(PyObject* self, PyObject* n) { +static PyObject* remmina_plugin_python_log_printf_wrapper(PyObject* self, PyObject* n) +{ + TRACE_CALL(__func__); char* fmt = NULL; if (!PyArg_ParseTuple(n, "s", &fmt)) { g_print("Failed to load.\n"); @@ -76,148 +197,139 @@ static PyObject* remmina_plugin_python_log_printf_wrapper(PyObject* self, PyObje return Py_None; } -static void remmina_protocol_init_wrapper(RemminaProtocolWidget *gp); -static void remmina_protocol_basic_settings_wrapper(RemminaProtocolWidget *gp); -static void remmina_protocol_features_wrapper(RemminaProtocolWidget *gp); -static gboolean remmina_protocol_open_connection_wrapper(RemminaProtocolWidget *gp); -static gboolean remmina_protocol_close_connection_wrapper(RemminaProtocolWidget *gp); -static gboolean remmina_protocol_query_feature_wrapper(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature); -static void remmina_protocol_call_feature_wrapper(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature); -static void remmina_protocol_send_keytrokes_wrapper(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen); -static gboolean remmina_protocol_get_plugin_screensho_wrapper(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd); - -static PyObject* remmina_plugin_python_register_plugin(PyObject* self, PyObject* pluginInstance) { - if (pluginInstance) { - g_ptr_array_add(remmina_plugin_registry, pluginInstance); - - /* Protocol plugin definition and features */ - RemminaProtocolPlugin* remmina_plugin = (RemminaProtocolPlugin*)malloc(sizeof(RemminaProtocolPlugin)); - remmina_plugin->type = REMMINA_PLUGIN_TYPE_PROTOCOL; - remmina_plugin->name = PyUnicode_AsUTF8(PyObject_GetAttrString(pluginInstance, "name")); // Name - remmina_plugin->description = PyUnicode_AsUTF8(PyObject_GetAttrString(pluginInstance, "description")); // Description - remmina_plugin->domain = GETTEXT_PACKAGE; // Translation domain - remmina_plugin->version = PyUnicode_AsUTF8(PyObject_GetAttrString(pluginInstance, "version")); // Version number - remmina_plugin->icon_name = PyUnicode_AsUTF8(PyObject_GetAttrString(pluginInstance, "icon_name")); // Icon for normal connection - remmina_plugin->icon_name_ssh = PyUnicode_AsUTF8(PyObject_GetAttrString(pluginInstance, "icon_name_ssh")); // Icon for SSH connection - remmina_plugin->basic_settings = NULL; // Array for basic settings - remmina_plugin->advanced_settings = NULL; // Array for advanced settings - remmina_plugin->ssh_setting = REMMINA_PROTOCOL_SSH_SETTING_TUNNEL; // SSH settings type - remmina_plugin->features = NULL; // Array for available features - remmina_plugin->init = remmina_protocol_init_wrapper ; // Plugin initialization - remmina_plugin->open_connection = remmina_protocol_open_connection_wrapper ; // Plugin open connection - remmina_plugin->close_connection = remmina_protocol_close_connection_wrapper ; // Plugin close connection - remmina_plugin->query_feature = remmina_protocol_query_feature_wrapper ; // Query for available features - remmina_plugin->call_feature = remmina_protocol_call_feature_wrapper ; // Call a feature - remmina_plugin->send_keystrokes = remmina_protocol_send_keytrokes_wrapper; // Send a keystroke - remmina_plugin->get_plugin_screenshot = remmina_protocol_get_plugin_screensho_wrapper; // Screenshot support unavailable - - remmina_plugin_manager_service.register_plugin((RemminaPlugin *)remmina_plugin); - - PyPlugin* plugin = (PyPlugin*)malloc(sizeof(PyPlugin)); - plugin->protocolPlugin = remmina_plugin; - plugin->pythonInstance = pluginInstance; - g_ptr_array_add(remmina_plugin_registry, plugin); - } - - return Py_None; +static PyObject* remmina_plugin_python_get_viewport(PyObject* self, PyObject* handle) { + PyRemminaProtocolWidget *gp = (PyRemminaProtocolWidget*)handle; + return pygobject_new(G_OBJECT(remmina_protocol_widget_gtkviewport(gp->gp))); } -static PyMethodDef remmina_python_module_type_methods[] = { - {"register_plugin", (PyCFunction)remmina_plugin_python_register_plugin, METH_O, NULL }, - {"log_print", (PyCFunction)remmina_plugin_python_log_printf_wrapper, METH_VARARGS, NULL }, - {NULL} /* Sentinel */ -}; - -static PyModuleDef remmina_python_module_type = { - PyModuleDef_HEAD_INIT, - .m_name = "remmina", - .m_doc = "Remmina API.", - .m_size = -1, - .m_methods = remmina_python_module_type_methods -}; - /** - * + * @brief Initializes the remmina module and maps it to the Python engine. */ -PyMODINIT_FUNC remmina_plugin_python_module_initialize(void); - -void remmina_plugin_python_module_init(void) { +void remmina_plugin_python_module_init(void) +{ + TRACE_CALL(__func__); remmina_plugin_registry = g_ptr_array_new(); - if (PyImport_AppendInittab("remmina", remmina_plugin_python_module_initialize)) - return FALSE; - -} - -PyMODINIT_FUNC remmina_plugin_python_module_initialize(void) { - PyObject* module_instance = NULL; - PyTypeObject* builtin; - PyMethodDef* method; - gint i; - - module_instance = PyModule_Create(&remmina_python_module_type); - - if (remmina_python_module_object_table) { - for (i = 0; i < remmina_python_module_object_table->len; i++) { - builtin = (PyTypeObject*)g_ptr_array_index(remmina_python_module_object_table, i); - if (builtin) { - Py_INCREF(builtin); - if (PyModule_AddObject(module_instance, "ProtocolPlugin", (PyObject*)builtin)) { - printf("%s: Error initializing\n", builtin->tp_name); - Py_INCREF(builtin); - } - } - } + if (PyImport_AppendInittab("remmina", remmina_plugin_python_module_initialize)) { + g_print("Error initializing remmina module for python!\n"); + PyErr_Print(); + return; } +} - // TODO: Initialize Remmina module for Python - return module_instance; +PyMODINIT_FUNC remmina_plugin_python_module_initialize(void) +{ + TRACE_CALL(__func__); + return PyModule_Create(&remmina_python_module_type); } -static void remmina_protocol_init_executor(PyPlugin* plugin, RemminaProtocolWidget *gp) { +/** + * + */ +static void remmina_protocol_init_executor(PyPlugin* plugin, RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); if (g_str_equal(plugin->protocolPlugin->name, gp->plugin->name)) { - PyObject_CallMethod(plugin->pythonInstance, "init", NULL); + plugin->gp->gp = gp; + PyObject_CallMethod(plugin->pythonInstance, "init", "O", plugin); } } -static void remmina_protocol_init_wrapper(RemminaProtocolWidget *gp) { +static void remmina_protocol_init_wrapper(RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); g_ptr_array_foreach(remmina_plugin_registry, (GFunc)remmina_protocol_init_executor, gp); } static PyObject* remmina_protocol_open_connection_executor_result = NULL; -static void remmina_protocol_open_connection_executor(PyPlugin* plugin, RemminaProtocolWidget *gp) { +static void remmina_protocol_open_connection_executor(PyPlugin* plugin, RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); if (g_str_equal(plugin->protocolPlugin->name, gp->plugin->name)) { - remmina_protocol_open_connection_executor_result = PyObject_CallMethod(plugin->pythonInstance, "open_connection", "O", pygobject_new(G_OBJECT(remmina_protocol_widget_gtkviewport(gp)))); + remmina_protocol_open_connection_executor_result = PyObject_CallMethod(plugin->pythonInstance, "open_connection", "O", plugin); } } -static gboolean remmina_protocol_open_connection_wrapper(RemminaProtocolWidget *gp) { +static gboolean remmina_protocol_open_connection_wrapper(RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); remmina_plugin_manager_service.protocol_plugin_signal_connection_opened(gp); g_ptr_array_foreach(remmina_plugin_registry, (GFunc)remmina_protocol_open_connection_executor, gp); return remmina_protocol_open_connection_executor_result == Py_True; } static PyObject* remmina_protocol_close_connection_executor_result = NULL; -static void remmina_protocol_close_connection_executor(PyPlugin* plugin, RemminaProtocolWidget *gp) { +static void remmina_protocol_close_connection_executor(PyPlugin* plugin, RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); if (g_str_equal(plugin->protocolPlugin->name, gp->plugin->name)) { - remmina_protocol_close_connection_executor_result = PyObject_CallMethod(plugin->pythonInstance, "close_connection","O", pygobject_new(G_OBJECT(remmina_protocol_widget_get_gtkwindow(gp)))); + remmina_protocol_close_connection_executor_result = PyObject_CallMethod(plugin->pythonInstance, "close_connection", "O", plugin); } } -static gboolean remmina_protocol_close_connection_wrapper(RemminaProtocolWidget *gp) { +static gboolean remmina_protocol_close_connection_wrapper(RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); g_ptr_array_foreach(remmina_plugin_registry, (GFunc)remmina_protocol_close_connection_executor, gp); return remmina_protocol_close_connection_executor_result == Py_True; } -static gboolean remmina_protocol_query_feature_wrapper(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature) { +static gboolean remmina_protocol_query_feature_wrapper(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature) +{ + TRACE_CALL(__func__); return TRUE; } -static void remmina_protocol_call_feature_wrapper(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature) { +static void remmina_protocol_call_feature_wrapper(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature) +{ + TRACE_CALL(__func__); return; } -static void remmina_protocol_send_keytrokes_wrapper(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen) { +static void remmina_protocol_send_keytrokes_wrapper(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen) +{ + TRACE_CALL(__func__); return; } -static gboolean remmina_protocol_get_plugin_screensho_wrapper(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd) { +static gboolean remmina_protocol_get_plugin_screensho_wrapper(RemminaProtocolPlugin* plugin, RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd) +{ + TRACE_CALL(__func__); return TRUE; } +static PyObject* remmina_plugin_python_register_plugin(PyObject* self, PyObject* pluginInstance) +{ + TRACE_CALL(__func__); + if (pluginInstance) { + g_ptr_array_add(remmina_plugin_registry, pluginInstance); + + /* Protocol plugin definition and features */ + RemminaProtocolPlugin* remmina_plugin = (RemminaProtocolPlugin*)malloc(sizeof(RemminaProtocolPlugin)); + remmina_plugin->type = REMMINA_PLUGIN_TYPE_PROTOCOL; + remmina_plugin->name = PyUnicode_AsUTF8(PyObject_GetAttrString(pluginInstance, "name")); // Name + remmina_plugin->description = PyUnicode_AsUTF8(PyObject_GetAttrString(pluginInstance, "description")); // Description + remmina_plugin->domain = GETTEXT_PACKAGE; // Translation domain + remmina_plugin->version = PyUnicode_AsUTF8(PyObject_GetAttrString(pluginInstance, "version")); // Version number + remmina_plugin->icon_name = PyUnicode_AsUTF8(PyObject_GetAttrString(pluginInstance, "icon_name")); // Icon for normal connection + remmina_plugin->icon_name_ssh = PyUnicode_AsUTF8(PyObject_GetAttrString(pluginInstance, "icon_name_ssh")); // Icon for SSH connection + remmina_plugin->basic_settings = NULL; // Array for basic settings + remmina_plugin->advanced_settings = NULL; // Array for advanced settings + remmina_plugin->ssh_setting = REMMINA_PROTOCOL_SSH_SETTING_TUNNEL; // SSH settings type + remmina_plugin->features = NULL; // Array for available features + remmina_plugin->init = remmina_protocol_init_wrapper ; // Plugin initialization + remmina_plugin->open_connection = remmina_protocol_open_connection_wrapper ; // Plugin open connection + remmina_plugin->close_connection = remmina_protocol_close_connection_wrapper ; // Plugin close connection + remmina_plugin->query_feature = remmina_protocol_query_feature_wrapper ; // Query for available features + remmina_plugin->call_feature = remmina_protocol_call_feature_wrapper ; // Call a feature + remmina_plugin->send_keystrokes = remmina_protocol_send_keytrokes_wrapper; // Send a keystroke + remmina_plugin->get_plugin_screenshot = remmina_protocol_get_plugin_screensho_wrapper; // Screenshot support unavailable + + remmina_plugin_manager_service.register_plugin((RemminaPlugin *)remmina_plugin); + + PyPlugin* plugin = (PyPlugin*)malloc(sizeof(PyPlugin)); + plugin->protocolPlugin = remmina_plugin; + plugin->pythonInstance = pluginInstance; + plugin->gp = malloc(sizeof(PyRemminaProtocolWidget)); + plugin->gp->gp = NULL; + g_ptr_array_add(remmina_plugin_registry, plugin); + } + + return Py_None; +}
\ No newline at end of file |