[connections/upstream/latest] New upstream version 41~rc
- From: Jeremy Bicha <jbicha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [connections/upstream/latest] New upstream version 41~rc
- Date: Sat, 11 Sep 2021 12:52:27 +0000 (UTC)
commit 8ec3e7729106392ac78965162ee2f9138a8351d9
Author: Jeremy Bicha <jbicha ubuntu com>
Date: Sat Sep 11 08:07:37 2021 -0400
New upstream version 41~rc
.gitignore | 4 -
.gitlab-ci.yml | 36 -
.gitlab/issue_templates/Bug.md | 44 -
.gitmodules | 4 -
subprojects/gtk-frdp | 1 -
subprojects/gtk-frdp/COPYING | 165 ++++
subprojects/gtk-frdp/examples/gtk-frdp-viewer.c | 80 ++
subprojects/gtk-frdp/examples/gtk-frdp-viewer.py | 13 +
subprojects/gtk-frdp/examples/gtk-frdp-viewer.vala | 88 ++
subprojects/gtk-frdp/examples/meson.build | 22 +
subprojects/gtk-frdp/gtk-frdp.doap | 25 +
subprojects/gtk-frdp/meson.build | 34 +
subprojects/gtk-frdp/meson_options.txt | 8 +
subprojects/gtk-frdp/org.gnome.GtkFrdpViewer.json | 77 ++
subprojects/gtk-frdp/src/GtkFrdp-0.1.metadata | 4 +
subprojects/gtk-frdp/src/frdp-display.c | 551 ++++++++++++
subprojects/gtk-frdp/src/frdp-display.h | 59 ++
subprojects/gtk-frdp/src/frdp-session.c | 995 +++++++++++++++++++++
subprojects/gtk-frdp/src/frdp-session.h | 99 ++
subprojects/gtk-frdp/src/gtk-frdp-version.h.in | 97 ++
subprojects/gtk-frdp/src/gtk-frdp.h | 34 +
subprojects/gtk-frdp/src/meson.build | 98 ++
22 files changed, 2449 insertions(+), 89 deletions(-)
---
diff --git a/subprojects/gtk-frdp/COPYING b/subprojects/gtk-frdp/COPYING
new file mode 100644
index 0000000..0a04128
--- /dev/null
+++ b/subprojects/gtk-frdp/COPYING
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/subprojects/gtk-frdp/examples/gtk-frdp-viewer.c b/subprojects/gtk-frdp/examples/gtk-frdp-viewer.c
new file mode 100644
index 0000000..0211203
--- /dev/null
+++ b/subprojects/gtk-frdp/examples/gtk-frdp-viewer.c
@@ -0,0 +1,80 @@
+/* gtk-frdp-viewer.c
+ *
+ * Copyright (C) 2018 Felipe Borges <felipeborges gnome org>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtk-frdp.h>
+
+static void
+on_rdp_auth_failure (GObject *source_object,
+ const gchar *message,
+ gpointer user_data)
+{
+ g_print ("-> %s\n", message);
+
+ g_application_quit (user_data);
+}
+
+static void
+on_activate (GtkApplication *app)
+{
+ GtkWindow *window;
+ GtkWidget *display;
+
+ g_assert (GTK_IS_APPLICATION (app));
+
+ /* Get the current window or create one if necessary. */
+ window = gtk_application_get_active_window (app);
+ if (window == NULL)
+ window = g_object_new (GTK_TYPE_APPLICATION_WINDOW,
+ "application", app,
+ NULL);
+
+ display = frdp_display_new ();
+
+ g_signal_connect (display,
+ "rdp-auth-failure",
+ G_CALLBACK (on_rdp_auth_failure),
+ app);
+
+ gtk_container_add (GTK_CONTAINER (window), display);
+ gtk_widget_show (display);
+
+ frdp_display_open_host (FRDP_DISPLAY (display), "109.168.97.222", 3389);
+ g_object_set (display,
+ "username", "demo2",
+ "password", "D3m02014*Test",
+ NULL);
+
+
+ gtk_window_present (window);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_autoptr(GtkApplication) app = NULL;
+ int ret;
+
+ app = gtk_application_new ("org.gnome.GtkFrdpViewer", G_APPLICATION_FLAGS_NONE);
+
+ g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL);
+
+ ret = g_application_run (G_APPLICATION (app), argc, argv);
+
+ return ret;
+}
diff --git a/subprojects/gtk-frdp/examples/gtk-frdp-viewer.py
b/subprojects/gtk-frdp/examples/gtk-frdp-viewer.py
new file mode 100644
index 0000000..5de689a
--- /dev/null
+++ b/subprojects/gtk-frdp/examples/gtk-frdp-viewer.py
@@ -0,0 +1,13 @@
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk, GtkFrdp
+
+win = Gtk.Window()
+
+display = GtkFrdp.Display()
+display.open_host("10.43.12.92", 3389)
+win.add(display)
+
+win.connect("destroy", Gtk.main_quit)
+win.show_all()
+Gtk.main()
diff --git a/subprojects/gtk-frdp/examples/gtk-frdp-viewer.vala
b/subprojects/gtk-frdp/examples/gtk-frdp-viewer.vala
new file mode 100644
index 0000000..6a14ce9
--- /dev/null
+++ b/subprojects/gtk-frdp/examples/gtk-frdp-viewer.vala
@@ -0,0 +1,88 @@
+/* gtk-frdp-viewer.vala
+ *
+ * Copyright (C) 2018 Felipe Borges <felipeborges gnome org>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+using Gtk;
+using Frdp;
+
+private class GtkRdpViewer.Application: Gtk.Application {
+ public const int DEFAULT_RDP_PORT = 3389;
+
+ private Gtk.ApplicationWindow window;
+ private Frdp.Display display;
+
+ static string option_connect_address;
+ const OptionEntry[] options = {
+ { "connect", 0, 0, OptionArg.STRING, ref option_connect_address, "Connect to address", null },
+ { null }
+ };
+
+ public Application () {
+ application_id = "org.gnome.GtkRdpViewer";
+ flags |= ApplicationFlags.HANDLES_COMMAND_LINE;
+ }
+
+ protected override void activate () {
+ if (window != null)
+ return;
+
+ window = new Gtk.ApplicationWindow (this);
+ display = new Frdp.Display();
+
+ window.add(display);
+ window.show_all ();
+ }
+
+ protected override int command_line (GLib.ApplicationCommandLine cmdline) {
+ option_connect_address = null;
+
+ var opt_context = new OptionContext ("A RDP Viewer");
+ opt_context.add_main_entries (options, null);
+ opt_context.set_help_enabled (true);
+
+ try {
+ string[] args1 = cmdline.get_arguments ();
+ unowned string[] args2 = args1;
+
+ opt_context.parse (ref args2);
+ } catch (OptionError error) {
+ cmdline.printerr ("%s\n", error.message);
+
+ return 1;
+ }
+
+ if (option_connect_address != null) {
+ connect_to (option_connect_address);
+ }
+
+ return 0;
+ }
+
+ private void connect_to (string address) {
+ activate ();
+
+ display.open_host (address, DEFAULT_RDP_PORT);
+ }
+}
+
+public int main (string[] args) {
+ var app = new GtkRdpViewer.Application ();
+
+ var exit_status = app.run (args);
+
+ return exit_status;
+}
diff --git a/subprojects/gtk-frdp/examples/meson.build b/subprojects/gtk-frdp/examples/meson.build
new file mode 100644
index 0000000..03591fe
--- /dev/null
+++ b/subprojects/gtk-frdp/examples/meson.build
@@ -0,0 +1,22 @@
+example_application_sources = [
+ 'gtk-frdp-viewer.c',
+]
+
+example_application = executable('gtk-frdp-viewer', example_application_sources,
+ dependencies: gtk_frdp_dep,
+ install: true
+)
+
+vala_args = [
+ '--vapidir', vapidir,
+]
+
+vala_example_application = executable('gtk-frdp-viewer-vala',
+ 'gtk-frdp-viewer.vala',
+ vala_args: vala_args,
+ link_with: gtk_frdp_lib,
+ dependencies: [
+ dependency ('gtk+-3.0'),
+ gtk_frdp_vapi,
+ ],
+)
diff --git a/subprojects/gtk-frdp/gtk-frdp.doap b/subprojects/gtk-frdp/gtk-frdp.doap
new file mode 100644
index 0000000..96a603c
--- /dev/null
+++ b/subprojects/gtk-frdp/gtk-frdp.doap
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
+ xmlns:foaf="http://xmlns.com/foaf/0.1/"
+ xmlns:gnome="http://api.gnome.org/doap-extensions#"
+ xmlns="http://usefulinc.com/ns/doap#">
+
+ <name>gtk-frdp</name>
+ <shortname>gtk-frdp</shortname>
+ <shortdesc>A RDP viewer widget for Gtk+</shortdesc>
+ <description>gtk-frdp is a RDP viewer widget powered by FreeRDP and Gtk+, developed initially to be used
in GNOME Boxes.</description>
+ <homepage rdf:resource="https://wiki.gnome.org/Apps/Boxes" />
+ <download-page rdf:resource="http://download.gnome.org/sources/gtk-frdp/" />
+
+ <programming-language>C</programming-language>
+
+ <maintainer>
+ <foaf:Person>
+ <foaf:name>Felipe Borges</foaf:name>
+ <foaf:mbox rdf:resource="mailto:felipeborges gnome org" />
+ <gnome:userid>felipeborges</gnome:userid>
+ </foaf:Person>
+ </maintainer>
+
+</Project>
diff --git a/subprojects/gtk-frdp/meson.build b/subprojects/gtk-frdp/meson.build
new file mode 100644
index 0000000..7f94529
--- /dev/null
+++ b/subprojects/gtk-frdp/meson.build
@@ -0,0 +1,34 @@
+project('gtk-frdp',
+ ['c', 'vala'],
+ version: '0.1.0',
+ meson_version: '>= 0.40.0',
+)
+
+
+config_h = configuration_data()
+
+configure_file(
+ output: 'gtk-frdp-config.h',
+ configuration: config_h,
+)
+add_project_arguments([
+ '-I' + meson.build_root(),
+], language: 'c')
+
+package_subdir = get_option('package_subdir')
+libdir = join_paths(get_option('libdir'), package_subdir)
+girdir = join_paths(get_option('datadir'), package_subdir, 'gir-1.0')
+typelibdir = join_paths(libdir, 'girepository-1.0')
+if package_subdir != ''
+ vapidir = join_paths(get_option('datadir'), package_subdir, 'vapi')
+else
+ vapidir = join_paths(get_option('datadir'), 'vala', 'vapi')
+endif
+
+gnome = import('gnome')
+
+subdir('src')
+
+if get_option('examples')
+ subdir('examples')
+endif
diff --git a/subprojects/gtk-frdp/meson_options.txt b/subprojects/gtk-frdp/meson_options.txt
new file mode 100644
index 0000000..903cda7
--- /dev/null
+++ b/subprojects/gtk-frdp/meson_options.txt
@@ -0,0 +1,8 @@
+option ('examples',
+ type: 'boolean',
+ value: false,
+ description: 'Whether to build and install the example/demo app')
+
+option ('package_subdir',
+ type: 'string',
+ description: 'Subdir for when gtk-frdp is consumed as a subproject')
diff --git a/subprojects/gtk-frdp/org.gnome.GtkFrdpViewer.json
b/subprojects/gtk-frdp/org.gnome.GtkFrdpViewer.json
new file mode 100644
index 0000000..2acdaa9
--- /dev/null
+++ b/subprojects/gtk-frdp/org.gnome.GtkFrdpViewer.json
@@ -0,0 +1,77 @@
+{
+ "app-id": "org.gnome.GtkFrdpViewer",
+ "runtime": "org.gnome.Platform",
+ "runtime-version": "master",
+ "sdk": "org.gnome.Sdk",
+ "command": "gtk-frdp-viewer",
+ "finish-args": [
+ "--share=network",
+ "--share=ipc",
+ "--socket=fallback-x11",
+ "--socket=wayland"
+ ],
+ "cleanup": [
+ "/include",
+ "/lib/pkgconfig",
+ "/man",
+ "/share/doc",
+ "/share/gtk-doc",
+ "/share/man",
+ "/share/pkgconfig",
+ "/share/vala",
+ "*.la",
+ "*.a"
+ ],
+ "modules": [
+ {
+ "name" : "libusb",
+ "config-opts" : [
+ "--disable-udev"
+ ],
+ "sources" : [
+ {
+ "type" : "archive",
+ "url" :
"https://github.com/libusb/libusb/releases/download/v1.0.23/libusb-1.0.23.tar.bz2",
+ "sha256" : "db11c06e958a82dac52cf3c65cb4dd2c3f339c8a988665110e0d24d19312ad8d"
+ }
+ ]
+ },
+ {
+ "name" : "freerdp",
+ "buildsystem": "cmake-ninja",
+ "builddir": true,
+ "config-opts": [
+ "-DCMAKE_BUILD_TYPE=RelWithDebInfo",
+ "-DWITH_OPENH264=ON",
+ "-DCMAKE_INSTALL_PREFIX=/app",
+ "-DCMAKE_INSTALL_LIBDIR=lib",
+ "-DWITH_WAYLAND:BOOL=ON",
+ "-DCHANNEL_TSMF:BOOL=ON",
+ "-DWITH_FFMPEG:BOOL=ON",
+ "-DWITH_MANPAGES:BOOL=OFF",
+ "-DWITH_SERVER:BOOL=OFF"
+ ],
+ "sources" : [
+ {
+ "type" : "archive",
+ "url" : "https://pub.freerdp.com/releases/freerdp-2.3.2.tar.gz",
+ "sha256" : "deb888034a441c7f76dc8b3ddea67fac3c0d815739fc2146e1243480ce56c91c"
+ }
+ ]
+ },
+ {
+ "name" : "gtk-frdp",
+ "config-opts" : [
+ "--libdir=/app/lib",
+ "-Dexamples=true"
+ ],
+ "buildsystem" : "meson",
+ "sources" : [
+ {
+ "type" : "git",
+ "url" : "https://gitlab.gnome.org/gnome/gtk-frdp.git"
+ }
+ ]
+ }
+ ]
+}
diff --git a/subprojects/gtk-frdp/src/GtkFrdp-0.1.metadata b/subprojects/gtk-frdp/src/GtkFrdp-0.1.metadata
new file mode 100644
index 0000000..45221bd
--- /dev/null
+++ b/subprojects/gtk-frdp/src/GtkFrdp-0.1.metadata
@@ -0,0 +1,4 @@
+Display
+ .authenticate.username out unowned=false
+ .authenticate.password out unowned=false
+ .authenticate.domain out unowned=false
diff --git a/subprojects/gtk-frdp/src/frdp-display.c b/subprojects/gtk-frdp/src/frdp-display.c
new file mode 100644
index 0000000..4f2b07b
--- /dev/null
+++ b/subprojects/gtk-frdp/src/frdp-display.c
@@ -0,0 +1,551 @@
+/* frdp-display.c
+ *
+ * Copyright (C) 2018 Felipe Borges <felipeborges gnome org>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "frdp-display.h"
+
+#include "frdp-session.h"
+
+struct _FrdpDisplayPrivate
+{
+ FrdpSession *session;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (FrdpDisplay, frdp_display, GTK_TYPE_DRAWING_AREA)
+
+enum
+{
+ PROP_O = 0,
+ PROP_USERNAME,
+ PROP_PASSWORD,
+ PROP_SCALING
+};
+
+enum
+{
+ RDP_CONNECTED,
+ RDP_DISCONNECTED,
+ RDP_NEEDS_AUTHENTICATION,
+ RDP_AUTH_FAILURE,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+static gboolean
+frdp_display_is_initialized (FrdpDisplay *self)
+{
+ FrdpDisplayPrivate *priv = frdp_display_get_instance_private (self);
+
+ return priv->session != NULL && frdp_display_is_open (self);
+}
+
+static gboolean
+frdp_display_key_press_event (GtkWidget *widget,
+ GdkEventKey *key)
+{
+ FrdpDisplay *self = FRDP_DISPLAY (widget);
+ FrdpDisplayPrivate *priv = frdp_display_get_instance_private (self);
+ guint16 keycode = key->hardware_keycode;
+
+ if (!frdp_display_is_initialized (self))
+ return TRUE;
+
+ switch (key->type) {
+ case GDK_KEY_PRESS:
+ frdp_session_send_key (priv->session, FRDP_KEY_EVENT_PRESS, keycode);
+ break;
+ case GDK_KEY_RELEASE:
+ frdp_session_send_key (priv->session, FRDP_KEY_EVENT_RELEASE, keycode);
+ break;
+ default:
+ g_warn_if_reached ();
+ break;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+frdp_display_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ FrdpDisplay *self = FRDP_DISPLAY (widget);
+ FrdpDisplayPrivate *priv = frdp_display_get_instance_private (self);
+
+ if (!frdp_display_is_initialized (self))
+ return TRUE;
+
+ frdp_session_mouse_event (priv->session,
+ FRDP_MOUSE_EVENT_MOVE,
+ event->x,
+ event->y);
+
+ return TRUE;
+}
+
+static gboolean
+frdp_display_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ FrdpDisplay *self = FRDP_DISPLAY (widget);
+ FrdpDisplayPrivate *priv = frdp_display_get_instance_private (self);
+ guint16 flags = 0;
+
+ if (!frdp_display_is_initialized (self))
+ return TRUE;
+
+ if ((event->button < 1) || (event->button > 3))
+ return FALSE;
+
+ if ((event->type != GDK_BUTTON_PRESS) &&
+ (event->type != GDK_BUTTON_RELEASE))
+ return FALSE;
+
+ if (event->type == GDK_BUTTON_PRESS)
+ flags |= FRDP_MOUSE_EVENT_DOWN;
+ switch(event->button) {
+ case GDK_BUTTON_PRIMARY:
+ flags |= FRDP_MOUSE_EVENT_BUTTON1;
+ break;
+ case GDK_BUTTON_MIDDLE:
+ flags |= FRDP_MOUSE_EVENT_BUTTON3;
+ break;
+ case GDK_BUTTON_SECONDARY:
+ flags |= FRDP_MOUSE_EVENT_BUTTON2;
+ break;
+ case 8:
+ flags |= FRDP_MOUSE_EVENT_BUTTON4;
+ break;
+ case 9:
+ flags |= FRDP_MOUSE_EVENT_BUTTON5;
+ break;
+ default:
+ return FALSE;
+ }
+
+ frdp_session_mouse_event (priv->session,
+ flags,
+ event->x,
+ event->y);
+
+ return TRUE;
+}
+
+static gboolean
+frdp_display_scroll_event (GtkWidget *widget,
+ GdkEventScroll *event)
+{
+ FrdpDisplay *self = FRDP_DISPLAY (widget);
+ FrdpDisplayPrivate *priv = frdp_display_get_instance_private (self);
+ guint16 flags = FRDP_MOUSE_EVENT_WHEEL;
+
+ if (!frdp_display_is_initialized (self))
+ return TRUE;
+
+ switch (event->direction) {
+ case GDK_SCROLL_UP:
+ flags = FRDP_MOUSE_EVENT_WHEEL;
+ break;
+ case GDK_SCROLL_DOWN:
+ flags = FRDP_MOUSE_EVENT_WHEEL | FRDP_MOUSE_EVENT_WHEEL_NEGATIVE;
+ break;
+ case GDK_SCROLL_LEFT:
+ flags = FRDP_MOUSE_EVENT_HWHEEL | FRDP_MOUSE_EVENT_WHEEL_NEGATIVE;
+ break;
+ case GDK_SCROLL_RIGHT:
+ flags = FRDP_MOUSE_EVENT_HWHEEL;
+ break;
+ case GDK_SCROLL_SMOOTH:
+ /* Calculate delta and decide which event we have
+ * a delta X means horizontal, a delta Y means vertical scroll.
+ * Fixes https://bugzilla.gnome.org/show_bug.cgi?id=675959
+ */
+ if (event->delta_x > 0.5)
+ flags = FRDP_MOUSE_EVENT_HWHEEL;
+ else if (event->delta_x < -0.5)
+ flags = FRDP_MOUSE_EVENT_HWHEEL | FRDP_MOUSE_EVENT_WHEEL_NEGATIVE;
+ else if (event->delta_y > 0.5)
+ flags = FRDP_MOUSE_EVENT_WHEEL;
+ else if (event->delta_y < -0.5)
+ flags = FRDP_MOUSE_EVENT_WHEEL | FRDP_MOUSE_EVENT_WHEEL_NEGATIVE;
+ else {
+ g_debug ("scroll smooth unhandled");
+ return FALSE;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+
+ frdp_session_mouse_event (priv->session,
+ flags,
+ event->x,
+ event->y);
+
+ return TRUE;
+}
+
+static gboolean
+frdp_enter_notify_event (GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ FrdpDisplay *self = FRDP_DISPLAY (widget);
+ FrdpDisplayPrivate *priv = frdp_display_get_instance_private (self);
+ frdp_session_mouse_pointer(priv->session, TRUE);
+ return TRUE;
+}
+
+static gboolean
+frdp_leave_notify_event (GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ FrdpDisplay *self = FRDP_DISPLAY (widget);
+ FrdpDisplayPrivate *priv = frdp_display_get_instance_private (self);
+ frdp_session_mouse_pointer(priv->session, FALSE);
+ return TRUE;
+}
+
+static void
+frdp_display_auth_failure (GObject *source_object,
+ const gchar *message,
+ gpointer user_data)
+{
+ g_signal_emit (user_data, signals[RDP_AUTH_FAILURE], 0, message);
+}
+
+static void
+frdp_display_disconnected (GObject *source_object,
+ gpointer user_data)
+{
+ FrdpDisplay *self = FRDP_DISPLAY (user_data);
+
+ g_signal_emit (self, signals[RDP_DISCONNECTED], 0);
+
+ g_debug ("rdp disconnected");
+}
+
+static void
+frdp_display_open_host_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ FrdpDisplay *self = FRDP_DISPLAY (user_data);
+ FrdpSession *session = (FrdpSession*) source_object;
+ gboolean success;
+ GError *error = NULL;
+
+ success = frdp_session_connect_finish (session,
+ result,
+ &error);
+
+ if (success) {
+ g_signal_emit (self, signals[RDP_CONNECTED], 0);
+
+ g_debug ("Connection established");
+ } else {
+ g_signal_emit (self, signals[RDP_DISCONNECTED], 0);
+
+ g_debug ("Connection failed");
+ }
+}
+
+static void
+frdp_display_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ FrdpDisplay *self = FRDP_DISPLAY (object);
+ FrdpDisplayPrivate *priv = frdp_display_get_instance_private (self);
+ FrdpSession *session = priv->session;
+ gpointer str_property;
+
+ switch (property_id)
+ {
+ case PROP_USERNAME:
+ g_object_get (session, "username", &str_property, NULL);
+ g_value_set_string (value, str_property);
+ break;
+ case PROP_PASSWORD:
+ g_object_get (session, "password", &str_property, NULL);
+ g_value_set_string (value, str_property);
+ break;
+ case PROP_SCALING:
+ g_object_get (session, "scaling", &str_property, NULL);
+ g_value_set_boolean (value, (gboolean)GPOINTER_TO_INT (str_property));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+frdp_display_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ FrdpDisplay *self = FRDP_DISPLAY (object);
+ FrdpDisplayPrivate *priv = frdp_display_get_instance_private (self);
+ FrdpSession *session = priv->session;
+
+ switch (property_id)
+ {
+ case PROP_USERNAME:
+ g_object_set (session, "username", g_value_get_string (value), NULL);
+ break;
+ case PROP_PASSWORD:
+ g_object_set (session, "password", g_value_get_string (value), NULL);
+ break;
+ case PROP_SCALING:
+ frdp_display_set_scaling (self, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+frdp_display_class_init (FrdpDisplayClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gobject_class->get_property = frdp_display_get_property;
+ gobject_class->set_property = frdp_display_set_property;
+
+ widget_class->key_press_event = frdp_display_key_press_event;
+ widget_class->key_release_event = frdp_display_key_press_event;
+ widget_class->motion_notify_event = frdp_display_motion_notify_event;
+ widget_class->button_press_event = frdp_display_button_press_event;
+ widget_class->button_release_event = frdp_display_button_press_event;
+ widget_class->scroll_event = frdp_display_scroll_event;
+ widget_class->enter_notify_event = frdp_enter_notify_event;
+ widget_class->leave_notify_event = frdp_leave_notify_event;
+
+ g_object_class_install_property (gobject_class,
+ PROP_USERNAME,
+ g_param_spec_string ("username",
+ "username",
+ "username",
+ NULL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_PASSWORD,
+ g_param_spec_string ("password",
+ "password",
+ "password",
+ NULL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_SCALING,
+ g_param_spec_boolean ("scaling",
+ "scaling",
+ "scaling",
+ TRUE,
+ G_PARAM_READWRITE));
+
+ signals[RDP_CONNECTED] = g_signal_new ("rdp-connected",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ signals[RDP_DISCONNECTED] = g_signal_new ("rdp-disconnected",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ signals[RDP_NEEDS_AUTHENTICATION] = g_signal_new ("rdp-needs-authentication",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+ signals[RDP_AUTH_FAILURE] = g_signal_new ("rdp-auth-failure",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+}
+
+static void
+frdp_display_init (FrdpDisplay *self)
+{
+ FrdpDisplayPrivate *priv = frdp_display_get_instance_private (self);
+
+ gtk_widget_add_events (GTK_WIDGET (self),
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_SCROLL_MASK |
+ GDK_SMOOTH_SCROLL_MASK |
+ GDK_KEY_PRESS_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK);
+
+ gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
+
+ priv->session = frdp_session_new (self);
+}
+
+/**
+ * frdp_display_open_host:
+ * @display: (transfer none): the RDP display widget
+ * @host: (transfer none): the hostname or IP address
+ * @port: the service name or port number
+ *
+ * Opens a TCP connection to the given @host litening on
+ * @port.
+ */
+void
+frdp_display_open_host (FrdpDisplay *display,
+ const gchar *host,
+ guint port)
+{
+ FrdpDisplayPrivate *priv = frdp_display_get_instance_private (display);
+
+ g_return_if_fail (host != NULL);
+
+ g_signal_connect (priv->session, "rdp-disconnected",
+ G_CALLBACK (frdp_display_disconnected),
+ display);
+ g_signal_connect (priv->session, "rdp-auth-failure",
+ G_CALLBACK (frdp_display_auth_failure),
+ display);
+
+ frdp_session_connect (priv->session,
+ host,
+ port,
+ NULL, // TODO: Cancellable
+ frdp_display_open_host_cb,
+ g_object_ref (display));
+
+ g_debug ("Connecting to %s…", host);
+}
+
+/**
+ * frdp_display_is_open:
+ * @display: (transfer none): the RDP display widget
+ *
+ * Check if the connection for the display is currently open
+ *
+ * Returns: TRUE if open, FALSE if closing/closed
+ */
+gboolean
+frdp_display_is_open (FrdpDisplay *display)
+{
+ FrdpDisplayPrivate *priv = frdp_display_get_instance_private (display);
+
+ return frdp_session_is_open (priv->session);
+}
+
+/**
+ * frdp_display_close:
+ * @display: (transfer none): the RDP display widget
+ *
+ * Request the closing of the RDP session.
+ */
+void
+frdp_display_close (FrdpDisplay *display)
+{
+ FrdpDisplayPrivate *priv = frdp_display_get_instance_private (display);
+
+ frdp_session_close (priv->session);
+}
+
+/**
+ * frdp_display_set_scaling:
+ * @display: (transfer none): the RDP display widget
+ * @scaling: TRUE to scale the desktop to fit, FALSE otherwise
+ *
+ * Set whether the remote desktop contents is automatically
+ * scaled to fit the available widget size, or whether it will
+ * be rendered at 1:1 size
+ */
+void
+frdp_display_set_scaling (FrdpDisplay *display,
+ gboolean scaling)
+{
+ FrdpDisplayPrivate *priv = frdp_display_get_instance_private (display);
+
+ g_object_set (priv->session, "scaling", scaling, NULL);
+
+ if (scaling) {
+ gtk_widget_set_size_request (GTK_WIDGET (display), -1, -1);
+
+ gtk_widget_set_halign (GTK_WIDGET (display), GTK_ALIGN_FILL);
+ gtk_widget_set_valign (GTK_WIDGET (display), GTK_ALIGN_FILL);
+ } else {
+ gtk_widget_set_halign (GTK_WIDGET (display), GTK_ALIGN_CENTER);
+ gtk_widget_set_valign (GTK_WIDGET (display), GTK_ALIGN_CENTER);
+ }
+
+ gtk_widget_queue_draw_area (GTK_WIDGET (display), 0, 0,
+ gtk_widget_get_allocated_width (GTK_WIDGET (display)),
+ gtk_widget_get_allocated_height (GTK_WIDGET (display)));
+}
+
+/*
+ * frdp_display_new:
+ *
+ * Create a new widget capable of connecting to a RDP server
+ * and displaying its contents
+ *
+ * The widget will be initially be in a disconnected state
+ *
+ * Returns: (transfer full): the new RDP display widget
+ */
+GtkWidget *frdp_display_new (void)
+{
+ return GTK_WIDGET (g_object_new (FRDP_TYPE_DISPLAY, NULL));
+}
+
+gboolean
+frdp_display_authenticate (FrdpDisplay *self,
+ gchar **username,
+ gchar **password,
+ gchar **domain)
+{
+ FrdpDisplayClass *klass = FRDP_DISPLAY_DISPLAY_GET_CLASS (self);
+
+ g_signal_emit (self, signals[RDP_NEEDS_AUTHENTICATION], 0);
+
+ return klass->authenticate (self, username, password, domain);
+}
+
+/**
+ * frdp_display_get_pixbuf:
+ * @display: (transfer none): the RDP display widget
+ *
+ * Take a screenshot of the display.
+ *
+ * Returns: (transfer full): a #GdkPixbuf with the screenshot image buffer
+ */
+GdkPixbuf *
+frdp_display_get_pixbuf (FrdpDisplay *display)
+{
+ FrdpDisplayPrivate *priv = frdp_display_get_instance_private (display);
+
+ return frdp_session_get_pixbuf (priv->session);
+}
diff --git a/subprojects/gtk-frdp/src/frdp-display.h b/subprojects/gtk-frdp/src/frdp-display.h
new file mode 100644
index 0000000..06fd9f8
--- /dev/null
+++ b/subprojects/gtk-frdp/src/frdp-display.h
@@ -0,0 +1,59 @@
+/* frdp-display.h
+ *
+ * Copyright (C) 2018 Felipe Borges <felipeborges gnome org>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define FRDP_TYPE_DISPLAY (frdp_display_get_type())
+#define FRDP_DISPLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), FRDP_TYPE_DISPLAY, FrdpDisplay))
+
+G_DECLARE_DERIVABLE_TYPE (FrdpDisplay, frdp_display, FRDP_DISPLAY, DISPLAY, GtkDrawingArea)
+
+typedef struct _FrdpDisplayPrivate FrdpDisplayPrivate;
+
+struct _FrdpDisplayClass
+{
+ GtkDrawingAreaClass parent_parent;
+
+ gboolean (*authenticate) (FrdpDisplay *self, gchar **username, gchar **password, gchar **domain);
+};
+
+GtkWidget *frdp_display_new (void);
+
+void frdp_display_open_host (FrdpDisplay *display,
+ const gchar *host,
+ guint port);
+
+gboolean frdp_display_is_open (FrdpDisplay *display);
+
+void frdp_display_close (FrdpDisplay *display);
+
+void frdp_display_set_scaling (FrdpDisplay *display,
+ gboolean scaling);
+
+gboolean frdp_display_authenticate (FrdpDisplay *self,
+ gchar **username,
+ gchar **password,
+ gchar **domain);
+
+GdkPixbuf *frdp_display_get_pixbuf (FrdpDisplay *display);
+
+G_END_DECLS
diff --git a/subprojects/gtk-frdp/src/frdp-session.c b/subprojects/gtk-frdp/src/frdp-session.c
new file mode 100644
index 0000000..2301652
--- /dev/null
+++ b/subprojects/gtk-frdp/src/frdp-session.c
@@ -0,0 +1,995 @@
+/* frdp-session.c
+ *
+ * Copyright (C) 2018 Felipe Borges <felipeborges gnome org>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <freerdp/freerdp.h>
+#include <freerdp/gdi/gdi.h>
+#include <gdk/gdk.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+#include <math.h>
+
+#include "frdp-session.h"
+
+#define SELECT_TIMEOUT 50
+#define FRDP_CONNECTION_THREAD_MAX_ERRORS 10
+
+struct frdp_pointer
+{
+ rdpPointer pointer;
+ cairo_surface_t *data;
+};
+typedef struct frdp_pointer frdpPointer;
+
+struct _FrdpSessionPrivate
+{
+ freerdp *freerdp_session;
+
+ GtkWidget *display;
+ cairo_surface_t *surface;
+ gboolean scaling;
+ double scale;
+ double offset_x;
+ double offset_y;
+
+ guint update_id;
+
+ gboolean is_connected;
+
+ gchar *hostname;
+ gchar *username;
+ gchar *password;
+ guint port;
+
+ gboolean show_cursor;
+ gboolean cursor_null;
+ frdpPointer *cursor;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (FrdpSession, frdp_session, G_TYPE_OBJECT)
+
+enum
+{
+ PROP_0 = 0,
+ PROP_HOSTNAME,
+ PROP_PORT,
+ PROP_USERNAME,
+ PROP_PASSWORD,
+ PROP_DISPLAY,
+ PROP_SCALING
+};
+
+enum
+{
+ RDP_CONNECTED,
+ RDP_DISCONNECTED,
+ RDP_AUTH_FAILURE,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+struct frdp_context
+{
+ rdpContext context;
+ FrdpSession *self;
+};
+typedef struct frdp_context frdpContext;
+
+static void
+frdp_session_update_mouse_pointer (FrdpSession *self)
+{
+ FrdpSessionPrivate *priv = self->priv;
+ GdkCursor *cursor;
+ GdkDisplay *display;
+ GdkWindow *window;
+
+ window = gtk_widget_get_window (priv->display);
+ if (window == NULL)
+ return;
+
+ display = gtk_widget_get_display(priv->display);
+ if (priv->show_cursor && priv->cursor_null) {
+ cairo_surface_t *surface;
+ cairo_t *cairo;
+
+ /* Create a 1x1 image with transparent color */
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
+ cairo = cairo_create (surface);
+ cairo_set_source_rgba (cairo, 0.0, 0.0, 0.0, 0.0);
+ cairo_set_line_width(cairo, 1);
+ cairo_rectangle(cairo, 0, 0, 1, 1);
+ cairo_fill (cairo);
+
+ cursor = gdk_cursor_new_from_surface (display, surface, 0, 0);
+ cairo_surface_destroy (surface);
+ cairo_destroy (cairo);
+ } else if (!priv->show_cursor || !priv->cursor)
+ /* No cursor set or none to show */
+ cursor = gdk_cursor_new_from_name (display, "default");
+ else {
+ rdpPointer *pointer = &priv->cursor->pointer;
+ double scale = self->priv->scale;
+ double x = priv->cursor->pointer.xPos * scale;
+ double y = priv->cursor->pointer.yPos * scale;
+ double w = pointer->width * scale;
+ double h = pointer->height * scale;
+ cairo_surface_t *surface;
+ cairo_t *cairo;
+
+ if (!self->priv->scaling) {
+ scale = 1.0;
+ }
+
+ /* Scale the source image according to current settings. */
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
+ cairo = cairo_create (surface);
+
+ cairo_scale(cairo, scale, scale);
+ cairo_set_source_surface (cairo, priv->cursor->data, 0, 0);
+ cairo_paint (cairo);
+
+ cairo_fill (cairo);
+ cursor = gdk_cursor_new_from_surface (display, surface, x, y);
+ cairo_surface_destroy (surface);
+ cairo_destroy (cairo);
+ }
+
+ gdk_window_set_cursor (window, cursor);
+}
+
+static BOOL
+frdp_Pointer_New(rdpContext* context, rdpPointer* pointer)
+{
+ frdpContext *fcontext = (frdpContext*) context;
+ frdpPointer *fpointer = (frdpPointer*) pointer;
+ int stride;
+ unsigned char *data;
+ cairo_surface_t *surface;
+
+ if (!fcontext || !fpointer)
+ return FALSE;
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, pointer->width,
+ pointer->height);
+ if (!surface) {
+ return FALSE;
+ }
+
+ { /* FreeRDP BUG https://github.com/FreeRDP/FreeRDP/issues/5061
+ * the function freerdp_image_copy_from_pointer_data
+ * does not initialize the buffer which results in broken alpha data. */
+ cairo_t* cairo = cairo_create (surface);
+
+ cairo_set_source_rgba (cairo, 0.0, 0.0, 0.0, 1.0);
+ cairo_fill (cairo);
+ cairo_paint (cairo);
+ cairo_destroy (cairo);
+ }
+
+ data = cairo_image_surface_get_data (surface);
+ if (!data) {
+ goto fail;
+ }
+
+ stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, pointer->width);
+ if (!freerdp_image_copy_from_pointer_data (data, PIXEL_FORMAT_BGRA32,
+ stride, 0, 0, pointer->width,
+ pointer->height,
+ pointer->xorMaskData,
+ pointer->lengthXorMask,
+ pointer->andMaskData,
+ pointer->lengthAndMask,
+ pointer->xorBpp,
+ &context->gdi->palette))
+ goto fail;
+
+ fpointer->data = surface;
+ return TRUE;
+fail:
+ if (surface)
+ cairo_surface_destroy (surface);
+ return FALSE;
+}
+
+static void
+frdp_Pointer_Free (rdpContext* context, rdpPointer* pointer)
+{
+ frdpPointer *fpointer = (frdpPointer*) pointer;
+
+ if (fpointer && fpointer->data) {
+ cairo_surface_destroy (fpointer->data);
+ fpointer->data = NULL;
+ }
+}
+
+static BOOL
+frdp_Pointer_Set (rdpContext* context,
+ const rdpPointer* pointer)
+{
+ frdpContext *fcontext = (frdpContext*) context;
+ frdpPointer *fpointer = (frdpPointer*) pointer;
+ FrdpSessionPrivate *priv = fcontext->self->priv;
+
+ priv->cursor = fpointer;
+ priv->cursor_null = FALSE;
+
+ frdp_session_update_mouse_pointer (fcontext->self);
+ return TRUE;
+}
+
+static BOOL
+frdp_Pointer_SetNull (rdpContext* context)
+{
+ frdpContext *fcontext = (frdpContext*) context;
+ FrdpSessionPrivate *priv = fcontext->self->priv;
+
+ priv->cursor = NULL;
+ priv->cursor_null = TRUE;
+
+ frdp_session_update_mouse_pointer (fcontext->self);
+
+ return TRUE;
+}
+
+static BOOL
+frdp_Pointer_SetDefault (rdpContext* context)
+{
+ frdpContext *fcontext = (frdpContext*) context;
+ FrdpSessionPrivate *priv = fcontext->self->priv;
+
+ priv->cursor = NULL;
+ priv->cursor_null = FALSE;
+ frdp_session_update_mouse_pointer (fcontext->self);
+ return TRUE;
+}
+
+static BOOL
+frdp_Pointer_SetPosition (rdpContext* context, UINT32 x, UINT32 y)
+{
+ return TRUE;
+}
+
+static void
+frdp_register_pointer (rdpGraphics* graphics)
+{
+ rdpPointer pointer;
+
+ pointer.size = sizeof(frdpPointer);
+ pointer.New = frdp_Pointer_New;
+ pointer.Free = frdp_Pointer_Free;
+ pointer.Set = frdp_Pointer_Set;
+ pointer.SetNull = frdp_Pointer_SetNull;
+ pointer.SetDefault = frdp_Pointer_SetDefault;
+ pointer.SetPosition = frdp_Pointer_SetPosition;
+ graphics_register_pointer(graphics, &pointer);
+}
+
+static guint32
+frdp_session_get_best_color_depth (FrdpSession *self)
+{
+ GdkScreen *display;
+ GdkVisual *visual;
+
+ display = gdk_screen_get_default ();
+ visual = gdk_screen_get_rgba_visual (display);
+
+ return gdk_visual_get_depth (visual);
+}
+
+static void
+frdp_session_configure_event (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ FrdpSession *self = (FrdpSession*) user_data;
+ rdpSettings *settings = self->priv->freerdp_session->settings;
+ double width, height;
+
+ if (self->priv->scaling) {
+ width = (double)gtk_widget_get_allocated_width (widget);
+ height = (double)gtk_widget_get_allocated_height (widget);
+
+ if (width < height)
+ self->priv->scale = width / settings->DesktopWidth;
+ else
+ self->priv->scale = height / settings->DesktopHeight;
+
+ settings->DesktopScaleFactor = self->priv->scale;
+
+ self->priv->offset_x = (width - settings->DesktopWidth * self->priv->scale) / 2.0;
+ self->priv->offset_y = (height - settings->DesktopHeight * self->priv->scale) / 2.0;
+ }
+
+ frdp_session_update_mouse_pointer (self);
+}
+
+static void
+frdp_session_set_scaling (FrdpSession *self,
+ gboolean scaling)
+{
+ self->priv->scaling = scaling;
+
+ frdp_session_configure_event (self->priv->display, NULL, self);
+}
+
+static gboolean
+frdp_session_draw (GtkWidget *widget,
+ cairo_t *cr,
+ gpointer user_data)
+{
+ FrdpSession *self = (FrdpSession*) user_data;
+
+ if (self->priv->scaling) {
+ cairo_translate (cr, self->priv->offset_x, self->priv->offset_y);
+ cairo_scale (cr, self->priv->scale, self->priv->scale);
+ }
+ cairo_set_source_surface (cr, self->priv->surface, 0, 0);
+ cairo_paint (cr);
+
+ return TRUE;
+}
+
+static guint
+frdp_certificate_verify (freerdp *freerdp_session,
+ const gchar *common_name,
+ const gchar* subject,
+ const gchar* issuer,
+ const gchar* fingerprint,
+ gboolean host_mismatch)
+{
+ /* TODO */
+ return TRUE;
+}
+
+static guint
+frdp_changed_certificate_verify (freerdp *freerdp_session,
+ const gchar *common_name,
+ const gchar *subject,
+ const gchar *issuer,
+ const gchar *new_fingerprint,
+ const gchar *old_subject,
+ const gchar *old_issuer,
+ const gchar *old_fingerprint)
+{
+ /* TODO */
+ return TRUE;
+}
+
+static gboolean
+frdp_authenticate (freerdp *freerdp_session,
+ gchar **username,
+ gchar **password,
+ gchar **domain)
+{
+ FrdpSession *self = ((frdpContext *) freerdp_session->context)->self;
+
+ return frdp_display_authenticate (FRDP_DISPLAY (self->priv->display),
+ username,
+ password,
+ domain);
+}
+
+static gboolean
+frdp_pre_connect (freerdp *freerdp_session)
+{
+ return TRUE;
+}
+
+static gboolean
+frdp_begin_paint (rdpContext *context)
+{
+ rdpGdi *gdi = context->gdi;
+
+ gdi->primary->hdc->hwnd->invalid->null = 1;
+ gdi->primary->hdc->hwnd->ninvalid = 0;
+
+ return TRUE;
+}
+
+static gboolean
+frdp_end_paint (rdpContext *context)
+{
+ FrdpSessionPrivate *priv;
+ FrdpSession *self = ((frdpContext *) context)->self;
+ rdpGdi *gdi = context->gdi;
+ gint x, y, w, h;
+ gint pos_x, pos_y;
+
+ if (gdi->primary->hdc->hwnd->invalid->null)
+ return TRUE;
+
+ 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;
+
+ priv = self->priv;
+
+ if (priv->scaling) {
+ pos_x = self->priv->offset_x + x * priv->scale;
+ pos_y = self->priv->offset_y + y * priv->scale;
+ gtk_widget_queue_draw_area (priv->display,
+ floor (pos_x),
+ floor (pos_y),
+ ceil (pos_x + w * priv->scale) - floor (pos_x),
+ ceil (pos_y + h * priv->scale) - floor (pos_y));
+ } else {
+ gtk_widget_queue_draw_area (priv->display, x, y, w, h);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+frdp_post_connect (freerdp *freerdp_session)
+{
+ FrdpSession *self = ((frdpContext *) freerdp_session->context)->self;
+ cairo_format_t cairo_format;
+ rdpGdi *gdi;
+ guint32 color_format;
+ gint stride;
+
+ switch (frdp_session_get_best_color_depth (self)) {
+ case 32:
+ color_format = PIXEL_FORMAT_BGRA32;
+ cairo_format = CAIRO_FORMAT_ARGB32;
+ break;
+ case 24:
+ color_format = PIXEL_FORMAT_BGRX32;
+ cairo_format = CAIRO_FORMAT_RGB24;
+ break;
+ case 16:
+ case 15:
+ color_format = PIXEL_FORMAT_BGR16;
+ cairo_format = CAIRO_FORMAT_RGB16_565;
+ break;
+ default:
+ color_format = PIXEL_FORMAT_BGRX32;
+ cairo_format = CAIRO_FORMAT_RGB16_565;
+ break;
+ }
+
+ gdi_init (freerdp_session, color_format);
+ gdi = freerdp_session->context->gdi;
+
+ frdp_register_pointer (freerdp_session->context->graphics);
+ pointer_cache_register_callbacks(freerdp_session->context->update);
+ freerdp_session->update->BeginPaint = frdp_begin_paint;
+ freerdp_session->update->EndPaint = frdp_end_paint;
+
+ stride = cairo_format_stride_for_width (cairo_format, gdi->width);
+ self->priv->surface =
+ cairo_image_surface_create_for_data ((unsigned char*) gdi->primary_buffer,
+ cairo_format,
+ gdi->width,
+ gdi->height,
+ stride);
+
+ gtk_widget_queue_draw_area (self->priv->display,
+ 0,
+ 0,
+ gdi->width,
+ gdi->height);
+
+ return TRUE;
+}
+
+static gboolean
+idle_close (gpointer user_data)
+{
+ FrdpSession *self = (FrdpSession*) user_data;
+
+ self->priv->is_connected = FALSE;
+
+ if (self->priv->update_id > 0) {
+ g_source_remove (self->priv->update_id);
+ self->priv->update_id = 0;
+ }
+
+ if (self->priv->freerdp_session != NULL) {
+ freerdp_disconnect (self->priv->freerdp_session);
+ freerdp_context_free (self->priv->freerdp_session);
+ g_clear_pointer (&self->priv->freerdp_session, freerdp_free);
+ }
+
+ g_clear_pointer (&self->priv->hostname, g_free);
+ g_clear_pointer (&self->priv->username, g_free);
+ g_clear_pointer (&self->priv->password, g_free);
+
+ g_signal_emit (self, signals[RDP_DISCONNECTED], 0);
+ g_debug ("RDP client disconnected");
+
+ return FALSE;
+}
+
+static gboolean
+update (gpointer user_data)
+{
+ DWORD status;
+ HANDLE handles[64];
+ DWORD usedHandles;
+ FrdpSessionPrivate *priv;
+ FrdpSession *self = (FrdpSession*) user_data;
+
+ priv = self->priv;
+
+ usedHandles = freerdp_get_event_handles (priv->freerdp_session->context,
+ handles, ARRAYSIZE(handles));
+ if (usedHandles == 0) {
+ g_warning ("Failed to get FreeRDP event handle");
+ return FALSE;
+ }
+
+ status = WaitForMultipleObjects (usedHandles, handles, FALSE, SELECT_TIMEOUT);
+ if (status == WAIT_TIMEOUT)
+ return TRUE;
+ if (status == WAIT_FAILED)
+ return FALSE;
+
+ if (!freerdp_check_event_handles (priv->freerdp_session->context)) {
+ g_warning ("Failed to check FreeRDP file descriptor");
+ return FALSE;
+ }
+
+ if (freerdp_shall_disconnect (priv->freerdp_session)) {
+ g_idle_add ((GSourceFunc) idle_close, self);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+frdp_session_init_freerdp (FrdpSession *self)
+{
+ FrdpSessionPrivate *priv = self->priv;
+ rdpSettings *settings;
+
+ /* Setup FreeRDP session */
+ priv->freerdp_session = freerdp_new ();
+ priv->freerdp_session->PreConnect = frdp_pre_connect;
+ priv->freerdp_session->PostConnect = frdp_post_connect;
+ priv->freerdp_session->Authenticate = frdp_authenticate;
+ priv->freerdp_session->VerifyCertificate = frdp_certificate_verify;
+ priv->freerdp_session->VerifyChangedCertificate = frdp_changed_certificate_verify;
+
+ priv->freerdp_session->ContextSize = sizeof (frdpContext);
+
+ freerdp_context_new (priv->freerdp_session);
+ ((frdpContext *) priv->freerdp_session->context)->self = self;
+
+ settings = priv->freerdp_session->settings;
+
+ settings->ServerHostname = g_strdup (priv->hostname);
+ settings->ServerPort = priv->port;
+ settings->Username = g_strdup (priv->username);
+ settings->Password = g_strdup (priv->password);
+
+ settings->AllowFontSmoothing = TRUE;
+ settings->AllowUnanouncedOrdersFromServer = TRUE;
+
+ /* Security settings */
+ settings->RdpSecurity = TRUE;
+ settings->TlsSecurity = TRUE;
+ settings->NlaSecurity = TRUE;
+ settings->EncryptionMethods = ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_128BIT | ENCRYPTION_METHOD_FIPS;
+ settings->EncryptionLevel = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE;
+ settings->UseRdpSecurityLayer = FALSE;
+
+ settings->NegotiateSecurityLayer = TRUE;
+}
+
+static void
+frdp_session_connect_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ FrdpSession *self = (FrdpSession*) source_object;
+
+ frdp_session_init_freerdp (self);
+
+ self->priv->is_connected = freerdp_connect (self->priv->freerdp_session);
+ if (!self->priv->is_connected) {
+ guint32 error_code;
+
+ error_code = freerdp_get_last_error (self->priv->freerdp_session->context);
+ switch (error_code) {
+ case FREERDP_ERROR_AUTHENTICATION_FAILED:
+ case FREERDP_ERROR_CONNECT_FAILED:
+ case FREERDP_ERROR_SERVER_DENIED_CONNECTION:
+ case FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS:
+ case FREERDP_ERROR_CONNECT_LOGON_FAILURE:
+ case STATUS_LOGON_FAILURE:
+ case STATUS_PASSWORD_EXPIRED:
+ case FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED:
+ case FREERDP_ERROR_CONNECT_TRANSPORT_FAILED:
+ g_signal_emit (self,
+ signals[RDP_AUTH_FAILURE], 0,
+ freerdp_get_last_error_string (error_code));
+
+ g_warning ("Failed to connect RPD host with error '%s'",
+ freerdp_get_last_error_string (error_code));
+ break;
+
+ default:
+ g_warning ("Unhandled FreeRDP error: '%s'",
+ freerdp_get_last_error_string (error_code));
+ break;
+ }
+
+ g_idle_add ((GSourceFunc) idle_close, self);
+ g_task_return_boolean (task, FALSE);
+
+ return;
+ }
+
+ g_signal_connect (self->priv->display, "draw",
+ G_CALLBACK (frdp_session_draw), self);
+ g_signal_connect (self->priv->display, "configure-event",
+ G_CALLBACK (frdp_session_configure_event), self);
+ frdp_session_set_scaling (self, TRUE);
+
+ self->priv->update_id = g_idle_add ((GSourceFunc) update, self);
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+frdp_session_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ FrdpSession *self = (FrdpSession*) object;
+ rdpSettings *settings = self->priv->freerdp_session->settings;
+
+ switch (property_id)
+ {
+ case PROP_HOSTNAME:
+ g_value_set_string (value, settings->ServerHostname);
+ break;
+ case PROP_PORT:
+ g_value_set_uint (value, settings->ServerPort);
+ break;
+ case PROP_USERNAME:
+ g_value_set_string (value, settings->Username);
+ break;
+ case PROP_PASSWORD:
+ g_value_set_string (value, settings->Password);
+ break;
+ case PROP_DISPLAY:
+ g_value_set_object (value, self->priv->display);
+ break;
+ case PROP_SCALING:
+ g_value_set_boolean (value, self->priv->scaling);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+frdp_session_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ FrdpSession *self = (FrdpSession*) object;
+
+ switch (property_id)
+ {
+ case PROP_HOSTNAME:
+ g_free (self->priv->hostname);
+ self->priv->hostname = g_value_dup_string (value);
+ break;
+ case PROP_PORT:
+ self->priv->port = g_value_get_uint (value);
+ break;
+ case PROP_USERNAME:
+ g_free (self->priv->username);
+ self->priv->username = g_value_dup_string (value);
+ break;
+ case PROP_PASSWORD:
+ g_free (self->priv->password);
+ self->priv->password = g_value_dup_string (value);
+ break;
+ case PROP_DISPLAY:
+ self->priv->display = g_value_get_object (value);
+ break;
+ case PROP_SCALING:
+ frdp_session_set_scaling (self, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+frdp_session_finalize (GObject *object)
+{
+ FrdpSession *self = (FrdpSession*) object;
+
+ idle_close (self);
+
+ G_OBJECT_CLASS (frdp_session_parent_class)->finalize (object);
+}
+
+static void
+frdp_session_class_init (FrdpSessionClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = frdp_session_finalize;
+ gobject_class->get_property = frdp_session_get_property;
+ gobject_class->set_property = frdp_session_set_property;
+
+ g_object_class_install_property (gobject_class,
+ PROP_HOSTNAME,
+ g_param_spec_string ("hostname",
+ "hostname",
+ "hostname",
+ NULL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_PORT,
+ g_param_spec_uint ("port",
+ "port",
+ "port",
+ 0, G_MAXUINT16, 3389,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_USERNAME,
+ g_param_spec_string ("username",
+ "username",
+ "username",
+ NULL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_PASSWORD,
+ g_param_spec_string ("password",
+ "password",
+ "password",
+ NULL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_DISPLAY,
+ g_param_spec_object ("display",
+ "display",
+ "display",
+ GTK_TYPE_WIDGET,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_SCALING,
+ g_param_spec_boolean ("scaling",
+ "scaling",
+ "scaling",
+ TRUE,
+ G_PARAM_READWRITE));
+
+ signals[RDP_CONNECTED] = g_signal_new ("rdp-connected",
+ FRDP_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+ signals[RDP_DISCONNECTED] = g_signal_new ("rdp-disconnected",
+ FRDP_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+ signals[RDP_AUTH_FAILURE] = g_signal_new ("rdp-auth-failure",
+ FRDP_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+
+}
+
+static void
+frdp_session_init (FrdpSession *self)
+{
+ self->priv = frdp_session_get_instance_private (self);
+
+ self->priv->is_connected = FALSE;
+}
+
+FrdpSession*
+frdp_session_new (FrdpDisplay *display)
+{
+ gtk_widget_show (GTK_WIDGET (display));
+
+ return g_object_new (FRDP_TYPE_SESSION,
+ "display", display,
+ NULL);
+}
+
+void
+frdp_session_connect (FrdpSession *self,
+ const gchar *hostname,
+ guint port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ self->priv->hostname = g_strdup (hostname);
+ self->priv->port = port;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_run_in_thread (task, frdp_session_connect_thread);
+
+ g_object_unref (task);
+}
+
+gboolean
+frdp_session_connect_finish (FrdpSession *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+gboolean
+frdp_session_is_open (FrdpSession *self)
+{
+ return self->priv->is_connected;
+}
+
+void
+frdp_session_close (FrdpSession *self)
+{
+ idle_close (self);
+
+ g_debug ("Closing RDP session");
+}
+
+void
+frdp_session_mouse_event (FrdpSession *self,
+ FrdpMouseEvent event,
+ guint16 x,
+ guint16 y)
+{
+ FrdpSessionPrivate *priv = self->priv;
+ rdpInput *input;
+ guint16 flags = 0;
+ guint16 xflags = 0;
+
+ g_return_if_fail (priv->freerdp_session != NULL);
+
+ if (event & FRDP_MOUSE_EVENT_MOVE)
+ flags |= PTR_FLAGS_MOVE;
+ if (event & FRDP_MOUSE_EVENT_DOWN)
+ flags |= PTR_FLAGS_DOWN;
+ if (event & FRDP_MOUSE_EVENT_WHEEL) {
+ flags |= PTR_FLAGS_WHEEL;
+ if (event & FRDP_MOUSE_EVENT_WHEEL_NEGATIVE)
+ flags |= PTR_FLAGS_WHEEL_NEGATIVE | 0x0088;
+ else
+ flags |= 0x0078;
+ }
+ if (event & FRDP_MOUSE_EVENT_HWHEEL) {
+ flags |= PTR_FLAGS_HWHEEL;
+ if (event & FRDP_MOUSE_EVENT_WHEEL_NEGATIVE)
+ flags |= PTR_FLAGS_WHEEL_NEGATIVE | 0x0088;
+ else
+ flags |= 0x0078;
+ }
+
+ if (event & FRDP_MOUSE_EVENT_BUTTON1)
+ flags |= PTR_FLAGS_BUTTON1;
+ if (event & FRDP_MOUSE_EVENT_BUTTON2)
+ flags |= PTR_FLAGS_BUTTON2;
+ if (event & FRDP_MOUSE_EVENT_BUTTON3)
+ flags |= PTR_FLAGS_BUTTON3;
+ if (event & FRDP_MOUSE_EVENT_BUTTON4)
+ xflags |= PTR_XFLAGS_BUTTON1;
+ if (event & FRDP_MOUSE_EVENT_BUTTON5)
+ xflags |= PTR_XFLAGS_BUTTON2;
+
+ input = priv->freerdp_session->input;
+
+ if (priv->scaling) {
+ x = (x - priv->offset_x) / priv->scale;
+ y = (y - priv->offset_y) / priv->scale;
+ }
+
+ x = x < 0.0 ? 0.0 : x;
+ y = y < 0.0 ? 0.0 : y;
+ if (xflags != 0) {
+ if (event & FRDP_MOUSE_EVENT_DOWN)
+ xflags |= PTR_XFLAGS_DOWN;
+ freerdp_input_send_extended_mouse_event(input, xflags, x, y);
+ } else if (flags != 0) {
+ freerdp_input_send_mouse_event (input, flags, x, y);
+ }
+}
+
+void
+frdp_session_mouse_pointer (FrdpSession *self,
+ gboolean enter)
+{
+ FrdpSessionPrivate *priv = self->priv;
+
+ priv->show_cursor = enter;
+ frdp_session_update_mouse_pointer (self);
+}
+
+static unsigned char keycode_scancodes[] = {
+ 0, 0, 0, 0, 0, 0, 0, 28,
+ 29, 53, 55, 56, 0, 71, 72, 73,
+ 75, 77, 79, 80, 81, 82, 83, 0,
+ 0, 0, 0, 0, 0, 0, 69, 0,
+ 0, 0, 0, 0, 91, 92, 93,
+};
+
+static guint16
+frdp_session_get_scancode_by_keycode (guint16 keycode)
+{
+ if (keycode < 8)
+ return 0;
+ else if (keycode < 97)
+ return keycode - 8;
+ else if (keycode < 97 + sizeof (keycode_scancodes))
+ return keycode_scancodes[keycode - 97];
+ else
+ return 0;
+}
+
+void
+frdp_session_send_key (FrdpSession *self,
+ FrdpKeyEvent event,
+ guint16 keycode)
+{
+ rdpInput *input = self->priv->freerdp_session->input;
+ guint16 flags = 0;
+ guint16 scancode =
+ frdp_session_get_scancode_by_keycode (keycode);
+
+ if (event == FRDP_KEY_EVENT_PRESS)
+ flags |= KBD_FLAGS_DOWN;
+ else
+ flags |= KBD_FLAGS_RELEASE;
+
+ input->KeyboardEvent (input, flags, scancode);
+}
+
+GdkPixbuf *
+frdp_session_get_pixbuf (FrdpSession *self)
+{
+ guint width, height;
+
+ width = gtk_widget_get_allocated_width (self->priv->display);
+ height = gtk_widget_get_allocated_height (self->priv->display);
+
+ return gdk_pixbuf_get_from_surface (self->priv->surface,
+ 0, 0,
+ width, height);
+}
diff --git a/subprojects/gtk-frdp/src/frdp-session.h b/subprojects/gtk-frdp/src/frdp-session.h
new file mode 100644
index 0000000..829bf43
--- /dev/null
+++ b/subprojects/gtk-frdp/src/frdp-session.h
@@ -0,0 +1,99 @@
+/* frdp-session.h
+ *
+ * Copyright (C) 2018 Felipe Borges <felipeborges gnome org>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <glib-object.h>
+#include <frdp-display.h>
+
+G_BEGIN_DECLS
+
+#define FRDP_TYPE_SESSION (frdp_session_get_type())
+
+G_DECLARE_FINAL_TYPE (FrdpSession, frdp_session, FRDP_SESSION, SESSION, GObject)
+
+typedef struct _FrdpSessionPrivate FrdpSessionPrivate;
+
+struct _FrdpSession
+{
+ GObject parent;
+
+ FrdpSessionPrivate *priv;
+
+ /* Do not add fields to this struct */
+};
+
+typedef enum
+{
+ FRDP_MOUSE_EVENT_MOVE = 1 << 0,
+ FRDP_MOUSE_EVENT_DOWN = 1 << 1,
+ FRDP_MOUSE_EVENT_WHEEL = 1 << 2,
+ FRDP_MOUSE_EVENT_WHEEL_NEGATIVE = 1 << 3,
+ FRDP_MOUSE_EVENT_BUTTON1 = 1 << 4,
+ FRDP_MOUSE_EVENT_BUTTON2 = 1 << 5,
+ FRDP_MOUSE_EVENT_BUTTON3 = 1 << 6,
+ FRDP_MOUSE_EVENT_BUTTON4 = 1 << 7,
+ FRDP_MOUSE_EVENT_BUTTON5 = 1 << 8,
+ FRDP_MOUSE_EVENT_HWHEEL = 1 << 9,
+} FrdpMouseEvent;
+
+typedef enum
+{
+ FRDP_KEY_EVENT_PRESS = 1 << 0,
+ FRDP_KEY_EVENT_RELEASE = 1 << 1,
+} FrdpKeyEvent;
+
+FrdpSession *frdp_session_new (FrdpDisplay *display);
+
+void frdp_session_connect (FrdpSession *self,
+ const gchar *hostname,
+ guint port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean frdp_session_connect_finish (FrdpSession *self,
+ GAsyncResult *result,
+ GError **error);
+
+gboolean frdp_session_is_open (FrdpSession *self);
+
+void frdp_session_close (FrdpSession *self);
+
+void frdp_session_mouse_event (FrdpSession *self,
+ FrdpMouseEvent event,
+ guint16 x,
+ guint16 y);
+
+void frdp_session_mouse_pointer (FrdpSession *self,
+ gboolean enter);
+
+void frdp_session_send_key (FrdpSession *self,
+ FrdpKeyEvent event,
+ guint16 keycode);
+
+GdkPixbuf *frdp_session_get_pixbuf (FrdpSession *self);
+/*FreeRDP fatal error codes*/
+typedef enum {
+ FRDP_ERRCONNECT_CONNECT_CANCELLED = 0x2000B,
+ FRDP_ERRCONNECT_AUTHENTICATION_FAILED = 0x20009,
+ FRDP_ERRCONNECT_SECURITY_NEGO_CONNECT_FAILED = 0x2000c,
+
+} FrdpErrConnect;
+
+G_END_DECLS
diff --git a/subprojects/gtk-frdp/src/gtk-frdp-version.h.in b/subprojects/gtk-frdp/src/gtk-frdp-version.h.in
new file mode 100644
index 0000000..826c831
--- /dev/null
+++ b/subprojects/gtk-frdp/src/gtk-frdp-version.h.in
@@ -0,0 +1,97 @@
+/* gtk-frdp-version.h.in
+ *
+ * Copyright (C) 2018 Felipe Borges
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTK_FRDP_VERSION_H
+#define GTK_FRDP_VERSION_H
+
+#if !defined(GTK_FRDP_INSIDE) && !defined(GTK_FRDP_COMPILATION)
+# error "Only <gtk-frdp.h> can be included directly."
+#endif
+
+/**
+ * SECTION:gtk-frdpversion
+ * @short_description: gtk-frdp version checking
+ *
+ * gtk-frdp provides macros to check the version of the library
+ * at compile-time
+ */
+
+/**
+ * GTK_FRDP_MAJOR_VERSION:
+ *
+ * gtk-frdp major version component (e.g. 1 if %GTK_FRDP_VERSION is 1.2.3)
+ */
+#define GTK_FRDP_MAJOR_VERSION (@MAJOR_VERSION@)
+
+/**
+ * GTK_FRDP_MINOR_VERSION:
+ *
+ * gtk-frdp minor version component (e.g. 2 if %GTK_FRDP_VERSION is 1.2.3)
+ */
+#define GTK_FRDP_MINOR_VERSION (@MINOR_VERSION@)
+
+/**
+ * GTK_FRDP_MICRO_VERSION:
+ *
+ * gtk-frdp micro version component (e.g. 3 if %GTK_FRDP_VERSION is 1.2.3)
+ */
+#define GTK_FRDP_MICRO_VERSION (@MICRO_VERSION@)
+
+/**
+ * GTK_FRDP_VERSION
+ *
+ * gtk-frdp version.
+ */
+#define GTK_FRDP_VERSION (@VERSION@)
+
+/**
+ * GTK_FRDP_VERSION_S:
+ *
+ * gtk-frdp version, encoded as a string, useful for printing and
+ * concatenation.
+ */
+#define GTK_FRDP_VERSION_S "@VERSION@"
+
+#define GTK_FRDP_ENCODE_VERSION(major,minor,micro) \
+ ((major) << 24 | (minor) << 16 | (micro) << 8)
+
+/**
+ * GTK_FRDP_VERSION_HEX:
+ *
+ * gtk-frdp version, encoded as an hexadecimal number, useful for
+ * integer comparisons.
+ */
+#define GTK_FRDP_VERSION_HEX \
+ (GTK_FRDP_ENCODE_VERSION (GTK_FRDP_MAJOR_VERSION, GTK_FRDP_MINOR_VERSION, GTK_FRDP_MICRO_VERSION))
+
+/**
+ * GTK_FRDP_CHECK_VERSION:
+ * @major: required major version
+ * @minor: required minor version
+ * @micro: required micro version
+ *
+ * Compile-time version checking. Evaluates to %TRUE if the version
+ * of gtk-frdp is greater than the required one.
+ */
+#define GTK_FRDP_CHECK_VERSION(major,minor,micro) \
+ (GTK_FRDP_MAJOR_VERSION > (major) || \
+ (GTK_FRDP_MAJOR_VERSION == (major) && GTK_FRDP_MINOR_VERSION > (minor)) || \
+ (GTK_FRDP_MAJOR_VERSION == (major) && GTK_FRDP_MINOR_VERSION == (minor) && \
+ GTK_FRDP_MICRO_VERSION >= (micro)))
+
+#endif /* GTK_FRDP_VERSION_H */
diff --git a/subprojects/gtk-frdp/src/gtk-frdp.h b/subprojects/gtk-frdp/src/gtk-frdp.h
new file mode 100644
index 0000000..0ac8bdf
--- /dev/null
+++ b/subprojects/gtk-frdp/src/gtk-frdp.h
@@ -0,0 +1,34 @@
+/* gtk-frdp.h
+ *
+ * Copyright (C) 2018 Felipe Borges <felipeborges gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTK_FRDP_H
+#define GTK_FRDP_H
+
+#include <frdp-display.h>
+#include <gtk/gtk.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define GTK_FRDP_INSIDE
+# include "gtk-frdp-version.h"
+#undef GTK_FRDP_INSIDE
+
+G_END_DECLS
+
+#endif /* GTK_FRDP_H */
diff --git a/subprojects/gtk-frdp/src/meson.build b/subprojects/gtk-frdp/src/meson.build
new file mode 100644
index 0000000..acecc39
--- /dev/null
+++ b/subprojects/gtk-frdp/src/meson.build
@@ -0,0 +1,98 @@
+api_version = '0.1'
+
+gtk_frdp_sources = [
+ 'frdp-display.c',
+ 'frdp-session.c',
+]
+
+gtk_frdp_headers = [
+ 'frdp-display.h',
+ 'frdp-session.h',
+ 'gtk-frdp.h',
+]
+
+version_split = meson.project_version().split('.')
+MAJOR_VERSION = version_split[0]
+MINOR_VERSION = version_split[1]
+MICRO_VERSION = version_split[2]
+
+version_conf = configuration_data()
+version_conf.set('VERSION', meson.project_version())
+version_conf.set('MAJOR_VERSION', MAJOR_VERSION)
+version_conf.set('MINOR_VERSION', MINOR_VERSION)
+version_conf.set('MICRO_VERSION', MICRO_VERSION)
+
+gtk_frdp_header_subdir = join_paths(package_subdir, 'gtk-frdp')
+gtk_frdp_header_dir = join_paths(get_option('includedir'), gtk_frdp_header_subdir)
+
+configure_file(
+ input: 'gtk-frdp-version.h.in',
+ output: 'gtk-frdp-version.h',
+ configuration: version_conf,
+ install: true,
+ install_dir: gtk_frdp_header_dir
+)
+
+cc = meson.get_compiler('c')
+vala = meson.get_compiler('vala')
+
+gtk_frdp_deps = [
+ # The 2.0.0-rc4 version is needed at least, but there is no easy way to detect this.
+ dependency('winpr2', version: '>= 2.0.0'),
+ dependency('freerdp2', version: '>= 2.0.0'),
+
+ dependency('gio-2.0', version: '>= 2.50'),
+ dependency('gtk+-3.0'),
+ cc.find_library('m'),
+]
+
+gtk_frdp_lib = shared_library('gtk-frdp-' + api_version,
+ gtk_frdp_sources,
+ dependencies: gtk_frdp_deps,
+ install: true,
+ install_dir: libdir
+)
+
+gtk_frdp_dep = declare_dependency(
+ sources: gtk_frdp_headers,
+ dependencies: gtk_frdp_deps,
+ link_with: gtk_frdp_lib,
+ include_directories: include_directories('.'),
+)
+
+install_headers(gtk_frdp_headers, subdir: gtk_frdp_header_subdir)
+
+gtk_frdp_gir = gnome.generate_gir(gtk_frdp_lib,
+ sources: gtk_frdp_sources + gtk_frdp_headers,
+ nsversion: api_version,
+ namespace: 'GtkFrdp',
+ symbol_prefix: 'frdp',
+ identifier_prefix: 'Frdp',
+ link_with: gtk_frdp_lib,
+ includes: ['Gio-2.0', 'Gtk-3.0'],
+ install: true,
+ install_dir_gir: girdir,
+ install_dir_typelib: typelibdir,
+ extra_args: [ '--c-include=gtk-frdp.h', '--quiet' ],
+)
+
+gtk_frdp_vapi = gnome.generate_vapi('gtk-frdp-' + api_version,
+ sources: gtk_frdp_gir[0],
+ packages: [ 'gio-2.0', 'gtk+-3.0' ],
+ install: true,
+ install_dir: vapidir,
+ metadata_dirs: [ meson.current_source_dir() ],
+)
+
+pkg = import('pkgconfig')
+
+pkg.generate(
+ description: 'A shared library for ...',
+ libraries: gtk_frdp_lib,
+ name: 'gtk-frdp',
+ filebase: 'gtk-frdp-' + api_version,
+ version: meson.project_version(),
+ subdirs: gtk_frdp_header_subdir,
+ requires: 'glib-2.0',
+ install_dir: join_paths(libdir, 'pkgconfig')
+)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]