Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/Remmina/Remmina.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntenore Gatta <antenore@simbiosi.org>2018-03-24 00:19:39 +0300
committerAntenore Gatta <antenore@simbiosi.org>2018-03-24 00:19:39 +0300
commit9bdd19746a0d62fc76e906d561f59f1855c1a54f (patch)
treebd50983a12e2461780c36d63110f2eefacd25be8 /plugins/rdp
parentda29a0661b5734c48490f567a8464fff37acbc5a (diff)
Refactoring - Moving all plugins inside the plugins directory
Diffstat (limited to 'plugins/rdp')
-rw-r--r--plugins/rdp/16x16/emblems/remmina-rdp-ssh.pngbin0 -> 803 bytes
-rw-r--r--plugins/rdp/16x16/emblems/remmina-rdp.pngbin0 -> 747 bytes
-rw-r--r--plugins/rdp/22x22/emblems/remmina-rdp-ssh.pngbin0 -> 1273 bytes
-rw-r--r--plugins/rdp/22x22/emblems/remmina-rdp.pngbin0 -> 1181 bytes
-rw-r--r--plugins/rdp/CMakeLists.txt89
-rw-r--r--plugins/rdp/rdp_channels.c94
-rw-r--r--plugins/rdp/rdp_channels.h55
-rw-r--r--plugins/rdp/rdp_cliprdr.c821
-rw-r--r--plugins/rdp/rdp_cliprdr.h50
-rw-r--r--plugins/rdp/rdp_event.c1182
-rw-r--r--plugins/rdp/rdp_event.h52
-rw-r--r--plugins/rdp/rdp_file.c307
-rw-r--r--plugins/rdp/rdp_file.h46
-rw-r--r--plugins/rdp/rdp_graphics.c435
-rw-r--r--plugins/rdp/rdp_graphics.h41
-rw-r--r--plugins/rdp/rdp_plugin.c1553
-rw-r--r--plugins/rdp/rdp_plugin.h309
-rw-r--r--plugins/rdp/rdp_settings.c640
-rw-r--r--plugins/rdp/rdp_settings.h47
19 files changed, 5721 insertions, 0 deletions
diff --git a/plugins/rdp/16x16/emblems/remmina-rdp-ssh.png b/plugins/rdp/16x16/emblems/remmina-rdp-ssh.png
new file mode 100644
index 000000000..9defcd6f6
--- /dev/null
+++ b/plugins/rdp/16x16/emblems/remmina-rdp-ssh.png
Binary files differ
diff --git a/plugins/rdp/16x16/emblems/remmina-rdp.png b/plugins/rdp/16x16/emblems/remmina-rdp.png
new file mode 100644
index 000000000..2d7724312
--- /dev/null
+++ b/plugins/rdp/16x16/emblems/remmina-rdp.png
Binary files differ
diff --git a/plugins/rdp/22x22/emblems/remmina-rdp-ssh.png b/plugins/rdp/22x22/emblems/remmina-rdp-ssh.png
new file mode 100644
index 000000000..e977bb3b7
--- /dev/null
+++ b/plugins/rdp/22x22/emblems/remmina-rdp-ssh.png
Binary files differ
diff --git a/plugins/rdp/22x22/emblems/remmina-rdp.png b/plugins/rdp/22x22/emblems/remmina-rdp.png
new file mode 100644
index 000000000..feb093c09
--- /dev/null
+++ b/plugins/rdp/22x22/emblems/remmina-rdp.png
Binary files differ
diff --git a/plugins/rdp/CMakeLists.txt b/plugins/rdp/CMakeLists.txt
new file mode 100644
index 000000000..c03054444
--- /dev/null
+++ b/plugins/rdp/CMakeLists.txt
@@ -0,0 +1,89 @@
+# remmina-plugin-rdp - The GTK+ Remote Desktop Client
+#
+# Copyright (C) 2011 Marc-Andre Moreau
+# Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
+# Copyright (C) 2016-2017 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(CMAKE_THREAD_PREFER_PTHREAD TRUE)
+find_package(Threads REQUIRED)
+find_package(X11)
+
+set(REMMINA_PLUGIN_RDP_SRCS
+ rdp_plugin.c
+ rdp_plugin.h
+ rdp_event.c
+ rdp_event.h
+ rdp_file.c
+ rdp_file.h
+ rdp_settings.c
+ rdp_settings.h
+ rdp_graphics.c
+ rdp_graphics.h
+ rdp_cliprdr.c
+ rdp_cliprdr.h
+ rdp_channels.c
+ rdp_channels.h
+ )
+
+add_definitions(-DFREERDP_REQUIRED_MAJOR=${FREERDP_REQUIRED_MAJOR})
+add_definitions(-DFREERDP_REQUIRED_MINOR=${FREERDP_REQUIRED_MINOR})
+add_definitions(-DFREERDP_REQUIRED_REVISION=${FREERDP_REQUIRED_REVISION})
+
+if(WITH_EXAMPLES)
+ message(STATUS "Enabling examples and test plugins.")
+ add_definitions(-DWITH_EXAMPLES)
+endif()
+
+option(WITH_GFX_H264 "Enable support for H264 modes in GFX" ON)
+if (WITH_GFX_H264)
+ add_definitions(-DWITH_GFX_H264)
+endif()
+
+
+add_library(remmina-plugin-rdp MODULE ${REMMINA_PLUGIN_RDP_SRCS})
+set_target_properties(remmina-plugin-rdp PROPERTIES PREFIX "")
+set_target_properties(remmina-plugin-rdp PROPERTIES NO_SONAME 1)
+
+include_directories(${REMMINA_COMMON_INCLUDE_DIRS} ${FREERDP_INCLUDE_DIRS} ${X11_INCLUDE_DIR})
+target_link_libraries(remmina-plugin-rdp
+ ${REMMINA_COMMON_LIBRARIES} ${FREERDP_LIBRARIES} ${X11_LIBRARIES})
+
+install(TARGETS remmina-plugin-rdp DESTINATION ${REMMINA_PLUGINDIR})
+
+install(FILES
+ 16x16/emblems/remmina-rdp-ssh.png
+ 16x16/emblems/remmina-rdp.png
+ DESTINATION ${APPICON16_EMBLEMS_DIR})
+install(FILES
+ 22x22/emblems/remmina-rdp-ssh.png
+ 22x22/emblems/remmina-rdp.png
+ DESTINATION ${APPICON22_EMBLEMS_DIR})
diff --git a/plugins/rdp/rdp_channels.c b/plugins/rdp/rdp_channels.c
new file mode 100644
index 000000000..eb8d3723f
--- /dev/null
+++ b/plugins/rdp/rdp_channels.c
@@ -0,0 +1,94 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2012-2012 Jean-Louis Dupond
+ * Copyright (C) 2016-2018 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 "rdp_plugin.h"
+#include "rdp_cliprdr.h"
+#include "rdp_channels.h"
+#include "rdp_event.h"
+
+#include <freerdp/freerdp.h>
+#include <freerdp/channels/channels.h>
+#include <freerdp/client/cliprdr.h>
+#include <freerdp/gdi/gfx.h>
+
+void remmina_rdp_OnChannelConnectedEventHandler(rdpContext* context, ChannelConnectedEventArgs* e)
+{
+ TRACE_CALL(__func__);
+
+ rfContext* rfi = (rfContext*)context;
+
+ if (g_strcmp0(e->name, RDPEI_DVC_CHANNEL_NAME) == 0) {
+ g_print("Unimplemented: channel %s connected but we can't use it\n", e->name);
+ // xfc->rdpei = (RdpeiClientContext*) e->pInterface;
+ }else if (g_strcmp0(e->name, TSMF_DVC_CHANNEL_NAME) == 0) {
+ g_print("Unimplemented: channel %s connected but we can't use it\n", e->name);
+ // xf_tsmf_init(xfc, (TsmfClientContext*) e->pInterface);
+ }else if (g_strcmp0(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0) {
+ if (rfi->settings->SoftwareGdi)
+ gdi_graphics_pipeline_init(context->gdi, (RdpgfxClientContext*) e->pInterface);
+ else
+ g_print("Unimplemented: channel %s connected but libfreerdp is in HardwareGdi mode\n", e->name);
+ }else if (g_strcmp0(e->name, RAIL_SVC_CHANNEL_NAME) == 0) {
+ g_print("Unimplemented: channel %s connected but we can't use it\n", e->name);
+ // xf_rail_init(xfc, (RailClientContext*) e->pInterface);
+ }else if (g_strcmp0(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0) {
+ remmina_rdp_cliprdr_init( rfi, (CliprdrClientContext*)e->pInterface);
+ }else if (g_strcmp0(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0) {
+ g_print("Unimplemented: channel %s connected but we can't use it\n", e->name);
+ // xf_encomsp_init(xfc, (EncomspClientContext*) e->pInterface);
+ }else if (g_strcmp0(e->name, DISP_DVC_CHANNEL_NAME) == 0) {
+ // "disp" channel connected, save its context pointer
+ rfi->dispcontext = (DispClientContext*)e->pInterface;
+ // Notify remmina_connection_window to unlock dynres capability
+ remmina_plugin_service->protocol_plugin_emit_signal(rfi->protocol_widget, "unlock-dynres");
+ // Send monitor layout message here to ask for resize of remote desktop now
+ if (rfi->scale == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES) {
+ remmina_rdp_event_send_delayed_monitor_layout(rfi->protocol_widget);
+ }
+ }remmina_plugin_service->log_printf("Channel %s has been opened\n", e->name);
+}
+
+void remmina_rdp_OnChannelDisconnectedEventHandler(rdpContext* context, ChannelConnectedEventArgs* e)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = (rfContext*)context;
+
+ if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0) {
+ if (rfi->settings->SoftwareGdi)
+ gdi_graphics_pipeline_uninit(context->gdi, (RdpgfxClientContext*) e->pInterface);
+ }
+ remmina_plugin_service->log_printf("Channel %s has been closed\n", e->name);
+
+}
diff --git a/plugins/rdp/rdp_channels.h b/plugins/rdp/rdp_channels.h
new file mode 100644
index 000000000..193706ed9
--- /dev/null
+++ b/plugins/rdp/rdp_channels.h
@@ -0,0 +1,55 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2012-2012 Jean-Louis Dupond
+ * Copyright (C) 2017 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.
+ *
+ */
+
+
+#pragma once
+
+#include <freerdp/freerdp.h>
+#include <freerdp/client/channels.h>
+#include <freerdp/client/rdpei.h>
+#include <freerdp/client/tsmf.h>
+#include <freerdp/client/rail.h>
+#include <freerdp/client/cliprdr.h>
+#include <freerdp/client/rdpgfx.h>
+#include <freerdp/client/encomsp.h>
+
+G_BEGIN_DECLS
+
+void remmina_rdp_OnChannelConnectedEventHandler(rdpContext* context, ChannelConnectedEventArgs* e);
+void remmina_rdp_OnChannelDisconnectedEventHandler(rdpContext* context, ChannelConnectedEventArgs* e);
+
+
+G_END_DECLS
+
diff --git a/plugins/rdp/rdp_cliprdr.c b/plugins/rdp/rdp_cliprdr.c
new file mode 100644
index 000000000..b564bd6fd
--- /dev/null
+++ b/plugins/rdp/rdp_cliprdr.c
@@ -0,0 +1,821 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2012-2012 Jean-Louis Dupond
+ * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
+ * Copyright (C) 2016-2017 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 "rdp_plugin.h"
+#include "rdp_cliprdr.h"
+#include "rdp_event.h"
+
+#include <freerdp/freerdp.h>
+#include <freerdp/channels/channels.h>
+#include <freerdp/client/cliprdr.h>
+#include <sys/time.h>
+
+#define CLIPBOARD_TRANSFER_WAIT_TIME 2
+
+UINT32 remmina_rdp_cliprdr_get_format_from_gdkatom(GdkAtom atom)
+{
+ TRACE_CALL(__func__);
+ UINT32 rc;
+ gchar* name = gdk_atom_name(atom);
+ rc = 0;
+ if (g_strcmp0("UTF8_STRING", name) == 0 || g_strcmp0("text/plain;charset=utf-8", name) == 0) {
+ rc = CF_UNICODETEXT;
+ }
+ if (g_strcmp0("TEXT", name) == 0 || g_strcmp0("text/plain", name) == 0) {
+ rc = CF_TEXT;
+ }
+ if (g_strcmp0("text/html", name) == 0) {
+ rc = CB_FORMAT_HTML;
+ }
+ if (g_strcmp0("image/png", name) == 0) {
+ rc = CB_FORMAT_PNG;
+ }
+ if (g_strcmp0("image/jpeg", name) == 0) {
+ rc = CB_FORMAT_JPEG;
+ }
+ if (g_strcmp0("image/bmp", name) == 0) {
+ rc = CF_DIB;
+ }
+ g_free(name);
+ return rc;
+}
+
+void remmina_rdp_cliprdr_get_target_types(UINT32** formats, UINT16* size, GdkAtom* types, int count)
+{
+ TRACE_CALL(__func__);
+ int i;
+ *size = 1;
+ *formats = (UINT32*)malloc(sizeof(UINT32) * (count + 1));
+
+ *formats[0] = 0;
+ for (i = 0; i < count; i++) {
+ UINT32 format = remmina_rdp_cliprdr_get_format_from_gdkatom(types[i]);
+ if (format != 0) {
+ (*formats)[*size] = format;
+ (*size)++;
+ }
+ }
+
+ *formats = realloc(*formats, sizeof(UINT32) * (*size));
+}
+
+static UINT8* lf2crlf(UINT8* data, int* size)
+{
+ TRACE_CALL(__func__);
+ UINT8 c;
+ UINT8* outbuf;
+ UINT8* out;
+ UINT8* in_end;
+ UINT8* in;
+ int out_size;
+
+ out_size = (*size) * 2 + 1;
+ outbuf = (UINT8*)malloc(out_size);
+ out = outbuf;
+ in = data;
+ in_end = data + (*size);
+
+ while (in < in_end) {
+ c = *in++;
+ if (c == '\n') {
+ *out++ = '\r';
+ *out++ = '\n';
+ }else {
+ *out++ = c;
+ }
+ }
+
+ *out++ = 0;
+ *size = out - outbuf;
+
+ return outbuf;
+}
+
+static void crlf2lf(UINT8* data, size_t* size)
+{
+ TRACE_CALL(__func__);
+ UINT8 c;
+ UINT8* out;
+ UINT8* in;
+ UINT8* in_end;
+
+ out = data;
+ in = data;
+ in_end = data + (*size);
+
+ while (in < in_end) {
+ c = *in++;
+ if (c != '\r')
+ *out++ = c;
+ }
+
+ *size = out - data;
+}
+
+int remmina_rdp_cliprdr_server_file_contents_request(CliprdrClientContext* context, CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
+{
+ TRACE_CALL(__func__);
+ return -1;
+}
+int remmina_rdp_cliprdr_server_file_contents_response(CliprdrClientContext* context, CLIPRDR_FILE_CONTENTS_RESPONSE* fileContentsResponse)
+{
+ TRACE_CALL(__func__);
+ return 1;
+}
+
+void remmina_rdp_cliprdr_send_client_format_list(RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ RemminaPluginRdpUiObject* ui;
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ rfClipboard* clipboard;
+ CLIPRDR_FORMAT_LIST *pFormatList;
+ RemminaPluginRdpEvent rdp_event = { 0 };
+
+ if (!rfi || !rfi->connected || rfi->is_reconnecting)
+ return;
+
+ clipboard = &(rfi->clipboard);
+
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_CLIPBOARD;
+ ui->clipboard.clipboard = clipboard;
+ ui->clipboard.type = REMMINA_RDP_UI_CLIPBOARD_FORMATLIST;
+ pFormatList = remmina_rdp_event_queue_ui_sync_retptr(gp, ui);
+
+ rdp_event.type = REMMINA_RDP_EVENT_TYPE_CLIPBOARD_SEND_CLIENT_FORMAT_LIST;
+ rdp_event.clipboard_formatlist.pFormatList = pFormatList;
+ remmina_rdp_event_event_push(gp, &rdp_event);
+
+}
+
+static void remmina_rdp_cliprdr_send_client_capabilities(rfClipboard* clipboard)
+{
+ TRACE_CALL(__func__);
+ CLIPRDR_CAPABILITIES capabilities;
+ CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet;
+
+ capabilities.cCapabilitiesSets = 1;
+ capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET*)&(generalCapabilitySet);
+
+ generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
+ generalCapabilitySet.capabilitySetLength = 12;
+
+ generalCapabilitySet.version = CB_CAPS_VERSION_2;
+ generalCapabilitySet.generalFlags = CB_USE_LONG_FORMAT_NAMES;
+
+ clipboard->context->ClientCapabilities(clipboard->context, &capabilities);
+}
+
+
+static UINT remmina_rdp_cliprdr_monitor_ready(CliprdrClientContext* context, CLIPRDR_MONITOR_READY* monitorReady)
+{
+ TRACE_CALL(__func__);
+ rfClipboard* clipboard = (rfClipboard*)context->custom;
+ RemminaProtocolWidget* gp;
+
+ remmina_rdp_cliprdr_send_client_capabilities(clipboard);
+ gp = clipboard->rfi->protocol_widget;
+ remmina_rdp_cliprdr_send_client_format_list(gp);
+
+ return CHANNEL_RC_OK;
+}
+
+static UINT remmina_rdp_cliprdr_server_capabilities(CliprdrClientContext* context, CLIPRDR_CAPABILITIES* capabilities)
+{
+ TRACE_CALL(__func__);
+ return CHANNEL_RC_OK;
+}
+
+
+static UINT remmina_rdp_cliprdr_server_format_list(CliprdrClientContext* context, CLIPRDR_FORMAT_LIST* formatList)
+{
+ TRACE_CALL(__func__);
+
+ /* Called when a user do a "Copy" on the server: we collect all formats
+ * the server send us and then setup the local clipboard with the appropiate
+ * functions to request server data */
+
+ RemminaPluginRdpUiObject* ui;
+ RemminaProtocolWidget* gp;
+ rfClipboard* clipboard;
+ CLIPRDR_FORMAT* format;
+ CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse;
+
+ int i;
+
+ clipboard = (rfClipboard*)context->custom;
+ gp = clipboard->rfi->protocol_widget;
+ GtkTargetList* list = gtk_target_list_new(NULL, 0);
+
+ for (i = 0; i < formatList->numFormats; i++) {
+ format = &formatList->formats[i];
+ if (format->formatId == CF_UNICODETEXT) {
+ GdkAtom atom = gdk_atom_intern("UTF8_STRING", TRUE);
+ gtk_target_list_add(list, atom, 0, CF_UNICODETEXT);
+ }else if (format->formatId == CF_TEXT) {
+ GdkAtom atom = gdk_atom_intern("TEXT", TRUE);
+ gtk_target_list_add(list, atom, 0, CF_TEXT);
+ }else if (format->formatId == CF_DIB) {
+ GdkAtom atom = gdk_atom_intern("image/bmp", TRUE);
+ gtk_target_list_add(list, atom, 0, CF_DIB);
+ }else if (format->formatId == CF_DIBV5) {
+ GdkAtom atom = gdk_atom_intern("image/bmp", TRUE);
+ gtk_target_list_add(list, atom, 0, CF_DIBV5);
+ }else if (format->formatId == CB_FORMAT_JPEG) {
+ GdkAtom atom = gdk_atom_intern("image/jpeg", TRUE);
+ gtk_target_list_add(list, atom, 0, CB_FORMAT_JPEG);
+ }else if (format->formatId == CB_FORMAT_PNG) {
+ GdkAtom atom = gdk_atom_intern("image/png", TRUE);
+ gtk_target_list_add(list, atom, 0, CB_FORMAT_PNG);
+ }else if (format->formatId == CB_FORMAT_HTML) {
+ GdkAtom atom = gdk_atom_intern("text/html", TRUE);
+ gtk_target_list_add(list, atom, 0, CB_FORMAT_HTML);
+ }
+ }
+
+ /* Now we tell GTK to change the local keyboard calling gtk_clipboard_set_with_owner
+ * via REMMINA_RDP_UI_CLIPBOARD_SET_DATA
+ * GTK will immediately fire an "owner-change" event, that we should ignore */
+
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_CLIPBOARD;
+ ui->clipboard.clipboard = clipboard;
+ ui->clipboard.type = REMMINA_RDP_UI_CLIPBOARD_SET_DATA;
+ ui->clipboard.targetlist = list;
+ remmina_rdp_event_queue_ui_sync_retint(gp, ui);
+
+ /* Send FormatListResponse to server */
+
+ formatListResponse.msgType = CB_FORMAT_LIST_RESPONSE;
+ formatListResponse.msgFlags = CB_RESPONSE_OK; // Can be CB_RESPONSE_FAIL in case of error
+ formatListResponse.dataLen = 0;
+
+ return clipboard->context->ClientFormatListResponse(clipboard->context, &formatListResponse);
+
+}
+
+static UINT remmina_rdp_cliprdr_server_format_list_response(CliprdrClientContext* context, CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse)
+{
+ TRACE_CALL(__func__);
+ return CHANNEL_RC_OK;
+}
+
+
+static UINT remmina_rdp_cliprdr_server_format_data_request(CliprdrClientContext* context, CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
+{
+ TRACE_CALL(__func__);
+
+ RemminaPluginRdpUiObject* ui;
+ RemminaProtocolWidget* gp;
+ rfClipboard* clipboard;
+
+ clipboard = (rfClipboard*)context->custom;
+ gp = clipboard->rfi->protocol_widget;
+
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_CLIPBOARD;
+ ui->clipboard.clipboard = clipboard;
+ ui->clipboard.type = REMMINA_RDP_UI_CLIPBOARD_GET_DATA;
+ ui->clipboard.format = formatDataRequest->requestedFormatId;
+ remmina_rdp_event_queue_ui_sync_retint(gp, ui);
+
+ return CHANNEL_RC_OK;
+}
+
+static UINT remmina_rdp_cliprdr_server_format_data_response(CliprdrClientContext* context, CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
+{
+ TRACE_CALL(__func__);
+
+ UINT8* data;
+ size_t size;
+ rfContext* rfi;
+ RemminaProtocolWidget* gp;
+ rfClipboard* clipboard;
+ GdkPixbufLoader *pixbuf;
+ gpointer output = NULL;
+ RemminaPluginRdpUiObject *ui;
+
+ clipboard = (rfClipboard*)context->custom;
+ gp = clipboard->rfi->protocol_widget;
+ rfi = GET_PLUGIN_DATA(gp);
+
+ data = formatDataResponse->requestedFormatData;
+ size = formatDataResponse->dataLen;
+
+ // formatDataResponse->requestedFormatData is allocated
+ // by freerdp and freed after returning from this callback function.
+ // So we must make a copy if we need to preserve it
+
+ if (size > 0) {
+ switch (rfi->clipboard.format) {
+ case CF_UNICODETEXT:
+ {
+ size = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*)data, size / 2, (CHAR**)&output, 0, NULL, NULL);
+ crlf2lf(output, &size);
+ break;
+ }
+
+ case CF_TEXT:
+ case CB_FORMAT_HTML:
+ {
+ output = (gpointer)calloc(1, size + 1);
+ if (output) {
+ memcpy(output, data, size);
+ crlf2lf(output, &size);
+ }
+ break;
+ }
+
+ case CF_DIBV5:
+ case CF_DIB:
+ {
+ wStream* s;
+ UINT32 offset;
+ GError *perr;
+ BITMAPINFOHEADER* pbi;
+ BITMAPV5HEADER* pbi5;
+
+ pbi = (BITMAPINFOHEADER*)data;
+
+ // offset calculation inspired by http://downloads.poolelan.com/MSDN/MSDNLibrary6/Disk1/Samples/VC/OS/WindowsXP/GetImage/BitmapUtil.cpp
+ offset = 14 + pbi->biSize;
+ if (pbi->biClrUsed != 0)
+ offset += sizeof(RGBQUAD) * pbi->biClrUsed;
+ else if (pbi->biBitCount <= 8)
+ offset += sizeof(RGBQUAD) * (1 << pbi->biBitCount);
+ if (pbi->biSize == sizeof(BITMAPINFOHEADER)) {
+ if (pbi->biCompression == 3) // BI_BITFIELDS is 3
+ offset += 12;
+ } else if (pbi->biSize >= sizeof(BITMAPV5HEADER)) {
+ pbi5 = (BITMAPV5HEADER*)pbi;
+ if (pbi5->bV5ProfileData <= offset)
+ offset += pbi5->bV5ProfileSize;
+ }
+ s = Stream_New(NULL, 14 + size);
+ Stream_Write_UINT8(s, 'B');
+ Stream_Write_UINT8(s, 'M');
+ Stream_Write_UINT32(s, 14 + size);
+ Stream_Write_UINT32(s, 0);
+ Stream_Write_UINT32(s, offset);
+ Stream_Write(s, data, size);
+
+ data = Stream_Buffer(s);
+ size = Stream_Length(s);
+
+ pixbuf = gdk_pixbuf_loader_new();
+ perr = NULL;
+ if ( !gdk_pixbuf_loader_write(pixbuf, data, size, &perr) ) {
+ remmina_plugin_service->log_printf("[RDP] rdp_cliprdr: gdk_pixbuf_loader_write() returned error %s\n", perr->message);
+ }else {
+ if ( !gdk_pixbuf_loader_close(pixbuf, &perr) ) {
+ remmina_plugin_service->log_printf("[RDP] rdp_cliprdr: gdk_pixbuf_loader_close() returned error %s\n", perr->message);
+ perr = NULL;
+ }
+ Stream_Free(s, TRUE);
+ output = g_object_ref(gdk_pixbuf_loader_get_pixbuf(pixbuf));
+ }
+ g_object_unref(pixbuf);
+ break;
+ }
+
+ case CB_FORMAT_PNG:
+ case CB_FORMAT_JPEG:
+ {
+ pixbuf = gdk_pixbuf_loader_new();
+ gdk_pixbuf_loader_write(pixbuf, data, size, NULL);
+ output = g_object_ref(gdk_pixbuf_loader_get_pixbuf(pixbuf));
+ gdk_pixbuf_loader_close(pixbuf, NULL);
+ g_object_unref(pixbuf);
+ break;
+ }
+ }
+ }
+
+ pthread_mutex_lock(&clipboard->transfer_clip_mutex);
+ pthread_cond_signal(&clipboard->transfer_clip_cond);
+ if ( clipboard->srv_clip_data_wait == SCDW_BUSY_WAIT ) {
+ clipboard->srv_data = output;
+ }else {
+ // Clipboard data arrived from server when we are not busywaiting.
+ // Just put it on the local clipboard
+
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_CLIPBOARD;
+ ui->clipboard.clipboard = clipboard;
+ ui->clipboard.type = REMMINA_RDP_UI_CLIPBOARD_SET_CONTENT;
+ ui->clipboard.data = output;
+ ui->clipboard.format = clipboard->format;
+ remmina_rdp_event_queue_ui_sync_retint(gp, ui);
+
+ clipboard->srv_clip_data_wait = SCDW_NONE;
+
+ }
+ pthread_mutex_unlock(&clipboard->transfer_clip_mutex);
+
+ return CHANNEL_RC_OK;
+}
+
+void remmina_rdp_cliprdr_request_data(GtkClipboard *gtkClipboard, GtkSelectionData *selection_data, guint info, RemminaProtocolWidget* gp )
+{
+ TRACE_CALL(__func__);
+
+ /* Called by GTK when someone press "Paste" on the client side.
+ * We ask to the server the data we need */
+
+ GdkAtom target;
+ CLIPRDR_FORMAT_DATA_REQUEST* pFormatDataRequest;
+ rfClipboard* clipboard;
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ RemminaPluginRdpEvent rdp_event = { 0 };
+ struct timespec to;
+ struct timeval tv;
+ int rc;
+
+ clipboard = &(rfi->clipboard);
+ if ( clipboard->srv_clip_data_wait != SCDW_NONE ) {
+ remmina_plugin_service->log_printf("[RDP] Cannot paste now, I'm transferring clipboard data from server. Try again later\n");
+ return;
+ }
+
+ target = gtk_selection_data_get_target(selection_data);
+ // clipboard->format = remmina_rdp_cliprdr_get_format_from_gdkatom(target);
+ clipboard->format = info;
+
+ /* Request Clipboard content from the server, the request is async */
+
+ pthread_mutex_lock(&clipboard->transfer_clip_mutex);
+
+ pFormatDataRequest = (CLIPRDR_FORMAT_DATA_REQUEST*)malloc(sizeof(CLIPRDR_FORMAT_DATA_REQUEST));
+ ZeroMemory(pFormatDataRequest, sizeof(CLIPRDR_FORMAT_DATA_REQUEST));
+ pFormatDataRequest->requestedFormatId = clipboard->format;
+ clipboard->srv_clip_data_wait = SCDW_BUSY_WAIT;
+
+ rdp_event.type = REMMINA_RDP_EVENT_TYPE_CLIPBOARD_SEND_CLIENT_FORMAT_DATA_REQUEST;
+ rdp_event.clipboard_formatdatarequest.pFormatDataRequest = pFormatDataRequest;
+ remmina_rdp_event_event_push(gp, &rdp_event);
+
+
+ /* Busy wait clibpoard data for CLIPBOARD_TRANSFER_WAIT_TIME seconds */
+ gettimeofday(&tv, NULL);
+ to.tv_sec = tv.tv_sec + CLIPBOARD_TRANSFER_WAIT_TIME;
+ to.tv_nsec = tv.tv_usec * 1000;
+ rc = pthread_cond_timedwait(&clipboard->transfer_clip_cond, &clipboard->transfer_clip_mutex, &to);
+
+ if ( rc == 0 ) {
+ /* Data has arrived without timeout */
+ if (clipboard->srv_data != NULL) {
+ if (info == CB_FORMAT_PNG || info == CF_DIB || info == CF_DIBV5 || info == CB_FORMAT_JPEG) {
+ gtk_selection_data_set_pixbuf(selection_data, clipboard->srv_data);
+ g_object_unref(clipboard->srv_data);
+ }else {
+ gtk_selection_data_set_text(selection_data, clipboard->srv_data, -1);
+ free(clipboard->srv_data);
+ }
+ }
+ clipboard->srv_clip_data_wait = SCDW_NONE;
+ } else {
+ clipboard->srv_clip_data_wait = SCDW_ASYNCWAIT;
+ if ( rc == ETIMEDOUT ) {
+ remmina_plugin_service->log_printf("[RDP] Clipboard data has not been transferred from the server in %d seconds. Try to paste later.\n",
+ CLIPBOARD_TRANSFER_WAIT_TIME);
+ }else {
+ remmina_plugin_service->log_printf("[RDP] internal error: pthread_cond_timedwait() returned %d\n", rc);
+ clipboard->srv_clip_data_wait = SCDW_NONE;
+ }
+ }
+ pthread_mutex_unlock(&clipboard->transfer_clip_mutex);
+
+}
+
+void remmina_rdp_cliprdr_empty_clipboard(GtkClipboard *gtkClipboard, rfClipboard *clipboard)
+{
+ TRACE_CALL(__func__);
+ /* No need to do anything here */
+}
+
+CLIPRDR_FORMAT_LIST *remmina_rdp_cliprdr_get_client_format_list(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+
+ GtkClipboard* gtkClipboard;
+ rfClipboard* clipboard;
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ GdkAtom* targets;
+ gboolean result = 0;
+ gint loccount, srvcount;
+ gint formatId, i;
+ CLIPRDR_FORMAT *formats;
+ struct retp_t {
+ CLIPRDR_FORMAT_LIST pFormatList;
+ CLIPRDR_FORMAT formats[];
+ } *retp;
+
+ clipboard = &(rfi->clipboard);
+ formats = NULL;
+
+ retp = NULL;
+
+ gtkClipboard = gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD);
+ if (gtkClipboard) {
+ result = gtk_clipboard_wait_for_targets(gtkClipboard, &targets, &loccount);
+ }
+
+ if (result && loccount > 0) {
+ formats = (CLIPRDR_FORMAT*)malloc(loccount * sizeof(CLIPRDR_FORMAT));
+ srvcount = 0;
+ for (i = 0; i < loccount; i++) {
+ formatId = remmina_rdp_cliprdr_get_format_from_gdkatom(targets[i]);
+ if ( formatId != 0 ) {
+ formats[srvcount].formatId = formatId;
+ formats[srvcount].formatName = NULL;
+ srvcount++;
+ }
+ }
+ if (srvcount > 0) {
+ retp = (struct retp_t *)malloc(sizeof(struct retp_t) + sizeof(CLIPRDR_FORMAT) * srvcount);
+ retp->pFormatList.formats = retp->formats;
+ retp->pFormatList.numFormats = srvcount;
+ memcpy(retp->formats, formats, sizeof(CLIPRDR_FORMAT) * srvcount);
+ } else {
+ retp = (struct retp_t *)malloc(sizeof(struct retp_t));
+ retp->pFormatList.formats = NULL;
+ retp->pFormatList.numFormats = 0;
+ }
+ free(formats);
+ } else {
+ retp = (struct retp_t *)malloc(sizeof(struct retp_t) + sizeof(CLIPRDR_FORMAT));
+ retp->pFormatList.formats = NULL;
+ retp->pFormatList.numFormats = 0;
+ }
+
+ if (result)
+ g_free(targets);
+
+ retp->pFormatList.msgFlags = CB_RESPONSE_OK;
+
+ return (CLIPRDR_FORMAT_LIST*)retp;
+}
+
+static void remmina_rdp_cliprdr_mt_get_format_list(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
+{
+ TRACE_CALL(__func__);
+ ui->retptr = (void*)remmina_rdp_cliprdr_get_client_format_list(gp);
+}
+
+
+void remmina_rdp_cliprdr_get_clipboard_data(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
+{
+ TRACE_CALL(__func__);
+ GtkClipboard* gtkClipboard;
+ rfClipboard* clipboard;
+ UINT8* inbuf = NULL;
+ UINT8* outbuf = NULL;
+ GdkPixbuf *image = NULL;
+ int size = 0;
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ RemminaPluginRdpEvent rdp_event = { 0 };
+ CLIPRDR_FORMAT_DATA_RESPONSE* pFormatDataResponse;
+
+ clipboard = ui->clipboard.clipboard;
+ gtkClipboard = gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD);
+ if (gtkClipboard) {
+ switch (ui->clipboard.format) {
+ case CF_TEXT:
+ case CF_UNICODETEXT:
+ case CB_FORMAT_HTML:
+ {
+ inbuf = (UINT8*)gtk_clipboard_wait_for_text(gtkClipboard);
+ break;
+ }
+
+ case CB_FORMAT_PNG:
+ case CB_FORMAT_JPEG:
+ case CF_DIB:
+ case CF_DIBV5:
+ {
+ image = gtk_clipboard_wait_for_image(gtkClipboard);
+ break;
+ }
+ }
+ }
+
+ /* No data received, send nothing */
+ if (inbuf != NULL || image != NULL) {
+ switch (ui->clipboard.format) {
+ case CF_TEXT:
+ case CB_FORMAT_HTML:
+ {
+ size = strlen((char*)inbuf);
+ outbuf = lf2crlf(inbuf, &size);
+ break;
+ }
+ case CF_UNICODETEXT:
+ {
+ size = strlen((char*)inbuf);
+ inbuf = lf2crlf(inbuf, &size);
+ size = (ConvertToUnicode(CP_UTF8, 0, (CHAR*)inbuf, -1, (WCHAR**)&outbuf, 0) ) * sizeof(WCHAR);
+ g_free(inbuf);
+ break;
+ }
+ case CB_FORMAT_PNG:
+ {
+ gchar* data;
+ gsize buffersize;
+ gdk_pixbuf_save_to_buffer(image, &data, &buffersize, "png", NULL, NULL);
+ outbuf = (UINT8*)malloc(buffersize);
+ memcpy(outbuf, data, buffersize);
+ size = buffersize;
+ g_object_unref(image);
+ break;
+ }
+ case CB_FORMAT_JPEG:
+ {
+ gchar* data;
+ gsize buffersize;
+ gdk_pixbuf_save_to_buffer(image, &data, &buffersize, "jpeg", NULL, NULL);
+ outbuf = (UINT8*)malloc(buffersize);
+ memcpy(outbuf, data, buffersize);
+ size = buffersize;
+ g_object_unref(image);
+ break;
+ }
+ case CF_DIB:
+ case CF_DIBV5:
+ {
+ gchar* data;
+ gsize buffersize;
+ gdk_pixbuf_save_to_buffer(image, &data, &buffersize, "bmp", NULL, NULL);
+ size = buffersize - 14;
+ outbuf = (UINT8*)malloc(size);
+ memcpy(outbuf, data + 14, size);
+ g_object_unref(image);
+ break;
+ }
+ }
+ }
+
+ pFormatDataResponse = (CLIPRDR_FORMAT_DATA_RESPONSE*)malloc(sizeof(CLIPRDR_FORMAT_DATA_RESPONSE));
+ if (!pFormatDataResponse) {
+ if (outbuf) free(outbuf);
+ return;
+ }
+
+ ZeroMemory(pFormatDataResponse, sizeof(CLIPRDR_FORMAT_DATA_RESPONSE));
+ rdp_event.type = REMMINA_RDP_EVENT_TYPE_CLIPBOARD_SEND_CLIENT_FORMAT_DATA_RESPONSE;
+ rdp_event.clipboard_formatdataresponse.pFormatDataResponse = pFormatDataResponse;
+ pFormatDataResponse->msgFlags = CB_RESPONSE_OK;
+ pFormatDataResponse->dataLen = size;
+ pFormatDataResponse->requestedFormatData = outbuf;
+ remmina_rdp_event_event_push(gp, &rdp_event);
+
+}
+
+void remmina_rdp_cliprdr_set_clipboard_content(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
+{
+ TRACE_CALL(__func__);
+ GtkClipboard* gtkClipboard;
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+
+ gtkClipboard = gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD);
+ if (ui->clipboard.format == CB_FORMAT_PNG || ui->clipboard.format == CF_DIB || ui->clipboard.format == CF_DIBV5 || ui->clipboard.format == CB_FORMAT_JPEG) {
+ gtk_clipboard_set_image( gtkClipboard, ui->clipboard.data );
+ g_object_unref(ui->clipboard.data);
+ }else {
+ gtk_clipboard_set_text( gtkClipboard, ui->clipboard.data, -1 );
+ free(ui->clipboard.data);
+ }
+
+}
+
+void remmina_rdp_cliprdr_set_clipboard_data(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
+{
+ TRACE_CALL(__func__);
+ GtkClipboard* gtkClipboard;
+ GtkTargetEntry* targets;
+ gint n_targets;
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ rfClipboard* clipboard;
+
+ clipboard = ui->clipboard.clipboard;
+ targets = gtk_target_table_new_from_list(ui->clipboard.targetlist, &n_targets);
+ gtkClipboard = gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD);
+ if (gtkClipboard && targets) {
+ gtk_clipboard_set_with_owner(gtkClipboard, targets, n_targets,
+ (GtkClipboardGetFunc)remmina_rdp_cliprdr_request_data,
+ (GtkClipboardClearFunc)remmina_rdp_cliprdr_empty_clipboard, G_OBJECT(gp));
+ gtk_target_table_free(targets, n_targets);
+ }
+}
+
+void remmina_rdp_cliprdr_detach_owner(RemminaProtocolWidget* gp)
+{
+ /* When closing a rdp connection, we should check if gp is a clipboard owner.
+ * If it's an owner, detach it from the clipboard */
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ GtkClipboard* gtkClipboard;
+
+ gtkClipboard = gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD);
+ if (gtkClipboard && gtk_clipboard_get_owner(gtkClipboard) == (GObject*)gp) {
+ gtk_clipboard_clear(gtkClipboard);
+ }
+
+}
+
+void remmina_rdp_event_process_clipboard(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
+{
+ TRACE_CALL(__func__);
+
+ switch (ui->clipboard.type) {
+
+ case REMMINA_RDP_UI_CLIPBOARD_FORMATLIST:
+ remmina_rdp_cliprdr_mt_get_format_list(gp, ui);
+ break;
+
+ case REMMINA_RDP_UI_CLIPBOARD_GET_DATA:
+ remmina_rdp_cliprdr_get_clipboard_data(gp, ui);
+ break;
+
+ case REMMINA_RDP_UI_CLIPBOARD_SET_DATA:
+ remmina_rdp_cliprdr_set_clipboard_data(gp, ui);
+ break;
+
+ case REMMINA_RDP_UI_CLIPBOARD_SET_CONTENT:
+ remmina_rdp_cliprdr_set_clipboard_content(gp, ui);
+ break;
+ }
+}
+
+void remmina_rdp_clipboard_init(rfContext *rfi)
+{
+ TRACE_CALL(__func__);
+ // Future: initialize rfi->clipboard
+}
+void remmina_rdp_clipboard_free(rfContext *rfi)
+{
+ TRACE_CALL(__func__);
+ // Future: deinitialize rfi->clipboard
+}
+
+
+void remmina_rdp_cliprdr_init(rfContext* rfi, CliprdrClientContext* cliprdr)
+{
+ TRACE_CALL(__func__);
+
+ rfClipboard* clipboard;
+ clipboard = &(rfi->clipboard);
+
+ rfi->clipboard.rfi = rfi;
+ cliprdr->custom = (void*)clipboard;
+
+ clipboard->context = cliprdr;
+ pthread_mutex_init(&clipboard->transfer_clip_mutex, NULL);
+ pthread_cond_init(&clipboard->transfer_clip_cond, NULL);
+ clipboard->srv_clip_data_wait = SCDW_NONE;
+
+ cliprdr->MonitorReady = remmina_rdp_cliprdr_monitor_ready;
+ cliprdr->ServerCapabilities = remmina_rdp_cliprdr_server_capabilities;
+ cliprdr->ServerFormatList = remmina_rdp_cliprdr_server_format_list;
+ cliprdr->ServerFormatListResponse = remmina_rdp_cliprdr_server_format_list_response;
+ cliprdr->ServerFormatDataRequest = remmina_rdp_cliprdr_server_format_data_request;
+ cliprdr->ServerFormatDataResponse = remmina_rdp_cliprdr_server_format_data_response;
+
+// cliprdr->ServerFileContentsRequest = remmina_rdp_cliprdr_server_file_contents_request;
+// cliprdr->ServerFileContentsResponse = remmina_rdp_cliprdr_server_file_contents_response;
+
+}
+
diff --git a/plugins/rdp/rdp_cliprdr.h b/plugins/rdp/rdp_cliprdr.h
new file mode 100644
index 000000000..d40bff98c
--- /dev/null
+++ b/plugins/rdp/rdp_cliprdr.h
@@ -0,0 +1,50 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2010-2011 Vic Lee
+ * Copyright (C) 2012-2012 Jean-Louis Dupond
+ * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
+ * Copyright (C) 2016-2017 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.
+ *
+ */
+
+#pragma once
+
+
+#include <freerdp/freerdp.h>
+#include "rdp_plugin.h"
+
+void remmina_rdp_clipboard_init(rfContext* rfi);
+void remmina_rdp_clipboard_free(rfContext* rfi);
+void remmina_rdp_cliprdr_init(rfContext* rfc, CliprdrClientContext* cliprdr);
+void remmina_rdp_channel_cliprdr_process(RemminaProtocolWidget* gp, wMessage* event);
+void remmina_rdp_event_process_clipboard(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui);
+CLIPRDR_FORMAT_LIST *remmina_rdp_cliprdr_get_client_format_list(RemminaProtocolWidget* gp);
+void remmina_rdp_cliprdr_detach_owner(RemminaProtocolWidget* gp);
diff --git a/plugins/rdp/rdp_event.c b/plugins/rdp/rdp_event.c
new file mode 100644
index 000000000..48fe41c8e
--- /dev/null
+++ b/plugins/rdp/rdp_event.c
@@ -0,0 +1,1182 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2010 Jay Sorg
+ * Copyright (C) 2010-2011 Vic Lee
+ * Copyright (C) 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
+ * Copyright (C) 2016-2017 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 "rdp_plugin.h"
+#include "rdp_event.h"
+#include "rdp_cliprdr.h"
+#include "rdp_settings.h"
+#include <gdk/gdkkeysyms.h>
+#include <cairo/cairo-xlib.h>
+#include <freerdp/locale/keyboard.h>
+#include <execinfo.h>
+
+static void remmina_rdp_event_on_focus_in(GtkWidget* widget, GdkEventKey* event, RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ rdpInput* input;
+ GdkModifierType state;
+#if GTK_CHECK_VERSION(3, 20, 0)
+ GdkSeat *seat;
+#else
+ GdkDeviceManager *manager;
+#endif
+ GdkDevice *keyboard = NULL;
+
+ if (!rfi || !rfi->connected || rfi->is_reconnecting)
+ return;
+
+ input = rfi->instance->input;
+ UINT32 toggle_keys_state = 0;
+
+#if GTK_CHECK_VERSION(3, 20, 0)
+ seat = gdk_display_get_default_seat(gdk_display_get_default());
+ keyboard = gdk_seat_get_pointer(seat);
+#else
+ manager = gdk_display_get_device_manager(gdk_display_get_default());
+ keyboard = gdk_device_manager_get_client_pointer(manager);
+#endif
+ gdk_window_get_device_position(gdk_get_default_root_window(), keyboard, NULL, NULL, &state);
+
+ if (state & GDK_LOCK_MASK) {
+ toggle_keys_state |= KBD_SYNC_CAPS_LOCK;
+ }
+ if (state & GDK_MOD2_MASK) {
+ toggle_keys_state |= KBD_SYNC_NUM_LOCK;
+ }
+ if (state & GDK_MOD5_MASK) {
+ toggle_keys_state |= KBD_SYNC_SCROLL_LOCK;
+ }
+
+ input->SynchronizeEvent(input, toggle_keys_state);
+ input->KeyboardEvent(input, KBD_FLAGS_RELEASE, 0x0F);
+}
+
+void remmina_rdp_event_event_push(RemminaProtocolWidget* gp, const RemminaPluginRdpEvent* e)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ RemminaPluginRdpEvent* event;
+
+ /* Called by the main GTK thread to send an event to the libfreerdp thread */
+
+ if (!rfi || !rfi->connected || rfi->is_reconnecting)
+ return;
+
+ if (rfi->event_queue) {
+ event = g_memdup(e, sizeof(RemminaPluginRdpEvent));
+ g_async_queue_push(rfi->event_queue, event);
+
+ if (write(rfi->event_pipe[1], "\0", 1)) {
+ }
+ }
+}
+
+static void remmina_rdp_event_release_all_keys(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ RemminaPluginRdpEvent rdp_event = { 0 };
+ int i;
+
+ /* Send all release key events for previously pressed keys */
+ for (i = 0; i < rfi->pressed_keys->len; i++) {
+ rdp_event = g_array_index(rfi->pressed_keys, RemminaPluginRdpEvent, i);
+ if ((rdp_event.type == REMMINA_RDP_EVENT_TYPE_SCANCODE ||
+ rdp_event.type == REMMINA_RDP_EVENT_TYPE_SCANCODE_UNICODE) &&
+ rdp_event.key_event.up == False) {
+ rdp_event.key_event.up = True;
+ remmina_rdp_event_event_push(gp, &rdp_event);
+ }
+ }
+
+ g_array_set_size(rfi->pressed_keys, 0);
+}
+
+static void remmina_rdp_event_release_key(RemminaProtocolWidget* gp, RemminaPluginRdpEvent rdp_event)
+{
+ TRACE_CALL(__func__);
+ gint i;
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ RemminaPluginRdpEvent rdp_event_2 = { 0 };
+
+ rdp_event_2.type = REMMINA_RDP_EVENT_TYPE_SCANCODE;
+
+ if ((rdp_event.type == REMMINA_RDP_EVENT_TYPE_SCANCODE ||
+ rdp_event.type == REMMINA_RDP_EVENT_TYPE_SCANCODE_UNICODE) &&
+ rdp_event.key_event.up ) {
+ /* Unregister the keycode only */
+ for (i = 0; i < rfi->pressed_keys->len; i++) {
+ rdp_event_2 = g_array_index(rfi->pressed_keys, RemminaPluginRdpEvent, i);
+
+ if (rdp_event_2.key_event.key_code == rdp_event.key_event.key_code &&
+ rdp_event_2.key_event.unicode_code == rdp_event.key_event.unicode_code &&
+ rdp_event_2.key_event.extended == rdp_event.key_event.extended) {
+ g_array_remove_index_fast(rfi->pressed_keys, i);
+ break;
+ }
+ }
+ }
+}
+
+static void keypress_list_add(RemminaProtocolWidget *gp, RemminaPluginRdpEvent rdp_event)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ if (!rdp_event.key_event.key_code)
+ return;
+
+ if (rdp_event.key_event.up) {
+ remmina_rdp_event_release_key(gp, rdp_event);
+ } else {
+ g_array_append_val(rfi->pressed_keys, rdp_event);
+ }
+
+}
+
+
+static void remmina_rdp_event_scale_area(RemminaProtocolWidget* gp, gint* x, gint* y, gint* w, gint* h)
+{
+ TRACE_CALL(__func__);
+ gint width, height;
+ gint sx, sy, sw, sh;
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+
+ if (!rfi || !rfi->connected || rfi->is_reconnecting || !rfi->surface)
+ return;
+
+ width = remmina_plugin_service->protocol_plugin_get_width(gp);
+ height = remmina_plugin_service->protocol_plugin_get_height(gp);
+
+ if ((width == 0) || (height == 0))
+ return;
+
+ if ((rfi->scale_width == width) && (rfi->scale_height == height)) {
+ /* Same size, just copy the pixels */
+ *x = MIN(MAX(0, *x), width - 1);
+ *y = MIN(MAX(0, *y), height - 1);
+ *w = MIN(width - *x, *w);
+ *h = MIN(height - *y, *h);
+ return;
+ }
+
+ /* We have to extend the scaled region one scaled pixel, to avoid gaps */
+
+ sx = MIN(MAX(0, (*x) * rfi->scale_width / width
+ - rfi->scale_width / width - 2), rfi->scale_width - 1);
+
+ sy = MIN(MAX(0, (*y) * rfi->scale_height / height
+ - rfi->scale_height / height - 2), rfi->scale_height - 1);
+
+ sw = MIN(rfi->scale_width - sx, (*w) * rfi->scale_width / width
+ + rfi->scale_width / width + 4);
+
+ sh = MIN(rfi->scale_height - sy, (*h) * rfi->scale_height / height
+ + rfi->scale_height / height + 4);
+
+ *x = sx;
+ *y = sy;
+ *w = sw;
+ *h = sh;
+}
+
+void remmina_rdp_event_update_region(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ gint x, y, w, h;
+
+ x = ui->region.x;
+ y = ui->region.y;
+ w = ui->region.width;
+ h = ui->region.height;
+
+ if (rfi->scale == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED)
+ remmina_rdp_event_scale_area(gp, &x, &y, &w, &h);
+
+ gtk_widget_queue_draw_area(rfi->drawing_area, x, y, w, h);
+}
+
+void remmina_rdp_event_update_rect(RemminaProtocolWidget* gp, gint x, gint y, gint w, gint h)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+
+ if (rfi->scale == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED)
+ remmina_rdp_event_scale_area(gp, &x, &y, &w, &h);
+
+ gtk_widget_queue_draw_area(rfi->drawing_area, x, y, w, h);
+}
+
+static void remmina_rdp_event_update_scale_factor(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ GtkAllocation a;
+ gint rdwidth, rdheight;
+ gint gpwidth, gpheight;
+ RemminaFile* remminafile;
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+
+ remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+
+ gtk_widget_get_allocation(GTK_WIDGET(gp), &a);
+ gpwidth = a.width;
+ gpheight = a.height;
+
+ if (rfi->scale == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED) {
+ if ((gpwidth > 1) && (gpheight > 1)) {
+ rdwidth = remmina_plugin_service->protocol_plugin_get_width(gp);
+ rdheight = remmina_plugin_service->protocol_plugin_get_height(gp);
+
+ rfi->scale_width = gpwidth;
+ rfi->scale_height = gpheight;
+
+ rfi->scale_x = (gdouble)rfi->scale_width / (gdouble)rdwidth;
+ rfi->scale_y = (gdouble)rfi->scale_height / (gdouble)rdheight;
+ }
+ }else {
+ rfi->scale_width = 0;
+ rfi->scale_height = 0;
+ rfi->scale_x = 0;
+ rfi->scale_y = 0;
+ }
+
+}
+
+static gboolean remmina_rdp_event_on_draw(GtkWidget* widget, cairo_t* context, RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ guint width, height;
+ gchar *msg;
+ cairo_text_extents_t extents;
+
+ if (!rfi || !rfi->connected)
+ return FALSE;
+
+
+ if (rfi->is_reconnecting) {
+ /* freerdp is reconnecting, just show a message to the user */
+
+ width = gtk_widget_get_allocated_width(widget);
+ height = gtk_widget_get_allocated_height(widget);
+
+ /* Draw text */
+ msg = g_strdup_printf(_("Reconnection in progress. Attempt %d of %d..."),
+ rfi->reconnect_nattempt, rfi->reconnect_maxattempts);
+
+ cairo_select_font_face(context, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_font_size(context, 24);
+ cairo_set_source_rgb(context, 0.9, 0.9, 0.9);
+ cairo_text_extents(context, msg, &extents);
+ cairo_move_to(context, (width - (extents.width + extents.x_bearing)) / 2, (height - (extents.height + extents.y_bearing)) / 2);
+ cairo_show_text(context, msg);
+ g_free(msg);
+
+ }else {
+ /* Standard drawing: we copy the surface from RDP */
+
+ if (!rfi->surface)
+ return FALSE;
+
+ GtkAllocation a;
+ gtk_widget_get_allocation(GTK_WIDGET(gp), &a);
+
+ if (rfi->scale == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED)
+ cairo_scale(context, rfi->scale_x, rfi->scale_y);
+
+ cairo_set_source_surface(context, rfi->surface, 0, 0);
+
+ cairo_set_operator(context, CAIRO_OPERATOR_SOURCE); // Ignore alpha channel from FreeRDP
+ cairo_paint(context);
+ }
+
+ return TRUE;
+}
+
+static gboolean remmina_rdp_event_delayed_monitor_layout(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ RemminaPluginRdpEvent rdp_event = { 0 };
+ GtkAllocation a;
+ RemminaFile* remminafile;
+ gint desktopOrientation, desktopScaleFactor, deviceScaleFactor;
+
+ if (!rfi || !rfi->connected || rfi->is_reconnecting)
+ return FALSE;
+
+ if (rfi->scale != REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES)
+ return FALSE;
+
+ rfi->delayed_monitor_layout_handler = 0;
+ gint gpwidth, gpheight, prevwidth, prevheight;
+
+ if (rfi->dispcontext && rfi->dispcontext->SendMonitorLayout) {
+ remmina_rdp_settings_get_orientation_scale_prefs(&desktopOrientation, &desktopScaleFactor, &deviceScaleFactor);
+ gtk_widget_get_allocation(GTK_WIDGET(gp), &a);
+ gpwidth = a.width;
+ gpheight = a.height;
+ prevwidth = remmina_plugin_service->protocol_plugin_get_width(gp);
+ prevheight = remmina_plugin_service->protocol_plugin_get_height(gp);
+ remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+
+ if ((gpwidth != prevwidth || gpheight != prevheight) &&
+ gpwidth >= 200 && gpwidth < 8192 &&
+ gpheight >= 200 && gpheight < 8192
+ ) {
+ rdp_event.type = REMMINA_RDP_EVENT_TYPE_SEND_MONITOR_LAYOUT;
+ rdp_event.monitor_layout.width = gpwidth;
+ rdp_event.monitor_layout.height = gpheight;
+ rdp_event.monitor_layout.desktopOrientation = desktopOrientation;
+ rdp_event.monitor_layout.desktopScaleFactor = desktopScaleFactor;
+ rdp_event.monitor_layout.deviceScaleFactor = deviceScaleFactor;
+ remmina_rdp_event_event_push(gp, &rdp_event);
+ remmina_plugin_service->file_set_int(remminafile, "dynamic_resolution_width", gpwidth);
+ remmina_plugin_service->file_set_int(remminafile, "dynamic_resolution_height", gpheight);
+ }
+ }
+
+ return FALSE;
+}
+
+void remmina_rdp_event_send_delayed_monitor_layout(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ if (!rfi || !rfi->connected || rfi->is_reconnecting)
+ return;
+ if (rfi->delayed_monitor_layout_handler) {
+ g_source_remove(rfi->delayed_monitor_layout_handler);
+ rfi->delayed_monitor_layout_handler = 0;
+ }
+ if (rfi->scale == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES) {
+ rfi->delayed_monitor_layout_handler = g_timeout_add(500, (GSourceFunc)remmina_rdp_event_delayed_monitor_layout, gp);
+ }
+
+}
+
+static gboolean remmina_rdp_event_on_configure(GtkWidget* widget, GdkEventConfigure* event, RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ /* Called when gp changes its size or position */
+
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ if (!rfi || !rfi->connected || rfi->is_reconnecting)
+ return FALSE;
+
+ remmina_rdp_event_update_scale_factor(gp);
+
+ /* If the scaler is not active, schedule a delayed remote resolution change */
+ remmina_rdp_event_send_delayed_monitor_layout(gp);
+
+
+ return FALSE;
+}
+
+static void remmina_rdp_event_translate_pos(RemminaProtocolWidget* gp, int ix, int iy, UINT16* ox, UINT16* oy)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+
+ /*
+ * Translate a position from local window coordinates (ix,iy) to
+ * RDP coordinates and put result on (*ox,*uy)
+ * */
+
+ if (!rfi || !rfi->connected || rfi->is_reconnecting)
+ return;
+
+ if ((rfi->scale == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED) && (rfi->scale_width >= 1) && (rfi->scale_height >= 1)) {
+ *ox = (UINT16)(ix * remmina_plugin_service->protocol_plugin_get_width(gp) / rfi->scale_width);
+ *oy = (UINT16)(iy * remmina_plugin_service->protocol_plugin_get_height(gp) / rfi->scale_height);
+ }else {
+ *ox = (UINT16)ix;
+ *oy = (UINT16)iy;
+ }
+}
+
+static void remmina_rdp_event_reverse_translate_pos_reverse(RemminaProtocolWidget* gp, int ix, int iy, int* ox, int* oy)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+
+ /*
+ * Translate a position from RDP coordinates (ix,iy) to
+ * local window coordinates and put result on (*ox,*uy)
+ * */
+
+ if (!rfi || !rfi->connected || rfi->is_reconnecting)
+ return;
+
+ if ((rfi->scale == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED) && (rfi->scale_width >= 1) && (rfi->scale_height >= 1)) {
+ *ox = (ix * rfi->scale_width) / remmina_plugin_service->protocol_plugin_get_width(gp);
+ *oy = (iy * rfi->scale_height) / remmina_plugin_service->protocol_plugin_get_height(gp);
+ }else {
+ *ox = ix;
+ *oy = iy;
+ }
+}
+
+static gboolean remmina_rdp_event_on_motion(GtkWidget* widget, GdkEventMotion* event, RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ RemminaPluginRdpEvent rdp_event = { 0 };
+
+ rdp_event.type = REMMINA_RDP_EVENT_TYPE_MOUSE;
+ rdp_event.mouse_event.flags = PTR_FLAGS_MOVE;
+ rdp_event.mouse_event.extended = FALSE;
+
+ remmina_rdp_event_translate_pos(gp, event->x, event->y, &rdp_event.mouse_event.x, &rdp_event.mouse_event.y);
+ remmina_rdp_event_event_push(gp, &rdp_event);
+
+ return TRUE;
+}
+
+static gboolean remmina_rdp_event_on_button(GtkWidget* widget, GdkEventButton* event, RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ gint flag;
+ gboolean extended = FALSE;
+ RemminaPluginRdpEvent rdp_event = { 0 };
+
+ /* We bypass 2button-press and 3button-press events */
+ if ((event->type != GDK_BUTTON_PRESS) && (event->type != GDK_BUTTON_RELEASE))
+ return TRUE;
+
+ flag = 0;
+
+ switch (event->button) {
+ case 1:
+ flag |= PTR_FLAGS_BUTTON1;
+ break;
+ case 2:
+ flag |= PTR_FLAGS_BUTTON3;
+ break;
+ case 3:
+ flag |= PTR_FLAGS_BUTTON2;
+ break;
+ case 8: /* back */
+ case 97: /* Xming */
+ extended = TRUE;
+ flag |= PTR_XFLAGS_BUTTON1;
+ break;
+ case 9: /* forward */
+ case 112: /* Xming */
+ extended = TRUE;
+ flag |= PTR_XFLAGS_BUTTON2;
+ break;
+ default:
+ return FALSE;
+ }
+
+ if (event->type == GDK_BUTTON_PRESS) {
+ if (extended)
+ flag |= PTR_XFLAGS_DOWN;
+ else
+ flag |= PTR_FLAGS_DOWN;
+ }
+
+ rdp_event.type = REMMINA_RDP_EVENT_TYPE_MOUSE;
+ remmina_rdp_event_translate_pos(gp, event->x, event->y, &rdp_event.mouse_event.x, &rdp_event.mouse_event.y);
+
+ if (flag != 0) {
+ rdp_event.mouse_event.flags = flag;
+ rdp_event.mouse_event.extended = extended;
+ remmina_rdp_event_event_push(gp, &rdp_event);
+ }
+
+ return TRUE;
+}
+
+static gboolean remmina_rdp_event_on_scroll(GtkWidget* widget, GdkEventScroll* event, RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ gint flag;
+ RemminaPluginRdpEvent rdp_event = { 0 };
+
+ flag = 0;
+ rdp_event.type = REMMINA_RDP_EVENT_TYPE_MOUSE;
+
+ switch (event->direction) {
+ case GDK_SCROLL_UP:
+ flag = PTR_FLAGS_WHEEL | 0x0078;
+ break;
+
+ case GDK_SCROLL_DOWN:
+ flag = PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x0088;
+ break;
+
+#ifdef GDK_SCROLL_SMOOTH
+ case GDK_SCROLL_SMOOTH:
+ if (event->delta_y < 0)
+ flag = PTR_FLAGS_WHEEL | 0x0078;
+ if (event->delta_y > 0)
+ flag = PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x0088;
+ if (!flag)
+ return FALSE;
+ break;
+#endif
+
+ default:
+ return FALSE;
+ }
+
+ rdp_event.mouse_event.flags = flag;
+ rdp_event.mouse_event.extended = FALSE;
+ remmina_rdp_event_translate_pos(gp, event->x, event->y, &rdp_event.mouse_event.x, &rdp_event.mouse_event.y);
+ remmina_rdp_event_event_push(gp, &rdp_event);
+
+ return TRUE;
+}
+
+static gboolean remmina_rdp_event_on_key(GtkWidget* widget, GdkEventKey* event, RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ GdkDisplay* display;
+ guint32 unicode_keyval;
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ RemminaPluginRdpEvent rdp_event;
+ DWORD scancode = 0;
+
+ if (!rfi || !rfi->connected || rfi->is_reconnecting)
+ return FALSE;
+
+#ifdef ENABLE_GTK_INSPECTOR_KEY
+ /* GTK inspector key is propagated up. Disabled by default.
+ * enable it by defining ENABLE_GTK_INSPECTOR_KEY */
+ if ( ( event->state & GDK_CONTROL_MASK ) != 0 && ( event->keyval == GDK_KEY_I || event->keyval == GDK_KEY_D ) ) {
+ return FALSE;
+ }
+#endif
+
+ rdp_event.type = REMMINA_RDP_EVENT_TYPE_SCANCODE;
+ rdp_event.key_event.up = (event->type == GDK_KEY_PRESS ? False : True);
+ rdp_event.key_event.extended = False;
+
+ switch (event->keyval) {
+ case GDK_KEY_Pause:
+ /*
+ * See https://msdn.microsoft.com/en-us/library/cc240584.aspx
+ * 2.2.8.1.1.3.1.1.1 Keyboard Event (TS_KEYBOARD_EVENT)
+ * for pause key management
+ */
+ rdp_event.key_event.key_code = 0x1D;
+ rdp_event.key_event.up = False;
+ remmina_rdp_event_event_push(gp, &rdp_event);
+ rdp_event.key_event.key_code = 0x45;
+ rdp_event.key_event.up = False;
+ remmina_rdp_event_event_push(gp, &rdp_event);
+ rdp_event.key_event.key_code = 0x1D;
+ rdp_event.key_event.up = True;
+ remmina_rdp_event_event_push(gp, &rdp_event);
+ rdp_event.key_event.key_code = 0x45;
+ rdp_event.key_event.up = True;
+ remmina_rdp_event_event_push(gp, &rdp_event);
+ break;
+
+ default:
+ if (!rfi->use_client_keymap) {
+ scancode = freerdp_keyboard_get_rdp_scancode_from_x11_keycode(event->hardware_keycode);
+ rdp_event.key_event.key_code = scancode & 0xFF;
+ rdp_event.key_event.extended = scancode & 0x100;
+ if (rdp_event.key_event.key_code) {
+ remmina_rdp_event_event_push(gp, &rdp_event);
+ keypress_list_add(gp, rdp_event);
+ }
+ } else {
+ display = gtk_widget_get_display(widget);
+ unicode_keyval = gdk_keyval_to_unicode(event->keyval);
+ /* Decide when whe should send a keycode or a unicode character.
+ * - All non char keys (shift, alt, win) should be sent as keycode
+ * - Space should be sent as keycode (see issue #1364)
+ * - All special keys (F1-F10, numeric pad, home/end/arrows/pgup/pgdn/ins/del) keycode
+ * - All key pressed while CTRL or ALT or WIN is down are not decoded by gdk_keyval_to_unicode(), so send it as keycode
+ * - All keycodes not translatable to unicode chars, as keycode
+ * - The rest as unicode char
+ */
+ if (event->keyval >= 0xfe00 || // arrows, shift, alt, Fn, num keypad...
+ event->hardware_keycode == 0x41 || // space bar
+ unicode_keyval == 0 || // impossible to translate
+ (event->state & (GDK_MOD1_MASK | GDK_CONTROL_MASK | GDK_SUPER_MASK)) != 0 // a modifier not recognized by gdk_keyval_to_unicode()
+ ) {
+ scancode = freerdp_keyboard_get_rdp_scancode_from_x11_keycode(event->hardware_keycode);
+ rdp_event.key_event.key_code = scancode & 0xFF;
+ rdp_event.key_event.extended = scancode & 0x100;
+ if (rdp_event.key_event.key_code) {
+ remmina_rdp_event_event_push(gp, &rdp_event);
+ keypress_list_add(gp, rdp_event);
+ }
+ } else {
+ rdp_event.type = REMMINA_RDP_EVENT_TYPE_SCANCODE_UNICODE;
+ rdp_event.key_event.unicode_code = unicode_keyval;
+ rdp_event.key_event.extended = False;
+ remmina_rdp_event_event_push(gp, &rdp_event);
+ keypress_list_add(gp, rdp_event);
+ }
+ }
+ break;
+ }
+
+ return TRUE;
+}
+
+gboolean remmina_rdp_event_on_clipboard(GtkClipboard *gtkClipboard, GdkEvent *event, RemminaProtocolWidget *gp)
+{
+ /* Signal handler for GTK clipboard owner-change */
+ TRACE_CALL(__func__);
+ RemminaPluginRdpEvent rdp_event = { 0 };
+ CLIPRDR_FORMAT_LIST* pFormatList;
+
+ /* Usually "owner-change" is fired when a user pres "COPY" on the client
+ * OR when this plugin calls gtk_clipboard_set_with_owner()
+ * after receivina a RDP server format list in remmina_rdp_cliprdr_server_format_list()
+ * In the latter case, we must ignore owner change */
+
+ if (gtk_clipboard_get_owner(gtkClipboard) != (GObject*)gp) {
+ pFormatList = remmina_rdp_cliprdr_get_client_format_list(gp);
+ rdp_event.type = REMMINA_RDP_EVENT_TYPE_CLIPBOARD_SEND_CLIENT_FORMAT_LIST;
+ rdp_event.clipboard_formatlist.pFormatList = pFormatList;
+ remmina_rdp_event_event_push(gp, &rdp_event);
+ }
+ return TRUE;
+}
+
+void remmina_rdp_event_init(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ gchar* s;
+ gint flags;
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ GtkClipboard* clipboard;
+
+ if (!rfi) return;
+
+ rfi->drawing_area = gtk_drawing_area_new();
+ gtk_widget_show(rfi->drawing_area);
+ gtk_container_add(GTK_CONTAINER(gp), rfi->drawing_area);
+
+ gtk_widget_add_events(rfi->drawing_area, GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_SCROLL_MASK | GDK_FOCUS_CHANGE_MASK);
+ gtk_widget_set_can_focus(rfi->drawing_area, TRUE);
+
+ remmina_plugin_service->protocol_plugin_register_hostkey(gp, rfi->drawing_area);
+
+ s = remmina_plugin_service->pref_get_value("rdp_use_client_keymap");
+ rfi->use_client_keymap = (s && s[0] == '1' ? TRUE : FALSE);
+ g_free(s);
+
+ g_signal_connect(G_OBJECT(rfi->drawing_area), "draw",
+ G_CALLBACK(remmina_rdp_event_on_draw), gp);
+ g_signal_connect(G_OBJECT(rfi->drawing_area), "configure-event",
+ G_CALLBACK(remmina_rdp_event_on_configure), gp);
+ g_signal_connect(G_OBJECT(rfi->drawing_area), "motion-notify-event",
+ G_CALLBACK(remmina_rdp_event_on_motion), gp);
+ g_signal_connect(G_OBJECT(rfi->drawing_area), "button-press-event",
+ G_CALLBACK(remmina_rdp_event_on_button), gp);
+ g_signal_connect(G_OBJECT(rfi->drawing_area), "button-release-event",
+ G_CALLBACK(remmina_rdp_event_on_button), gp);
+ g_signal_connect(G_OBJECT(rfi->drawing_area), "scroll-event",
+ G_CALLBACK(remmina_rdp_event_on_scroll), gp);
+ g_signal_connect(G_OBJECT(rfi->drawing_area), "key-press-event",
+ G_CALLBACK(remmina_rdp_event_on_key), gp);
+ g_signal_connect(G_OBJECT(rfi->drawing_area), "key-release-event",
+ G_CALLBACK(remmina_rdp_event_on_key), gp);
+ g_signal_connect(G_OBJECT(rfi->drawing_area), "focus-in-event",
+ G_CALLBACK(remmina_rdp_event_on_focus_in), gp);
+
+ RemminaFile* remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+ if (!remmina_plugin_service->file_get_int(remminafile, "disableclipboard", FALSE)) {
+ clipboard = gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD);
+ rfi->clipboard.clipboard_handler = g_signal_connect(clipboard, "owner-change", G_CALLBACK(remmina_rdp_event_on_clipboard), gp);
+ }
+
+ rfi->pressed_keys = g_array_new(FALSE, TRUE, sizeof(RemminaPluginRdpEvent));
+ rfi->event_queue = g_async_queue_new_full(g_free);
+ rfi->ui_queue = g_async_queue_new();
+ pthread_mutex_init(&rfi->ui_queue_mutex, NULL);
+
+ if (pipe(rfi->event_pipe)) {
+ g_print("Error creating pipes.\n");
+ rfi->event_pipe[0] = -1;
+ rfi->event_pipe[1] = -1;
+ rfi->event_handle = NULL;
+ }else {
+ flags = fcntl(rfi->event_pipe[0], F_GETFL, 0);
+ fcntl(rfi->event_pipe[0], F_SETFL, flags | O_NONBLOCK);
+ rfi->event_handle = CreateFileDescriptorEvent(NULL, FALSE, FALSE, rfi->event_pipe[0], WINPR_FD_READ);
+ if (!rfi->event_handle) {
+ g_print("CreateFileDescriptorEvent() failed\n");
+ }
+ }
+
+ rfi->object_table = g_hash_table_new_full(NULL, NULL, NULL, g_free);
+
+ rfi->display = gdk_display_get_default();
+ rfi->bpp = gdk_visual_get_best_depth();
+}
+
+
+
+void remmina_rdp_event_free_event(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* obj)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+
+ switch (obj->type) {
+ case REMMINA_RDP_UI_RFX:
+ rfx_message_free(rfi->rfx_context, obj->rfx.message);
+ break;
+
+ case REMMINA_RDP_UI_NOCODEC:
+ free(obj->nocodec.bitmap);
+ break;
+
+ default:
+ break;
+ }
+
+ g_free(obj);
+}
+
+
+void remmina_rdp_event_uninit(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ RemminaPluginRdpUiObject* ui;
+
+ if (!rfi) return;
+
+ /* unregister the clipboard monitor */
+ if (rfi->clipboard.clipboard_handler) {
+ g_signal_handler_disconnect(G_OBJECT(gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD)), rfi->clipboard.clipboard_handler);
+ rfi->clipboard.clipboard_handler = 0;
+ }
+ if (rfi->delayed_monitor_layout_handler) {
+ g_source_remove(rfi->delayed_monitor_layout_handler);
+ rfi->delayed_monitor_layout_handler = 0;
+ }
+ if (rfi->ui_handler) {
+ g_source_remove(rfi->ui_handler);
+ rfi->ui_handler = 0;
+ }
+ while ((ui = (RemminaPluginRdpUiObject*)g_async_queue_try_pop(rfi->ui_queue)) != NULL) {
+ remmina_rdp_event_free_event(gp, ui);
+ }
+ if (rfi->surface) {
+ cairo_surface_destroy(rfi->surface);
+ rfi->surface = NULL;
+ }
+
+ g_hash_table_destroy(rfi->object_table);
+
+ g_array_free(rfi->pressed_keys, TRUE);
+ g_async_queue_unref(rfi->event_queue);
+ rfi->event_queue = NULL;
+ g_async_queue_unref(rfi->ui_queue);
+ rfi->ui_queue = NULL;
+ pthread_mutex_destroy(&rfi->ui_queue_mutex);
+
+ if (rfi->event_handle) {
+ CloseHandle(rfi->event_handle);
+ rfi->event_handle = NULL;
+ }
+
+ close(rfi->event_pipe[0]);
+ close(rfi->event_pipe[1]);
+}
+
+static void remmina_rdp_event_create_cairo_surface(rfContext* rfi)
+{
+ int stride;
+ rdpGdi* gdi;
+
+ gdi = ((rdpContext *)rfi)->gdi;
+ if (!rfi || !gdi)
+ return;
+
+ if (rfi->surface) {
+ cairo_surface_destroy(rfi->surface);
+ rfi->surface = NULL;
+ }
+ stride = cairo_format_stride_for_width(rfi->cairo_format, rfi->width);
+ rfi->surface = cairo_image_surface_create_for_data((unsigned char*)gdi->primary_buffer, rfi->cairo_format, rfi->width, rfi->height, stride);
+}
+
+void remmina_rdp_event_update_scale(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ gint width, height;
+ RemminaFile* remminafile;
+ rdpGdi* gdi;
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+
+ remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+ width = remmina_plugin_service->protocol_plugin_get_width(gp);
+ height = remmina_plugin_service->protocol_plugin_get_height(gp);
+
+ rfi->scale = remmina_plugin_service->remmina_protocol_widget_get_current_scale_mode(gp);
+
+ /* See if we also must rellocate rfi->surface with different width and height,
+ * this usually happens after a DesktopResize RDP event*/
+ if ( rfi->surface && (width != cairo_image_surface_get_width(rfi->surface) ||
+ height != cairo_image_surface_get_height(rfi->surface) )) {
+ /* Destroys and recreate rfi->surface with new width and height,
+ * calls gdi_resize and save new gdi->primary buffer pointer */
+ if (rfi->surface) {
+ cairo_surface_destroy(rfi->surface);
+ rfi->surface = NULL;
+ }
+ rfi->width = width;
+ rfi->height = height;
+ gdi = ((rdpContext*)rfi)->gdi;
+ gdi_resize(gdi, width, height);
+ rfi->primary_buffer = gdi->primary_buffer;
+ remmina_rdp_event_create_cairo_surface(rfi);
+ }
+
+ remmina_rdp_event_update_scale_factor(gp);
+
+ if (rfi->scale == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED || rfi->scale == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES) {
+ /* In scaled mode and autores mode, drawing_area will get its dimensions from its parent */
+ gtk_widget_set_size_request(rfi->drawing_area, -1, -1 );
+ }else {
+ /* In non scaled mode, the plugins forces dimensions of drawing area */
+ gtk_widget_set_size_request(rfi->drawing_area, width, height);
+ }
+ remmina_plugin_service->protocol_plugin_emit_signal(gp, "update-align");
+}
+
+static void remmina_rdp_event_connected(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+
+ remmina_plugin_service->protocol_plugin_emit_signal(gp, "connect");
+ gtk_widget_realize(rfi->drawing_area);
+
+ remmina_rdp_event_create_cairo_surface(rfi);
+ gtk_widget_queue_draw_area(rfi->drawing_area, 0, 0, rfi->width, rfi->height);
+
+ remmina_rdp_event_update_scale(gp);
+}
+
+static void remmina_rdp_event_reconnect_progress(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ gdk_window_invalidate_rect(gtk_widget_get_window(rfi->drawing_area), NULL, TRUE);
+}
+
+static BOOL remmina_rdp_event_create_cursor(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
+{
+ TRACE_CALL(__func__);
+ GdkPixbuf* pixbuf;
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ rdpPointer* pointer = (rdpPointer*)ui->cursor.pointer;
+ cairo_surface_t* surface;
+ UINT8* data = malloc(pointer->width * pointer->height * 4);
+
+ if (freerdp_image_copy_from_pointer_data(
+ (BYTE*)data, PIXEL_FORMAT_BGRA32,
+ pointer->width * 4, 0, 0, pointer->width, pointer->height,
+ pointer->xorMaskData, pointer->lengthXorMask,
+ pointer->andMaskData, pointer->lengthAndMask,
+ pointer->xorBpp, &(ui->cursor.context->gdi->palette)) < 0) {
+ free(data);
+ return FALSE;
+ }
+
+ surface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, pointer->width, pointer->height, cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, pointer->width));
+ pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, pointer->width, pointer->height);
+ cairo_surface_destroy(surface);
+ free(data);
+ ((rfPointer*)ui->cursor.pointer)->cursor = gdk_cursor_new_from_pixbuf(rfi->display, pixbuf, pointer->xPos, pointer->yPos);
+ g_object_unref(pixbuf);
+
+ return TRUE;
+}
+
+static void remmina_rdp_event_free_cursor(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
+{
+ TRACE_CALL(__func__);
+ g_object_unref(ui->cursor.pointer->cursor);
+ ui->cursor.pointer->cursor = NULL;
+}
+
+static BOOL remmina_rdp_event_set_pointer_position(RemminaProtocolWidget *gp, gint x, gint y)
+{
+ TRACE_CALL(__func__);
+ GdkWindow *w, *nw;
+ gint nx, ny, wx, wy;
+#if GTK_CHECK_VERSION(3, 20, 0)
+ GdkSeat *seat;
+#else
+ GdkDeviceManager *manager;
+#endif
+ GdkDevice *dev;
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+
+ if (rfi == NULL)
+ return FALSE;
+
+ w = gtk_widget_get_window(rfi->drawing_area);
+#if GTK_CHECK_VERSION(3, 20, 0)
+ seat = gdk_display_get_default_seat(gdk_display_get_default());
+ dev = gdk_seat_get_pointer(seat);
+#else
+ manager = gdk_display_get_device_manager(gdk_display_get_default());
+ dev = gdk_device_manager_get_client_pointer(manager);
+#endif
+
+ nw = gdk_device_get_window_at_position(dev, NULL, NULL);
+
+ if (nw == w) {
+ nx = 0;
+ ny = 0;
+ remmina_rdp_event_reverse_translate_pos_reverse(gp, x, y, &nx, &ny);
+ gdk_window_get_root_coords(w, nx, ny, &wx, &wy);
+ gdk_device_warp(dev, gdk_window_get_screen(w), wx, wy);
+ }
+ return TRUE;
+}
+
+static void remmina_rdp_event_cursor(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+
+ switch (ui->cursor.type) {
+ case REMMINA_RDP_POINTER_NEW:
+ ui->retval = remmina_rdp_event_create_cursor(gp, ui) ? 1 : 0;
+ break;
+
+ case REMMINA_RDP_POINTER_FREE:
+ remmina_rdp_event_free_cursor(gp, ui);
+ break;
+
+ case REMMINA_RDP_POINTER_SET:
+ gdk_window_set_cursor(gtk_widget_get_window(rfi->drawing_area), ui->cursor.pointer->cursor);
+ ui->retval = 1;
+ break;
+
+ case REMMINA_RDP_POINTER_SETPOS:
+ ui->retval = remmina_rdp_event_set_pointer_position(gp, ui->pos.x, ui->pos.y) ? 1 : 0;
+ break;
+
+ case REMMINA_RDP_POINTER_NULL:
+ gdk_window_set_cursor(gtk_widget_get_window(rfi->drawing_area),
+ gdk_cursor_new_for_display(gdk_display_get_default(),
+ GDK_BLANK_CURSOR));
+ ui->retval = 1;
+ break;
+
+ case REMMINA_RDP_POINTER_DEFAULT:
+ gdk_window_set_cursor(gtk_widget_get_window(rfi->drawing_area), NULL);
+ ui->retval = 1;
+ break;
+ }
+}
+
+static void remmina_rdp_ui_event_update_scale(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
+{
+ TRACE_CALL(__func__);
+ remmina_rdp_event_update_scale(gp);
+}
+
+void remmina_rdp_event_unfocus(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ remmina_rdp_event_release_all_keys(gp);
+}
+
+static void remmina_rdp_event_process_event(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
+{
+ TRACE_CALL(__func__);
+ switch (ui->event.type) {
+ case REMMINA_RDP_UI_EVENT_UPDATE_SCALE:
+ remmina_rdp_ui_event_update_scale(gp, ui);
+ break;
+ }
+}
+
+static void remmina_rdp_event_process_ui_event(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
+{
+ TRACE_CALL(__func__);
+ switch (ui->type) {
+ case REMMINA_RDP_UI_UPDATE_REGION:
+ remmina_rdp_event_update_region(gp, ui);
+ break;
+
+ case REMMINA_RDP_UI_CONNECTED:
+ remmina_rdp_event_connected(gp, ui);
+ break;
+
+ case REMMINA_RDP_UI_RECONNECT_PROGRESS:
+ remmina_rdp_event_reconnect_progress(gp, ui);
+ break;
+
+ case REMMINA_RDP_UI_CURSOR:
+ remmina_rdp_event_cursor(gp, ui);
+ break;
+
+ case REMMINA_RDP_UI_CLIPBOARD:
+ remmina_rdp_event_process_clipboard(gp, ui);
+ break;
+
+ case REMMINA_RDP_UI_EVENT:
+ remmina_rdp_event_process_event(gp, ui);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static gboolean remmina_rdp_event_process_ui_queue(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ RemminaPluginRdpUiObject* ui;
+
+ pthread_mutex_lock(&rfi->ui_queue_mutex);
+ ui = (RemminaPluginRdpUiObject*)g_async_queue_try_pop(rfi->ui_queue);
+ if (ui) {
+ pthread_mutex_lock(&ui->sync_wait_mutex);
+ if (!rfi->thread_cancelled) {
+ remmina_rdp_event_process_ui_event(gp, ui);
+ }
+ // Should we signal the caller thread to unlock ?
+ if (ui->sync) {
+ ui->complete = TRUE;
+ pthread_cond_signal(&ui->sync_wait_cond);
+ pthread_mutex_unlock(&ui->sync_wait_mutex);
+
+ } else {
+ remmina_rdp_event_free_event(gp, ui);
+ }
+
+ pthread_mutex_unlock(&rfi->ui_queue_mutex);
+ return TRUE;
+ }else {
+ rfi->ui_handler = 0;
+ pthread_mutex_unlock(&rfi->ui_queue_mutex);
+ return FALSE;
+ }
+}
+
+static void remmina_rdp_event_queue_ui(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ gboolean ui_sync_save;
+ int oldcanceltype;
+
+ if (rfi->thread_cancelled) {
+ return;
+ }
+
+ if (remmina_plugin_service->is_main_thread()) {
+ remmina_rdp_event_process_ui_event(gp, ui);
+ return;
+ }
+
+ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldcanceltype);
+
+ pthread_mutex_lock(&rfi->ui_queue_mutex);
+
+ ui_sync_save = ui->sync;
+ ui->complete = FALSE;
+
+ if (ui_sync_save) {
+ pthread_mutex_init(&ui->sync_wait_mutex, NULL);
+ pthread_cond_init(&ui->sync_wait_cond, NULL);
+ }
+
+ ui->complete = FALSE;
+
+ g_async_queue_push(rfi->ui_queue, ui);
+
+ if (!rfi->ui_handler) {
+ rfi->ui_handler = IDLE_ADD((GSourceFunc)remmina_rdp_event_process_ui_queue, gp);
+ }
+
+ if (ui_sync_save) {
+ /* Wait for main thread function completion before returning */
+ pthread_mutex_lock(&ui->sync_wait_mutex);
+ pthread_mutex_unlock(&rfi->ui_queue_mutex);
+ while (!ui->complete) {
+ pthread_cond_wait(&ui->sync_wait_cond, &ui->sync_wait_mutex);
+ }
+ pthread_cond_destroy(&ui->sync_wait_cond);
+ pthread_mutex_destroy(&ui->sync_wait_mutex);
+ } else {
+ pthread_mutex_unlock(&rfi->ui_queue_mutex);
+ }
+ pthread_setcanceltype(oldcanceltype, NULL);
+}
+
+void remmina_rdp_event_queue_ui_async(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
+{
+ ui->sync = FALSE;
+ remmina_rdp_event_queue_ui(gp, ui);
+}
+
+int remmina_rdp_event_queue_ui_sync_retint(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
+{
+ TRACE_CALL(__func__);
+ int retval;
+ ui->sync = TRUE;
+ remmina_rdp_event_queue_ui(gp, ui);
+ retval = ui->retval;
+ remmina_rdp_event_free_event(gp, ui);
+ return retval;
+}
+
+void *remmina_rdp_event_queue_ui_sync_retptr(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui)
+{
+ TRACE_CALL(__func__);
+ void *rp;
+ ui->sync = TRUE;
+ remmina_rdp_event_queue_ui(gp, ui);
+ rp = ui->retptr;
+ remmina_rdp_event_free_event(gp, ui);
+ return rp;
+}
diff --git a/plugins/rdp/rdp_event.h b/plugins/rdp/rdp_event.h
new file mode 100644
index 000000000..f671ef380
--- /dev/null
+++ b/plugins/rdp/rdp_event.h
@@ -0,0 +1,52 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2010-2011 Vic Lee
+ * Copyright (C) 2017 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.
+ *
+ */
+
+#pragma once
+
+G_BEGIN_DECLS
+
+
+void remmina_rdp_event_init(RemminaProtocolWidget* gp);
+void remmina_rdp_event_uninit(RemminaProtocolWidget* gp);
+void remmina_rdp_event_update_scale(RemminaProtocolWidget* gp);
+void remmina_rdp_event_unfocus(RemminaProtocolWidget* gp);
+void remmina_rdp_event_send_delayed_monitor_layout(RemminaProtocolWidget* gp);
+void remmina_rdp_event_update_rect(RemminaProtocolWidget* gp, gint x, gint y, gint w, gint h);
+void remmina_rdp_event_queue_ui_async(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui);
+int remmina_rdp_event_queue_ui_sync_retint(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui);
+void *remmina_rdp_event_queue_ui_sync_retptr(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui);
+
+G_END_DECLS
+
diff --git a/plugins/rdp/rdp_file.c b/plugins/rdp/rdp_file.c
new file mode 100644
index 000000000..8f85d4934
--- /dev/null
+++ b/plugins/rdp/rdp_file.c
@@ -0,0 +1,307 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2010-2011 Vic Lee
+ * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
+ * Copyright (C) 2016-2017 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 "rdp_plugin.h"
+#include "rdp_file.h"
+
+gboolean remmina_rdp_file_import_test(const gchar* from_file)
+{
+ TRACE_CALL(__func__);
+ gchar* ext;
+
+ ext = strrchr(from_file, '.');
+
+ if (!ext)
+ return FALSE;
+
+ ext++;
+
+ if (g_strcmp0(ext, "RDP") == 0)
+ return TRUE;
+
+ if (g_strcmp0(ext, "rdp") == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static void remmina_rdp_file_import_field(RemminaFile* remminafile, const gchar* key, const gchar* value)
+{
+ TRACE_CALL(__func__);
+ if (g_strcmp0(key, "desktopwidth") == 0) {
+ remmina_plugin_service->file_set_string(remminafile, "resolution_width", value);
+ }else if (g_strcmp0(key, "desktopheight") == 0) {
+ remmina_plugin_service->file_set_string(remminafile, "resolution_height", value);
+ }else if (g_strcmp0(key, "session bpp") == 0) {
+ remmina_plugin_service->file_set_string(remminafile, "colordepth", value);
+ }else if (g_strcmp0(key, "keyboardhook") == 0) {
+ remmina_plugin_service->file_set_int(remminafile, "keyboard_grab", (atoi(value) == 1));
+ }else if (g_strcmp0(key, "full address") == 0) {
+ remmina_plugin_service->file_set_string(remminafile, "server", value);
+ }else if (g_strcmp0(key, "audiomode") == 0) {
+ switch (atoi(value)) {
+ case 0:
+ remmina_plugin_service->file_set_string(remminafile, "sound", "local");
+ break;
+ case 1:
+ remmina_plugin_service->file_set_string(remminafile, "sound", "remote");
+ break;
+ }
+ }else if (g_strcmp0(key, "microphone") == 0) {
+ remmina_plugin_service->file_set_int(remminafile, "microphone", (atoi(value) == 1));
+ } else if (g_strcmp0(key, "redirectprinters") == 0) {
+ remmina_plugin_service->file_set_int(remminafile, "shareprinter", (atoi(value) == 1));
+ }else if (g_strcmp0(key, "redirectsmartcard") == 0) {
+ remmina_plugin_service->file_set_int(remminafile, "sharesmartcard", (atoi(value) == 1));
+ }else if (g_strcmp0(key, "redirectclipboard") == 0) {
+ remmina_plugin_service->file_set_int(remminafile, "disableclipboard", (atoi(value) != 1));
+ }else if (g_strcmp0(key, "alternate shell") == 0) {
+ remmina_plugin_service->file_set_string(remminafile, "exec", value);
+ }else if (g_strcmp0(key, "shell working directory") == 0) {
+ remmina_plugin_service->file_set_string(remminafile, "execpath", value);
+ }else if (g_strcmp0(key, "loadbalanceinfo") == 0) {
+ remmina_plugin_service->file_set_string(remminafile, "loadbalanceinfo", value);
+ }else if (g_strcmp0(key, "gatewayhostname") == 0) {
+ remmina_plugin_service->file_set_string(remminafile, "gateway_server", value);
+ }else if (g_strcmp0(key, "gatewayusagemethod") == 0) {
+ remmina_plugin_service->file_set_string(remminafile, "gatewayusagemethod", value);
+ }else if (g_strcmp0(key, "gatewaycredentialssource") == 0) {
+ remmina_plugin_service->file_set_string(remminafile, "gatewaycredentialssource", value);
+ }else if (g_strcmp0(key, "gatewayprofileusagemethod") == 0) {
+ remmina_plugin_service->file_set_string(remminafile, "gatewayprofileusagemethod", value);
+ }
+ /* tsclient fields, import only */
+ else if (g_strcmp0(key, "client hostname") == 0) {
+ remmina_plugin_service->file_set_string(remminafile, "clientname", value);
+ }else if (g_strcmp0(key, "domain") == 0) {
+ remmina_plugin_service->file_set_string(remminafile, "domain", value);
+ }else if (g_strcmp0(key, "username") == 0) {
+ remmina_plugin_service->file_set_string(remminafile, "username", value);
+ }else if (g_strcmp0(key, "password") == 0) {
+ remmina_plugin_service->file_set_string(remminafile, "password", value);
+ }
+}
+
+static RemminaFile* remmina_rdp_file_import_channel(GIOChannel* channel)
+{
+ TRACE_CALL(__func__);
+ gchar* p;
+ const gchar* enc;
+ gchar* line = NULL;
+ GError* error = NULL;
+ gsize bytes_read = 0;
+ RemminaFile* remminafile;
+ guchar magic[2] = { 0 };
+
+ if (g_io_channel_set_encoding(channel, NULL, &error) != G_IO_STATUS_NORMAL) {
+ g_print("g_io_channel_set_encoding: %s\n", error->message);
+ return NULL;
+ }
+
+ /* Try to detect the UTF-16 encoding */
+ if (g_io_channel_read_chars(channel, (gchar*)magic, 2, &bytes_read, &error) != G_IO_STATUS_NORMAL) {
+ g_print("g_io_channel_read_chars: %s\n", error->message);
+ return NULL;
+ }
+
+ if (magic[0] == 0xFF && magic[1] == 0xFE) {
+ enc = "UTF-16LE";
+ }else if (magic[0] == 0xFE && magic[1] == 0xFF) {
+ enc = "UTF-16BE";
+ }else {
+ enc = "UTF-8";
+ if (g_io_channel_seek_position(channel, 0, G_SEEK_SET, &error) != G_IO_STATUS_NORMAL) {
+ g_print("g_io_channel_seek: failed\n");
+ return NULL;
+ }
+ }
+
+ if (g_io_channel_set_encoding(channel, enc, &error) != G_IO_STATUS_NORMAL) {
+ g_print("g_io_channel_set_encoding: %s\n", error->message);
+ return NULL;
+ }
+
+ remminafile = remmina_plugin_service->file_new();
+
+ while (g_io_channel_read_line(channel, &line, NULL, &bytes_read, &error) == G_IO_STATUS_NORMAL) {
+ if (line == NULL)
+ break;
+
+ line[bytes_read] = '\0';
+ p = strchr(line, ':');
+
+ if (p) {
+ *p++ = '\0';
+ p = strchr(p, ':');
+
+ if (p) {
+ p++;
+ remmina_rdp_file_import_field(remminafile, line, p);
+ }
+ }
+
+ g_free(line);
+ }
+
+ remmina_plugin_service->file_set_string(remminafile, "name",
+ remmina_plugin_service->file_get_string(remminafile, "server"));
+ remmina_plugin_service->file_set_string(remminafile, "protocol", "RDP");
+
+ return remminafile;
+}
+
+RemminaFile* remmina_rdp_file_import(const gchar* from_file)
+{
+ TRACE_CALL(__func__);
+ GIOChannel* channel;
+ GError* error = NULL;
+ RemminaFile* remminafile;
+
+ channel = g_io_channel_new_file(from_file, "r", &error);
+
+ if (channel == NULL) {
+ g_print("Failed to import %s: %s\n", from_file, error->message);
+ return NULL;
+ }
+
+ remminafile = remmina_rdp_file_import_channel(channel);
+ g_io_channel_shutdown(channel, TRUE, &error);
+
+ return remminafile;
+}
+
+gboolean remmina_rdp_file_export_test(RemminaFile* remminafile)
+{
+ TRACE_CALL(__func__);
+ if (g_strcmp0(remmina_plugin_service->file_get_string(remminafile, "protocol"), "RDP") == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+gboolean remmina_rdp_file_export_channel(RemminaFile* remminafile, FILE* fp)
+{
+ TRACE_CALL(__func__);
+ const gchar* cs;
+ int w, h;
+
+ fprintf(fp, "screen mode id:i:2\r\n");
+ w = remmina_plugin_service->file_get_int(remminafile, "resolution_width", -1);
+ h = remmina_plugin_service->file_get_int(remminafile, "resolution_height", -1);
+ if ( w > 0 && h > 0 ) {
+ fprintf(fp, "desktopwidth:i:%d\r\n", w);
+ fprintf(fp, "desktopheight:i:%d\r\n", h);
+ }
+
+ fprintf(fp, "session bpp:i:%i\r\n", remmina_plugin_service->file_get_int(remminafile, "colordepth", 8));
+ //fprintf(fp, "winposstr:s:0,1,123,34,931,661\r\n");
+ fprintf(fp, "compression:i:1\r\n");
+ fprintf(fp, "keyboardhook:i:2\r\n");
+ fprintf(fp, "displayconnectionbar:i:1\r\n");
+ fprintf(fp, "disable wallpaper:i:1\r\n");
+ fprintf(fp, "disable full window drag:i:1\r\n");
+ fprintf(fp, "allow desktop composition:i:0\r\n");
+ fprintf(fp, "allow font smoothing:i:0\r\n");
+ fprintf(fp, "disable menu anims:i:1\r\n");
+ fprintf(fp, "disable themes:i:0\r\n");
+ fprintf(fp, "disable cursor setting:i:0\r\n");
+ fprintf(fp, "bitmapcachepersistenable:i:1\r\n");
+ cs = remmina_plugin_service->file_get_string(remminafile, "server");
+ fprintf(fp, "full address:s:%s\r\n", cs ? cs : "" );
+ if (g_strcmp0(remmina_plugin_service->file_get_string(remminafile, "sound"), "local") == 0)
+ fprintf(fp, "audiomode:i:0\r\n");
+ else if (g_strcmp0(remmina_plugin_service->file_get_string(remminafile, "sound"), "remote") == 0)
+ fprintf(fp, "audiomode:i:1\r\n");
+ else
+ fprintf(fp, "audiomode:i:2\r\n");
+ fprintf(fp, "microphone:i:%i\r\n", remmina_plugin_service->file_get_int(remminafile, "microphone", FALSE) ? 1 : 0);
+ fprintf(fp, "redirectprinters:i:%i\r\n", remmina_plugin_service->file_get_int(remminafile, "shareprinter", FALSE) ? 1 : 0);
+ fprintf(fp, "redirectsmartcard:i:%i\r\n", remmina_plugin_service->file_get_int(remminafile, "sharesmartcard", FALSE) ? 1 : 0);
+ fprintf(fp, "redirectcomports:i:0\r\n");
+ fprintf(fp, "redirectsmartcards:i:0\r\n");
+ fprintf(fp, "redirectclipboard:i:1\r\n");
+ fprintf(fp, "redirectposdevices:i:0\r\n");
+ fprintf(fp, "autoreconnection enabled:i:1\r\n");
+ fprintf(fp, "authentication level:i:0\r\n");
+ fprintf(fp, "prompt for credentials:i:1\r\n");
+ fprintf(fp, "negotiate security layer:i:1\r\n");
+ fprintf(fp, "remoteapplicationmode:i:0\r\n");
+ cs = remmina_plugin_service->file_get_string(remminafile, "exec");
+ fprintf(fp, "alternate shell:s:%s\r\n", cs ? cs : "");
+ cs = remmina_plugin_service->file_get_string(remminafile, "execpath");
+ fprintf(fp, "shell working directory:s:%s\r\n", cs ? cs : "");
+ fprintf(fp, "gatewayhostname:s:\r\n");
+ fprintf(fp, "gatewayusagemethod:i:4\r\n");
+ fprintf(fp, "gatewaycredentialssource:i:4\r\n");
+ fprintf(fp, "gatewayprofileusagemethod:i:0\r\n");
+ fprintf(fp, "precommand:s:\r\n");
+ fprintf(fp, "promptcredentialonce:i:1\r\n");
+ fprintf(fp, "drivestoredirect:s:\r\n");
+
+ return TRUE;
+}
+
+gboolean remmina_rdp_file_export(RemminaFile* remminafile, const gchar* to_file)
+{
+ TRACE_CALL(__func__);
+ FILE* fp;
+ gchar* p;
+ gboolean ret;
+
+ p = strrchr(to_file, '.');
+
+ if (p && (g_strcmp0(p + 1, "rdp") == 0 || g_strcmp0(p + 1, "RDP") == 0)) {
+ p = g_strdup(to_file);
+ }else {
+ p = g_strdup_printf("%s.rdp", to_file);
+ }
+
+ fp = g_fopen(p, "w+");
+
+ if (fp == NULL) {
+ g_print("Failed to export %s\n", p);
+ g_free(p);
+ return FALSE;
+ }
+
+ g_free(p);
+ ret = remmina_rdp_file_export_channel(remminafile, fp);
+ fclose(fp);
+
+ return ret;
+}
+
diff --git a/plugins/rdp/rdp_file.h b/plugins/rdp/rdp_file.h
new file mode 100644
index 000000000..9ca04f024
--- /dev/null
+++ b/plugins/rdp/rdp_file.h
@@ -0,0 +1,46 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2010-2011 Vic Lee
+ * Copyright (C) 2017 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.
+ *
+ */
+
+#pragma once
+
+G_BEGIN_DECLS
+
+gboolean remmina_rdp_file_import_test(const gchar* from_file);
+RemminaFile* remmina_rdp_file_import(const gchar* from_file);
+gboolean remmina_rdp_file_export_test(RemminaFile* remminafile);
+gboolean remmina_rdp_file_export(RemminaFile* remminafile, const gchar* to_file);
+
+G_END_DECLS
+
diff --git a/plugins/rdp/rdp_graphics.c b/plugins/rdp/rdp_graphics.c
new file mode 100644
index 000000000..06404a611
--- /dev/null
+++ b/plugins/rdp/rdp_graphics.c
@@ -0,0 +1,435 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2010 Jay Sorg
+ * Copyright (C) 2010-2011 Vic Lee
+ * Copyright (C) 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
+ * Copyright (C) 2016-2017 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 "rdp_plugin.h"
+#include "rdp_event.h"
+#include "rdp_graphics.h"
+
+#include <freerdp/codec/color.h>
+#include <freerdp/codec/bitmap.h>
+#include <winpr/memory.h>
+
+//#define RF_BITMAP
+//#define RF_GLYPH
+
+/* Bitmap Class */
+
+BOOL rf_Bitmap_New(rdpContext* context, rdpBitmap* bitmap)
+{
+ TRACE_CALL(__func__);
+#ifdef RF_BITMAP
+ UINT8* data;
+ Pixmap pixmap;
+ XImage* image;
+ rfContext* rfi = (rfContext*)context;
+
+ XSetFunction(rfi->display, rfi->gc, GXcopy);
+ pixmap = XCreatePixmap(rfi->display, rfi->drawable, bitmap->width, bitmap->height, rfi->depth);
+
+ if (bitmap->data != NULL) {
+ data = freerdp_image_convert(bitmap->data, NULL,
+ bitmap->width, bitmap->height, rfi->srcBpp, rfi->bpp, rfi->clrconv);
+
+ if (bitmap->ephemeral != TRUE) {
+ image = XCreateImage(rfi->display, rfi->visual, rfi->depth,
+ ZPixmap, 0, (char*)data, bitmap->width, bitmap->height, rfi->scanline_pad, 0);
+
+ XPutImage(rfi->display, pixmap, rfi->gc, image, 0, 0, 0, 0, bitmap->width, bitmap->height);
+ XFree(image);
+
+ if (data != bitmap->data) && (data != NULL)
+ free(data);
+ }else {
+ if (data != bitmap->data) && (data != NULL)
+ free(bitmap->data);
+
+ bitmap->data = data;
+ }
+ }
+
+ ((rfBitmap*)bitmap)->pixmap = pixmap;
+#endif
+ return TRUE;
+}
+
+void rf_Bitmap_Free(rdpContext* context, rdpBitmap* bitmap)
+{
+ TRACE_CALL(__func__);
+#ifdef RF_BITMAP
+ rfContext* rfi = (rfContext*)context;
+
+ printf("rf_Bitmap_Free\n");
+
+ if (((rfBitmap*)bitmap)->pixmap != 0)
+ XFreePixmap(rfi->display, ((rfBitmap*)bitmap)->pixmap);
+#endif
+}
+
+BOOL rf_Bitmap_Paint(rdpContext* context, rdpBitmap* bitmap)
+{
+ TRACE_CALL(__func__);
+#ifdef RF_BITMAP
+ XImage* image;
+ int width, height;
+ rfContext* rfi = (rfContext*)context;
+
+ printf("rf_Bitmap_Paint\n");
+
+ width = bitmap->right - bitmap->left + 1;
+ height = bitmap->bottom - bitmap->top + 1;
+
+ XSetFunction(rfi->display, rfi->gc, GXcopy);
+
+ image = XCreateImage(rfi->display, rfi->visual, rfi->depth,
+ ZPixmap, 0, (char*)bitmap->data, bitmap->width, bitmap->height, rfi->scanline_pad, 0);
+
+ XPutImage(rfi->display, rfi->primary, rfi->gc,
+ image, 0, 0, bitmap->left, bitmap->top, width, height);
+
+ XFree(image);
+
+ //XCopyArea(rfi->display, rfi->primary, rfi->drawable, rfi->gc,
+ // bitmap->left, bitmap->top, width, height, bitmap->left, bitmap->top);
+
+ //gdi_InvalidateRegion(rfi->hdc, bitmap->left, bitmap->top, width, height);
+#endif
+ return FALSE;
+}
+
+BOOL rf_Bitmap_Decompress(rdpContext* context, rdpBitmap* bitmap,
+ const BYTE* data, UINT32 width, UINT32 height, UINT32 bpp, UINT32 length, BOOL compressed, UINT32 codec_id)
+{
+ TRACE_CALL(__func__);
+#ifdef RF_BITMAP
+ UINT16 size;
+
+ printf("rf_Bitmap_Decompress\n");
+
+ size = width * height * (bpp + 7) / 8;
+
+ if (bitmap->data == NULL)
+ bitmap->data = (UINT8*)xmalloc(size);
+ else
+ bitmap->data = (UINT8*)xrealloc(bitmap->data, size);
+
+ if (compressed) {
+ BOOL status;
+
+ status = bitmap_decompress(data, bitmap->data, width, height, length, bpp, bpp);
+
+ if (status != TRUE) {
+ printf("Bitmap Decompression Failed\n");
+ }
+ }else {
+ freerdp_image_flip(data, bitmap->data, width, height, bpp);
+ }
+
+ bitmap->compressed = FALSE;
+ bitmap->length = size;
+ bitmap->bpp = bpp;
+#endif
+ return TRUE;
+}
+
+BOOL rf_Bitmap_SetSurface(rdpContext* context, rdpBitmap* bitmap, BOOL primary)
+{
+ TRACE_CALL(__func__);
+#ifdef RF_BITMAP
+ rfContext* rfi = (rfContext*)context;
+
+ if (primary)
+ rfi->drawing = rfi->primary;
+ else
+ rfi->drawing = ((rfBitmap*)bitmap)->pixmap;
+#endif
+ return TRUE;
+}
+
+/* Pointer Class */
+
+BOOL rf_Pointer_New(rdpContext* context, rdpPointer* pointer)
+{
+ TRACE_CALL(__func__);
+ RemminaPluginRdpUiObject* ui;
+ rfContext* rfi = (rfContext*)context;
+
+ if ((pointer->andMaskData != 0) && (pointer->xorMaskData != 0)) {
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_CURSOR;
+ ui->cursor.context = context;
+ ui->cursor.pointer = (rfPointer*)pointer;
+ ui->cursor.type = REMMINA_RDP_POINTER_NEW;
+ return remmina_rdp_event_queue_ui_sync_retint(rfi->protocol_widget, ui) ? TRUE : FALSE;
+ }
+ return FALSE;
+}
+
+void rf_Pointer_Free(rdpContext* context, rdpPointer* pointer)
+{
+ TRACE_CALL(__func__);
+ RemminaPluginRdpUiObject* ui;
+ rfContext* rfi = (rfContext*)context;
+
+#if GTK_VERSION == 2
+ if (((rfPointer*)pointer)->cursor != NULL)
+#else
+ if (G_IS_OBJECT(((rfPointer*)pointer)->cursor))
+#endif
+ {
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_CURSOR;
+ ui->cursor.context = context;
+ ui->cursor.pointer = (rfPointer*)pointer;
+ ui->cursor.type = REMMINA_RDP_POINTER_FREE;
+ remmina_rdp_event_queue_ui_sync_retint(rfi->protocol_widget, ui);
+ }
+}
+
+BOOL rf_Pointer_Set(rdpContext* context, const rdpPointer* pointer)
+{
+ TRACE_CALL(__func__);
+ RemminaPluginRdpUiObject* ui;
+ rfContext* rfi = (rfContext*)context;
+
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_CURSOR;
+ ui->cursor.pointer = (rfPointer*)pointer;
+ ui->cursor.type = REMMINA_RDP_POINTER_SET;
+
+ return remmina_rdp_event_queue_ui_sync_retint(rfi->protocol_widget, ui) ? TRUE : FALSE;
+
+}
+
+BOOL rf_Pointer_SetNull(rdpContext* context)
+{
+ TRACE_CALL(__func__);
+ RemminaPluginRdpUiObject* ui;
+ rfContext* rfi = (rfContext*)context;
+
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_CURSOR;
+ ui->cursor.type = REMMINA_RDP_POINTER_NULL;
+
+ return remmina_rdp_event_queue_ui_sync_retint(rfi->protocol_widget, ui) ? TRUE : FALSE;
+}
+
+BOOL rf_Pointer_SetDefault(rdpContext* context)
+{
+ TRACE_CALL(__func__);
+ RemminaPluginRdpUiObject* ui;
+ rfContext* rfi = (rfContext*)context;
+
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_CURSOR;
+ ui->cursor.type = REMMINA_RDP_POINTER_DEFAULT;
+
+ return remmina_rdp_event_queue_ui_sync_retint(rfi->protocol_widget, ui) ? TRUE : FALSE;
+}
+
+BOOL rf_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y)
+{
+ TRACE_CALL(__func__);
+ RemminaPluginRdpUiObject* ui;
+ rfContext* rfi = (rfContext*)context;
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_CURSOR;
+ ui->cursor.type = REMMINA_RDP_POINTER_SETPOS;
+ ui->pos.x = x;
+ ui->pos.y = y;
+
+ return remmina_rdp_event_queue_ui_sync_retint(rfi->protocol_widget, ui) ? TRUE : FALSE;
+}
+
+/* Glyph Class */
+
+BOOL rf_Glyph_New(rdpContext* context, const rdpGlyph* glyph)
+{
+ TRACE_CALL(__func__);
+#ifdef RF_GLYPH
+ int scanline;
+ XImage* image;
+ rfContext* rfi;
+ rfGlyph* rf_glyph;
+
+ rf_glyph = (rfGlyph*)glyph;
+ rfi = (rfContext*)context;
+
+ scanline = (glyph->cx + 7) / 8;
+
+ rf_glyph->pixmap = XCreatePixmap(rfi->display, rfi->drawing, glyph->cx, glyph->cy, 1);
+
+ image = XCreateImage(rfi->display, rfi->visual, 1,
+ ZPixmap, 0, (char*)glyph->aj, glyph->cx, glyph->cy, 8, scanline);
+
+ image->byte_order = MSBFirst;
+ image->bitmap_bit_order = MSBFirst;
+
+ XInitImage(image);
+ XPutImage(rfi->display, rf_glyph->pixmap, rfi->gc_mono, image, 0, 0, 0, 0, glyph->cx, glyph->cy);
+ XFree(image);
+#endif
+ return TRUE;
+}
+
+void rf_Glyph_Free(rdpContext* context, rdpGlyph* glyph)
+{
+ TRACE_CALL(__func__);
+#ifdef RF_GLYPH
+ rfContext* rfi = (rfContext*)context;
+
+ if (((rfGlyph*)glyph)->pixmap != 0)
+ XFreePixmap(rfi->display, ((rfGlyph*)glyph)->pixmap);
+#endif
+}
+
+static BOOL rf_Glyph_Draw(rdpContext* context, const rdpGlyph* glyph, UINT32 x,
+ UINT32 y, UINT32 w, UINT32 h, UINT32 sx, UINT32 sy,
+ BOOL fOpRedundant)
+{
+ TRACE_CALL(__func__);
+#ifdef RF_GLYPH
+ rfGlyph* rf_glyph;
+ rfContext* rfi = (rfContext*)context;
+
+ rf_glyph = (rfGlyph*)glyph;
+
+ XSetStipple(rfi->display, rfi->gc, rf_glyph->pixmap);
+ XSetTSOrigin(rfi->display, rfi->gc, x, y);
+ XFillRectangle(rfi->display, rfi->drawing, rfi->gc, x, y, glyph->cx, glyph->cy);
+ XSetStipple(rfi->display, rfi->gc, rfi->bitmap_mono);
+#endif
+ return TRUE;
+}
+
+static BOOL rf_Glyph_BeginDraw(rdpContext* context, UINT32 x, UINT32 y,
+ UINT32 width, UINT32 height, UINT32 bgcolor,
+ UINT32 fgcolor, BOOL fOpRedundant)
+{
+ TRACE_CALL(__func__);
+#ifdef RF_GLYPH
+ rfContext* rfi = (rfContext*)context;
+
+ bgcolor = (rfi->clrconv->invert) ?
+ freerdp_color_convert_var_bgr(bgcolor, rfi->srcBpp, 32, rfi->clrconv) :
+ freerdp_color_convert_var_rgb(bgcolor, rfi->srcBpp, 32, rfi->clrconv);
+
+ fgcolor = (rfi->clrconv->invert) ?
+ freerdp_color_convert_var_bgr(fgcolor, rfi->srcBpp, 32, rfi->clrconv) :
+ freerdp_color_convert_var_rgb(fgcolor, rfi->srcBpp, 32, rfi->clrconv);
+
+ XSetFunction(rfi->display, rfi->gc, GXcopy);
+ XSetFillStyle(rfi->display, rfi->gc, FillSolid);
+ XSetForeground(rfi->display, rfi->gc, fgcolor);
+ XFillRectangle(rfi->display, rfi->drawing, rfi->gc, x, y, width, height);
+
+ XSetForeground(rfi->display, rfi->gc, bgcolor);
+ XSetBackground(rfi->display, rfi->gc, fgcolor);
+ XSetFillStyle(rfi->display, rfi->gc, FillStippled);
+#endif
+ return TRUE;
+}
+
+static BOOL rf_Glyph_EndDraw(rdpContext* context, UINT32 x, UINT32 y,
+ UINT32 width, UINT32 height,
+ UINT32 bgcolor, UINT32 fgcolor)
+{
+ TRACE_CALL(__func__);
+#ifdef RF_GLYPH
+ rfContext* rfi = (rfContext*)context;
+
+ if (rfi->drawing == rfi->primary) {
+ //XCopyArea(rfi->display, rfi->primary, rfi->drawable, rfi->gc, x, y, width, height, x, y);
+ //gdi_InvalidateRegion(rfi->hdc, x, y, width, height);
+ }
+#endif
+ return TRUE;
+}
+
+/* Graphics Module */
+
+void rf_register_graphics(rdpGraphics* graphics)
+{
+ TRACE_CALL(__func__);
+ rdpBitmap* bitmap;
+ rdpPointer* pointer;
+ rdpGlyph* glyph;
+
+ bitmap = (rdpBitmap*)malloc(sizeof(rdpBitmap));
+ ZeroMemory(bitmap, sizeof(rdpBitmap));
+ bitmap->size = sizeof(rfBitmap);
+
+ bitmap->New = rf_Bitmap_New;
+ bitmap->Free = rf_Bitmap_Free;
+ bitmap->Paint = rf_Bitmap_Paint;
+ bitmap->Decompress = rf_Bitmap_Decompress;
+ bitmap->SetSurface = rf_Bitmap_SetSurface;
+
+ graphics_register_bitmap(graphics, bitmap);
+ free(bitmap);
+
+ pointer = (rdpPointer*)malloc(sizeof(rdpPointer));
+ ZeroMemory(pointer, sizeof(rdpPointer));
+
+ pointer->size = sizeof(rfPointer);
+
+ pointer->New = rf_Pointer_New;
+ pointer->Free = rf_Pointer_Free;
+ pointer->Set = rf_Pointer_Set;
+ pointer->SetNull = rf_Pointer_SetNull;
+ pointer->SetDefault = rf_Pointer_SetDefault;
+ pointer->SetPosition = rf_Pointer_SetPosition;
+
+ graphics_register_pointer(graphics, pointer);
+
+ free(pointer);
+
+ glyph = (rdpGlyph*)malloc(sizeof(rdpGlyph));
+ ZeroMemory(glyph, sizeof(rdpGlyph));
+
+ glyph->size = sizeof(rfGlyph);
+
+ glyph->New = rf_Glyph_New;
+ glyph->Free = rf_Glyph_Free;
+ glyph->Draw = rf_Glyph_Draw;
+ glyph->BeginDraw = rf_Glyph_BeginDraw;
+ glyph->EndDraw = rf_Glyph_EndDraw;
+
+ graphics_register_glyph(graphics, glyph);
+
+ free(glyph);
+}
diff --git a/plugins/rdp/rdp_graphics.h b/plugins/rdp/rdp_graphics.h
new file mode 100644
index 000000000..1476a3114
--- /dev/null
+++ b/plugins/rdp/rdp_graphics.h
@@ -0,0 +1,41 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2010-2011 Vic Lee
+ * Copyright (C) 2017 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.
+ *
+ */
+
+#pragma once
+
+#include "rdp_plugin.h"
+
+void rf_register_graphics(rdpGraphics* graphics);
+
diff --git a/plugins/rdp/rdp_plugin.c b/plugins/rdp/rdp_plugin.c
new file mode 100644
index 000000000..9a8bf92fe
--- /dev/null
+++ b/plugins/rdp/rdp_plugin.c
@@ -0,0 +1,1553 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2010-2011 Vic Lee
+ * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
+ * Copyright (C) 2016-2017 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 "rdp_plugin.h"
+#include "rdp_event.h"
+#include "rdp_graphics.h"
+#include "rdp_file.h"
+#include "rdp_settings.h"
+#include "rdp_cliprdr.h"
+#include "rdp_channels.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <time.h>
+#include <cairo/cairo-xlib.h>
+#include <freerdp/freerdp.h>
+#include <freerdp/constants.h>
+#include <freerdp/client/cliprdr.h>
+#include <freerdp/client/channels.h>
+#include <freerdp/client/cmdline.h>
+#include <freerdp/error.h>
+#include <winpr/memory.h>
+
+#define REMMINA_RDP_FEATURE_TOOL_REFRESH 1
+#define REMMINA_RDP_FEATURE_SCALE 2
+#define REMMINA_RDP_FEATURE_UNFOCUS 3
+#define REMMINA_RDP_FEATURE_TOOL_SENDCTRLALTDEL 4
+#define REMMINA_RDP_FEATURE_DYNRESUPDATE 5
+
+/* Some string settings of freerdp are preallocated buffers of N bytes */
+#define FREERDP_CLIENTHOSTNAME_LEN 32
+
+RemminaPluginService* remmina_plugin_service = NULL;
+static char remmina_rdp_plugin_default_drive_name[] = "RemminaDisk";
+
+static BOOL rf_process_event_queue(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ UINT16 flags;
+ rdpInput* input;
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ RemminaPluginRdpEvent* event;
+ DISPLAY_CONTROL_MONITOR_LAYOUT* dcml;
+
+ if (rfi->event_queue == NULL)
+ return True;
+
+ input = rfi->instance->input;
+
+ while ((event = (RemminaPluginRdpEvent*)g_async_queue_try_pop(rfi->event_queue)) != NULL) {
+ switch (event->type) {
+ case REMMINA_RDP_EVENT_TYPE_SCANCODE:
+ flags = event->key_event.extended ? KBD_FLAGS_EXTENDED : 0;
+ flags |= event->key_event.up ? KBD_FLAGS_RELEASE : KBD_FLAGS_DOWN;
+ input->KeyboardEvent(input, flags, event->key_event.key_code);
+ break;
+
+ case REMMINA_RDP_EVENT_TYPE_SCANCODE_UNICODE:
+ /*
+ * TS_UNICODE_KEYBOARD_EVENT RDP message, see https://msdn.microsoft.com/en-us/library/cc240585.aspx
+ */
+ flags = event->key_event.up ? KBD_FLAGS_RELEASE : KBD_FLAGS_DOWN;
+ input->UnicodeKeyboardEvent(input, flags, event->key_event.unicode_code);
+ break;
+
+ case REMMINA_RDP_EVENT_TYPE_MOUSE:
+ if (event->mouse_event.extended)
+ input->ExtendedMouseEvent(input, event->mouse_event.flags,
+ event->mouse_event.x, event->mouse_event.y);
+ else
+ input->MouseEvent(input, event->mouse_event.flags,
+ event->mouse_event.x, event->mouse_event.y);
+ break;
+
+ case REMMINA_RDP_EVENT_TYPE_CLIPBOARD_SEND_CLIENT_FORMAT_LIST:
+ rfi->clipboard.context->ClientFormatList(rfi->clipboard.context, event->clipboard_formatlist.pFormatList);
+ free(event->clipboard_formatlist.pFormatList);
+ break;
+
+ case REMMINA_RDP_EVENT_TYPE_CLIPBOARD_SEND_CLIENT_FORMAT_DATA_RESPONSE:
+ rfi->clipboard.context->ClientFormatDataResponse(rfi->clipboard.context, event->clipboard_formatdataresponse.pFormatDataResponse);
+ if (event->clipboard_formatdataresponse.pFormatDataResponse->requestedFormatData)
+ free(event->clipboard_formatdataresponse.pFormatDataResponse->requestedFormatData);
+ free(event->clipboard_formatdataresponse.pFormatDataResponse);
+ break;
+
+ case REMMINA_RDP_EVENT_TYPE_CLIPBOARD_SEND_CLIENT_FORMAT_DATA_REQUEST:
+ rfi->clipboard.context->ClientFormatDataRequest(rfi->clipboard.context, event->clipboard_formatdatarequest.pFormatDataRequest);
+ free(event->clipboard_formatdatarequest.pFormatDataRequest);
+ break;
+
+ case REMMINA_RDP_EVENT_TYPE_SEND_MONITOR_LAYOUT:
+ dcml = g_malloc0(sizeof(DISPLAY_CONTROL_MONITOR_LAYOUT));
+ if (dcml) {
+ dcml->Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
+ dcml->Width = event->monitor_layout.width;
+ dcml->Height = event->monitor_layout.height;
+ dcml->Orientation = event->monitor_layout.desktopOrientation;
+ dcml->DesktopScaleFactor = event->monitor_layout.desktopScaleFactor;
+ dcml->DeviceScaleFactor = event->monitor_layout.deviceScaleFactor;
+ rfi->dispcontext->SendMonitorLayout(rfi->dispcontext, 1, dcml);
+ g_free(dcml);
+ }
+ break;
+ }
+
+ g_free(event);
+ }
+
+ return True;
+}
+
+static gboolean remmina_rdp_tunnel_init(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+
+ /* Opens the optional SSH tunnel if needed.
+ * Used also when reopening the same tunnel for a freerdp reconnect.
+ * Returns TRUE if all OK, and setups correct rfi->Settings values
+ * with connection and certificate parameters */
+
+ gchar* hostport;
+ gchar* s;
+ gchar* host;
+ gchar *cert_host;
+ gint cert_port;
+ gint port;
+ const gchar *cert_hostport;
+
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ RemminaFile* remminafile;
+
+ remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+
+ hostport = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, 3389, FALSE);
+ if (hostport == NULL) {
+ return FALSE;
+ }
+
+ remmina_plugin_service->get_server_port(hostport, 3389, &host, &port);
+
+ cert_host = host;
+ cert_port = port;
+
+ if (remmina_plugin_service->file_get_int(remminafile, "ssh_enabled", FALSE)) {
+ cert_hostport = remmina_plugin_service->file_get_string(remminafile, "server");
+ if ( cert_hostport ) {
+ remmina_plugin_service->get_server_port(cert_hostport, 3389, &cert_host, &cert_port);
+ }
+
+ }
+
+ if (!rfi->is_reconnecting) {
+ /* settings->CertificateName and settings->ServerHostname is created
+ * only on 1st connect, not on reconnections */
+
+ rfi->settings->ServerHostname = strdup(host);
+
+ if (cert_port == 3389) {
+ rfi->settings->CertificateName = strdup(cert_host);
+ }else {
+ s = g_strdup_printf("%s:%d", cert_host, cert_port);
+ rfi->settings->CertificateName = strdup(s);
+ g_free(s);
+ }
+ }
+
+ if (cert_host != host) g_free(cert_host);
+ g_free(host);
+ g_free(hostport);
+
+ rfi->settings->ServerPort = port;
+
+ return TRUE;
+}
+
+BOOL rf_auto_reconnect(rfContext* rfi)
+{
+ TRACE_CALL(__func__);
+ rdpSettings* settings = rfi->instance->settings;
+ RemminaPluginRdpUiObject* ui;
+ time_t treconn;
+
+ rfi->is_reconnecting = TRUE;
+ rfi->reconnect_maxattempts = settings->AutoReconnectMaxRetries;
+ rfi->reconnect_nattempt = 0;
+
+ /* Only auto reconnect on network disconnects. */
+ if (freerdp_error_info(rfi->instance) != 0) {
+ rfi->is_reconnecting = FALSE;
+ return FALSE;
+ }
+
+ if (!settings->AutoReconnectionEnabled) {
+ /* No auto-reconnect - just quit */
+ rfi->is_reconnecting = FALSE;
+ return FALSE;
+ }
+
+ /* A network disconnect was detected and we should try to reconnect */
+ remmina_plugin_service->log_printf("[RDP][%s] network disconnection detected, initiating reconnection attempt\n",
+ rfi->settings->ServerHostname);
+
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_RECONNECT_PROGRESS;
+ remmina_rdp_event_queue_ui_async(rfi->protocol_widget, ui);
+
+ /* Sleep half a second to allow:
+ * - processing of the ui event we just pushed on the queue
+ * - better network conditions
+ * Remember: we hare on a thread, so the main gui won't lock */
+
+ usleep(500000);
+
+ /* Perform an auto-reconnect. */
+ while (TRUE) {
+ /* Quit retrying if max retries has been exceeded */
+ if (rfi->reconnect_nattempt++ >= rfi->reconnect_maxattempts) {
+ remmina_plugin_service->log_printf("[RDP][%s] maximum number of reconnection attempts exceeded.\n",
+ rfi->settings->ServerHostname);
+ break;
+ }
+
+ /* Attempt the next reconnect */
+ remmina_plugin_service->log_printf("[RDP][%s] attempting reconnection, attempt #%d of %d\n",
+ rfi->settings->ServerHostname, rfi->reconnect_nattempt, rfi->reconnect_maxattempts);
+
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_RECONNECT_PROGRESS;
+ remmina_rdp_event_queue_ui_async(rfi->protocol_widget, ui);
+
+ treconn = time(NULL);
+
+ /* Reconnect the SSH tunnel, if needed */
+ if (!remmina_rdp_tunnel_init(rfi->protocol_widget)) {
+ remmina_plugin_service->log_printf("[RDP][%s] unable to recreate tunnel with remmina_rdp_tunnel_init.\n",
+ rfi->settings->ServerHostname);
+ } else {
+ if (freerdp_reconnect(rfi->instance)) {
+ /* Reconnection is successful */
+ remmina_plugin_service->log_printf("[RDP][%s] reconnection successful.\n",
+ rfi->settings->ServerHostname);
+ rfi->is_reconnecting = FALSE;
+ return TRUE;
+ }
+ }
+
+ /* Wait until 5 secs have elapsed from last reconnect attempt */
+ while (time(NULL) - treconn < 5)
+ sleep(1);
+ }
+
+ rfi->is_reconnecting = FALSE;
+ return FALSE;
+}
+
+BOOL rf_begin_paint(rdpContext* context)
+{
+ TRACE_CALL(__func__);
+ rdpGdi* gdi;
+ HGDI_WND hwnd;
+
+ if (!context)
+ return FALSE;
+
+ gdi = context->gdi;
+ if (!gdi || !gdi->primary || !gdi->primary->hdc || !gdi->primary->hdc->hwnd)
+ return FALSE;
+
+ hwnd = gdi->primary->hdc->hwnd;
+ if (!hwnd->ninvalid)
+ return FALSE;
+
+ hwnd->invalid->null = 1;
+ hwnd->ninvalid = 0;
+ return TRUE;
+}
+
+BOOL rf_end_paint(rdpContext* context)
+{
+ TRACE_CALL(__func__);
+ INT32 x, y;
+ UINT32 w, h;
+ rdpGdi* gdi;
+ rfContext* rfi;
+ RemminaProtocolWidget* gp;
+ RemminaPluginRdpUiObject* ui;
+
+ gdi = context->gdi;
+ rfi = (rfContext*)context;
+ gp = rfi->protocol_widget;
+
+ if (gdi->primary->hdc->hwnd->invalid->null)
+ return FALSE;
+
+ x = gdi->primary->hdc->hwnd->invalid->x;
+ y = gdi->primary->hdc->hwnd->invalid->y;
+ w = gdi->primary->hdc->hwnd->invalid->w;
+ h = gdi->primary->hdc->hwnd->invalid->h;
+
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_UPDATE_REGION;
+ ui->region.x = x;
+ ui->region.y = y;
+ ui->region.width = w;
+ ui->region.height = h;
+
+ remmina_rdp_event_queue_ui_async(rfi->protocol_widget, ui);
+
+ return TRUE;
+}
+
+static BOOL rf_desktop_resize(rdpContext* context)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi;
+ RemminaProtocolWidget* gp;
+ RemminaPluginRdpUiObject* ui;
+
+ rfi = (rfContext*)context;
+ gp = rfi->protocol_widget;
+
+ remmina_plugin_service->protocol_plugin_set_width(gp, rfi->settings->DesktopWidth);
+ remmina_plugin_service->protocol_plugin_set_height(gp, rfi->settings->DesktopHeight);
+
+ /* Call to remmina_rdp_event_update_scale(gp) on the main UI thread */
+
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_EVENT;
+ ui->event.type = REMMINA_RDP_UI_EVENT_UPDATE_SCALE;
+ remmina_rdp_event_queue_ui_sync_retint(gp, ui);
+
+ remmina_plugin_service->protocol_plugin_emit_signal(gp, "desktop-resize");
+
+ return TRUE;
+}
+
+static BOOL remmina_rdp_pre_connect(freerdp* instance)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi;
+ ALIGN64 rdpSettings* settings;
+ RemminaProtocolWidget* gp;
+
+ rfi = (rfContext*)instance->context;
+ settings = instance->settings;
+ gp = rfi->protocol_widget;
+
+ settings->OsMajorType = OSMAJORTYPE_UNIX;
+ settings->OsMinorType = OSMINORTYPE_UNSPECIFIED;
+ ZeroMemory(settings->OrderSupport, 32);
+
+ settings->BitmapCacheEnabled = True;
+ settings->OffscreenSupportLevel = True;
+
+
+ settings->OrderSupport[NEG_DSTBLT_INDEX] = True;
+ settings->OrderSupport[NEG_PATBLT_INDEX] = True;
+ settings->OrderSupport[NEG_SCRBLT_INDEX] = True;
+ settings->OrderSupport[NEG_OPAQUE_RECT_INDEX] = True;
+ settings->OrderSupport[NEG_DRAWNINEGRID_INDEX] = False;
+ settings->OrderSupport[NEG_MULTIDSTBLT_INDEX] = False;
+ settings->OrderSupport[NEG_MULTIPATBLT_INDEX] = False;
+ settings->OrderSupport[NEG_MULTISCRBLT_INDEX] = False;
+ settings->OrderSupport[NEG_MULTIOPAQUERECT_INDEX] = True;
+ settings->OrderSupport[NEG_MULTI_DRAWNINEGRID_INDEX] = False;
+ settings->OrderSupport[NEG_LINETO_INDEX] = True;
+ settings->OrderSupport[NEG_POLYLINE_INDEX] = True;
+ settings->OrderSupport[NEG_MEMBLT_INDEX] = settings->BitmapCacheEnabled;
+ settings->OrderSupport[NEG_MEM3BLT_INDEX] = settings->BitmapCacheEnabled;
+ settings->OrderSupport[NEG_MEMBLT_V2_INDEX] = settings->BitmapCacheEnabled;
+ settings->OrderSupport[NEG_MEM3BLT_V2_INDEX] = settings->BitmapCacheEnabled;
+ settings->OrderSupport[NEG_SAVEBITMAP_INDEX] = False;
+ settings->OrderSupport[NEG_GLYPH_INDEX_INDEX] = True;
+ settings->OrderSupport[NEG_FAST_INDEX_INDEX] = True;
+ settings->OrderSupport[NEG_FAST_GLYPH_INDEX] = True;
+ settings->OrderSupport[NEG_POLYGON_SC_INDEX] = False;
+ settings->OrderSupport[NEG_POLYGON_CB_INDEX] = False;
+ settings->OrderSupport[NEG_ELLIPSE_SC_INDEX] = False;
+ settings->OrderSupport[NEG_ELLIPSE_CB_INDEX] = False;
+
+ if (settings->RemoteFxCodec == True) {
+ settings->FrameAcknowledge = False;
+ settings->LargePointerFlag = True;
+ settings->PerformanceFlags = PERF_FLAG_NONE;
+
+ rfi->rfx_context = rfx_context_new(FALSE);
+ }
+
+ PubSub_SubscribeChannelConnected(instance->context->pubSub,
+ (pChannelConnectedEventHandler)remmina_rdp_OnChannelConnectedEventHandler);
+ PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
+ (pChannelDisconnectedEventHandler)remmina_rdp_OnChannelDisconnectedEventHandler);
+
+ freerdp_client_load_addins(instance->context->channels, instance->settings);
+
+ return True;
+}
+
+static UINT32 rf_get_local_color_format(rfContext* rfi, BOOL aligned)
+{
+ UINT32 DstFormat;
+ BOOL invert = aligned;
+
+ if (!rfi)
+ return 0;
+
+ if (rfi->bpp == 32)
+ DstFormat = (!invert) ? PIXEL_FORMAT_RGBA32 : PIXEL_FORMAT_BGRA32;
+ else if (rfi->bpp == 24) {
+ if (aligned)
+ DstFormat = (!invert) ? PIXEL_FORMAT_RGBX32 : PIXEL_FORMAT_BGRX32;
+ else
+ DstFormat = (!invert) ? PIXEL_FORMAT_RGB24 : PIXEL_FORMAT_BGR24;
+ }else if (rfi->bpp == 16)
+ DstFormat = (!invert) ? PIXEL_FORMAT_RGB16 : PIXEL_FORMAT_BGR16;
+ else if (rfi->bpp == 15)
+ DstFormat = (!invert) ? PIXEL_FORMAT_RGB16 : PIXEL_FORMAT_BGR16;
+ else
+ DstFormat = (!invert) ? PIXEL_FORMAT_RGBX32 : PIXEL_FORMAT_BGRX32;
+
+ return DstFormat;
+}
+
+
+static BOOL remmina_rdp_post_connect(freerdp* instance)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi;
+ RemminaProtocolWidget* gp;
+ RemminaPluginRdpUiObject* ui;
+ rdpGdi* gdi;
+ int hdcBytesPerPixel, hdcBitsPerPixel;
+
+ rfi = (rfContext*)instance->context;
+ gp = rfi->protocol_widget;
+ rfi->postconnect_error = REMMINA_POSTCONNECT_ERROR_OK;
+
+ rfi->width = rfi->settings->DesktopWidth;
+ rfi->height = rfi->settings->DesktopHeight;
+ rfi->srcBpp = rfi->settings->ColorDepth;
+
+ if (rfi->settings->RemoteFxCodec == FALSE)
+ rfi->sw_gdi = TRUE;
+
+ rf_register_graphics(instance->context->graphics);
+
+ if (rfi->bpp == 32) {
+ hdcBytesPerPixel = 4;
+ hdcBitsPerPixel = 32;
+ rfi->cairo_format = CAIRO_FORMAT_ARGB32;
+ }else if (rfi->bpp == 24) {
+ hdcBytesPerPixel = 4;
+ hdcBitsPerPixel = 32;
+ rfi->cairo_format = CAIRO_FORMAT_RGB24;
+ }else {
+ hdcBytesPerPixel = 2;
+ hdcBitsPerPixel = 16;
+ rfi->cairo_format = CAIRO_FORMAT_RGB16_565;
+ }
+
+ if (!gdi_init(instance, rf_get_local_color_format(rfi, TRUE))) {
+ rfi->postconnect_error = REMMINA_POSTCONNECT_ERROR_GDI_INIT;
+ return FALSE;
+ }
+
+ if (instance->context->codecs->h264 == NULL && rfi->settings->GfxH264) {
+ gdi_free(instance);
+ rfi->postconnect_error = REMMINA_POSTCONNECT_ERROR_NO_H264;
+ return FALSE;
+ }
+
+ gdi = instance->context->gdi;
+ rfi->primary_buffer = gdi->primary_buffer;
+
+ pointer_cache_register_callbacks(instance->update);
+
+ instance->update->BeginPaint = rf_begin_paint;
+ instance->update->EndPaint = rf_end_paint;
+ instance->update->DesktopResize = rf_desktop_resize;
+
+ remmina_rdp_clipboard_init(rfi);
+ rfi->connected = True;
+
+ ui = g_new0(RemminaPluginRdpUiObject, 1);
+ ui->type = REMMINA_RDP_UI_CONNECTED;
+ remmina_rdp_event_queue_ui_async(gp, ui);
+
+ return TRUE;
+}
+
+static BOOL remmina_rdp_authenticate(freerdp* instance, char** username, char** password, char** domain)
+{
+ TRACE_CALL(__func__);
+ gchar *s_username, *s_password, *s_domain;
+ gint ret;
+ rfContext* rfi;
+ RemminaProtocolWidget* gp;
+ gboolean save;
+ gboolean disablepasswordstoring;
+ RemminaFile* remminafile;
+
+ rfi = (rfContext*)instance->context;
+ gp = rfi->protocol_widget;
+ remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+ disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
+ ret = remmina_plugin_service->protocol_plugin_init_authuserpwd(gp, TRUE, !disablepasswordstoring);
+
+ if (ret == GTK_RESPONSE_OK) {
+ s_username = remmina_plugin_service->protocol_plugin_init_get_username(gp);
+ if (s_username) rfi->settings->Username = strdup(s_username);
+
+ s_password = remmina_plugin_service->protocol_plugin_init_get_password(gp);
+ if (s_password) rfi->settings->Password = strdup(s_password);
+
+ s_domain = remmina_plugin_service->protocol_plugin_init_get_domain(gp);
+ if (s_domain) rfi->settings->Domain = strdup(s_domain);
+
+ save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp);
+ if (save) {
+ // User has requested to save credentials. We put all the new cretentials
+ // into remminafile->settings. They will be saved later, on successful connection, by
+ // remmina_connection_window.c
+
+ remmina_plugin_service->file_set_string( remminafile, "username", s_username );
+ remmina_plugin_service->file_set_string( remminafile, "password", s_password );
+ remmina_plugin_service->file_set_string( remminafile, "domain", s_domain );
+
+ }
+
+ if ( s_username ) g_free( s_username );
+ if ( s_password ) g_free( s_password );
+ if ( s_domain ) g_free( s_domain );
+
+ return True;
+ }else {
+ rfi->user_cancelled = TRUE;
+ return False;
+ }
+
+ return True;
+}
+
+static DWORD remmina_rdp_verify_certificate(freerdp* instance, const char *common_name, const char* subject,
+ const char* issuer, const char* fingerprint, BOOL host_mismatch)
+{
+ TRACE_CALL(__func__);
+ gint status;
+ rfContext* rfi;
+ RemminaProtocolWidget* gp;
+
+ rfi = (rfContext*)instance->context;
+ gp = rfi->protocol_widget;
+
+ status = remmina_plugin_service->protocol_plugin_init_certificate(gp, subject, issuer, fingerprint);
+
+ if (status == GTK_RESPONSE_OK)
+ return 1;
+
+ return 0;
+}
+static DWORD remmina_rdp_verify_changed_certificate(freerdp* instance,
+ const char* common_name, const char* subject, const char* issuer,
+ const char* new_fingerprint, const char* old_subject, const char* old_issuer, const char* old_fingerprint)
+{
+ TRACE_CALL(__func__);
+ gint status;
+ rfContext* rfi;
+ RemminaProtocolWidget* gp;
+
+ rfi = (rfContext*)instance->context;
+ gp = rfi->protocol_widget;
+
+ status = remmina_plugin_service->protocol_plugin_changed_certificate(gp, subject, issuer, new_fingerprint, old_fingerprint);
+
+ if (status == GTK_RESPONSE_OK)
+ return 1;
+
+ return 0;
+}
+
+static void remmina_rdp_main_loop(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ DWORD nCount;
+ DWORD status;
+ HANDLE handles[64];
+ gchar buf[100];
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+
+ while (!freerdp_shall_disconnect(rfi->instance)) {
+ nCount = freerdp_get_event_handles(rfi->instance->context, &handles[0], 64);
+ if (rfi->event_handle) {
+ handles[nCount++] = rfi->event_handle;
+ }
+
+ if (nCount == 0) {
+ fprintf(stderr, "freerdp_get_event_handles failed\n");
+ break;
+ }
+
+ status = WaitForMultipleObjects(nCount, handles, FALSE, 100);
+
+ if (status == WAIT_FAILED) {
+ fprintf(stderr, "WaitForMultipleObjects failed with %lu\n", (unsigned long)status);
+ break;
+ }
+
+ if (rfi->event_handle && WaitForSingleObject(rfi->event_handle, 0) == WAIT_OBJECT_0) {
+ if (!rf_process_event_queue(gp)) {
+ fprintf(stderr, "Failed to process local kb/mouse event queue\n");
+ break;
+ }
+ if (read(rfi->event_pipe[0], buf, sizeof(buf))) {
+ }
+ }
+
+ if (!freerdp_check_event_handles(rfi->instance->context)) {
+ if (rf_auto_reconnect(rfi)) {
+ /* Reset the possible reason/error which made us doing many reconnection reattempts and continue */
+ remmina_plugin_service->protocol_plugin_set_error(gp, NULL);
+ continue;
+ }
+ fprintf(stderr, "Failed to check FreeRDP event handles\n");
+ break;
+ }
+ }
+}
+
+int remmina_rdp_load_static_channel_addin(rdpChannels* channels, rdpSettings* settings, char* name, void* data)
+{
+ TRACE_CALL(__func__);
+ void* entry;
+
+ entry = freerdp_load_channel_addin_entry(name, NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC);
+ if (entry) {
+ if (freerdp_channels_client_load(channels, settings, entry, data) == 0) {
+ fprintf(stderr, "loading channel %s\n", name);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/* Send CTRL+ALT+DEL keys keystrokes to the plugin drawing_area widget */
+static void remmina_rdp_send_ctrlaltdel(RemminaProtocolWidget *gp)
+{
+ TRACE_CALL(__func__);
+ guint keys[] = { GDK_KEY_Control_L, GDK_KEY_Alt_L, GDK_KEY_Delete };
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+
+ remmina_plugin_service->protocol_plugin_send_keys_signals(rfi->drawing_area,
+ keys, G_N_ELEMENTS(keys), GDK_KEY_PRESS | GDK_KEY_RELEASE);
+}
+
+static gboolean remmina_rdp_main(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ const gchar* s;
+ gchar *sm;
+ gchar* value;
+ gint rdpsnd_rate;
+ gint rdpsnd_channel;
+ char *rdpsnd_params[3];
+ int rdpsnd_nparams;
+ char rdpsnd_param1[16];
+ char rdpsnd_param2[16];
+ const gchar* cs;
+ RemminaFile* remminafile;
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ gchar *gateway_host;
+ gint gateway_port;
+ gint desktopOrientation, desktopScaleFactor, deviceScaleFactor;
+ gint dynresw, dynresh;
+
+ remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+
+ if (!remmina_rdp_tunnel_init(gp))
+ return FALSE;
+
+ rfi->settings->AutoReconnectionEnabled = ( remmina_plugin_service->file_get_int(remminafile, "disableautoreconnect", FALSE) ? FALSE : TRUE );
+ /* Disable RDP auto reconnection when ssh tunnel is enabled */
+ if (remmina_plugin_service->file_get_int(remminafile, "ssh_enabled", FALSE)) {
+ rfi->settings->AutoReconnectionEnabled = FALSE;
+ }
+
+ rfi->settings->ColorDepth = remmina_plugin_service->file_get_int(remminafile, "colordepth", 66);
+
+ rfi->settings->SoftwareGdi = TRUE;
+
+ if (rfi->settings->ColorDepth == 0) {
+ /* RFX (Win7)*/
+ rfi->settings->RemoteFxCodec = TRUE;
+ rfi->settings->SupportGraphicsPipeline = FALSE;
+ rfi->settings->ColorDepth = 32;
+ } else if (rfi->settings->ColorDepth == 64) {
+ /* /gfx:rfx (Win8) */
+ rfi->settings->ColorDepth = 32;
+ rfi->settings->SupportGraphicsPipeline = TRUE;
+ rfi->settings->GfxH264 = FALSE;
+ rfi->settings->GfxAVC444 = FALSE;
+ } else if (rfi->settings->ColorDepth == 65) {
+ /* /gfx:avc420 (Win8.1) */
+ rfi->settings->ColorDepth = 32;
+ rfi->settings->SupportGraphicsPipeline = TRUE;
+#ifdef WITH_GFX_H264
+ rfi->settings->GfxH264 = TRUE;
+ rfi->settings->GfxAVC444 = FALSE;
+#endif
+ } else if (rfi->settings->ColorDepth >= 66) {
+ /* /gfx:avc444 (Win10) */
+ rfi->settings->ColorDepth = 32;
+ rfi->settings->SupportGraphicsPipeline = TRUE;
+#ifdef WITH_GFX_H264
+ rfi->settings->GfxH264 = TRUE;
+ rfi->settings->GfxAVC444 = TRUE;
+#endif
+ }
+
+ rfi->settings->DesktopWidth = remmina_plugin_service->get_profile_remote_width(gp);
+ rfi->settings->DesktopHeight = remmina_plugin_service->get_profile_remote_height(gp);
+ dynresw = remmina_plugin_service->file_get_int(remminafile, "dynamic_resolution_width", 0);
+ dynresh = remmina_plugin_service->file_get_int(remminafile, "dynamic_resolution_height", 0);
+
+ if (rfi->scale == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES && dynresh != 0 && dynresw != 0) {
+ rfi->settings->DesktopWidth = dynresw;
+ rfi->settings->DesktopHeight = dynresh;
+ }
+ remmina_plugin_service->protocol_plugin_set_width(gp, rfi->settings->DesktopWidth);
+ remmina_plugin_service->protocol_plugin_set_height(gp, rfi->settings->DesktopHeight);
+
+ if (remmina_plugin_service->file_get_string(remminafile, "username"))
+ rfi->settings->Username = strdup(remmina_plugin_service->file_get_string(remminafile, "username"));
+
+ if (remmina_plugin_service->file_get_string(remminafile, "domain"))
+ rfi->settings->Domain = strdup(remmina_plugin_service->file_get_string(remminafile, "domain"));
+
+ s = remmina_plugin_service->file_get_string(remminafile, "password");
+
+ if (s) {
+ rfi->settings->Password = strdup(s);
+ rfi->settings->AutoLogonEnabled = 1;
+ }
+ /* Remote Desktop Gateway server address */
+ rfi->settings->GatewayEnabled = FALSE;
+ s = remmina_plugin_service->file_get_string(remminafile, "gateway_server");
+ if (s) {
+ remmina_plugin_service->get_server_port(s, 443, &gateway_host, &gateway_port);
+ rfi->settings->GatewayHostname = gateway_host;
+ rfi->settings->GatewayPort = gateway_port;
+ rfi->settings->GatewayEnabled = TRUE;
+ rfi->settings->GatewayUseSameCredentials = TRUE;
+ }
+ /* Remote Desktop Gateway domain */
+ if (remmina_plugin_service->file_get_string(remminafile, "gateway_domain")) {
+ rfi->settings->GatewayDomain = strdup(remmina_plugin_service->file_get_string(remminafile, "gateway_domain"));
+ rfi->settings->GatewayUseSameCredentials = FALSE;
+ }
+ /* Remote Desktop Gateway username */
+ if (remmina_plugin_service->file_get_string(remminafile, "gateway_username")) {
+ rfi->settings->GatewayUsername = strdup(remmina_plugin_service->file_get_string(remminafile, "gateway_username"));
+ rfi->settings->GatewayUseSameCredentials = FALSE;
+ }
+ /* Remote Desktop Gateway password */
+ s = remmina_plugin_service->file_get_string(remminafile, "gateway_password");
+ if (s) {
+ rfi->settings->GatewayPassword = strdup(s);
+ rfi->settings->GatewayUseSameCredentials = FALSE;
+ }
+ /* If no different credentials were provided for the Remote Desktop Gateway
+ * use the same authentication credentials for the host */
+ if (rfi->settings->GatewayEnabled && rfi->settings->GatewayUseSameCredentials) {
+ g_free(rfi->settings->GatewayDomain);
+ rfi->settings->GatewayDomain = g_strdup(rfi->settings->Domain);
+ g_free(rfi->settings->GatewayUsername);
+ rfi->settings->GatewayUsername = g_strdup(rfi->settings->Username);
+ g_free(rfi->settings->GatewayPassword);
+ rfi->settings->GatewayPassword = g_strdup(rfi->settings->Password);
+ }
+ /* Remote Desktop Gateway usage */
+ if (rfi->settings->GatewayEnabled)
+ freerdp_set_gateway_usage_method(rfi->settings,
+ remmina_plugin_service->file_get_int(remminafile, "gateway_usage", FALSE) ? TSC_PROXY_MODE_DETECT : TSC_PROXY_MODE_DIRECT);
+ /* Certificate ignore */
+ rfi->settings->IgnoreCertificate = remmina_plugin_service->file_get_int(remminafile, "cert_ignore", 0);
+
+ /* ClientHostname is internally preallocated to 32 bytes by libfreerdp */
+ if ((cs = remmina_plugin_service->file_get_string(remminafile, "clientname"))) {
+ strncpy(rfi->settings->ClientHostname, cs, FREERDP_CLIENTHOSTNAME_LEN - 1);
+ }else {
+ strncpy(rfi->settings->ClientHostname, g_get_host_name(), FREERDP_CLIENTHOSTNAME_LEN - 1);
+ }
+ rfi->settings->ClientHostname[FREERDP_CLIENTHOSTNAME_LEN - 1] = 0;
+
+ if (remmina_plugin_service->file_get_string(remminafile, "loadbalanceinfo")) {
+ rfi->settings->LoadBalanceInfo = (BYTE*)strdup(remmina_plugin_service->file_get_string(remminafile, "loadbalanceinfo"));
+ rfi->settings->LoadBalanceInfoLength = (UINT32)strlen((char*)rfi->settings->LoadBalanceInfo);
+ }
+
+ if (remmina_plugin_service->file_get_string(remminafile, "exec")) {
+ rfi->settings->AlternateShell = strdup(remmina_plugin_service->file_get_string(remminafile, "exec"));
+ }
+
+ if (remmina_plugin_service->file_get_string(remminafile, "execpath")) {
+ rfi->settings->ShellWorkingDirectory = strdup(remmina_plugin_service->file_get_string(remminafile, "execpath"));
+ }
+
+ sm = g_strdup_printf("rdp_quality_%i", remmina_plugin_service->file_get_int(remminafile, "quality", DEFAULT_QUALITY_0));
+ value = remmina_plugin_service->pref_get_value(sm);
+ g_free(sm);
+
+ if (value && value[0]) {
+ rfi->settings->PerformanceFlags = strtoul(value, NULL, 16);
+ }else {
+ switch (remmina_plugin_service->file_get_int(remminafile, "quality", DEFAULT_QUALITY_0)) {
+ case 9:
+ rfi->settings->PerformanceFlags = DEFAULT_QUALITY_9;
+ break;
+
+ case 2:
+ rfi->settings->PerformanceFlags = DEFAULT_QUALITY_2;
+ break;
+
+ case 1:
+ rfi->settings->PerformanceFlags = DEFAULT_QUALITY_1;
+ break;
+
+ case 0:
+ default:
+ rfi->settings->PerformanceFlags = DEFAULT_QUALITY_0;
+ break;
+ }
+ }
+ g_free(value);
+
+ /* PerformanceFlags bitmask need also to be splitted into BOOL variables
+ * like rfi->settings->DisableWallpaper, rfi->settings->AllowFontSmoothing...
+ * or freerdp_get_param_bool() function will return the wrong value
+ */
+ freerdp_performance_flags_split(rfi->settings);
+
+ rfi->settings->KeyboardLayout = remmina_rdp_settings_get_keyboard_layout();
+
+ if (remmina_plugin_service->file_get_int(remminafile, "console", FALSE)) {
+ rfi->settings->ConsoleSession = True;
+ }
+
+ cs = remmina_plugin_service->file_get_string(remminafile, "security");
+
+
+
+ if (g_strcmp0(cs, "rdp") == 0) {
+ rfi->settings->RdpSecurity = True;
+ rfi->settings->TlsSecurity = False;
+ rfi->settings->NlaSecurity = False;
+ rfi->settings->ExtSecurity = False;
+ rfi->settings->UseRdpSecurityLayer = True;
+ }else if (g_strcmp0(cs, "tls") == 0) {
+ rfi->settings->RdpSecurity = False;
+ rfi->settings->TlsSecurity = True;
+ rfi->settings->NlaSecurity = False;
+ rfi->settings->ExtSecurity = False;
+ }else if (g_strcmp0(cs, "nla") == 0) {
+ rfi->settings->RdpSecurity = False;
+ rfi->settings->TlsSecurity = False;
+ rfi->settings->NlaSecurity = True;
+ rfi->settings->ExtSecurity = False;
+ }
+
+ /* This is "-nego" switch of xfreerdp */
+ rfi->settings->NegotiateSecurityLayer = True;
+
+ rfi->settings->CompressionEnabled = True;
+ rfi->settings->FastPathInput = True;
+ rfi->settings->FastPathOutput = True;
+
+ /* Orientation and scaling settings */
+ remmina_rdp_settings_get_orientation_scale_prefs(&desktopOrientation, &desktopScaleFactor, &deviceScaleFactor);
+
+ rfi->settings->DesktopOrientation = desktopOrientation;
+ if (desktopScaleFactor != 0 && deviceScaleFactor != 0) {
+ rfi->settings->DesktopScaleFactor = desktopScaleFactor;
+ rfi->settings->DeviceScaleFactor = deviceScaleFactor;
+ }
+
+ /* Try to enable "Display Control Virtual Channel Extension", needed to
+ * dynamically resize remote desktop. This will automatically open
+ * the "disp" dynamic channel, if available */
+ rfi->settings->SupportDisplayControl = TRUE;
+
+ cs = remmina_plugin_service->file_get_string(remminafile, "sound");
+
+ if (g_strcmp0(cs, "remote") == 0) {
+ rfi->settings->RemoteConsoleAudio = 1;
+ }else if (g_str_has_prefix(cs, "local")) {
+
+ rdpsnd_nparams = 0;
+ rdpsnd_params[rdpsnd_nparams++] = "rdpsnd";
+
+ cs = strchr(cs, ',');
+ if (cs) {
+ rdpsnd_rate = atoi(cs + 1);
+ if (rdpsnd_rate > 1000 && rdpsnd_rate < 150000) {
+ snprintf( rdpsnd_param1, sizeof(rdpsnd_param1), "rate:%d", rdpsnd_rate );
+ rdpsnd_params[rdpsnd_nparams++] = rdpsnd_param1;
+ cs = strchr(cs + 1, ',');
+ if (cs) {
+ rdpsnd_channel = atoi(cs + 1);
+ if (rdpsnd_channel >= 1 && rdpsnd_channel <= 2) {
+ snprintf( rdpsnd_param2, sizeof(rdpsnd_param2), "channel:%d", rdpsnd_channel );
+ rdpsnd_params[rdpsnd_nparams++] = rdpsnd_param2;
+ }
+ }
+ }
+ }
+
+ freerdp_client_add_static_channel(rfi->settings, rdpsnd_nparams, (char**)rdpsnd_params);
+
+ }
+
+ if ( remmina_plugin_service->file_get_int(remminafile, "microphone", FALSE) ? TRUE : FALSE ) {
+ char* p[1];
+ int count;
+
+ count = 1;
+ p[0] = "audin";
+
+ freerdp_client_add_dynamic_channel(rfi->settings, count, p);
+ }
+
+ rfi->settings->RedirectClipboard = ( remmina_plugin_service->file_get_int(remminafile, "disableclipboard", FALSE) ? FALSE : TRUE );
+
+ cs = remmina_plugin_service->file_get_string(remminafile, "sharefolder");
+
+ if (cs && cs[0] == '/') {
+ RDPDR_DRIVE* drive;
+ gsize sz;
+
+ drive = (RDPDR_DRIVE*)malloc(sizeof(RDPDR_DRIVE));
+ ZeroMemory(drive, sizeof(RDPDR_DRIVE));
+
+ s = strrchr( cs, '/' );
+ if ( s == NULL || s[1] == 0 )
+ s = remmina_rdp_plugin_default_drive_name;
+ else
+ s++;
+ sm = g_convert_with_fallback(s, -1, "ascii", "utf-8", "_", NULL, &sz, NULL);
+
+ drive->Type = RDPDR_DTYP_FILESYSTEM;
+ drive->Name = _strdup(sm);
+ drive->Path = _strdup(cs);
+ g_free(sm);
+
+ freerdp_device_collection_add(rfi->settings, (RDPDR_DEVICE*)drive);
+ rfi->settings->DeviceRedirection = TRUE;
+ }
+
+ if (remmina_plugin_service->file_get_int(remminafile, "shareprinter", FALSE)) {
+ RDPDR_PRINTER* printer;
+ printer = (RDPDR_PRINTER*)malloc(sizeof(RDPDR_PRINTER));
+ ZeroMemory(printer, sizeof(RDPDR_PRINTER));
+
+ printer->Type = RDPDR_DTYP_PRINT;
+
+ rfi->settings->DeviceRedirection = TRUE;
+ rfi->settings->RedirectPrinters = TRUE;
+
+ freerdp_device_collection_add(rfi->settings, (RDPDR_DEVICE*)printer);
+ }
+
+ if (remmina_plugin_service->file_get_int(remminafile, "sharesmartcard", FALSE)) {
+ RDPDR_SMARTCARD* smartcard;
+ smartcard = (RDPDR_SMARTCARD*)malloc(sizeof(RDPDR_SMARTCARD));
+ ZeroMemory(smartcard, sizeof(RDPDR_SMARTCARD));
+
+ smartcard->Type = RDPDR_DTYP_SMARTCARD;
+
+ smartcard->Name = _strdup("scard");
+
+ rfi->settings->DeviceRedirection = TRUE;
+ rfi->settings->RedirectSmartCards = TRUE;
+
+ freerdp_device_collection_add(rfi->settings, (RDPDR_DEVICE*)smartcard);
+ }
+
+ if (!freerdp_connect(rfi->instance)) {
+ if (!rfi->user_cancelled) {
+ UINT32 e;
+
+ e = freerdp_get_last_error(rfi->instance->context);
+
+ switch (e) {
+ case FREERDP_ERROR_AUTHENTICATION_FAILED:
+ case STATUS_LOGON_FAILURE: // wrong return code from FreeRDP introduced at the end of July 2016 ? (fixed with b86c0ba)
+#ifdef FREERDP_ERROR_CONNECT_LOGON_FAILURE
+ case FREERDP_ERROR_CONNECT_LOGON_FAILURE:
+#endif
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Authentication to RDP server %s failed.\nCheck username, password and domain."),
+ rfi->settings->ServerHostname );
+ // Invalidate the saved password, so the user will be re-asked at next logon
+ remmina_plugin_service->file_unsave_password(remminafile);
+ break;
+ case STATUS_ACCOUNT_LOCKED_OUT:
+#ifdef FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT
+ case FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT:
+#endif
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Access to RDP server %s failed.\nAccount is locked out."),
+ rfi->settings->ServerHostname );
+ break;
+ case STATUS_ACCOUNT_EXPIRED:
+#ifdef FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED
+ case FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED:
+#endif
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Access to RDP server %s failed.\nAccount is expired."),
+ rfi->settings->ServerHostname );
+ break;
+ case STATUS_PASSWORD_EXPIRED:
+#ifdef FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED
+ case FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED:
+#endif
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Access to RDP server %s failed.\nPassword expired."),
+ rfi->settings->ServerHostname );
+ break;
+ case STATUS_ACCOUNT_DISABLED:
+#ifdef FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED
+ case FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED:
+#endif
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Access to RDP server %s failed.\nAccount is disabled."),
+ rfi->settings->ServerHostname );
+ break;
+ case STATUS_ACCOUNT_RESTRICTION:
+#ifdef FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION
+ case FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION:
+#endif
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Access to RDP server %s failed.\nAccount has restrictions."),
+ rfi->settings->ServerHostname );
+ break;
+ case FREERDP_ERROR_CONNECT_FAILED:
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Connection to RDP server %s failed."), rfi->settings->ServerHostname );
+ break;
+ case FREERDP_ERROR_DNS_NAME_NOT_FOUND:
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Unable to find the address of RDP server %s."), rfi->settings->ServerHostname );
+ break;
+ case FREERDP_ERROR_TLS_CONNECT_FAILED:
+ remmina_plugin_service->protocol_plugin_set_error(gp,
+ _("Error connecting to RDP server %s. TLS connection failed. Check that client and server support a common TLS version."), rfi->settings->ServerHostname );
+ break;
+ case FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED:
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Unable to establish a connection to RDP server %s."), rfi->settings->ServerHostname );
+ break;
+#ifdef FREERDP_ERROR_POST_CONNECT_FAILED
+ case FREERDP_ERROR_POST_CONNECT_FAILED:
+ /* remmina_rdp_post_connect() returned FALSE to libfreerdp. We saved the error on rfi->postconnect_error */
+ switch(rfi->postconnect_error) {
+ case REMMINA_POSTCONNECT_ERROR_OK:
+ /* We should never come here */
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Unable to connect to RDP server %s."), rfi->settings->ServerHostname );
+ break;
+ case REMMINA_POSTCONNECT_ERROR_GDI_INIT:
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Unable to initialize libfreerdp gdi") );
+ break;
+ case REMMINA_POSTCONNECT_ERROR_NO_H264:
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("You requested an H264 GFX mode for server %s, but your libfreerdp does not support H264. Please check Color Depth settings."), rfi->settings->ServerHostname);
+ break;
+ }
+ break;
+#endif
+ default:
+ g_printf("%08X %08X\n", e, (unsigned)ERRCONNECT_POST_CONNECT_FAILED);
+ remmina_plugin_service->protocol_plugin_set_error(gp, _("Unable to connect to RDP server %s"), rfi->settings->ServerHostname);
+ break;
+ }
+
+ }
+
+ return FALSE;
+ }
+
+ remmina_rdp_main_loop(gp);
+
+ return TRUE;
+}
+
+static gpointer remmina_rdp_main_thread(gpointer data)
+{
+ TRACE_CALL(__func__);
+ RemminaProtocolWidget* gp;
+ rfContext* rfi;
+
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ CANCEL_ASYNC
+
+ gp = (RemminaProtocolWidget*)data;
+ rfi = GET_PLUGIN_DATA(gp);
+ remmina_rdp_main(gp);
+ rfi->thread = 0;
+
+
+ /* Signal main thread that we closed the connection. But wait 200ms, because we may
+ * have outstaiding events to process in the meanwhile */
+ g_timeout_add(200, ((GSourceFunc)remmina_plugin_service->protocol_plugin_close_connection), gp);
+
+ return NULL;
+}
+
+static void remmina_rdp_init(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ freerdp* instance;
+ rfContext* rfi;
+
+ instance = freerdp_new();
+ instance->PreConnect = remmina_rdp_pre_connect;
+ instance->PostConnect = remmina_rdp_post_connect;
+ instance->Authenticate = remmina_rdp_authenticate;
+ instance->VerifyCertificate = remmina_rdp_verify_certificate;
+ instance->VerifyChangedCertificate = remmina_rdp_verify_changed_certificate;
+
+ instance->ContextSize = sizeof(rfContext);
+ freerdp_context_new(instance);
+ rfi = (rfContext*)instance->context;
+
+ g_object_set_data_full(G_OBJECT(gp), "plugin-data", rfi, free);
+
+ rfi->protocol_widget = gp;
+ rfi->instance = instance;
+ rfi->settings = instance->settings;
+ rfi->connected = False;
+ rfi->is_reconnecting = False;
+
+ freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0);
+
+ remmina_rdp_event_init(gp);
+}
+
+static gboolean remmina_rdp_open_connection(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+
+ rfi->scale = remmina_plugin_service->remmina_protocol_widget_get_current_scale_mode(gp);
+
+ if (pthread_create(&rfi->thread, NULL, remmina_rdp_main_thread, gp)) {
+ remmina_plugin_service->protocol_plugin_set_error(gp, "%s",
+ "Failed to initialize pthread. Falling back to non-thread mode...");
+
+ rfi->thread = 0;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean remmina_rdp_close_connection(RemminaProtocolWidget* gp)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ freerdp* instance;
+
+ if (!remmina_plugin_service->is_main_thread()) {
+ g_printf("WARNING: %s called on a subthread, may not work or crash remmina.\n", __func__);
+ }
+
+ /* Immediately deatch GTK clipboard from this connection */
+ remmina_rdp_cliprdr_detach_owner(gp);
+
+ if (freerdp_get_last_error(rfi->instance->context) == 0x10005) {
+ remmina_plugin_service->protocol_plugin_set_error(gp, "Another user connected to the server (%s), forcing the disconnection of the current connection.", rfi->settings->ServerHostname);
+ }
+ instance = rfi->instance;
+ if (rfi->thread) {
+ rfi->thread_cancelled = TRUE; // Avoid all rf_queue function to run
+ pthread_cancel(rfi->thread);
+
+ if (rfi->thread)
+ pthread_join(rfi->thread, NULL);
+
+ }
+
+ if (instance) {
+ if ( rfi->connected ) {
+ freerdp_disconnect(instance);
+ rfi->connected = False;
+ }
+ }
+
+ if (rfi->hdc) {
+ gdi_DeleteDC(rfi->hdc);
+ rfi->hdc = NULL;
+ }
+
+ remmina_rdp_clipboard_free(rfi);
+ if (rfi->rfx_context) {
+ rfx_context_free(rfi->rfx_context);
+ rfi->rfx_context = NULL;
+ }
+
+ if (instance) {
+ gdi_free(instance);
+ cache_free(instance->context->cache);
+ instance->context->cache = NULL;
+ }
+
+ /* Destroy event queue. Pending async events will be discarded. Should we flush it ? */
+ remmina_rdp_event_uninit(gp);
+
+ if (instance) {
+ freerdp_context_free(instance); /* context is rfContext* rfi */
+ freerdp_free(instance); /* This implicitly frees instance->context and rfi is no longer valid */
+ }
+
+ /* Remove instance->context from gp object data to avoid double free */
+ g_object_steal_data(G_OBJECT(gp), "plugin-data");
+
+ /* Now let remmina to complete its disconnection tasks */
+ remmina_plugin_service->protocol_plugin_emit_signal(gp, "disconnect");
+
+ return FALSE;
+}
+
+static gboolean remmina_rdp_query_feature(RemminaProtocolWidget* gp, const RemminaProtocolFeature* feature)
+{
+ TRACE_CALL(__func__);
+ return TRUE;
+}
+
+static void remmina_rdp_call_feature(RemminaProtocolWidget* gp, const RemminaProtocolFeature* feature)
+{
+ TRACE_CALL(__func__);
+ RemminaFile* remminafile;
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+
+ remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
+
+ switch (feature->id) {
+ case REMMINA_RDP_FEATURE_UNFOCUS:
+ remmina_rdp_event_unfocus(gp);
+ break;
+
+ case REMMINA_RDP_FEATURE_SCALE:
+ rfi->scale = remmina_plugin_service->remmina_protocol_widget_get_current_scale_mode(gp);
+ remmina_rdp_event_update_scale(gp);
+ break;
+
+ case REMMINA_RDP_FEATURE_DYNRESUPDATE:
+ break;
+
+ case REMMINA_RDP_FEATURE_TOOL_REFRESH:
+ gtk_widget_queue_draw_area(rfi->drawing_area, 0, 0,
+ remmina_plugin_service->protocol_plugin_get_width(gp),
+ remmina_plugin_service->protocol_plugin_get_height(gp));
+ break;
+
+ case REMMINA_RDP_FEATURE_TOOL_SENDCTRLALTDEL:
+ remmina_rdp_send_ctrlaltdel(gp);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Send a keystroke to the plugin window */
+static void remmina_rdp_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
+{
+ TRACE_CALL(__func__);
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ remmina_plugin_service->protocol_plugin_send_keys_signals(rfi->drawing_area,
+ keystrokes, keylen, GDK_KEY_PRESS | GDK_KEY_RELEASE);
+ return;
+}
+
+static gboolean remmina_rdp_get_screenshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd)
+{
+ rfContext* rfi = GET_PLUGIN_DATA(gp);
+ rdpGdi* gdi;
+ size_t szmem;
+
+ UINT32 bytesPerPixel;
+ UINT32 bitsPerPixel;
+
+ if (!rfi)
+ return FALSE;
+
+ gdi = ((rdpContext*)rfi)->gdi;
+
+ bytesPerPixel = GetBytesPerPixel(gdi->hdc->format);
+ bitsPerPixel = GetBitsPerPixel(gdi->hdc->format);
+
+ /** @todo we should lock freerdp subthread to update rfi->primary_buffer, rfi->gdi and w/h,
+ * from here to memcpy, but... how ? */
+
+ szmem = gdi->width * gdi->height * bytesPerPixel;
+
+ remmina_plugin_service->log_printf("[RDP] allocating %zu bytes for a full screenshot\n", szmem);
+ rpsd->buffer = malloc(szmem);
+ if (!rpsd->buffer) {
+ remmina_plugin_service->log_printf("[RDP] unable to allocate %zu bytes for a full screenshot\n", szmem);
+ return FALSE;
+ }
+ rpsd->width = gdi->width;
+ rpsd->height = gdi->height;
+ rpsd->bitsPerPixel = bitsPerPixel;
+ rpsd->bytesPerPixel = bytesPerPixel;
+
+ memcpy(rpsd->buffer, gdi->primary_buffer, szmem);
+
+ /* Returning TRUE instruct also the caller to deallocate rpsd->buffer */
+ return TRUE;
+
+}
+
+/* Array of key/value pairs for color depths */
+static gpointer colordepth_list[] =
+{
+ /* 1st one is the default in a new install */
+#ifdef WITH_GFX_H264
+ "66", N_("GFX AVC444 (32 bpp)"),
+ "65", N_("GFX AVC420 (32 bpp)"),
+#endif
+ "64", N_("GFX RFX (32 bpp)"),
+ "0", N_("RemoteFX (32 bpp)"),
+ "32", N_("True color (32 bpp)"),
+ "24", N_("True color (24 bpp)"),
+ "16", N_("High color (16 bpp)"),
+ "15", N_("High color (15 bpp)"),
+ "8", N_("256 colors (8 bpp)"),
+ NULL
+};
+
+/* Array of key/value pairs for quality selection */
+static gpointer quality_list[] =
+{
+ "0", N_("Poor (fastest)"),
+ "1", N_("Medium"),
+ "2", N_("Good"),
+ "9", N_("Best (slowest)"),
+ NULL
+};
+
+/* Array of key/value pairs for sound options */
+static gpointer sound_list[] =
+{
+ "off", N_("Off"),
+ "local", N_("Local"),
+ "local,11025,1", N_("Local - low quality"),
+ "local,22050,2", N_("Local - medium quality"),
+ "local,44100,2", N_("Local - high quality"),
+ "remote", N_("Remote"),
+ NULL
+};
+
+/* Array of key/value pairs for security */
+static gpointer security_list[] =
+{
+ "", N_("Negotiate"),
+ "nla", "NLA",
+ "tls", "TLS",
+ "rdp", "RDP",
+ 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) Unused pointer
+ */
+static const RemminaProtocolSetting remmina_rdp_basic_settings[] =
+{
+ { REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "username", N_("User name"), FALSE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("User password"), FALSE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "domain", N_("Domain"), FALSE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_RESOLUTION, "resolution", NULL, FALSE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "colordepth", N_("Color depth"), FALSE, colordepth_list, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_FOLDER, "sharefolder", N_("Share folder"), FALSE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableautoreconnect", N_("Disable automatic reconnection"), FALSE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, 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) Unused pointer
+ */
+static const RemminaProtocolSetting remmina_rdp_advanced_settings[] =
+{
+ { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "quality", N_("Quality"), FALSE, quality_list, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "sound", N_("Sound"), FALSE, sound_list, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "security", N_("Security"), FALSE, security_list, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "gateway_server", N_("RD Gateway server"), FALSE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "gateway_username", N_("RD Gateway username"), FALSE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "gateway_password", N_("RD Gateway password"), FALSE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "gateway_domain", N_("RD Gateway domain"), FALSE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "clientname", N_("Client name"), FALSE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "exec", N_("Startup program"), FALSE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "execpath", N_("Startup path"), FALSE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "loadbalanceinfo", N_("Load Balance Info"), FALSE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "cert_ignore", N_("Ignore certificate"), TRUE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "microphone", N_("Redirect local microphone"), TRUE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "sharesmartcard", N_("Share smartcard"), TRUE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "shareprinter", N_("Share local printers"), TRUE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablepasswordstoring", N_("Disable password storing"), TRUE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableclipboard", N_("Disable clipboard sync"), TRUE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "console", N_("Attach to console (2003/2003 R2)"), FALSE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "gateway_usage", N_("Server detection using RD Gateway"), FALSE, NULL, NULL},
+ { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL}
+};
+
+/* Array for available features.
+ * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */
+static const RemminaProtocolFeature remmina_rdp_features[] =
+{
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_RDP_FEATURE_TOOL_REFRESH, N_("Refresh"), NULL, NULL},
+ { REMMINA_PROTOCOL_FEATURE_TYPE_SCALE, REMMINA_RDP_FEATURE_SCALE, NULL, NULL, NULL},
+ { REMMINA_PROTOCOL_FEATURE_TYPE_DYNRESUPDATE, REMMINA_RDP_FEATURE_DYNRESUPDATE, NULL, NULL, NULL},
+ { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_RDP_FEATURE_TOOL_SENDCTRLALTDEL, N_("Send Ctrl+Alt+Delete"), NULL, NULL},
+ { REMMINA_PROTOCOL_FEATURE_TYPE_UNFOCUS, REMMINA_RDP_FEATURE_UNFOCUS, NULL, NULL, NULL},
+ { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL}
+};
+
+/* Protocol plugin definition and features */
+static RemminaProtocolPlugin remmina_rdp =
+{
+ REMMINA_PLUGIN_TYPE_PROTOCOL, // Type
+ "RDP", // Name
+ N_("RDP - Remote Desktop Protocol"), // Description
+ GETTEXT_PACKAGE, // Translation domain
+ REMMINA_PLUGIN_RDP_VERSION, // Version number
+ "remmina-rdp", // Icon for normal connection
+ "remmina-rdp-ssh", // Icon for SSH connection
+ remmina_rdp_basic_settings, // Array for basic settings
+ remmina_rdp_advanced_settings, // Array for advanced settings
+ REMMINA_PROTOCOL_SSH_SETTING_TUNNEL, // SSH settings type
+ remmina_rdp_features, // Array for available features
+ remmina_rdp_init, // Plugin initialization
+ remmina_rdp_open_connection, // Plugin open connection
+ remmina_rdp_close_connection, // Plugin close connection
+ remmina_rdp_query_feature, // Query for available features
+ remmina_rdp_call_feature, // Call a feature
+ remmina_rdp_keystroke, // Send a keystroke
+ remmina_rdp_get_screenshot // Screenshot
+};
+
+/* File plugin definition and features */
+static RemminaFilePlugin remmina_rdpf =
+{
+ REMMINA_PLUGIN_TYPE_FILE, // Type
+ "RDPF", // Name
+ N_("RDP - RDP File Handler"), // Description
+ GETTEXT_PACKAGE, // Translation domain
+ REMMINA_PLUGIN_RDP_VERSION, // Version number
+ remmina_rdp_file_import_test, // Test import function
+ remmina_rdp_file_import, // Import function
+ remmina_rdp_file_export_test, // Test export function
+ remmina_rdp_file_export, // Export function
+ NULL
+};
+
+/* Preferences plugin definition and features */
+static RemminaPrefPlugin remmina_rdps =
+{
+ REMMINA_PLUGIN_TYPE_PREF, // Type
+ "RDPS", // Name
+ N_("RDP - Preferences"), // Description
+ GETTEXT_PACKAGE, // Translation domain
+ REMMINA_PLUGIN_RDP_VERSION, // Version number
+ "RDP", // Label
+ remmina_rdp_settings_new // Preferences body function
+};
+
+G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService* service)
+{
+ int vermaj, vermin, verrev;
+
+ TRACE_CALL(__func__);
+ remmina_plugin_service = service;
+
+ /* Check that we are linked to the correct version of libfreerdp */
+
+ freerdp_get_version(&vermaj, &vermin, &verrev);
+ if (vermaj < FREERDP_REQUIRED_MAJOR ||
+ (vermaj == FREERDP_REQUIRED_MAJOR && ( vermin < FREERDP_REQUIRED_MINOR ||
+ (vermin == FREERDP_REQUIRED_MINOR && verrev < FREERDP_REQUIRED_REVISION) ) ) ) {
+ g_printf("Unable to load RDP plugin due to bad freerdp library version. Required "
+ "libfreerdp version is at least %d.%d.%d but we found libfreerdp version %d.%d.%d\n",
+ FREERDP_REQUIRED_MAJOR, FREERDP_REQUIRED_MINOR, FREERDP_REQUIRED_REVISION,
+ vermaj, vermin, verrev );
+ return FALSE;
+ }
+
+ bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR);
+ bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+
+ if (!service->register_plugin((RemminaPlugin*)&remmina_rdp))
+ return FALSE;
+
+ remmina_rdpf.export_hints = _("Export connection in Windows .rdp file format");
+
+ if (!service->register_plugin((RemminaPlugin*)&remmina_rdpf))
+ return FALSE;
+
+ if (!service->register_plugin((RemminaPlugin*)&remmina_rdps))
+ return FALSE;
+
+ remmina_rdp_settings_init();
+
+ return TRUE;
+}
+
diff --git a/plugins/rdp/rdp_plugin.h b/plugins/rdp/rdp_plugin.h
new file mode 100644
index 000000000..9a43ee6aa
--- /dev/null
+++ b/plugins/rdp/rdp_plugin.h
@@ -0,0 +1,309 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2010-2011 Vic Lee
+ * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
+ * Copyright (C) 2016-2017 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.
+ *
+ */
+
+#pragma once
+
+#include "common/remmina_plugin.h"
+#include <freerdp/freerdp.h>
+#include <freerdp/version.h>
+#include <freerdp/channels/channels.h>
+#include <freerdp/codec/color.h>
+#include <freerdp/codec/rfx.h>
+#include <freerdp/gdi/gdi.h>
+#include <freerdp/gdi/dc.h>
+#include <freerdp/gdi/region.h>
+#include <freerdp/client/cliprdr.h>
+#include <freerdp/client/disp.h>
+#include <gdk/gdkx.h>
+
+#include <winpr/clipboard.h>
+
+typedef struct rf_context rfContext;
+
+#define GET_PLUGIN_DATA(gp) (rfContext*)g_object_get_data(G_OBJECT(gp), "plugin-data")
+
+#define DEFAULT_QUALITY_0 0x6f
+#define DEFAULT_QUALITY_1 0x07
+#define DEFAULT_QUALITY_2 0x01
+#define DEFAULT_QUALITY_9 0x80
+
+#define REMMINA_PLUGIN_RDP_VERSION "RDP Plugin: " VERSION " (git " REMMINA_GIT_REVISION \
+ "), FreeRDP lib: " FREERDP_VERSION_FULL " (git " GIT_REVISION ")"
+
+extern RemminaPluginService* remmina_plugin_service;
+
+struct rf_clipboard {
+ rfContext* rfi;
+ CliprdrClientContext* context;
+ wClipboard* system;
+ int requestedFormatId;
+
+ UINT32 format;
+ gulong clipboard_handler;
+
+ pthread_mutex_t transfer_clip_mutex;
+ pthread_cond_t transfer_clip_cond;
+ enum { SCDW_NONE, SCDW_BUSY_WAIT, SCDW_ASYNCWAIT } srv_clip_data_wait;
+ gpointer srv_data;
+
+};
+typedef struct rf_clipboard rfClipboard;
+
+
+struct rf_pointer {
+ rdpPointer pointer;
+ GdkCursor* cursor;
+};
+typedef struct rf_pointer rfPointer;
+
+struct rf_bitmap {
+ rdpBitmap bitmap;
+ Pixmap pixmap;
+ cairo_surface_t* surface;
+};
+typedef struct rf_bitmap rfBitmap;
+
+struct rf_glyph {
+ rdpGlyph glyph;
+ Pixmap pixmap;
+};
+typedef struct rf_glyph rfGlyph;
+
+
+typedef enum {
+ REMMINA_RDP_EVENT_TYPE_SCANCODE,
+ REMMINA_RDP_EVENT_TYPE_SCANCODE_UNICODE,
+ REMMINA_RDP_EVENT_TYPE_MOUSE,
+ REMMINA_RDP_EVENT_TYPE_CLIPBOARD_SEND_CLIENT_FORMAT_LIST,
+ REMMINA_RDP_EVENT_TYPE_CLIPBOARD_SEND_CLIENT_FORMAT_DATA_RESPONSE,
+ REMMINA_RDP_EVENT_TYPE_CLIPBOARD_SEND_CLIENT_FORMAT_DATA_REQUEST,
+ REMMINA_RDP_EVENT_TYPE_SEND_MONITOR_LAYOUT
+} RemminaPluginRdpEventType;
+
+struct remmina_plugin_rdp_event {
+ RemminaPluginRdpEventType type;
+ union {
+ struct {
+ BOOL up;
+ BOOL extended;
+ UINT8 key_code;
+ UINT32 unicode_code;
+ } key_event;
+ struct {
+ UINT16 flags;
+ UINT16 x;
+ UINT16 y;
+ BOOL extended;
+ } mouse_event;
+ struct {
+ CLIPRDR_FORMAT_LIST* pFormatList;
+ } clipboard_formatlist;
+ struct {
+ CLIPRDR_FORMAT_DATA_RESPONSE* pFormatDataResponse;
+ } clipboard_formatdataresponse;
+ struct {
+ CLIPRDR_FORMAT_DATA_REQUEST* pFormatDataRequest;
+ } clipboard_formatdatarequest;
+ struct {
+ gint width;
+ gint height;
+ gint desktopOrientation;
+ gint desktopScaleFactor;
+ gint deviceScaleFactor;
+ } monitor_layout;
+ };
+};
+typedef struct remmina_plugin_rdp_event RemminaPluginRdpEvent;
+
+typedef enum {
+ REMMINA_RDP_UI_UPDATE_REGION = 0,
+ REMMINA_RDP_UI_CONNECTED,
+ REMMINA_RDP_UI_RECONNECT_PROGRESS,
+ REMMINA_RDP_UI_CURSOR,
+ REMMINA_RDP_UI_RFX,
+ REMMINA_RDP_UI_NOCODEC,
+ REMMINA_RDP_UI_CLIPBOARD,
+ REMMINA_RDP_UI_EVENT
+} RemminaPluginRdpUiType;
+
+typedef enum {
+ REMMINA_RDP_UI_CLIPBOARD_FORMATLIST,
+ REMMINA_RDP_UI_CLIPBOARD_GET_DATA,
+ REMMINA_RDP_UI_CLIPBOARD_SET_DATA,
+ REMMINA_RDP_UI_CLIPBOARD_SET_CONTENT
+} RemminaPluginRdpUiClipboardType;
+
+typedef enum {
+ REMMINA_RDP_POINTER_NEW,
+ REMMINA_RDP_POINTER_FREE,
+ REMMINA_RDP_POINTER_SET,
+ REMMINA_RDP_POINTER_NULL,
+ REMMINA_RDP_POINTER_DEFAULT,
+ REMMINA_RDP_POINTER_SETPOS
+} RemminaPluginRdpUiPointerType;
+
+typedef enum {
+ REMMINA_RDP_UI_EVENT_UPDATE_SCALE
+} RemminaPluginRdpUiEeventType;
+
+struct remmina_plugin_rdp_ui_object {
+ RemminaPluginRdpUiType type;
+ gboolean sync;
+ gboolean complete;
+ pthread_mutex_t sync_wait_mutex;
+ pthread_cond_t sync_wait_cond;
+ union {
+ struct {
+ gint x;
+ gint y;
+ gint width;
+ gint height;
+ } region;
+ struct {
+ rdpContext* context;
+ rfPointer* pointer;
+ RemminaPluginRdpUiPointerType type;
+ } cursor;
+ struct {
+ gint left;
+ gint top;
+ RFX_MESSAGE* message;
+ } rfx;
+ struct {
+ gint left;
+ gint top;
+ gint width;
+ gint height;
+ UINT8* bitmap;
+ } nocodec;
+ struct {
+ RemminaPluginRdpUiClipboardType type;
+ GtkTargetList* targetlist;
+ UINT32 format;
+ rfClipboard* clipboard;
+ gpointer data;
+ } clipboard;
+ struct {
+ RemminaPluginRdpUiEeventType type;
+ } event;
+ struct {
+ gint x;
+ gint y;
+ } pos;
+ };
+ /* We can also return values here, usually integers*/
+ int retval;
+ /* Some functions also may return a pointer. */
+ void *retptr;
+};
+
+struct rf_context {
+ rdpContext _p;
+
+ RemminaProtocolWidget* protocol_widget;
+
+ /* main */
+ rdpSettings* settings;
+ freerdp* instance;
+
+ pthread_t thread;
+ RemminaScaleMode scale;
+ gboolean user_cancelled;
+ gboolean thread_cancelled;
+
+ CliprdrClientContext* cliprdr;
+ DispClientContext* dispcontext;
+
+ RDP_PLUGIN_DATA rdpdr_data[5];
+ RDP_PLUGIN_DATA drdynvc_data[5];
+ gchar rdpsnd_options[20];
+
+ RFX_CONTEXT* rfx_context;
+
+ gboolean connected;
+ gboolean is_reconnecting;
+ int reconnect_maxattempts;
+ int reconnect_nattempt;
+
+ gboolean sw_gdi;
+ GtkWidget* drawing_area;
+ gint scale_width;
+ gint scale_height;
+ gdouble scale_x;
+ gdouble scale_y;
+ guint delayed_monitor_layout_handler;
+ gboolean use_client_keymap;
+
+ HGDI_DC hdc;
+ gint srcBpp;
+ GdkDisplay* display;
+ GdkVisual* visual;
+ cairo_surface_t* surface;
+ cairo_format_t cairo_format;
+ gint bpp;
+ gint width;
+ gint height;
+ gint scanline_pad;
+ gint* colormap;
+ UINT8* primary_buffer;
+
+ guint object_id_seq;
+ GHashTable* object_table;
+
+ GAsyncQueue* ui_queue;
+ pthread_mutex_t ui_queue_mutex;
+ guint ui_handler;
+
+ GArray* pressed_keys;
+ GAsyncQueue* event_queue;
+ gint event_pipe[2];
+ HANDLE event_handle;
+
+ rfClipboard clipboard;
+
+ enum { REMMINA_POSTCONNECT_ERROR_OK = 0, REMMINA_POSTCONNECT_ERROR_GDI_INIT = 1, REMMINA_POSTCONNECT_ERROR_NO_H264 } postconnect_error;
+};
+
+typedef struct remmina_plugin_rdp_ui_object RemminaPluginRdpUiObject;
+
+void rf_init(RemminaProtocolWidget* gp);
+void rf_uninit(RemminaProtocolWidget* gp);
+void rf_get_fds(RemminaProtocolWidget* gp, void** rfds, int* rcount);
+BOOL rf_check_fds(RemminaProtocolWidget* gp);
+void rf_object_free(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* obj);
+
+void remmina_rdp_event_event_push(RemminaProtocolWidget* gp, const RemminaPluginRdpEvent* e);
+
diff --git a/plugins/rdp/rdp_settings.c b/plugins/rdp/rdp_settings.c
new file mode 100644
index 000000000..592feae07
--- /dev/null
+++ b/plugins/rdp/rdp_settings.c
@@ -0,0 +1,640 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2010-2011 Vic Lee
+ * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
+ * Copyright (C) 2016-2017 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 "rdp_plugin.h"
+#include "rdp_settings.h"
+#include <freerdp/locale/keyboard.h>
+
+static guint keyboard_layout = 0;
+static guint rdp_keyboard_layout = 0;
+
+static void remmina_rdp_settings_kbd_init(void)
+{
+ TRACE_CALL(__func__);
+ keyboard_layout = freerdp_keyboard_init(rdp_keyboard_layout);
+}
+
+void remmina_rdp_settings_init(void)
+{
+ TRACE_CALL(__func__);
+ gchar* value;
+
+ value = remmina_plugin_service->pref_get_value("rdp_keyboard_layout");
+
+ if (value && value[0])
+ rdp_keyboard_layout = strtoul(value, NULL, 16);
+
+ g_free(value);
+
+ remmina_rdp_settings_kbd_init();
+}
+
+guint remmina_rdp_settings_get_keyboard_layout(void)
+{
+ TRACE_CALL(__func__);
+ return keyboard_layout;
+}
+
+#define REMMINA_TYPE_PLUGIN_RDPSET_GRID (remmina_rdp_settings_grid_get_type())
+#define REMMINA_RDPSET_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), REMMINA_TYPE_PLUGIN_RDPSET_GRID, RemminaPluginRdpsetGrid))
+#define REMMINA_RDPSET_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), REMMINA_TYPE_PLUGIN_RDPSET_GRID, RemminaPluginRdpsetGridClass))
+#define REMMINA_IS_PLUGIN_RDPSET_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), REMMINA_TYPE_PLUGIN_RDPSET_GRID))
+#define REMMINA_IS_PLUGIN_RDPSET_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), REMMINA_TYPE_PLUGIN_RDPSET_GRID))
+#define REMMINA_RDPSET_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), REMMINA_TYPE_PLUGIN_RDPSET_GRID, RemminaPluginRdpsetGridClass))
+
+typedef struct _RemminaPluginRdpsetGrid {
+ GtkGrid grid;
+
+ GtkWidget* keyboard_layout_label;
+ GtkWidget* keyboard_layout_combo;
+ GtkListStore* keyboard_layout_store;
+
+ GtkWidget* quality_combo;
+ GtkListStore* quality_store;
+ GtkWidget* wallpaper_check;
+ GtkWidget* windowdrag_check;
+ GtkWidget* menuanimation_check;
+ GtkWidget* theme_check;
+ GtkWidget* cursorshadow_check;
+ GtkWidget* cursorblinking_check;
+ GtkWidget* fontsmoothing_check;
+ GtkWidget* composition_check;
+ GtkWidget* use_client_keymap_check;
+
+ /* FreeRDP /scale-desktop: Scaling of desktop app */
+ GtkWidget* desktop_scale_factor_spin;
+ /* FreeRDP /scale-device: Scaling of appstore app */
+ GtkListStore* device_scale_factor_store;
+ GtkWidget* device_scale_factor_combo;
+ /* FreeRDP /orientation: Orientation of display */
+ GtkListStore* desktop_orientation_store;
+ GtkWidget* desktop_orientation_combo;
+
+ guint quality_values[10];
+} RemminaPluginRdpsetGrid;
+
+typedef struct _RemminaPluginRdpsetGridClass {
+ GtkGridClass parent_class;
+} RemminaPluginRdpsetGridClass;
+
+GType remmina_rdp_settings_grid_get_type(void) G_GNUC_CONST;
+
+G_DEFINE_TYPE(RemminaPluginRdpsetGrid, remmina_rdp_settings_grid, GTK_TYPE_GRID)
+
+static void remmina_rdp_settings_grid_class_init(RemminaPluginRdpsetGridClass* klass)
+{
+ TRACE_CALL(__func__);
+}
+
+static void remmina_rdp_settings_grid_destroy(GtkWidget* widget, gpointer data)
+{
+ TRACE_CALL(__func__);
+ gchar* s;
+ guint new_layout;
+ GtkTreeIter iter;
+ RemminaPluginRdpsetGrid* grid;
+ gint val;
+
+ grid = REMMINA_RDPSET_GRID(widget);
+
+ if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(grid->keyboard_layout_combo), &iter)) {
+ gtk_tree_model_get(GTK_TREE_MODEL(grid->keyboard_layout_store), &iter, 0, &new_layout, -1);
+
+ if (new_layout != rdp_keyboard_layout) {
+ rdp_keyboard_layout = new_layout;
+ s = g_strdup_printf("%X", new_layout);
+ remmina_plugin_service->pref_set_value("rdp_keyboard_layout", s);
+ g_free(s);
+
+ remmina_rdp_settings_kbd_init();
+ }
+ }
+
+ remmina_plugin_service->pref_set_value("rdp_use_client_keymap",
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(grid->use_client_keymap_check)) ? "1" : "0");
+
+ s = g_strdup_printf("%X", grid->quality_values[0]);
+ remmina_plugin_service->pref_set_value("rdp_quality_0", s);
+ g_free(s);
+
+ s = g_strdup_printf("%X", grid->quality_values[1]);
+ remmina_plugin_service->pref_set_value("rdp_quality_1", s);
+ g_free(s);
+
+ s = g_strdup_printf("%X", grid->quality_values[2]);
+ remmina_plugin_service->pref_set_value("rdp_quality_2", s);
+ g_free(s);
+
+ s = g_strdup_printf("%X", grid->quality_values[9]);
+ remmina_plugin_service->pref_set_value("rdp_quality_9", s);
+ g_free(s);
+
+ if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(grid->device_scale_factor_combo), &iter)) {
+ gtk_tree_model_get(GTK_TREE_MODEL(grid->device_scale_factor_store), &iter, 0, &val, -1);
+ } else {
+ val = 0;
+ }
+ s = g_strdup_printf("%d", val);
+ remmina_plugin_service->pref_set_value("rdp_deviceScaleFactor", s);
+ g_free(s);
+
+ val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(grid->desktop_scale_factor_spin));
+ s = g_strdup_printf("%d", val);
+ remmina_plugin_service->pref_set_value("rdp_desktopScaleFactor", s);
+ g_free(s);
+
+ if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(grid->desktop_orientation_combo), &iter)) {
+ gtk_tree_model_get(GTK_TREE_MODEL(grid->desktop_orientation_store), &iter, 0, &val, -1);
+ } else {
+ val = 0;
+ }
+ s = g_strdup_printf("%d", val);
+ remmina_plugin_service->pref_set_value("rdp_desktopOrientation", s);
+ g_free(s);
+
+}
+
+static void remmina_rdp_settings_grid_load_layout(RemminaPluginRdpsetGrid* grid)
+{
+ TRACE_CALL(__func__);
+ gint i;
+ gchar* s;
+ GtkTreeIter iter;
+ RDP_KEYBOARD_LAYOUT* layouts;
+
+ gtk_list_store_append(grid->keyboard_layout_store, &iter);
+ gtk_list_store_set(grid->keyboard_layout_store, &iter, 0, 0, 1, _("<Auto detect>"), -1);
+
+ if (rdp_keyboard_layout == 0)
+ gtk_combo_box_set_active(GTK_COMBO_BOX(grid->keyboard_layout_combo), 0);
+
+ gtk_label_set_text(GTK_LABEL(grid->keyboard_layout_label), "-");
+
+ layouts = freerdp_keyboard_get_layouts(RDP_KEYBOARD_LAYOUT_TYPE_STANDARD | RDP_KEYBOARD_LAYOUT_TYPE_VARIANT);
+
+ for (i = 0; layouts[i].code; i++) {
+ s = g_strdup_printf("%08X - %s", layouts[i].code, layouts[i].name);
+ gtk_list_store_append(grid->keyboard_layout_store, &iter);
+ gtk_list_store_set(grid->keyboard_layout_store, &iter, 0, layouts[i].code, 1, s, -1);
+
+ if (rdp_keyboard_layout == layouts[i].code)
+ gtk_combo_box_set_active(GTK_COMBO_BOX(grid->keyboard_layout_combo), i + 1);
+
+ if (keyboard_layout == layouts[i].code)
+ gtk_label_set_text(GTK_LABEL(grid->keyboard_layout_label), s);
+
+ g_free(s);
+ }
+
+ free(layouts);
+}
+
+
+static void remmina_rdp_settings_grid_load_devicescalefactor_combo(RemminaPluginRdpsetGrid* grid)
+{
+ TRACE_CALL(__func__);
+ GtkTreeIter iter;
+
+ gtk_list_store_append(grid->device_scale_factor_store, &iter);
+ gtk_list_store_set(grid->device_scale_factor_store, &iter, 0, 0, 1, _("<Not set>"), -1);
+ gtk_list_store_append(grid->device_scale_factor_store, &iter);
+ gtk_list_store_set(grid->device_scale_factor_store, &iter, 0, 100, 1, "100%", -1);
+ gtk_list_store_append(grid->device_scale_factor_store, &iter);
+ gtk_list_store_set(grid->device_scale_factor_store, &iter, 0, 140, 1, "140%", -1);
+ gtk_list_store_append(grid->device_scale_factor_store, &iter);
+ gtk_list_store_set(grid->device_scale_factor_store, &iter, 0, 180, 1, "180%", -1);
+
+}
+
+static void remmina_rdp_settings_grid_load_desktoporientation_combo(RemminaPluginRdpsetGrid* grid)
+{
+ TRACE_CALL(__func__);
+ GtkTreeIter iter;
+
+ gtk_list_store_append(grid->desktop_orientation_store, &iter);
+ gtk_list_store_set(grid->desktop_orientation_store, &iter, 0, 0, 1, "0°", -1);
+ gtk_list_store_append(grid->desktop_orientation_store, &iter);
+ gtk_list_store_set(grid->desktop_orientation_store, &iter, 0, 90, 1, "90°", -1);
+ gtk_list_store_append(grid->desktop_orientation_store, &iter);
+ gtk_list_store_set(grid->desktop_orientation_store, &iter, 0, 180, 1, "180°", -1);
+ gtk_list_store_append(grid->desktop_orientation_store, &iter);
+ gtk_list_store_set(grid->desktop_orientation_store, &iter, 0, 270, 1, "270°", -1);
+
+}
+
+
+static void remmina_rdp_settings_grid_load_quality(RemminaPluginRdpsetGrid* grid)
+{
+ TRACE_CALL(__func__);
+ gchar* value;
+ GtkTreeIter iter;
+
+ gtk_list_store_append(grid->quality_store, &iter);
+ gtk_list_store_set(grid->quality_store, &iter, 0, -1, 1, _("<Choose a quality level to edit...>"), -1);
+ gtk_list_store_append(grid->quality_store, &iter);
+ gtk_list_store_set(grid->quality_store, &iter, 0, 0, 1, _("Poor (fastest)"), -1);
+ gtk_list_store_append(grid->quality_store, &iter);
+ gtk_list_store_set(grid->quality_store, &iter, 0, 1, 1, _("Medium"), -1);
+ gtk_list_store_append(grid->quality_store, &iter);
+ gtk_list_store_set(grid->quality_store, &iter, 0, 2, 1, _("Good"), -1);
+ gtk_list_store_append(grid->quality_store, &iter);
+ gtk_list_store_set(grid->quality_store, &iter, 0, 9, 1, _("Best (slowest)"), -1);
+
+ memset(grid->quality_values, 0, sizeof(grid->quality_values));
+
+ value = remmina_plugin_service->pref_get_value("rdp_quality_0");
+ grid->quality_values[0] = (value && value[0] ? strtoul(value, NULL, 16) : DEFAULT_QUALITY_0);
+ g_free(value);
+
+ value = remmina_plugin_service->pref_get_value("rdp_quality_1");
+ grid->quality_values[1] = (value && value[0] ? strtoul(value, NULL, 16) : DEFAULT_QUALITY_1);
+ g_free(value);
+
+ value = remmina_plugin_service->pref_get_value("rdp_quality_2");
+ grid->quality_values[2] = (value && value[0] ? strtoul(value, NULL, 16) : DEFAULT_QUALITY_2);
+ g_free(value);
+
+ value = remmina_plugin_service->pref_get_value("rdp_quality_9");
+ grid->quality_values[9] = (value && value[0] ? strtoul(value, NULL, 16) : DEFAULT_QUALITY_9);
+ g_free(value);
+}
+
+static void remmina_rdp_settings_appscale_on_changed(GtkComboBox *widget, RemminaPluginRdpsetGrid *grid)
+{
+ TRACE_CALL(__func__);
+ GtkTreeIter iter;
+ guint i = 0;
+
+ if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(grid->device_scale_factor_combo), &iter)) {
+ gtk_tree_model_get(GTK_TREE_MODEL(grid->device_scale_factor_store), &iter, 0, &i, -1);
+ }
+ if (i == 0) {
+ gtk_widget_set_sensitive(GTK_WIDGET(grid->desktop_scale_factor_spin), FALSE);
+ gtk_spin_button_set_range(GTK_SPIN_BUTTON(grid->desktop_scale_factor_spin), 0, 0);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(grid->desktop_scale_factor_spin), 0);
+ }else {
+ gtk_widget_set_sensitive(GTK_WIDGET(grid->desktop_scale_factor_spin), TRUE);
+ gtk_spin_button_set_range(GTK_SPIN_BUTTON(grid->desktop_scale_factor_spin), 100, 500);
+ // gtk_spin_button_set_value(GTK_SPIN_BUTTON(grid->desktop_scale_factor_spin), i);
+ }
+}
+
+static void remmina_rdp_settings_quality_on_changed(GtkComboBox *widget, RemminaPluginRdpsetGrid *grid)
+{
+ TRACE_CALL(__func__);
+ guint v;
+ guint i = 0;
+ GtkTreeIter iter;
+ gboolean sensitive;
+
+ if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(grid->quality_combo), &iter)) {
+ gtk_tree_model_get(GTK_TREE_MODEL(grid->quality_store), &iter, 0, &i, -1);
+ sensitive = ( i != -1 );
+
+ if (sensitive)
+ v = grid->quality_values[i];
+ else
+ v = 0x3f; /* All checkboxes disabled */
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(grid->wallpaper_check), (v & 1) == 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(grid->windowdrag_check), (v & 2) == 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(grid->menuanimation_check), (v & 4) == 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(grid->theme_check), (v & 8) == 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(grid->cursorshadow_check), (v & 0x20) == 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(grid->cursorblinking_check), (v & 0x40) == 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(grid->fontsmoothing_check), (v & 0x80) != 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(grid->composition_check), (v & 0x100) != 0);
+
+
+ gtk_widget_set_sensitive(GTK_WIDGET(grid->wallpaper_check), sensitive);
+ gtk_widget_set_sensitive(GTK_WIDGET(grid->windowdrag_check), sensitive);
+ gtk_widget_set_sensitive(GTK_WIDGET(grid->menuanimation_check), sensitive);
+ gtk_widget_set_sensitive(GTK_WIDGET(grid->theme_check), sensitive);
+ gtk_widget_set_sensitive(GTK_WIDGET(grid->cursorshadow_check), sensitive);
+ gtk_widget_set_sensitive(GTK_WIDGET(grid->cursorblinking_check), sensitive);
+ gtk_widget_set_sensitive(GTK_WIDGET(grid->fontsmoothing_check), sensitive);
+ gtk_widget_set_sensitive(GTK_WIDGET(grid->composition_check), sensitive);
+ }
+}
+
+static void remmina_rdp_settings_quality_option_on_toggled(GtkToggleButton* togglebutton, RemminaPluginRdpsetGrid* grid)
+{
+ TRACE_CALL(__func__);
+ guint v;
+ guint i = 0;
+ GtkTreeIter iter;
+
+ if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(grid->quality_combo), &iter)) {
+ gtk_tree_model_get(GTK_TREE_MODEL(grid->quality_store), &iter, 0, &i, -1);
+ if (i != -1) {
+ v = 0;
+ v |= (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(grid->wallpaper_check)) ? 0 : 1);
+ v |= (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(grid->windowdrag_check)) ? 0 : 2);
+ v |= (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(grid->menuanimation_check)) ? 0 : 4);
+ v |= (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(grid->theme_check)) ? 0 : 8);
+ v |= (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(grid->cursorshadow_check)) ? 0 : 0x20);
+ v |= (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(grid->cursorblinking_check)) ? 0 : 0x40);
+ v |= (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(grid->fontsmoothing_check)) ? 0x80 : 0);
+ v |= (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(grid->composition_check)) ? 0x100 : 0);
+ grid->quality_values[i] = v;
+ }
+ }
+}
+
+static void remmina_rdp_settings_set_combo_active_item(GtkComboBox* combo, int itemval)
+{
+ GtkTreeIter iter;
+ int i;
+ GtkTreeModel *m;
+ gboolean valid;
+
+ m = gtk_combo_box_get_model(combo);
+ if (!m) {
+ return;
+ }
+
+ valid = gtk_tree_model_get_iter_first(m, &iter);
+ while (valid) {
+ gtk_tree_model_get(m, &iter, 0, &i, -1);
+ if (i == itemval) {
+ gtk_combo_box_set_active_iter(combo, &iter);
+ }
+ valid = gtk_tree_model_iter_next(m, &iter);
+ }
+
+}
+
+static void remmina_rdp_settings_grid_init(RemminaPluginRdpsetGrid *grid)
+{
+ TRACE_CALL(__func__);
+ gchar* s;
+ GtkWidget* widget;
+ GtkCellRenderer* renderer;
+ int desktopOrientation, desktopScaleFactor, deviceScaleFactor;
+
+ /* Create the grid */
+ g_signal_connect(G_OBJECT(grid), "destroy", G_CALLBACK(remmina_rdp_settings_grid_destroy), NULL);
+ gtk_grid_set_row_homogeneous(GTK_GRID(grid), FALSE);
+ gtk_grid_set_column_homogeneous(GTK_GRID(grid), FALSE);
+ gtk_container_set_border_width(GTK_CONTAINER(grid), 8);
+ gtk_grid_set_row_spacing(GTK_GRID(grid), 4);
+ gtk_grid_set_column_spacing(GTK_GRID(grid), 4);
+
+ /* Create the content */
+ widget = gtk_label_new(_("Keyboard layout"));
+ gtk_widget_show(widget);
+ gtk_widget_set_halign(GTK_WIDGET(widget), GTK_ALIGN_START);
+ gtk_widget_set_valign(GTK_WIDGET(widget), GTK_ALIGN_CENTER);
+ gtk_grid_attach(GTK_GRID(grid), widget, 0, 0, 1, 1);
+
+ grid->keyboard_layout_store = gtk_list_store_new(2, G_TYPE_UINT, G_TYPE_STRING);
+ widget = gtk_combo_box_new_with_model(GTK_TREE_MODEL(grid->keyboard_layout_store));
+ gtk_widget_show(widget);
+ gtk_grid_attach(GTK_GRID(grid), widget, 1, 0, 4, 1);
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), renderer, TRUE);
+ gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(widget), renderer, "text", 1);
+ grid->keyboard_layout_combo = widget;
+
+ widget = gtk_label_new("-");
+ gtk_widget_show(widget);
+ gtk_widget_set_halign(GTK_WIDGET(widget), GTK_ALIGN_START);
+ gtk_widget_set_valign(GTK_WIDGET(widget), GTK_ALIGN_CENTER);
+ gtk_grid_attach(GTK_GRID(grid), widget, 1, 1, 4, 2);
+ grid->keyboard_layout_label = widget;
+
+ remmina_rdp_settings_grid_load_layout(grid);
+
+ widget = gtk_check_button_new_with_label(_("Use client keyboard mapping"));
+ gtk_widget_show(widget);
+ gtk_grid_attach(GTK_GRID(grid), widget, 1, 3, 3, 3);
+ grid->use_client_keymap_check = widget;
+
+ s = remmina_plugin_service->pref_get_value("rdp_use_client_keymap");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
+ s && s[0] == '1' ? TRUE : FALSE);
+ g_free(s);
+
+ widget = gtk_label_new(_("Quality settings"));
+ gtk_widget_show(widget);
+ gtk_widget_set_halign(GTK_WIDGET(widget), GTK_ALIGN_START);
+ gtk_widget_set_valign(GTK_WIDGET(widget), GTK_ALIGN_CENTER);
+ gtk_grid_attach(GTK_GRID(grid), widget, 0, 6, 1, 4);
+
+ grid->quality_store = gtk_list_store_new(2, G_TYPE_UINT, G_TYPE_STRING);
+ widget = gtk_combo_box_new_with_model(GTK_TREE_MODEL(grid->quality_store));
+ gtk_widget_show(widget);
+ gtk_grid_attach(GTK_GRID(grid), widget, 1, 6, 4, 4);
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), renderer, TRUE);
+ gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(widget), renderer, "text", 1);
+ g_signal_connect(G_OBJECT(widget), "changed",
+ G_CALLBACK(remmina_rdp_settings_quality_on_changed), grid);
+ grid->quality_combo = widget;
+
+ remmina_rdp_settings_grid_load_quality(grid);
+
+ widget = gtk_check_button_new_with_label(_("Wallpaper"));
+ gtk_widget_show(widget);
+ gtk_grid_attach(GTK_GRID(grid), widget, 1, 10, 2, 5);
+ g_signal_connect(G_OBJECT(widget), "toggled",
+ G_CALLBACK(remmina_rdp_settings_quality_option_on_toggled), grid);
+ grid->wallpaper_check = widget;
+
+ widget = gtk_check_button_new_with_label(_("Window drag"));
+ gtk_widget_show(widget);
+ gtk_grid_attach(GTK_GRID(grid), widget, 3, 10, 3, 5);
+ g_signal_connect(G_OBJECT(widget), "toggled",
+ G_CALLBACK(remmina_rdp_settings_quality_option_on_toggled), grid);
+ grid->windowdrag_check = widget;
+
+ widget = gtk_check_button_new_with_label(_("Menu animation"));
+ gtk_widget_show(widget);
+ gtk_grid_attach(GTK_GRID(grid), widget, 1, 13, 2, 6);
+ g_signal_connect(G_OBJECT(widget), "toggled",
+ G_CALLBACK(remmina_rdp_settings_quality_option_on_toggled), grid);
+ grid->menuanimation_check = widget;
+
+ widget = gtk_check_button_new_with_label(_("Theme"));
+ gtk_widget_show(widget);
+ gtk_grid_attach(GTK_GRID(grid), widget, 3, 13, 3, 6);
+ g_signal_connect(G_OBJECT(widget), "toggled",
+ G_CALLBACK(remmina_rdp_settings_quality_option_on_toggled), grid);
+ grid->theme_check = widget;
+
+ widget = gtk_check_button_new_with_label(_("Cursor shadow"));
+ gtk_widget_show(widget);
+ gtk_grid_attach(GTK_GRID(grid), widget, 1, 16, 2, 7);
+ g_signal_connect(G_OBJECT(widget), "toggled",
+ G_CALLBACK(remmina_rdp_settings_quality_option_on_toggled), grid);
+ grid->cursorshadow_check = widget;
+
+ widget = gtk_check_button_new_with_label(_("Cursor blinking"));
+ gtk_widget_show(widget);
+ gtk_grid_attach(GTK_GRID(grid), widget, 3, 16, 3, 7);
+ g_signal_connect(G_OBJECT(widget), "toggled",
+ G_CALLBACK(remmina_rdp_settings_quality_option_on_toggled), grid);
+ grid->cursorblinking_check = widget;
+
+ widget = gtk_check_button_new_with_label(_("Font smoothing"));
+ gtk_widget_show(widget);
+ gtk_grid_attach(GTK_GRID(grid), widget, 1, 19, 2, 8);
+ g_signal_connect(G_OBJECT(widget), "toggled",
+ G_CALLBACK(remmina_rdp_settings_quality_option_on_toggled), grid);
+ grid->fontsmoothing_check = widget;
+
+ widget = gtk_check_button_new_with_label(_("Composition"));
+ gtk_widget_show(widget);
+ gtk_grid_attach(GTK_GRID(grid), widget, 3, 19, 3, 8);
+ g_signal_connect(G_OBJECT(widget), "toggled",
+ G_CALLBACK(remmina_rdp_settings_quality_option_on_toggled), grid);
+ grid->composition_check = widget;
+
+ gtk_combo_box_set_active(GTK_COMBO_BOX(grid->quality_combo), 0);
+
+
+ widget = gtk_label_new(_("Remote scale factor"));
+ gtk_widget_show(widget);
+ gtk_widget_set_halign(GTK_WIDGET(widget), GTK_ALIGN_START);
+ gtk_widget_set_valign(GTK_WIDGET(widget), GTK_ALIGN_CENTER);
+ gtk_grid_attach(GTK_GRID(grid), widget, 0, 27, 1, 1);
+
+ grid->device_scale_factor_store = gtk_list_store_new(2, G_TYPE_INT, G_TYPE_STRING);
+ grid->desktop_orientation_store = gtk_list_store_new(2, G_TYPE_INT, G_TYPE_STRING);
+
+ remmina_rdp_settings_get_orientation_scale_prefs(&desktopOrientation, &desktopScaleFactor, &deviceScaleFactor);
+ remmina_rdp_settings_grid_load_devicescalefactor_combo(grid);
+ remmina_rdp_settings_grid_load_desktoporientation_combo(grid);
+
+ widget = gtk_label_new(_("Desktop scale factor %"));
+ gtk_widget_show(widget);
+ gtk_widget_set_halign(GTK_WIDGET(widget), GTK_ALIGN_START);
+ gtk_widget_set_valign(GTK_WIDGET(widget), GTK_ALIGN_CENTER);
+ gtk_grid_attach(GTK_GRID(grid), widget, 1, 27, 1, 1);
+
+ widget = gtk_spin_button_new_with_range(0, 10000, 1);
+ gtk_widget_show(widget);
+ gtk_grid_attach(GTK_GRID(grid), widget, 2, 27, 1, 1);
+ grid->desktop_scale_factor_spin = widget;
+
+ widget = gtk_label_new(_("Device scale factor %"));
+ gtk_widget_show(widget);
+ gtk_widget_set_halign(GTK_WIDGET(widget), GTK_ALIGN_START);
+ gtk_widget_set_valign(GTK_WIDGET(widget), GTK_ALIGN_CENTER);
+ gtk_grid_attach(GTK_GRID(grid), widget, 1, 28, 1, 1);
+
+ widget = gtk_combo_box_new_with_model(GTK_TREE_MODEL(grid->device_scale_factor_store));
+ gtk_widget_show(widget);
+ gtk_grid_attach(GTK_GRID(grid), widget, 2, 28, 1, 1);
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), renderer, TRUE);
+ gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(widget), renderer, "text", 1);
+ grid->device_scale_factor_combo = widget;
+
+ remmina_rdp_settings_set_combo_active_item(GTK_COMBO_BOX(grid->device_scale_factor_combo), deviceScaleFactor);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(grid->desktop_scale_factor_spin), (gdouble)desktopScaleFactor);
+
+ g_signal_connect(G_OBJECT(widget), "changed",
+ G_CALLBACK(remmina_rdp_settings_appscale_on_changed), grid);
+ remmina_rdp_settings_appscale_on_changed(GTK_COMBO_BOX(grid->device_scale_factor_combo), grid);
+
+ widget = gtk_label_new(_("Desktop orientation"));
+ gtk_widget_show(widget);
+ gtk_widget_set_halign(GTK_WIDGET(widget), GTK_ALIGN_START);
+ gtk_widget_set_valign(GTK_WIDGET(widget), GTK_ALIGN_CENTER);
+ gtk_grid_attach(GTK_GRID(grid), widget, 0, 29, 1, 1);
+
+ widget = gtk_combo_box_new_with_model(GTK_TREE_MODEL(grid->desktop_orientation_store));
+ gtk_widget_show(widget);
+ gtk_grid_attach(GTK_GRID(grid), widget, 1, 29, 1, 1);
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), renderer, TRUE);
+ gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(widget), renderer, "text", 1);
+ grid->desktop_orientation_combo = widget;
+
+ remmina_rdp_settings_set_combo_active_item(GTK_COMBO_BOX(grid->desktop_orientation_combo), desktopOrientation);
+
+}
+
+GtkWidget* remmina_rdp_settings_new(void)
+{
+ TRACE_CALL(__func__);
+ GtkWidget* widget;
+
+ widget = GTK_WIDGET(g_object_new(REMMINA_TYPE_PLUGIN_RDPSET_GRID, NULL));
+ gtk_widget_show(widget);
+
+ return widget;
+}
+
+void remmina_rdp_settings_get_orientation_scale_prefs(int *desktopOrientation, int *desktopScaleFactor, int *deviceScaleFactor)
+{
+ TRACE_CALL(__func__);
+
+ /* See https://msdn.microsoft.com/en-us/library/cc240510.aspx */
+
+ int orientation, dpsf, desf;
+ gchar* s;
+
+ *desktopOrientation = *desktopScaleFactor = *deviceScaleFactor = 0;
+
+ s = remmina_plugin_service->pref_get_value("rdp_desktopOrientation");
+ orientation = s ? atoi(s) : 0;
+ g_free(s);
+ if (orientation != 90 && orientation != 180 && orientation != 270)
+ orientation = 0;
+ *desktopOrientation = orientation;
+
+ s = remmina_plugin_service->pref_get_value("rdp_desktopScaleFactor");
+ dpsf = s ? atoi(s) : 0;
+ g_free(s);
+ if (dpsf < 100 || dpsf > 500)
+ return;
+
+ s = remmina_plugin_service->pref_get_value("rdp_deviceScaleFactor");
+ desf = s ? atoi(s) : 0;
+ g_free(s);
+ if (desf != 100 && desf != 140 && desf != 180)
+ return;
+
+ *desktopScaleFactor = dpsf;
+ *deviceScaleFactor = desf;
+
+}
diff --git a/plugins/rdp/rdp_settings.h b/plugins/rdp/rdp_settings.h
new file mode 100644
index 000000000..5dca220af
--- /dev/null
+++ b/plugins/rdp/rdp_settings.h
@@ -0,0 +1,47 @@
+/*
+ * Remmina - The GTK+ Remote Desktop Client
+ * Copyright (C) 2010-2011 Vic Lee
+ * Copyright (C) 2017 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.
+ *
+ */
+
+#pragma once
+
+G_BEGIN_DECLS
+
+void remmina_rdp_settings_init(void);
+guint remmina_rdp_settings_get_keyboard_layout(void);
+GtkWidget* remmina_rdp_settings_new(void);
+
+void remmina_rdp_settings_get_orientation_scale_prefs(int *desktopOrientation, int *desktopScaleFactor, int *deviceScaleFactor);
+
+G_END_DECLS
+