diff options
author | ToolsDevler <info@toolsdevler.net> | 2020-10-10 20:48:21 +0300 |
---|---|---|
committer | ToolsDevler <info@toolsdevler.net> | 2020-12-31 03:03:24 +0300 |
commit | c2d7c99c61d7b5f8879f14b073dc51d1603facdc (patch) | |
tree | 040df0f2face7c33bfa28a4135b28ff86887a968 | |
parent | 3b9043d6ed81d3afb451f2c07377e5741a6c0340 (diff) |
WIP: Fixed plugin loading
-rw-r--r-- | plugins/tool_hello_world_python/toolsdevler.py | 48 | ||||
-rw-r--r-- | src/remmina_plugin_manager.c | 18 | ||||
-rw-r--r-- | src/remmina_plugin_manager.h | 1 | ||||
-rw-r--r-- | src/remmina_plugin_python.c | 33 | ||||
-rw-r--r-- | src/remmina_plugin_python.h | 1 | ||||
-rw-r--r-- | src/remmina_plugin_python_module.c | 2 | ||||
-rw-r--r-- | src/remmina_plugin_python_plugin.c | 284 |
7 files changed, 282 insertions, 105 deletions
diff --git a/plugins/tool_hello_world_python/toolsdevler.py b/plugins/tool_hello_world_python/toolsdevler.py index d1f3c1041..7c79d7233 100644 --- a/plugins/tool_hello_world_python/toolsdevler.py +++ b/plugins/tool_hello_world_python/toolsdevler.py @@ -4,27 +4,27 @@ if not hasattr(sys, 'argv'): sys.argv = [''] import remmina +import gi +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, GLib +import psutil -class HelloPlugin(remmina.Plugin): +class HelloPlugin(remmina.ProtocolPlugin): def __init__(self): - self.type = remmina.PLUGIN_TYPE_PROTOCOL - self.name = "Hello" - self.description = "Just a Plugin saying hello :)" - self.version = "1.0" - self.appicon = "" - self.service = None - self.protocol_setting = None + super().__init__("Hello", "Just a Plugin saying hello :)", "1.0", "") def init(self, service): self.service = service - service.log_printf("[%s]: Plugin init\n" % self.name) + print("Init!") + self.show_gui() + super.init() def open_connection(self): - service.log_printf("[%s]: Plugin open connection\n" % self.name) - + self.service.log_printf("[%s]: Plugin open connection\n" % self.name) + super.open_connection() def close_connection(self): - service.log_printf("[%s]: Plugin close connection\n" % self.name) + self.service.log_printf("[%s]: Plugin close connection\n" % self.name) def query_feature(self): pass @@ -38,12 +38,18 @@ class HelloPlugin(remmina.Plugin): def get_plugin_screenshot(self): pass -print("import gi") -import gi -gi.require_version("Gtk", "3.0") -from gi.repository import Gtk - -window = Gtk.Window(title="Hello World") -window.show() -window.connect("destroy", Gtk.main_quit) -Gtk.main() + def show_gui(self): + self.window = Gtk.Window(title="Hello World") + self.lblMemUsage = Gtk.Label(label="CPU Usage: ") + self.window.add(self.lblMemUsage) + self.window.show_all() + self.window.connect("destroy", Gtk.main_quit) + GLib.timeout_add_seconds(1, self.threaded_mem_usage) + Gtk.main() + + def threaded_mem_usage(self): + self.lblMemUsage.set_text("CPU Usage: %s" % psutil.cpu_percent()) + return True + +myPlugin = HelloPlugin() +#myPlugin.show_gui() diff --git a/src/remmina_plugin_manager.c b/src/remmina_plugin_manager.c index 1f3731ec9..3f51beb85 100644 --- a/src/remmina_plugin_manager.c +++ b/src/remmina_plugin_manager.c @@ -320,7 +320,8 @@ void remmina_plugin_manager_init() if ((ptr = strrchr(name, '.')) == NULL) continue; ptr++; - if (g_strcmp0(ptr, G_MODULE_SUFFIX) != 0 && g_strcmp0(ptr, "py")) + // 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)) continue; fullpath = g_strdup_printf(REMMINA_RUNTIME_PLUGINDIR "/%s", name); remmina_plugin_manager_load_plugin(fullpath); @@ -358,6 +359,21 @@ void remmina_plugin_manager_init() g_slist_free(secret_plugins); } +gboolean remmina_plugin_manager_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(); diff --git a/src/remmina_plugin_manager.h b/src/remmina_plugin_manager.h index 578d95d5b..731f48e0c 100644 --- a/src/remmina_plugin_manager.h +++ b/src/remmina_plugin_manager.h @@ -65,6 +65,7 @@ typedef struct { RemminaPluginLoaderFunc func; } RemminaPluginLoader; +gboolean remmina_plugin_manager_supported(const char *filetype); void remmina_plugin_manager_add_loader(char *filetype, RemminaPluginLoaderFunc func); G_END_DECLS diff --git a/src/remmina_plugin_python.c b/src/remmina_plugin_python.c index d2db79696..43257175b 100644 --- a/src/remmina_plugin_python.c +++ b/src/remmina_plugin_python.c @@ -13,6 +13,7 @@ #include "remmina_plugin_python_module.h" #include "remmina_plugin_python_plugin.h" + void remmina_plugin_python_init(void) { TRACE_CALL(__FUNC__); // Initialize Python environment @@ -20,14 +21,33 @@ void remmina_plugin_python_init(void) { remmina_plugin_python_module_register_object(plugin_object); remmina_plugin_python_module_init(); - Py_Initialize(); PyRun_SimpleString("import sys"); PyRun_SimpleString("sys.path.append('" REMMINA_RUNTIME_PLUGINDIR "')"); } -static char* basename(const char* path) { - return "toolsdevler"; +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 @@ -36,14 +56,15 @@ remmina_plugin_python_load(RemminaPluginService* service, const char* name) { PyObject *plugin_name, *plugin_file; PyObject *pArgs, *pValue; int i; - - plugin_name = PyUnicode_DecodeFSDefault(basename(name)); + 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 { - PyErr_Print(); g_print("Failed to load \"%s\"\n", name); + PyErr_Print(); return FALSE; } return TRUE; diff --git a/src/remmina_plugin_python.h b/src/remmina_plugin_python.h index 307588904..fbf921669 100644 --- a/src/remmina_plugin_python.h +++ b/src/remmina_plugin_python.h @@ -41,5 +41,6 @@ G_BEGIN_DECLS gboolean remmina_plugin_python_load(RemminaPluginService* service, const char* name); +void remmina_plugin_python_init(void); G_END_DECLS diff --git a/src/remmina_plugin_python_module.c b/src/remmina_plugin_python_module.c index 17d896d91..fc1e25f53 100644 --- a/src/remmina_plugin_python_module.c +++ b/src/remmina_plugin_python_module.c @@ -75,7 +75,7 @@ PyMODINIT_FUNC remmina_plugin_python_module_initialize(void) { builtin = (PyTypeObject*)g_ptr_array_index(remmina_python_module_object_table, i); if (builtin) { Py_INCREF(builtin); - if (PyModule_AddObject(module_instance, "Plugin", (PyObject*)builtin)) { + if (PyModule_AddObject(module_instance, "ProtocolPlugin", (PyObject*)builtin)) { printf("%s: Error initializing\n", builtin->tp_name); Py_INCREF(builtin); } diff --git a/src/remmina_plugin_python_plugin.c b/src/remmina_plugin_python_plugin.c index edf833e87..a25df8026 100644 --- a/src/remmina_plugin_python_plugin.c +++ b/src/remmina_plugin_python_plugin.c @@ -6,6 +6,7 @@ #include <glib.h> #include <gtk/gtk.h> +#include "remmina_plugin_manager.h" #include "remmina/plugin.h" #include "remmina_plugin_python_plugin.h" @@ -16,73 +17,96 @@ static const gchar* STR_REMMINA_PLUGIN_TYPE_TOOL = "remmina.plugin_type.tool"; static const gchar* STR_REMMINA_PLUGIN_TYPE_PREF = "remmina.plugin_type.pref"; static const gchar* STR_REMMINA_PLUGIN_TYPE_SECRET = "remmina.plugin_type.secret"; +GPtrArray* remmina_plugin_registry = NULL; -static int -remmina_remmina_plugin_init(RemminaPluginPython *self, PyObject *args, PyObject *kwds); +typedef struct { + PyObject * name; + PyObject * description; + PyObject * version; + PyObject * appicon; + PyObject * icon_name; + PyObject * icon_name_ssh; + PyObject * basic_settings; + PyObject * advanced_settings; + PyObject * ssh_setting; + PyObject * features; + RemminaProtocolWidget* widget; +} RemminaPluginPythonProtocolPlugin; -static PyObject * -remmina_remmina_plugin_new(PyTypeObject *type, PyObject *args, PyObject *kwds); - -static PyMemberDef RemminaPluginPython_members[] = { - {"name", T_OBJECT_EX, offsetof(RemminaPluginPython, name), 0, - "plugin name"}, - {"description", T_OBJECT_EX, offsetof(RemminaPluginPython, description), 0, - "plugin description"}, - {"version", T_OBJECT_EX, offsetof(RemminaPluginPython, version), 0, - "plugin version"}, - {"appicon", T_OBJECT_EX, offsetof(RemminaPluginPython, appicon), 0, - "plugin app-icon name"}, + +static PyMemberDef remmina_plugin_python_protocol_plugin_members[] = { + {"name", T_OBJECT_EX, offsetof(RemminaPluginPythonProtocolPlugin, name), 0, "plugin name"}, + {"description", T_OBJECT_EX, offsetof(RemminaPluginPythonProtocolPlugin, description), 0, "plugin description"}, + {"version", T_OBJECT_EX, offsetof(RemminaPluginPythonProtocolPlugin, version), 0, "plugin version"}, + {"appicon", T_OBJECT_EX, offsetof(RemminaPluginPythonProtocolPlugin, appicon), 0, "plugin app-icon name"}, + {"icon_name", T_OBJECT_EX, offsetof(RemminaPluginPythonProtocolPlugin, icon_name), 0, "icon_name"}, + {"icon_name_ssh", T_OBJECT_EX, offsetof(RemminaPluginPythonProtocolPlugin, icon_name_ssh), 0, "icon_name_ssh"}, + {"basic_settings", T_OBJECT_EX, offsetof(RemminaPluginPythonProtocolPlugin, basic_settings), 0, "basic_settings"}, + {"advanced_settings", T_INT, offsetof(RemminaPluginPythonProtocolPlugin, advanced_settings), 0, "advanced_settings"}, + {"ssh_setting", T_INT, offsetof(RemminaPluginPythonProtocolPlugin, ssh_setting), 0, "ssh_setting"}, + {"features", T_OBJECT_EX, offsetof(RemminaPluginPythonProtocolPlugin, features), 0, "features"}, {NULL} /* Sentinel */ }; +/* Array for available features. + * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */ +static const RemminaProtocolFeature remmina_protocol_python_plugin_features[] = +{ + { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL } +}; -typedef struct { - PyObject_HEAD - PyObject *name; - PyObject *description; - PyObject *version; - PyObject *appicon; - RemminaPlugin* remmina_plugin; -} RemminaPluginPython; -typedef struct { - PyObject * type; - PyObject * name; - PyObject * description; - PyObject * domain; - PyObject * version; - - PyObject * icon_name; - PyObject * icon_name_ssh; - PyObject * basic_settings; - PyObject * advanced_settings; - PyObject * ssh_setting; - PyObject * features; - - PyMethodDef m_mehods[] = { +PyMethodDef remmina_plugin_python_protocol_plugin_methods[] = { {"init", METH_VARARGS, NULL }, {"open_connection", METH_VARARGS, NULL }, {"close_connection", METH_VARARGS, NULL }, {"query_feature", METH_VARARGS, NULL }, {"call_feature", METH_VARARGS, NULL }, {"send_keystrokes", METH_VARARGS, NULL }, - {"get_plugin_screenshot", METH_VARARGS, NULL }}; -} RemminaProtocolPluginPython; + {"get_plugin_screenshot", METH_VARARGS, NULL } +}; + +/* + void (*init)(RemminaProtocolWidget *gp); + gboolean (*open_connection)(RemminaProtocolWidget *gp); + gboolean (*close_connection)(RemminaProtocolWidget *gp); + gboolean (*query_feature)(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature); + void (*call_feature)(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature); + void (*send_keystrokes)(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen); + gboolean (*get_plugin_screenshot)(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd);*/ -static PyTypeObject RemminaPluginPythonType = { +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(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature); +static void remmina_protocol_plugin_wrapper_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature); +static void remmina_protocol_plugin_wrapper_send_keystrokes(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen); +static gboolean remmina_protocol_plugin_wrapper_get_plugin_screenshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd); + +static void +remmina_plugin_python_init_protocol_plugin(RemminaProtocolPlugin* plugin, RemminaPluginPythonProtocolPlugin* pythonObject); + +static int +remmina_protocol_plugin_init(RemminaPluginPythonProtocolPlugin *self, PyObject *args, PyObject *kwds); + +static PyObject * +remmina_protocol_plugin_new(PyTypeObject *type, PyObject *args, PyObject *kwds); + +static PyTypeObject RemminaProtocolPluginPythonType = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "remmina.Plugin", + .tp_name = "remmina.ProtocolPlugin", .tp_doc = "Custom objects", - .tp_basicsize = sizeof(RemminaPluginPython), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_members = RemminaPluginPython_members, - .tp_new = remmina_remmina_plugin_new, - .tp_init = remmina_remmina_plugin_init + .tp_basicsize = sizeof(RemminaPluginPythonProtocolPlugin), + .tp_itemsize = sizeof(RemminaPluginPythonProtocolPlugin), + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_members = remmina_plugin_python_protocol_plugin_members, + .tp_methods = remmina_plugin_python_protocol_plugin_methods, + .tp_new = remmina_protocol_plugin_new, + .tp_init = remmina_protocol_plugin_init }; static void -RemminaPluginPython_dealloc(RemminaPluginPython *self){ +RemminaPluginPython_dealloc(RemminaPluginPythonProtocolPlugin *self){ Py_XDECREF(self->name); Py_XDECREF(self->description); Py_XDECREF(self->version); @@ -90,46 +114,107 @@ RemminaPluginPython_dealloc(RemminaPluginPython *self){ Py_TYPE(self)->tp_free((PyObject *) self); } -PyTypeObject* remmina_plugin_python_plugin_create(void) { return &RemminaPluginPythonType; } +static void +remmina_plugin_python_init_protocol_plugin(RemminaProtocolPlugin* plugin, RemminaPluginPythonProtocolPlugin* pythonObject) { + +} + +struct PyPlugin { + PyObject_HEAD + RemminaPluginPythonProtocolPlugin* internal; +}; + +PyTypeObject* remmina_plugin_python_plugin_create(void) { return &RemminaProtocolPluginPythonType; } static PyObject * -remmina_remmina_plugin_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +remmina_protocol_plugin_new(PyTypeObject *type, PyObject *parent, PyObject *args) { - RemminaPluginPython *self; + struct PyPlugin *state; + PyObject *self; + self = PyType_GenericNew(type, parent, args); + if (self == NULL) + return NULL; + // Cast the object to the appropriate type + state = (struct PyPlugin *) self; + // Initialize your internal structure + state->internal = malloc(sizeof(*state->internal)); + if (state->internal == NULL) + return NULL; + memset(state->internal, 0, sizeof(*state->internal)); + // This means no error occurred - self = (RemminaPluginPython *)type->tp_alloc(type, 0); - if (!self) + state->internal->name = PyUnicode_FromString(""); + if (!state->internal->name) { + Py_DECREF(self); return NULL; - - self->name = PyUnicode_FromString(""); - if (!self->name) { + } + + state->internal->name = PyUnicode_FromString(""); + if (!state->internal->name) { Py_DECREF(self); return NULL; } - - self->description = PyUnicode_FromString(""); - if (!self->description) { + + state->internal->description = PyUnicode_FromString(""); + if (!state->internal->description) { Py_DECREF(self); return NULL; } - - self->version = PyUnicode_FromString(""); - if (!self->version) { + + state->internal->version = PyUnicode_FromString(""); + if (!state->internal->version) { Py_DECREF(self); return NULL; } - - self->appicon = PyUnicode_FromString(""); - if (!self->appicon) { + + state->internal->appicon = PyUnicode_FromString(""); + if (!state->internal->appicon) { + Py_DECREF(self); + return NULL; + } + + state->internal->icon_name = PyUnicode_FromString(""); + if (!state->internal->icon_name) { Py_DECREF(self); return NULL; } - return (PyObject *)self; + state->internal->icon_name_ssh = PyUnicode_FromString(""); + if (!state->internal->icon_name_ssh) { + Py_DECREF(self); + return NULL; + } + + state->internal->basic_settings = PyUnicode_FromString(""); + if (!state->internal->basic_settings) { + Py_DECREF(self); + return NULL; + } + + state->internal->advanced_settings = PyUnicode_FromString(""); + if (!state->internal->advanced_settings) { + Py_DECREF(self); + return NULL; + } + + state->internal->ssh_setting = PyUnicode_FromString(""); + if (!state->internal->ssh_setting) { + Py_DECREF(self); + return NULL; + } + + state->internal->features = PyUnicode_FromString(""); + if (!state->internal->features) { + Py_DECREF(self); + return NULL; + } + + return self; } + static int -remmina_remmina_plugin_init(RemminaPluginPython *self, PyObject *args, PyObject *kwds) +remmina_protocol_plugin_init(RemminaPluginPythonProtocolPlugin *self, PyObject *args, PyObject *kwds) { static char* kwlist[] = { "name", "description", "version", "appicon", NULL }; PyObject *name = NULL; @@ -145,25 +230,21 @@ remmina_remmina_plugin_init(RemminaPluginPython *self, PyObject *args, PyObject tmp = self->name; Py_INCREF(name); self->name = name; - Py_XDECREF(tmp); } if (description) { tmp = self->description; Py_INCREF(description); self->description = description; - Py_XDECREF(tmp); } if (version) { tmp = self->version; Py_INCREF(version); self->version = version; - Py_XDECREF(tmp); } if (appicon) { tmp = self->appicon; Py_INCREF(appicon); self->appicon = appicon; - Py_XDECREF(tmp); } const char *name_str; @@ -171,12 +252,63 @@ remmina_remmina_plugin_init(RemminaPluginPython *self, PyObject *args, PyObject const char *version_str; const char *appicon_str; - if (PyArg_ParseTuple(args, "ssss", &name_str, &version_str, &appicon_str, &description_str)) + if (PyArg_ParseTuple(args, "ssss", &name_str, &description_str, &version_str, &appicon_str)) printf("Initialied plugin: %s\nVersion: %s\nApp-Icon: %s\nDescription: %s\n", name_str, version_str, appicon_str, description_str); - self->remmina_plugin = (RemminaPlugin*)malloc(sizeof(*self->remmina_plugin)); - self->remmina_plugin->name = name_str; - self->remmina_plugin->type = REMMINA_PLUGIN_TYPE_PROTOCOL; + + /* Protocol plugin definition and features */ + RemminaProtocolPlugin* remmina_plugin = (RemminaProtocolPlugin*)malloc(sizeof(RemminaProtocolPlugin)); + remmina_plugin->type = REMMINA_PLUGIN_TYPE_PROTOCOL; + remmina_plugin->name = name_str; // Name + remmina_plugin->description = description_str; // Description + remmina_plugin->domain = GETTEXT_PACKAGE; // Translation domain + remmina_plugin->version = version_str; // Version number + remmina_plugin->icon_name = appicon_str; // Icon for normal connection + remmina_plugin->icon_name_ssh = appicon_str; // Icon for SSH connection + remmina_plugin->basic_settings = remmina_protocol_python_plugin_features; // 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 = remmina_protocol_python_plugin_features; // Array for available features + remmina_plugin->init = remmina_protocol_plugin_wrapper_init; // Plugin initialization + remmina_plugin->open_connection = remmina_protocol_plugin_wrapper_open_connection; // Plugin open connection + remmina_plugin->close_connection = remmina_protocol_plugin_wrapper_close_connection; // Plugin close connection + remmina_plugin->query_feature = remmina_protocol_plugin_wrapper_query_feature; // Query for available features + remmina_plugin->call_feature = remmina_protocol_plugin_wrapper_call_feature; // Call a feature + remmina_plugin->send_keystrokes = NULL; // Send a keystroke + remmina_plugin->get_plugin_screenshot = NULL; // Screenshot support unavailable + + remmina_plugin_manager_service.register_plugin((RemminaPlugin *)remmina_plugin); + return 0; -}
\ No newline at end of file +} + + +static void remmina_protocol_plugin_wrapper_init(RemminaProtocolWidget *gp) { + +} + +static gboolean remmina_protocol_plugin_wrapper_open_connection(RemminaProtocolWidget *gp) { + return TRUE; +} + +static gboolean remmina_protocol_plugin_wrapper_close_connection(RemminaProtocolWidget *gp) { + return TRUE; +} + +static gboolean remmina_protocol_plugin_wrapper_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature) { + return TRUE; +} + +static void remmina_protocol_plugin_wrapper_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature) { + +} + +static void remmina_protocol_plugin_wrapper_send_keystrokes(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen) { + +} + +static gboolean remmina_protocol_plugin_wrapper_get_plugin_screenshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd) { + return TRUE; +} + |