summaryrefslogtreecommitdiffstats
path: root/plugins/gvnc
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/gvnc')
-rw-r--r--plugins/gvnc/CMakeLists.txt54
-rw-r--r--plugins/gvnc/gvnc_plugin.c941
-rw-r--r--plugins/gvnc/gvnc_plugin.h131
-rw-r--r--plugins/gvnc/gvnc_plugin_config.h43
-rw-r--r--plugins/gvnc/scalable/emblems/org.remmina.Remmina-gvnc-ssh-symbolic.svg90
-rw-r--r--plugins/gvnc/scalable/emblems/org.remmina.Remmina-gvnc-symbolic.svg115
6 files changed, 1374 insertions, 0 deletions
diff --git a/plugins/gvnc/CMakeLists.txt b/plugins/gvnc/CMakeLists.txt
new file mode 100644
index 0000000..ca5c1cd
--- /dev/null
+++ b/plugins/gvnc/CMakeLists.txt
@@ -0,0 +1,54 @@
+# remmina-plugin-gvnc - The GTK+ Remote Desktop Client
+#
+# Copyright (C) 2016-2023 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.
+
+set(REMMINA_PLUGIN_GVNC_SRCS
+ gvnc_plugin_config.h
+ gvnc_plugin.h
+ gvnc_plugin.c
+ )
+
+add_library(remmina-plugin-gvnc MODULE ${REMMINA_PLUGIN_GVNC_SRCS})
+set_target_properties(remmina-plugin-gvnc PROPERTIES PREFIX "")
+set_target_properties(remmina-plugin-gvnc PROPERTIES NO_SONAME 1)
+
+include_directories(${REMMINA_COMMON_INCLUDE_DIRS} ${GTK-VNC_INCLUDE_DIRS})
+target_link_libraries(remmina-plugin-gvnc ${REMMINA_COMMON_LIBRARIES} ${GTK-VNC_LIBRARIES})
+
+install(TARGETS remmina-plugin-gvnc DESTINATION ${REMMINA_PLUGINDIR})
+
+install(FILES
+ scalable/emblems/org.remmina.Remmina-gvnc-symbolic.svg
+ DESTINATION ${APPICONSCALE_EMBLEMS_DIR})
+
+if(WITH_ICON_CACHE)
+ gtk_update_icon_cache("${REMMINA_DATADIR}/icons/hicolor")
+endif()
diff --git a/plugins/gvnc/gvnc_plugin.c b/plugins/gvnc/gvnc_plugin.c
new file mode 100644
index 0000000..14cead9
--- /dev/null
+++ b/plugins/gvnc/gvnc_plugin.c
@@ -0,0 +1,941 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2016-2023 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.
+ *
+ */
+
+#include "gvnc_plugin_config.h"
+#include "gvnc_plugin.h"
+
+#include <vncdisplay.h>
+#include <vncutil.h>
+#include <vncaudiopulse.h>
+
+#define GVNC_DEFAULT_PORT 5900
+
+#ifndef VNC_CHECK_VERSION
+# define VNC_CHECK_VERSION(a, b, c) 0
+#endif
+#if VNC_CHECK_VERSION(1, 2, 0)
+# define HAVE_VNC_REMOTE_RESIZE
+# define HAVE_VNC_POWER_CONTROL
+#endif
+
+enum {
+ GVNC_PLUGIN_FEATURE_PREF_VIEWONLY = 1,
+ GVNC_PLUGIN_FEATURE_DYNRESUPDATE,
+ GVNC_PLUGIN_FEATURE_PREF_DISABLECLIPBOARD,
+ GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTDEL,
+ GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTBACKSPACE,
+ GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF1,
+ GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF2,
+ GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF3,
+ GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF4,
+ GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF5,
+ GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF6,
+ GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF7,
+ GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF8,
+ GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF9,
+ GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF10,
+ GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF11,
+ GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF12,
+#ifdef HAVE_VNC_POWER_CONTROL
+ GVNC_PLUGIN_FEATURE_TOOL_REBOOT,
+ GVNC_PLUGIN_FEATURE_TOOL_RESET,
+ GVNC_PLUGIN_FEATURE_TOOL_SHUTDOWN,
+#endif
+ GVNC_PLUGIN_FEATURE_SCALE
+};
+
+
+static RemminaPluginService *remmina_plugin_service = NULL;
+
+gchar* str_replace(const gchar *string, const gchar *search, const gchar *replacement)
+{
+ TRACE_CALL(__func__);
+ gchar *str, **arr;
+
+ g_return_val_if_fail(string != NULL, NULL);
+ g_return_val_if_fail(search != NULL, NULL);
+
+ if (replacement == NULL)
+ replacement = "";
+
+ arr = g_strsplit(string, search, -1);
+ if (arr != NULL && arr[0] != NULL)
+ str = g_strjoinv(replacement, arr);
+ else
+ str = g_strdup(string);
+
+ g_strfreev(arr);
+ return str;
+}
+
+/* Send a keystroke to the plugin window */
+static void gvnc_plugin_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
+{
+ TRACE_CALL(__func__);
+ GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+
+ if (gpdata->vnc) {
+ vnc_display_send_keys(VNC_DISPLAY(gpdata->vnc),
+ keystrokes,
+ keylen);
+ }
+}
+
+#ifdef HAVE_VNC_POWER_CONTROL
+static void gvnc_plugin_power_ctrl(RemminaProtocolWidget *gp, VncConnectionPowerAction action)
+{
+ GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+ VncConnection *conn = vnc_display_get_connection(VNC_DISPLAY(gpdata->vnc));
+
+ if (!vnc_connection_get_power_control(conn)) {
+ REMMINA_PLUGIN_DEBUG("Power control not available on this server");
+ return;
+ }
+ vnc_connection_power_control(conn, action);
+}
+#endif
+
+
+static void gvnc_plugin_mouse_grab(GtkWidget *vncdisplay, RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+ VncGrabSequence *seq = vnc_display_get_grab_keys(VNC_DISPLAY(gpdata->vnc));
+ gchar *seqstr = vnc_grab_sequence_as_string(seq);
+
+ REMMINA_PLUGIN_DEBUG("Pointer grabbed: %s", seqstr);
+}
+
+static void gvnc_plugin_mouse_ungrab(GtkWidget *vncdisplay, RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ REMMINA_PLUGIN_DEBUG("Pointer ungrabbed");
+}
+
+static void gvnc_plugin_on_vnc_error(GtkWidget *vncdisplay G_GNUC_UNUSED, const gchar *msg, RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+
+ REMMINA_PLUGIN_DEBUG("Error: %s\n", msg);
+ /* "vnc-error" is always followed by "vnc-disconnected",
+ * so save the error for that signal */
+ g_free(gpdata->error_msg);
+ gpdata->error_msg = g_strdup(msg);
+}
+
+static gboolean gvnc_plugin_get_screenshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd) __attribute__ ((unused));
+static gboolean gvnc_plugin_get_screenshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd)
+{
+ GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+ //gsize szmem;
+ const VncPixelFormat *currentFormat;
+ //GError *err = NULL;
+
+ if (!gpdata)
+ return FALSE;
+
+ /* Get current pixel format for server */
+ currentFormat = vnc_connection_get_pixel_format(gpdata->conn);
+
+
+ GdkPixbuf *pix = vnc_display_get_pixbuf(VNC_DISPLAY(gpdata->vnc));
+
+ rpsd->width = gdk_pixbuf_get_width(pix);
+ rpsd->height = gdk_pixbuf_get_height(pix);
+ rpsd->bitsPerPixel = currentFormat->bits_per_pixel;
+ rpsd->bytesPerPixel = rpsd->bitsPerPixel / 8;
+ //szmem = gdk_pixbuf_get_byte_length(pix);
+
+ //szmem = rpsd->width * rpsd->height * rpsd->bytesPerPixel;
+
+ //REMMINA_PLUGIN_DEBUG("allocating %zu bytes for a full screenshot", szmem);
+ //REMMINA_PLUGIN_DEBUG("Calculated screenshot size: %zu", szcalc);
+ //rpsd->buffer = malloc(szmem);
+
+ //memcpy(rpsd->buffer, pix, szmem);
+ //gdk_pixbuf_save_to_buffer(pix, &rpsd->buffer, &szmem, "jpeg", &err, "quality", "100", NULL);
+
+
+ /* Returning TRUE instruct also the caller to deallocate rpsd->buffer */
+ return TRUE;
+}
+
+void gvnc_plugin_paste_text(RemminaProtocolWidget *gp, const gchar *text)
+{
+ TRACE_CALL(__func__);
+ GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+ gchar *out;
+ gsize a, b;
+ GError *error = NULL;
+
+ if (!gpdata) return;
+
+ out = g_convert_with_fallback(text, -1, "iso8859-1//TRANSLIT", "utf-8", NULL, &a, &b, &error);
+ if (out) {
+ REMMINA_PLUGIN_DEBUG("Pasting text");
+ vnc_display_client_cut_text(VNC_DISPLAY(gpdata->vnc), out);
+ g_free(out);
+ } else {
+ REMMINA_PLUGIN_DEBUG("Error pasting text: %s", error->message);
+ g_error_free(error);
+ }
+}
+
+static void gvnc_plugin_clipboard_cb(GtkClipboard *cb, GdkEvent *event, RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ //GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+ gchar *text;
+
+ REMMINA_PLUGIN_DEBUG("owner-change event received");
+
+ if (cb && gtk_clipboard_get_owner(cb) == (GObject *)gp)
+ return;
+
+ text = gtk_clipboard_wait_for_text(cb);
+ if (!text)
+ return;
+
+ gvnc_plugin_paste_text(gp, text);
+ g_free(text);
+}
+
+
+/* text was actually requested */
+static void gvnc_plugin_clipboard_copy(GtkClipboard *clipboard G_GNUC_UNUSED, GtkSelectionData *data, guint info G_GNUC_UNUSED, RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+
+ if (!gpdata) return;
+ gtk_selection_data_set_text(data, gpdata->clipstr, -1);
+ REMMINA_PLUGIN_DEBUG("Text copied");
+}
+
+static void gvnc_plugin_cut_text(VncDisplay *vnc G_GNUC_UNUSED, const gchar *text, RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+
+ REMMINA_PLUGIN_DEBUG("Got clipboard request for ā€œ%sā€", text);
+
+ GtkClipboard *cb;
+ gsize a, b;
+ GtkTargetEntry targets[] = {
+ { g_strdup("UTF8_STRING"), 0, 0 },
+ { g_strdup("COMPOUND_TEXT"), 0, 0 },
+ { g_strdup("TEXT"), 0, 0 },
+ { g_strdup("STRING"), 0, 0 },
+ };
+
+ if (!text)
+ return;
+ g_free(gpdata->clipstr);
+ gpdata->clipstr = g_convert(text, -1, "utf-8", "iso8859-1", &a, &b, NULL);
+
+ if (gpdata->clipstr) {
+ cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+
+ REMMINA_PLUGIN_DEBUG("setting clipboard with owner to owner %p", gp);
+ gtk_clipboard_set_with_owner(cb,
+ targets,
+ G_N_ELEMENTS(targets),
+ (GtkClipboardGetFunc)gvnc_plugin_clipboard_copy,
+ NULL,
+ G_OBJECT(gp));
+ }
+
+ //g_signal_emit_by_name(session, "session-cut-text", text);
+}
+
+
+static void gvnc_plugin_desktop_resize(GtkWidget *vncdisplay G_GNUC_UNUSED, int width, int height, RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+
+ REMMINA_PLUGIN_DEBUG("Remote desktop size changed to %dx%d\n", width, height);
+ REMMINA_PLUGIN_DEBUG("Desktop size is set to %dx%d\n", gpdata->width, gpdata->height);
+ remmina_plugin_service->protocol_plugin_set_width(gp, width);
+ remmina_plugin_service->protocol_plugin_set_height(gp, height);
+
+ remmina_plugin_service->protocol_plugin_desktop_resize(gp);
+}
+
+static void gvnc_plugin_on_bell(RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ REMMINA_PLUGIN_DEBUG("Bell message received");
+ //GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+ RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+
+ if (remmina_plugin_service->file_get_int(remminafile, "disableserverbell", FALSE))
+ return;
+ GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(gp));
+
+ if (window)
+ gdk_window_beep(window);
+ REMMINA_PLUGIN_DEBUG("Beep emitted");
+}
+static void gvnc_plugin_update_scale_mode(RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+
+ GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+ RemminaScaleMode scaleMode = remmina_plugin_service->remmina_protocol_widget_get_current_scale_mode(gp);
+
+#if 0
+ g_object_set(gpdata->display,
+ "scaling", (scaleMode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED),
+ "resize-guest", (scaleMode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES),
+ NULL);
+#endif
+ if (scaleMode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED || scaleMode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES)
+ vnc_display_set_scaling(VNC_DISPLAY(gpdata->vnc), TRUE);
+ else
+ vnc_display_set_scaling(VNC_DISPLAY(gpdata->vnc), FALSE);
+
+ //width = remmina_plugin_service->protocol_plugin_get_width(gp);
+ //height = remmina_plugin_service->protocol_plugin_get_height(gp);
+ gpdata->width = vnc_display_get_width(VNC_DISPLAY(gpdata->vnc));
+ gpdata->height = vnc_display_get_height(VNC_DISPLAY(gpdata->vnc));
+
+ if (scaleMode != REMMINA_PROTOCOL_WIDGET_SCALE_MODE_NONE) {
+ /* In scaled mode, the VncDisplay will get its dimensions from its parent */
+ gtk_widget_set_size_request(GTK_WIDGET(gpdata->vnc), -1, -1);
+ } else {
+ /* In non scaled mode, the plugins forces dimensions of the VncDisplay */
+#if 0
+ g_object_get(gpdata->display_channel,
+ "width", &width,
+ "height", &height,
+ NULL);
+ gtk_widget_set_size_request(GTK_WIDGET(gpdata->vnc), width, height);
+#endif
+ gtk_widget_set_size_request(GTK_WIDGET(gpdata->vnc), gpdata->width, gpdata->height);
+ }
+ remmina_plugin_service->protocol_plugin_update_align(gp);
+}
+
+static gboolean gvnc_plugin_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
+{
+ TRACE_CALL(__func__);
+
+ return TRUE;
+}
+
+static void gvnc_plugin_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
+{
+ TRACE_CALL(__func__);
+
+ GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+ RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+
+ guint keys[3];
+
+ switch (feature->id) {
+ case GVNC_PLUGIN_FEATURE_PREF_VIEWONLY:
+ gpdata->viewonly = remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE);
+ break;
+ case GVNC_PLUGIN_FEATURE_DYNRESUPDATE:
+ case GVNC_PLUGIN_FEATURE_SCALE:
+ gvnc_plugin_update_scale_mode(gp);
+ break;
+ case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTDEL:
+ keys[0] = GDK_KEY_Control_L;
+ keys[1] = GDK_KEY_Alt_L;
+ keys[2] = GDK_KEY_Delete;
+ gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
+ break;
+ case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTBACKSPACE:
+ keys[0] = GDK_KEY_Control_L;
+ keys[1] = GDK_KEY_Alt_L;
+ keys[2] = GDK_KEY_BackSpace;
+ gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
+ break;
+ case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF1:
+ keys[0] = GDK_KEY_Control_L;
+ keys[1] = GDK_KEY_Alt_L;
+ keys[2] = GDK_KEY_F1;
+ gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
+ break;
+ case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF2:
+ keys[0] = GDK_KEY_Control_L;
+ keys[1] = GDK_KEY_Alt_L;
+ keys[2] = GDK_KEY_F2;
+ gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
+ break;
+ case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF3:
+ keys[0] = GDK_KEY_Control_L;
+ keys[1] = GDK_KEY_Alt_L;
+ keys[2] = GDK_KEY_F3;
+ gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
+ break;
+ case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF4:
+ keys[0] = GDK_KEY_Control_L;
+ keys[1] = GDK_KEY_Alt_L;
+ keys[2] = GDK_KEY_F4;
+ gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
+ break;
+ case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF5:
+ keys[0] = GDK_KEY_Control_L;
+ keys[1] = GDK_KEY_Alt_L;
+ keys[2] = GDK_KEY_F5;
+ gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
+ break;
+ case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF6:
+ keys[0] = GDK_KEY_Control_L;
+ keys[1] = GDK_KEY_Alt_L;
+ keys[2] = GDK_KEY_F6;
+ gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
+ break;
+ case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF7:
+ keys[0] = GDK_KEY_Control_L;
+ keys[1] = GDK_KEY_Alt_L;
+ keys[2] = GDK_KEY_F7;
+ gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
+ break;
+ case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF9:
+ keys[0] = GDK_KEY_Control_L;
+ keys[1] = GDK_KEY_Alt_L;
+ keys[2] = GDK_KEY_F9;
+ gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
+ break;
+ case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF10:
+ keys[0] = GDK_KEY_Control_L;
+ keys[1] = GDK_KEY_Alt_L;
+ keys[2] = GDK_KEY_F10;
+ gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
+ break;
+ case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF11:
+ keys[0] = GDK_KEY_Control_L;
+ keys[1] = GDK_KEY_Alt_L;
+ keys[2] = GDK_KEY_F11;
+ gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
+ break;
+ case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF12:
+ keys[0] = GDK_KEY_Control_L;
+ keys[1] = GDK_KEY_Alt_L;
+ keys[2] = GDK_KEY_F12;
+ gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
+ break;
+#ifdef HAVE_VNC_POWER_CONTROL
+ case GVNC_PLUGIN_FEATURE_TOOL_REBOOT:
+ gvnc_plugin_power_ctrl(gp, VNC_CONNECTION_POWER_ACTION_REBOOT);
+ break;
+ case GVNC_PLUGIN_FEATURE_TOOL_RESET:
+ gvnc_plugin_power_ctrl(gp, VNC_CONNECTION_POWER_ACTION_RESET);
+ break;
+ case GVNC_PLUGIN_FEATURE_TOOL_SHUTDOWN:
+ gvnc_plugin_power_ctrl(gp, VNC_CONNECTION_POWER_ACTION_SHUTDOWN);
+ break;
+#endif
+ default:
+ break;
+ }
+}
+
+static void gvnc_plugin_auth_unsupported(VncDisplay *vnc G_GNUC_UNUSED, unsigned int authType, RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+
+ g_clear_pointer(&gpdata->error_msg, g_free);
+ gchar *msg = g_strdup_printf(_("Unsupported authentication type %u"), authType);
+
+ remmina_plugin_service->protocol_plugin_set_error(gp, "%s", msg);
+ g_free(msg);
+}
+
+static void gvnc_plugin_auth_failure(VncDisplay *vnc G_GNUC_UNUSED, const gchar *reason, RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+
+ g_clear_pointer(&gpdata->error_msg, g_free);
+ gchar *msg = g_strdup_printf(_("Authentication failure: %s"), reason);
+
+ remmina_plugin_service->protocol_plugin_set_error(gp, "%s", msg);
+ g_free(msg);
+}
+
+static gboolean gvnc_plugin_ask_auth(GtkWidget *vncdisplay, GValueArray *credList, RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+
+ gint ret;
+ gboolean disablepasswordstoring;
+ gchar *s_username = NULL, *s_password = NULL;
+ gboolean wantPassword = FALSE, wantUsername = FALSE;
+ int i;
+ gboolean save;
+
+
+ GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+ RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+
+
+ GArray *credArray = g_array_sized_new(FALSE, TRUE, sizeof(GValue), credList->n_values);
+ g_array_set_clear_func(credArray, (GDestroyNotify) g_value_unset);
+ g_array_append_vals(credArray, credList->values, credList->n_values);
+
+ REMMINA_PLUGIN_DEBUG("Got credential request for %d credential(s)\n", credArray->len);
+
+ for (i = 0 ; i < credArray->len ; i++) {
+ GValue *cred = &g_array_index(credArray, GValue, i);
+ switch (g_value_get_enum(cred)) {
+ case VNC_DISPLAY_CREDENTIAL_USERNAME:
+ wantUsername = TRUE;
+ break;
+ case VNC_DISPLAY_CREDENTIAL_PASSWORD:
+ wantPassword = TRUE;
+ break;
+ case VNC_DISPLAY_CREDENTIAL_CLIENTNAME:
+ break;
+ default:
+ REMMINA_PLUGIN_DEBUG("Unsupported credential type %d", g_value_get_enum(cred));
+ vnc_display_close(VNC_DISPLAY(gpdata->vnc));
+ goto cleanup;
+ }
+ }
+ disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
+
+ ret = remmina_plugin_service->protocol_plugin_init_auth(gp,
+ (disablepasswordstoring ? 0 : REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD)
+ | (wantUsername ? REMMINA_MESSAGE_PANEL_FLAG_USERNAME : 0),
+ _("Enter VNC authentication credentials"),
+ (wantUsername ? remmina_plugin_service->file_get_string(remminafile, "username") : NULL),
+ (wantPassword ? remmina_plugin_service->file_get_string(remminafile, "password") : NULL),
+ NULL,
+ NULL);
+ if (ret == GTK_RESPONSE_OK) {
+ s_username = remmina_plugin_service->protocol_plugin_init_get_username(gp);
+ s_password = remmina_plugin_service->protocol_plugin_init_get_password(gp);
+ remmina_plugin_service->file_set_string(remminafile, "username", s_username);
+
+ save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp);
+ if (save) {
+ // User has requested to save credentials. We put the password
+ // into remminafile->settings. It will be saved later, on successful connection, by
+ // rcw.c
+ remmina_plugin_service->file_set_string(remminafile, "password", s_password);
+ } else {
+ remmina_plugin_service->file_set_string(remminafile, "password", NULL);
+ }
+
+ for (i = 0 ; i < credArray->len ; i++) {
+ GValue *cred = &g_array_index(credArray, GValue, i);
+ switch (g_value_get_enum(cred)) {
+ case VNC_DISPLAY_CREDENTIAL_USERNAME:
+ if (!s_username ||
+ vnc_display_set_credential(VNC_DISPLAY(gpdata->vnc),
+ g_value_get_enum(cred),
+ s_username)) {
+ g_debug("Failed to set credential type %d", g_value_get_enum(cred));
+ vnc_display_close(VNC_DISPLAY(gpdata->vnc));
+ }
+ break;
+ case VNC_DISPLAY_CREDENTIAL_PASSWORD:
+ if (!s_password ||
+ vnc_display_set_credential(VNC_DISPLAY(gpdata->vnc),
+ g_value_get_enum(cred),
+ s_password)) {
+ g_debug("Failed to set credential type %d", g_value_get_enum(cred));
+ vnc_display_close(VNC_DISPLAY(gpdata->vnc));
+ }
+ break;
+ case VNC_DISPLAY_CREDENTIAL_CLIENTNAME:
+ if (vnc_display_set_credential(VNC_DISPLAY(gpdata->vnc),
+ g_value_get_enum(cred),
+ "remmina")) {
+ g_debug("Failed to set credential type %d", g_value_get_enum(cred));
+ vnc_display_close(VNC_DISPLAY(gpdata->vnc));
+ }
+ break;
+ default:
+ g_debug("Unsupported credential type %d", g_value_get_enum(cred));
+ vnc_display_close(VNC_DISPLAY(gpdata->vnc));
+ }
+ }
+
+ if (s_username) g_free(s_username);
+ if (s_password) g_free(s_password);
+
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+
+cleanup:
+ g_free(s_username);
+ g_free(s_password);
+
+ //g_object_set(gpdata->session, "password", s_password, NULL);
+ return TRUE;
+}
+
+static void gvnc_plugin_initialized(GtkWidget *vncdisplay, RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+
+ GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+ RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+
+ REMMINA_PLUGIN_DEBUG("Connection initialized");
+ g_return_if_fail(gpdata != NULL);
+ REMMINA_PLUGIN_DEBUG("Presenting the window");
+
+ VncAudioFormat format = {
+ VNC_AUDIO_FORMAT_RAW_S32,
+ 2,
+ 44100,
+ };
+
+ REMMINA_PLUGIN_DEBUG("Gathering the VNC connection object");
+ gpdata->conn = vnc_display_get_connection(VNC_DISPLAY(gpdata->vnc));
+ g_return_if_fail(gpdata->conn != NULL);
+
+ if (remmina_plugin_service->file_get_int(remminafile, "enableaudio", FALSE)) {
+ REMMINA_PLUGIN_DEBUG("Setting up VNC audio channel");
+ if (vnc_connection_set_audio_format(gpdata->conn, &format))
+ REMMINA_PLUGIN_DEBUG("VNC audio format set");
+ else {
+ REMMINA_PLUGIN_DEBUG("VNC audio format returned an error");
+ return;
+ }
+
+ if (vnc_connection_set_audio(gpdata->conn, VNC_AUDIO(gpdata->pa)))
+ REMMINA_PLUGIN_DEBUG("VNC audio channel has been set");
+ else {
+ REMMINA_PLUGIN_DEBUG("VNC audio channel cannot be set");
+ return;
+ }
+ REMMINA_PLUGIN_DEBUG("Enabling audio");
+ if (vnc_connection_audio_enable(gpdata->conn))
+ REMMINA_PLUGIN_DEBUG("Audio enabled");
+ else
+ REMMINA_PLUGIN_DEBUG("Audio cannot be enabled");
+ }
+ gpdata->width = vnc_display_get_width(VNC_DISPLAY(gpdata->vnc));
+ gpdata->width = vnc_display_get_height(VNC_DISPLAY(gpdata->vnc));
+}
+
+static void gvnc_plugin_disconnected(VncDisplay *vnc G_GNUC_UNUSED, RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ //GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+
+ REMMINA_PLUGIN_DEBUG("[%s] Plugin disconnected", PLUGIN_NAME);
+}
+static gboolean gvnc_plugin_close_connection(RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+
+ REMMINA_PLUGIN_DEBUG("[%s] Plugin close connection", PLUGIN_NAME);
+
+ if (gpdata) {
+ if (gpdata->error_msg) g_free(gpdata->error_msg);
+ if (gpdata->vnc)
+ vnc_display_close(VNC_DISPLAY(gpdata->vnc));
+ //g_object_unref(gpdata->vnc);
+ }
+
+ /* Remove instance->context from gp object data to avoid double free */
+ g_object_steal_data(G_OBJECT(gp), "plugin-data");
+ remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
+ return FALSE;
+}
+
+static void gvnc_plugin_init(RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ GVncPluginData *gpdata;
+ //VncGrabSequence *seq;
+
+ GtkClipboard *cb;
+
+ gpdata = g_new0(GVncPluginData, 1);
+ g_object_set_data_full(G_OBJECT(gp), "plugin-data", gpdata, g_free);
+
+ gpdata->pa = NULL;
+
+ REMMINA_PLUGIN_DEBUG("[%s] Plugin init", PLUGIN_NAME);
+
+ RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+
+ if (remmina_plugin_service->file_get_int(remminafile, "gvncdebug", FALSE))
+ vnc_util_set_debug(TRUE);
+
+ gpdata->vnc = vnc_display_new();
+ if (remmina_plugin_service->file_get_int(remminafile, "enableaudio", FALSE))
+ gpdata->pa = vnc_audio_pulse_new();
+
+
+ g_signal_connect(gpdata->vnc, "vnc-auth-credential",
+ G_CALLBACK(gvnc_plugin_ask_auth), gp);
+ g_signal_connect(gpdata->vnc, "vnc-auth-failure",
+ G_CALLBACK(gvnc_plugin_auth_failure), gp);
+ g_signal_connect(gpdata->vnc, "vnc-auth-unsupported",
+ G_CALLBACK(gvnc_plugin_auth_unsupported), gp);
+ g_signal_connect(gpdata->vnc, "vnc-disconnected",
+ G_CALLBACK(gvnc_plugin_disconnected), gp);
+ g_signal_connect(gpdata->vnc, "vnc-initialized",
+ G_CALLBACK(gvnc_plugin_initialized), gp);
+ g_signal_connect(gpdata->vnc, "vnc-desktop-resize",
+ G_CALLBACK(gvnc_plugin_desktop_resize), gp);
+ g_signal_connect(gpdata->vnc, "vnc-bell",
+ G_CALLBACK(gvnc_plugin_on_bell), gp);
+ g_signal_connect(gpdata->vnc, "vnc-error",
+ G_CALLBACK(gvnc_plugin_on_vnc_error), gp);
+ g_signal_connect(gpdata->vnc, "vnc-pointer-grab",
+ G_CALLBACK(gvnc_plugin_mouse_grab), gp);
+ g_signal_connect(gpdata->vnc, "vnc-pointer-ungrab",
+ G_CALLBACK(gvnc_plugin_mouse_ungrab), gp);
+ g_signal_connect(gpdata->vnc, "vnc-server-cut-text",
+ G_CALLBACK(gvnc_plugin_cut_text), gp);
+ //seq = vnc_grab_sequence_new_from_string ("Control_R");
+ //vnc_display_set_grab_keys(VNC_DISPLAY(gpdata->vnc), seq);
+ //vnc_grab_sequence_free(seq);
+
+ /* Setup the clipboard */
+ cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+ gpdata->signal_clipboard = g_signal_connect(cb,
+ "owner-change",
+ G_CALLBACK(gvnc_plugin_clipboard_cb),
+ gp);
+}
+
+static gboolean gvnc_plugin_open_connection(RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+
+ gint port;
+ gchar *host = NULL, *tunnel = NULL;
+ GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
+ RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+
+ REMMINA_PLUGIN_DEBUG("[%s] Plugin open connection", PLUGIN_NAME);
+
+ gpdata->box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add(GTK_CONTAINER(gp), gpdata->box);
+ gtk_widget_set_hexpand(GTK_WIDGET(gpdata->vnc), TRUE);
+ gtk_widget_set_vexpand(GTK_WIDGET(gpdata->vnc), TRUE);
+ gtk_container_add(GTK_CONTAINER(gpdata->box), gpdata->vnc);
+
+ remmina_plugin_service->protocol_plugin_register_hostkey(gp, gpdata->vnc);
+
+
+ const gchar *address = remmina_plugin_service->file_get_string(remminafile, "server");
+ if(strstr(g_strdup(address), "unix:///") != NULL) {
+ REMMINA_PLUGIN_DEBUG("address contain unix:// -> %s", address);
+ gchar *val = str_replace (address, "unix://", "");
+ REMMINA_PLUGIN_DEBUG("address after cleaning = %s", val);
+ gint fd = remmina_plugin_service->open_unix_sock(val);
+ REMMINA_PLUGIN_DEBUG("Unix socket fd: %d", fd);
+ gpdata->fd = fd;
+ g_free(val);
+
+ } else {
+ /* Setup SSH tunnel if needed */
+ tunnel = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, GVNC_DEFAULT_PORT, FALSE);
+ if (!tunnel)
+ return FALSE;
+ remmina_plugin_service->get_server_port(tunnel,
+ GVNC_DEFAULT_PORT,
+ &host,
+ &port);
+ }
+
+
+
+ gpdata->depth_profile = remmina_plugin_service->file_get_int(remminafile, "depth_profile", 24);
+ gpdata->viewonly = remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE);
+ vnc_display_set_depth(VNC_DISPLAY(gpdata->vnc), gpdata->depth_profile);
+ if (gpdata->fd > 0)
+ vnc_display_open_fd(VNC_DISPLAY(gpdata->vnc), gpdata->fd);
+ else
+ vnc_display_open_host(VNC_DISPLAY(gpdata->vnc), host, g_strdup_printf("%d", port));
+ gpdata->lossy_encoding = remmina_plugin_service->file_get_int(remminafile, "lossy_encoding", FALSE);
+ vnc_display_set_lossy_encoding(VNC_DISPLAY(gpdata->vnc), gpdata->lossy_encoding);
+ vnc_display_set_shared_flag(VNC_DISPLAY(gpdata->vnc), gpdata->shared);
+
+ if(host) g_free(host);
+ if(tunnel) g_free(tunnel);
+
+ /* TRUE Conflict with remmina? */
+ vnc_display_set_keyboard_grab(VNC_DISPLAY(gpdata->vnc), FALSE);
+ vnc_display_set_read_only(VNC_DISPLAY(gpdata->vnc), gpdata->viewonly);
+ /* TRUE Conflict with remmina? */
+ vnc_display_set_pointer_grab(VNC_DISPLAY(gpdata->vnc), FALSE);
+ vnc_display_set_pointer_local(VNC_DISPLAY(gpdata->vnc), TRUE);
+
+ vnc_display_set_force_size(VNC_DISPLAY(gpdata->vnc), FALSE);
+ vnc_display_set_scaling(VNC_DISPLAY(gpdata->vnc), TRUE);
+#ifdef HAVE_VNC_REMOTE_RESIZE
+ vnc_display_set_allow_resize(VNC_DISPLAY(gpdata->vnc), TRUE);
+ vnc_display_set_zoom_level(VNC_DISPLAY(gpdata->vnc), opt_zoom);
+#endif
+ remmina_plugin_service->protocol_plugin_signal_connection_opened(gp);
+ gtk_widget_show_all(gpdata->box);
+ return TRUE;
+}
+
+/* Array of key/value pairs for color depths */
+static gpointer colordepth_list[] =
+{
+ "0", N_("Use server settings"),
+ "1", N_("True colour (24 bits)"),
+ "2", N_("High colour (16 bits)"),
+ "3", N_("Low colour (8 bits)"),
+ "4", N_("Ultra low colour (3 bits)"),
+ NULL
+};
+
+/* Array of RemminaProtocolSetting for basic settings.
+ * Each item is composed by:
+ * a) RemminaProtocolSettingType for setting type
+ * b) Setting name
+ * c) Setting description
+ * d) Compact disposition
+ * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
+ * f) Setting tooltip
+ * g) Validation data pointer, will be passed to the validation callback method.
+ * h) Validation callback method (Can be NULL. Every entry will be valid then.)
+ * use following prototype:
+ * gboolean mysetting_validator_method(gpointer key, gpointer value,
+ * gpointer validator_data);
+ * gpointer key is a gchar* containing the setting's name,
+ * gpointer value contains the value which should be validated,
+ * gpointer validator_data contains your passed data.
+ */
+static const RemminaProtocolSetting gvnc_plugin_basic_settings[] =
+{
+ { REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, NULL, NULL, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("VNC password"), FALSE, NULL, NULL, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "depth_profile", N_("Colour depth"), FALSE, colordepth_list, NULL, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "lossy_encoding", N_("Use JPEG Compression"), TRUE, NULL, N_("This might not work on all VNC servers"), NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "gvncdebug", N_("Enable GTK-VNC debug"), FALSE, NULL, NULL, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL, NULL, NULL }
+};
+
+/* Array of RemminaProtocolSetting for advanced settings.
+ * Each item is composed by:
+ * a) RemminaProtocolSettingType for setting type
+ * b) Setting name
+ * c) Setting description
+ * d) Compact disposition
+ * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
+ * f) Setting Tooltip
+ */
+static const RemminaProtocolSetting gvnc_plugin_advanced_settings[] =
+{
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableclipboard", N_("No clipboard sync"), TRUE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablepasswordstoring", N_("Forget passwords after use"), FALSE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableserverbell", N_("Ignore remote bell messages"), TRUE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "enableaudio", N_("Enable audio channel"), FALSE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "viewonly", N_("View only"), TRUE, NULL, NULL },
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "shared", N_("Shared connection"), TRUE, NULL, N_("If the server should try to share the desktop by leaving other clients connected") },
+ { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, TRUE, NULL, NULL }
+};
+
+/* Array for available features.
+ * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */
+static const RemminaProtocolFeature gvnc_plugin_features[] =
+{
+ { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, GVNC_PLUGIN_FEATURE_PREF_VIEWONLY, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "viewonly", N_("View only") },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, GVNC_PLUGIN_FEATURE_PREF_DISABLECLIPBOARD, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "disableclipboard", N_("No clipboard sync") },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTDEL, N_("Send Ctrl+Alt+_Del"), NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTBACKSPACE, N_("Send Ctrl+Alt+_Backspace"), NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF1, N_("Send Ctrl+Alt+_F1"), NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF2, N_("Send Ctrl+Alt+_F2"), NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF3, N_("Send Ctrl+Alt+_F3"), NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF4, N_("Send Ctrl+Alt+_F4"), NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF5, N_("Send Ctrl+Alt+_F5"), NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF6, N_("Send Ctrl+Alt+_F6"), NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF7, N_("Send Ctrl+Alt+_F7"), NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF8, N_("Send Ctrl+Alt+_F8"), NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF9, N_("Send Ctrl+Alt+_F9"), NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF10, N_("Send Ctrl+Alt+_F10"), NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF11, N_("Send Ctrl+Alt+_F11"), NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF12, N_("Send Ctrl+Alt+_F12"), NULL, NULL },
+#ifdef HAVE_VNC_POWER_CONTROL
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_REBOOT, N_("Reboot remote host"), NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_RESET, N_("Reset remote host (hard reboot)"), NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SHUTDOWN, N_("Shutdown remote host"), NULL, NULL },
+#endif
+ { REMMINA_PROTOCOL_FEATURE_TYPE_DYNRESUPDATE, GVNC_PLUGIN_FEATURE_DYNRESUPDATE, NULL, NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_SCALE, GVNC_PLUGIN_FEATURE_SCALE, NULL, NULL, NULL },
+ { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL }
+};
+
+/* Protocol plugin definition and features */
+static RemminaProtocolPlugin remmina_plugin = {
+ REMMINA_PLUGIN_TYPE_PROTOCOL, // Type
+ PLUGIN_NAME, // Name
+ PLUGIN_DESCRIPTION, // Description
+ GETTEXT_PACKAGE, // Translation domain
+ PLUGIN_VERSION, // Version number
+ PLUGIN_APPICON, // Icon for normal connection
+ PLUGIN_APPICON, // Icon for SSH connection
+ gvnc_plugin_basic_settings, // Array for basic settings
+ gvnc_plugin_advanced_settings, // Array for advanced settings
+ REMMINA_PROTOCOL_SSH_SETTING_TUNNEL, // SSH settings type
+ gvnc_plugin_features, // Array for available features
+ gvnc_plugin_init, // Plugin initialization
+ gvnc_plugin_open_connection, // Plugin open connection
+ gvnc_plugin_close_connection, // Plugin close connection
+ gvnc_plugin_query_feature, // Query for available features
+ gvnc_plugin_call_feature, // Call a feature
+ gvnc_plugin_keystroke, // Send a keystroke
+ NULL, // No screenshot support available
+ //gvnc_plugin_get_screenshot, // No screenshot support available
+ NULL, // RCW map event
+ NULL // RCW unmap event
+};
+
+G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
+{
+ TRACE_CALL(__func__);
+ remmina_plugin_service = service;
+
+ bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR);
+ bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+
+ if (!service->register_plugin((RemminaPlugin *)&remmina_plugin))
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/plugins/gvnc/gvnc_plugin.h b/plugins/gvnc/gvnc_plugin.h
new file mode 100644
index 0000000..613d032
--- /dev/null
+++ b/plugins/gvnc/gvnc_plugin.h
@@ -0,0 +1,131 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2016-2023 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.
+ *
+ */
+
+#include "common/remmina_plugin.h"
+
+#include <gdk/gdkkeysyms.h>
+#include <vncdisplay.h>
+#include <vncutil.h>
+#include <vncaudiopulse.h>
+
+
+#ifndef GDK_Return
+#define GDK_Return GDK_KEY_Return
+#endif
+#ifndef GDK_Escape
+#define GDK_Escape GDK_KEY_Escape
+#endif
+#ifndef GDK_BackSpace
+#define GDK_BackSpace GDK_KEY_BackSpace
+#endif
+#ifndef GDK_Delete
+#define GDK_Delete GDK_KEY_Delete
+#endif
+#ifndef GDK_Control_L
+#define GDK_Control_L GDK_KEY_Control_L
+#endif
+#ifndef GDK_Alt_L
+#define GDK_Alt_L GDK_KEY_Alt_L
+#endif
+#ifndef GDK_F1
+#define GDK_F1 GDK_KEY_F1
+#endif
+#ifndef GDK_F2
+#define GDK_F2 GDK_KEY_F2
+#endif
+#ifndef GDK_F3
+#define GDK_F3 GDK_KEY_F3
+#endif
+#ifndef GDK_F4
+#define GDK_F4 GDK_KEY_F4
+#endif
+#ifndef GDK_F5
+#define GDK_F5 GDK_KEY_F5
+#endif
+#ifndef GDK_F6
+#define GDK_F6 GDK_KEY_F6
+#endif
+#ifndef GDK_F7
+#define GDK_F7 GDK_KEY_F7
+#endif
+#ifndef GDK_F8
+#define GDK_F8 GDK_KEY_F8
+#endif
+#ifndef GDK_F11
+#define GDK_F11 GDK_KEY_F11
+#endif
+
+#define GET_PLUGIN_DATA(gp) (GVncPluginData *)g_object_get_data(G_OBJECT(gp), "plugin-data")
+
+#define REMMINA_PLUGIN_INFO(fmt, ...) \
+ remmina_plugin_service->_remmina_info(__func__, fmt, ##__VA_ARGS__)
+
+#define REMMINA_PLUGIN_MESSAGE(fmt, ...) \
+ remmina_plugin_service->_remmina_message(__func, fmt, ##__VA_ARGS__)
+
+#define REMMINA_PLUGIN_DEBUG(fmt, ...) \
+ remmina_plugin_service->_remmina_debug(__func__, fmt, ##__VA_ARGS__)
+
+#define REMMINA_PLUGIN_WARNING(fmt, ...) \
+ remmina_plugin_service->_remmina_warning(__func__, fmt, ##__VA_ARGS__)
+
+/* This will intentionally crash Remmina */
+#define REMMINA_PLUGIN_ERROR(fmt, ...) \
+ remmina_plugin_service->_remmina_error(__func__, fmt, ##__VA_ARGS__)
+
+#define REMMINA_PLUGIN_CRITICAL(fmt, ...) \
+ remmina_plugin_service->_remmina_critical(__func__, fmt, ##__VA_ARGS__)
+#define REMMINA_PLUGIN_AUDIT(fmt, ...) \
+ remmina_plugin_service->_remmina_audit(__func__, fmt, ##__VA_ARGS__)
+
+typedef struct _GVncPluginData {
+ GtkWidget * box;
+ GtkWidget * vnc;
+ VncConnection * conn;
+ VncAudioPulse * pa;
+ gchar * error_msg;
+ gchar * clipstr;
+ gulong signal_clipboard;
+ gint depth_profile;
+ gint shared;
+ gboolean lossy_encoding;
+ gboolean viewonly;
+ gint width;
+ gint height;
+ gint fd;
+ gchar * addr;
+} GVncPluginData;
+
+G_BEGIN_DECLS
+G_END_DECLS
diff --git a/plugins/gvnc/gvnc_plugin_config.h b/plugins/gvnc/gvnc_plugin_config.h
new file mode 100644
index 0000000..af55b3c
--- /dev/null
+++ b/plugins/gvnc/gvnc_plugin_config.h
@@ -0,0 +1,43 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
+ * Copyright (C) 2016-2023 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.
+ *
+ */
+
+#ifndef __PLUGIN_CONFIG_H
+#define __PLUGIN_CONFIG_H
+
+#define PLUGIN_NAME "GVNC"
+#define PLUGIN_DESCRIPTION N_("Remmina VNC plugin for GNOME and KVM")
+#define PLUGIN_VERSION "0.1b"
+#define PLUGIN_APPICON "org.remmina.Remmina-gvnc-symbolic"
+#endif
diff --git a/plugins/gvnc/scalable/emblems/org.remmina.Remmina-gvnc-ssh-symbolic.svg b/plugins/gvnc/scalable/emblems/org.remmina.Remmina-gvnc-ssh-symbolic.svg
new file mode 100644
index 0000000..6c1da59
--- /dev/null
+++ b/plugins/gvnc/scalable/emblems/org.remmina.Remmina-gvnc-ssh-symbolic.svg
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="99.772003"
+ height="99.772079"
+ viewBox="0 0 26.39801 26.398028"
+ version="1.1"
+ id="svg8"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="vnc over ssh.svg">
+ <defs
+ id="defs2" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.0493442"
+ inkscape:cx="-11.459557"
+ inkscape:cy="109.76653"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:document-rotation="0"
+ showgrid="false"
+ borderlayer="true"
+ inkscape:showpageshadow="true"
+ units="px"
+ inkscape:pagecheckerboard="false"
+ showguides="true"
+ inkscape:window-width="1366"
+ inkscape:window-height="715"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ objecttolerance="10"
+ guidetolerance="10"
+ inkscape:snap-tangential="true"
+ inkscape:snap-perpendicular="true"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0">
+ <inkscape:grid
+ type="xygrid"
+ id="grid10"
+ dotted="false"
+ originx="-162.7958"
+ originy="-83.119086" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ <cc:license
+ rdf:resource="" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-162.79579,-187.48289)">
+ <path
+ d="m 175.99485,195.20186 c -1.73487,0 -3.13161,1.42611 -3.13161,3.18029 v 1.51686 h -0.7829 c -0.43365,0 -0.7829,0.34925 -0.7829,0.7829 v 4.69715 c 0,0.43365 0.34925,0.7829 0.7829,0.7829 h 7.82875 c 0.43366,0 0.78291,-0.34925 0.78291,-0.7829 v -4.69715 c 0,-0.43365 -0.34925,-0.7829 -0.78291,-0.7829 h -0.7829 v -1.51686 c 0,-1.75365 -1.39673,-3.18029 -3.13134,-3.18029 z m 0,1.56581 c 0.8673,0 1.56554,0.69823 1.56554,1.56554 v 1.5658 h -3.13135 v -1.5658 c 0,-0.86731 0.69824,-1.56554 1.56581,-1.56554 z"
+ id="path34"
+ inkscape:connector-curvature="0"
+ style="isolation:isolate;fill:#171717;stroke-width:0.26458332" />
+ <path
+ d="m 187.30817,213.88092 c 0,0 0.14367,-0.008 0.34237,-0.0442 0.38523,-0.0569 0.67627,-0.195 0.89138,-0.38312 0.0415,-0.0317 0.082,-0.0661 0.12144,-0.10319 0.037,-0.0394 0.0712,-0.0796 0.10319,-0.12144 0.18785,-0.21484 0.32623,-0.50615 0.38285,-0.89112 0.0368,-0.1987 0.0444,-0.34263 0.0444,-0.34263 v -22.62691 c 0,-1.88542 -1.88568,-1.88542 -1.88568,-1.88542 h -22.6269 c 0,0 -1.88543,0 -1.88543,1.88542 v 22.6269 c 0,0 0.008,0.14393 0.0442,0.34264 0.0585,0.38497 0.19447,0.67627 0.38285,0.89111 0.032,0.0418 0.0675,0.082 0.10319,0.12145 0.0397,0.037 0.0802,0.0714 0.12144,0.10318 0.21511,0.18812 0.50668,0.32624 0.89138,0.38312 0.19976,0.0363 0.34238,0.0442 0.34238,0.0442 z m -1.88569,-4.5347 h -18.85553 v -18.09221 h 18.85553 z"
+ id="path36"
+ inkscape:connector-curvature="0"
+ style="isolation:isolate;fill:#171717;stroke-width:0.26458332" />
+ </g>
+</svg>
diff --git a/plugins/gvnc/scalable/emblems/org.remmina.Remmina-gvnc-symbolic.svg b/plugins/gvnc/scalable/emblems/org.remmina.Remmina-gvnc-symbolic.svg
new file mode 100644
index 0000000..060a62a
--- /dev/null
+++ b/plugins/gvnc/scalable/emblems/org.remmina.Remmina-gvnc-symbolic.svg
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="100"
+ height="100"
+ viewBox="0 0 26.458335 26.458332"
+ version="1.1"
+ id="svg8"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="vnc protocol.svg">
+ <defs
+ id="defs2">
+ <mask
+ id="_mask_t2Us8opi94eYjaS69EfyEcHSNVQ03DDZ"
+ x="-200%"
+ y="-200%"
+ width="400%"
+ height="400%">
+ <rect
+ x="0"
+ y="0"
+ width="100%"
+ height="100%"
+ style="fill:#ffffff"
+ id="rect40" />
+ <path
+ d="m 850.286,202.852 c 0,0 0.452,-0.025 1.077,-0.139 1.211,-0.179 2.128,-0.614 2.804,-1.205 0.13,-0.1 0.257,-0.208 0.382,-0.324 0.116,-0.125 0.224,-0.251 0.324,-0.382 0.592,-0.677 1.026,-1.593 1.205,-2.804 0.115,-0.625 0.139,-1.077 0.139,-1.077 V 125.75 c 0,-5.931 -5.931,-5.931 -5.931,-5.931 h -71.171 c 0,0 -5.931,0 -5.931,5.931 v 71.171 c 0,0 0.026,0.452 0.139,1.077 0.184,1.211 0.612,2.127 1.205,2.804 0.101,0.131 0.212,0.257 0.324,0.382 0.125,0.116 0.252,0.224 0.383,0.324 0.676,0.591 1.593,1.026 2.803,1.205 0.628,0.114 1.077,0.139 1.077,0.139 z m -5.931,-14.263 h -59.309 v -56.908 h 59.309 z"
+ id="path42"
+ inkscape:connector-curvature="0"
+ style="fill:#000000;stroke:none" />
+ </mask>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4839967"
+ inkscape:cx="-65.390539"
+ inkscape:cy="-17.020138"
+ inkscape:document-units="px"
+ inkscape:current-layer="g820"
+ inkscape:document-rotation="0"
+ showgrid="false"
+ borderlayer="true"
+ inkscape:showpageshadow="true"
+ units="px"
+ inkscape:pagecheckerboard="false"
+ showguides="true"
+ inkscape:window-width="1366"
+ inkscape:window-height="715"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ objecttolerance="10"
+ guidetolerance="10"
+ inkscape:snap-tangential="true"
+ inkscape:snap-perpendicular="true"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0">
+ <inkscape:grid
+ type="xygrid"
+ id="grid10"
+ dotted="false"
+ originx="-146.65307"
+ originy="-135.56714" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ <cc:license
+ rdf:resource="" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-146.65306,-134.97453)">
+ <g
+ id="g820"
+ transform="matrix(1.0697634,0,0,1.0697634,-9.2735492,-11.925523)">
+ <path
+ inkscape:connector-curvature="0"
+ id="path38"
+ d="m 147.83075,137.94029 c 0,0 -1.17769,2e-5 -1.17769,1.17769 v 14.13263 c 0,0 0.005,0.09 0.0274,0.21405 0.0365,0.24056 0.12144,0.42255 0.23928,0.55686 0.02,0.0261 0.0421,0.051 0.0644,0.0757 0.0248,0.023 0.0503,0.0445 0.0761,0.0644 0.13431,0.11739 0.31608,0.20363 0.55643,0.23928 0.12474,0.0227 0.21405,0.0278 0.21405,0.0278 h 4.62807 v -2.83218 H 149.0084 V 140.2961 h 11.77727 v 4.05511 h 2.35536 v -5.23323 c 0,-1.17767 -1.17768,-1.17769 -1.17768,-1.17769 z m 12.95495,10.06883 v 3.58743 h -4.66941 v 2.83218 h 5.84709 c 0,0 0.09,-0.005 0.21405,-0.0278 0.24056,-0.0356 0.4221,-0.1219 0.55643,-0.23928 0.0258,-0.0199 0.0514,-0.0414 0.0761,-0.0644 0.023,-0.0247 0.0445,-0.0497 0.0644,-0.0757 0.11738,-0.13431 0.20386,-0.3163 0.23927,-0.55686 0.023,-0.12408 0.0274,-0.21405 0.0274,-0.21405 v -5.24149 z"
+ style="isolation:isolate;fill:#171717;stroke-width:0.22274615" />
+ <path
+ style="isolation:isolate;fill:#171717;stroke-width:0.22274615"
+ inkscape:connector-curvature="0"
+ id="path38-5"
+ d="m 168.41828,161.43286 c 0,0 0.0898,-0.005 0.21384,-0.0276 0.24056,-0.0356 0.42232,-0.12185 0.55664,-0.23923 0.0258,-0.0199 0.0513,-0.0414 0.0759,-0.0644 0.0229,-0.0248 0.0445,-0.0499 0.0644,-0.0759 0.11739,-0.13432 0.20381,-0.31608 0.23923,-0.55665 0.023,-0.12407 0.0276,-0.21405 0.0276,-0.21405 v -14.13259 c 0,-1.17766 -1.17767,-1.17766 -1.17767,-1.17766 h -14.13279 c 0,0 -1.17765,0 -1.17765,1.17766 v 14.13258 c 0,0 0.005,0.09 0.0276,0.21406 0.0365,0.24057 0.1214,0.42233 0.23924,0.55664 0.02,0.0261 0.0421,0.0512 0.0644,0.0759 0.0248,0.023 0.0501,0.0445 0.0759,0.0644 0.13432,0.11738 0.3163,0.20359 0.55664,0.23923 0.12474,0.0227 0.21384,0.0276 0.21384,0.0276 z m -1.17788,-2.83222 v -11.30036 h -11.77704 11.77704 z"
+ sodipodi:nodetypes="cccccccsccsccccccccccccc" />
+ </g>
+ </g>
+</svg>