[connections/upstream/latest] New upstream version 41~rc




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]