Add initial code 44/263044/5
authorJunkyeong, Kim <jk0430.kim@samsung.com>
Wed, 25 Aug 2021 04:43:25 +0000 (13:43 +0900)
committerJunkyeong, Kim <jk0430.kim@samsung.com>
Fri, 27 Aug 2021 02:52:22 +0000 (11:52 +0900)
Change-Id: I6af04b464ae932acdee39d5e9a5ff57198277b48
Signed-off-by: Junkyeong, Kim <jk0430.kim@samsung.com>
COPYING [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
packaging/e-mod-tizen-rdp.spec [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/e_mod_main.c [new file with mode: 0644]
src/e_mod_main.h [new file with mode: 0644]
src/e_mod_rdp.c [new file with mode: 0644]
src/e_mod_rdp.h [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..f3bbff3
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,25 @@
+Copyright notice for Enlightenment:
+
+Copyright (C) 2000-2012 Carsten Haitzler and various contributors (see AUTHORS)
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+   1. Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+   2. Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..a30d363
--- /dev/null
@@ -0,0 +1,8 @@
+#maintainer-clean removes everything
+MAINTAINERCLEANFILES = aclocal.m4 compile config.sub config.guess config.h.in \
+                      configure depcomp install-sh ltmain.sh Makefile.in missing
+
+SUBDIRS = src
+
+MAINTAINERCLEANFILES = \
+                      Makefile.in
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..5a30991
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+set  -x
+aclocal
+autoconf
+libtoolize --copy --force
+autoheader
+automake --foreign --add-missing --copy
+
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..a9fb6c6
--- /dev/null
@@ -0,0 +1,129 @@
+# Process this file with autoconf to produce a configure script.
+dnl Process this file with autoconf to produce a configure script.
+
+# Note )
+#
+# AC_DEFINE(VARIABLE, VALUE, DESCRIPTION)
+#   output the following to config.h
+#   /* DESCRIPTION */
+#   #define VARIABLE VALUE
+#
+# AC_SUBST(VARIABLE, [VALUE])
+#   define $(VARIABLE) as VALUE in Makefile
+
+dnl ========================================================================
+# initialization
+dnl ========================================================================
+AC_INIT([e-mod-tizen-rdp], [0.1], [sj76.park@samsung.com])
+
+# check for tools needed by automake generated Makefiles
+# -Wall  : Turn all warnings on.
+# -Werror: report warings as errors.
+# foreign: relax some GNU standard requirements
+#AM_INIT_AUTOMAKE([-Wall -Werror foreign])
+AM_INIT_AUTOMAKE([-Wall foreign])
+AM_SILENT_RULES([yes])
+
+dnl ========================================================================
+# checks for programs
+dnl ========================================================================
+AC_PROG_CC
+AC_DISABLE_STATIC
+AC_PROG_LIBTOOL
+
+dnl ========================================================================
+# checks for libraries
+dnl ========================================================================
+
+dnl ========================================================================
+# checks for header files
+dnl ========================================================================
+#AC_HEADER_STDC
+AC_CHECK_HEADERS([math.h fcntl.h stdlib.h string.h unistd.h])
+
+dnl ========================================================================
+# checks for typedefs, structures, and compiler characteristics
+AC_C_CONST
+
+dnl ========================================================================
+# checks for library functions
+dnl ========================================================================
+#AC_FUNC_MALLOC
+AC_FUNC_MMAP
+AC_CHECK_FUNCS([memset munmap strcasecmp strdup])
+
+dnl ========================================================================
+# checks for pkg-config
+dnl ========================================================================
+PKG_PROG_PKG_CONFIG
+
+
+dnl ========================================================================
+# checks for pkg-config
+dnl ========================================================================
+AC_PATH_PROG([wayland_scanner], [wayland-scanner])
+if test x$wayland_scanner = x; then
+       AC_MSG_ERROR([wayland-scanner is needed to compile])
+fi
+
+have_wayland_only=no
+AC_ARG_ENABLE([wayland-only],
+  AS_HELP_STRING([--enable-wayland-only],[enable wayland-only version of enlightenment @<:@default=disabled@:>@]),
+  [have_wayland_only=$enableval],
+  [have_wayland_only=no])
+AC_MSG_CHECKING([whether wayland-only version is enabled])
+if test "x${have_wayland_only}" = "xyes"; then
+  AC_DEFINE_UNQUOTED([HAVE_WAYLAND_ONLY],[1],[enable wayland-only version of enlightenment])
+fi
+AM_CONDITIONAL(HAVE_WAYLAND_ONLY, [test "x${have_wayland_only}" = xyes])
+
+if test "x${have_wayland_only}" = "xyes"; then
+       PKG_CHECK_MODULES(ENLIGHTENMENT, [enlightenment, dlog, libtbm, pixman-1, wayland-server, tizen-extension-server, freerdp2])
+       PKG_CHECK_MODULES(WAYLAND_SCANNER, wayland-scanner)
+else
+       PKG_CHECK_MODULES(ENLIGHTENMENT, [enlightenment, dlog, libtbm, pixman-1, x11, utilX])
+       ENLIGHTENMENT_CFLAGS="${ENLIGHTENMENT_CFLAGS} -DNEED_X=1"
+fi
+
+AC_SUBST(ENLIGHTENMENT_CFLAGS)
+AC_SUBST(ENLIGHTENMENT_LIBS)
+
+# to include e_comp_wl.h
+if test "${have_wayland_only}" != "xno"; then
+        AC_DEFINE_UNQUOTED([HAVE_WAYLAND],[1],[enable wayland support])
+fi
+
+dnl =======================================================================
+
+release=$(pkg-config --variable=release enlightenment)
+MODULE_ARCH="$host_os-$host_cpu-$release"
+AC_SUBST(MODULE_ARCH)
+AC_DEFINE_UNQUOTED(MODULE_ARCH, "$MODULE_ARCH", "Module architecture")
+
+datadir=$(pkg-config --variable=modules enlightenment)/${PACKAGE}
+AC_ARG_ENABLE(homedir-install,
+       AS_HELP_STRING([--enable-homedir-install], [Install module in homedir]),
+       [ datadir="${HOME}/.e/e/modules/${PACKAGE}" ]
+)
+
+dnl ========================================================================
+# output files
+dnl ========================================================================
+
+# create HEADER for all HEADER.in.
+# HEADERS contain definitions made with AC_DEFINE.
+# the following command will create config.h from config.h.in
+AC_CONFIG_HEADERS([config.h])
+
+AC_SYS_LARGEFILE
+
+# create FILE for all FILE.in.
+# FILES contains definitions made with AC_SUBST.
+AC_CONFIG_FILES([
+                Makefile
+                src/Makefile
+                ])
+
+AC_OUTPUT
+
+
diff --git a/packaging/e-mod-tizen-rdp.spec b/packaging/e-mod-tizen-rdp.spec
new file mode 100644 (file)
index 0000000..aebaf60
--- /dev/null
@@ -0,0 +1,49 @@
+Name: e-mod-tizen-rdp
+Version: 0.1.0
+Release: 1
+Summary: The enlightenment rdp module for Tizen
+URL: http://www.enlightenment.org
+Group: Graphics & UI Framework/Other
+Source0: %{name}-%{version}.tar.gz
+License: BSD-2-Clause
+BuildRequires: pkgconfig(enlightenment)
+BuildRequires: pkgconfig(dlog)
+BuildRequires: pkgconfig(wayland-server)
+BuildRequires: pkgconfig(libtbm)
+BuildRequires: pkgconfig(freerdp2)
+BuildRequires: pkgconfig(pixman-1)
+BuildRequires: pkgconfig(openssl1.1)
+BuildRequires: libopenssl11
+BuildRequires: libopenssl1.1-devel
+
+%description
+This package is a enlightenment rdp module for Tizen.
+
+%global TZ_SYS_RO_SHARE  %{?TZ_SYS_RO_SHARE:%TZ_SYS_RO_SHARE}%{!?TZ_SYS_RO_SHARE:/usr/share}
+
+%prep
+%setup -q
+
+%build
+
+export GC_SECTIONS_FLAGS="-fdata-sections -ffunction-sections -Wl,--gc-sections"
+export CFLAGS+=" -Wall -Werror -g -fPIC -rdynamic ${GC_SECTIONS_FLAGS} "
+export LDFLAGS+=" -Wl,--hash-style=both -Wl,--as-needed -Wl,--rpath=/usr/lib"
+
+%reconfigure --enable-wayland-only
+
+make
+
+%install
+rm -rf %{buildroot}
+
+# install
+make install DESTDIR=%{buildroot}
+
+# clear useless textual files
+find  %{buildroot}%{_libdir}/enlightenment/modules/%{name} -name *.la | xargs rm
+
+%files
+%defattr(-,root,root,-)
+%{_libdir}/enlightenment/modules/e-mod-tizen-rdp
+%license COPYING
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..c674d32
--- /dev/null
@@ -0,0 +1,18 @@
+MAINTAINERCLEANFILES = Makefile.in
+MODULE = e-mod-tizen-rdp
+
+LDFLAGS +=
+
+pkgdir                 = $(libdir)/enlightenment/modules/$(MODULE)/$(MODULE_ARCH)
+pkg_LTLIBRARIES        = module.la
+module_la_SOURCES      = \
+       e_mod_main.c \
+       e_mod_rdp.c
+
+module_la_LIBADD       =
+module_la_CFLAGS       = @ENLIGHTENMENT_CFLAGS@
+module_la_LDFLAGS      = -module -avoid-version @ENLIGHTENMENT_LIBS@
+module_la_DEPENDENCIES = $(top_builddir)/config.h
+
+#uninstall:
+#  rm -rf $(DESTDIR)$(libdir)/enlightenment/modules/$(MODULE)
diff --git a/src/e_mod_main.c b/src/e_mod_main.c
new file mode 100644 (file)
index 0000000..e03d3fe
--- /dev/null
@@ -0,0 +1,64 @@
+#include "e.h"
+#include "dlog.h"
+#include "eina_log.h"
+#include "e_mod_main.h"
+#include "e_mod_rdp.h"
+
+/* this is needed to advertise a label for the module IN the code (not just
+ * the .desktop file) but more specifically the api version it was compiled
+ * for so E can skip modules that are compiled for an incorrect API version
+ * safely) */
+EAPI E_Module_Api e_modapi = { E_MODULE_API_VERSION, "RDP-Module" };
+
+const static char *_wr_log_dom_name = "e-rdp";
+
+EAPI void *
+e_modapi_init(E_Module *m)
+{
+   if (!eina_init())
+     {
+        ERR("failed eina_init()");
+        return NULL;
+     }
+
+   _wr_log_dom = eina_log_domain_register(_wr_log_dom_name, EINA_COLOR_GREEN);
+   if (_wr_log_dom < 0)
+     goto err_log;
+
+   eina_log_domain_level_set(_wr_log_dom_name, EINA_LOG_LEVEL_DBG);
+
+   if (!e_mod_rdp_init())
+     {
+        ERR("failed e_mod_rdp_init() (%s)", dlerror());
+        goto err_init;
+     }
+
+   INF("rdp module init success");
+
+   return m;
+
+err_init:
+   eina_log_domain_unregister(_wr_log_dom);
+err_log:
+   eina_shutdown();
+   return NULL;
+}
+
+EAPI int
+e_modapi_shutdown(E_Module *m)
+{
+   e_mod_rdp_deinit();
+   eina_log_domain_unregister(_wr_log_dom);
+   eina_shutdown();
+
+   INF("rdp module shutdown");
+
+   return 1;
+}
+
+EAPI int
+e_modapi_save(E_Module *m)
+{
+   /* Do Something */
+   return 1;
+}
diff --git a/src/e_mod_main.h b/src/e_mod_main.h
new file mode 100644 (file)
index 0000000..7919e2b
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __E_MOD_MAIN_H__
+#define __E_MOD_MAIN_H__
+
+#include "e.h"
+
+/*** E Module ***/
+EAPI extern E_Module_Api e_modapi;
+
+EAPI void* e_modapi_init (E_Module *m);
+EAPI int e_modapi_shutdown (E_Module *m);
+EAPI int e_modapi_save (E_Module *m);
+
+#endif//__E_MOD_MAIN_H__
diff --git a/src/e_mod_rdp.c b/src/e_mod_rdp.c
new file mode 100644 (file)
index 0000000..9a26c6d
--- /dev/null
@@ -0,0 +1,1375 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <linux/input.h>
+
+#include <freerdp/version.h>
+#include <freerdp/freerdp.h>
+#include <freerdp/listener.h>
+#include <freerdp/update.h>
+#include <freerdp/input.h>
+#include <freerdp/codec/color.h>
+#include <freerdp/codec/rfx.h>
+#include <freerdp/codec/nsc.h>
+#include <freerdp/locale/keyboard.h>
+#include <winpr/input.h>
+#include <winpr/ssl.h>
+
+#include <pixman.h>
+#include <tbm_surface.h>
+#include <tbm_surface_internal.h>
+
+#include "e_mod_rdp.h"
+
+#define E_RDP_WIDTH 1280
+#define E_RDP_HEIGHT 720
+#define E_MOD_RDP_CONFIG_VERSION  2
+#define E_MOD_RDP_NAME_LEN        64
+#define MAX_FREERDP_FDS           10
+#define DEFAULT_PIXEL_FORMAT PIXEL_FORMAT_BGRA32
+
+enum peer_item_flags
+{
+   RDP_PEER_ACTIVATED      = (1 << 0),
+   RDP_PEER_OUTPUT_ENABLED = (1 << 1),
+};
+
+typedef struct _E_Rdp_Config E_Rdp_Config;
+typedef struct _E_Rdp_Output E_Rdp_Output;
+typedef struct _E_Rdp_Backend E_Rdp_Backend;
+typedef struct _E_Rdp_Peer_Context E_Rdp_Peer_Context;
+typedef struct _E_Rdp_Peer_Item E_Rdp_Peer_Item;
+typedef struct _E_Rdp_Peer_Context E_Rdp_Peer_Context;
+
+static E_Rdp_Backend *g_rdp_backend;
+
+struct _E_Rdp_Config
+{
+   char *bind_address;
+   char *rdp_key;
+   char *server_cert;
+   char *server_key;
+   int port;
+   int no_clients_resize;
+   int force_no_compression;
+};
+
+struct _E_Rdp_Output
+{
+   E_Output *primary_output;
+   Ecore_Timer *frame_timer;
+   pixman_image_t *pix_surface;
+   tbm_surface_h tbm_surface;
+
+   struct wl_list peers;
+};
+
+struct _E_Rdp_Backend
+{
+   freerdp_listener *listener;
+   struct wl_event_source *listener_events[MAX_FREERDP_FDS];
+   E_Rdp_Output *output;
+
+   char *server_cert;
+   char *server_key;
+   char *rdp_key;
+   int tls_enabled;
+   int no_clients_resize;
+   int force_no_compression;
+};
+
+struct _E_Rdp_Peer_Item
+{
+   int flags;
+   freerdp_peer *peer;
+
+   struct wl_list link;
+};
+
+struct _E_Rdp_Peer_Context
+{
+   rdpContext _p;
+
+   E_Rdp_Backend *rdpBackend;
+   struct wl_event_source *events[MAX_FREERDP_FDS];
+   RFX_CONTEXT *rfx_context;
+   wStream *encode_stream;
+   RFX_RECT *rfx_rects;
+   NSC_CONTEXT *nsc_context;
+
+   E_Rdp_Peer_Item item;
+};
+
+struct rdp_to_xkb_keyboard_layout
+{
+   UINT32 rdpLayoutCode;
+   const char *xkbLayout;
+   const char *xkbVariant;
+};
+
+static tbm_surface_h
+_e_rdp_tbm_image_create(E_Rdp_Output *output, int w, int h, int color)
+{
+   tbm_surface_h tbm_surface = NULL;
+
+   tbm_surface = tbm_surface_internal_create_with_flags(w, h, TBM_FORMAT_XRGB8888, TBM_BO_SCANOUT);
+   if (!tbm_surface)
+     return NULL;
+
+   return tbm_surface;
+}
+
+static pixman_format_code_t
+_e_rdp_pixman_format_get(tbm_format format)
+{
+   switch (format)
+     {
+      case TBM_FORMAT_ARGB8888:
+        return PIXMAN_a8r8g8b8;
+
+      case TBM_FORMAT_XRGB8888:
+        return PIXMAN_x8r8g8b8;
+
+      default:
+        return 0;
+     }
+
+   return 0;
+}
+
+static pixman_image_t *
+_e_rdp_pixman_image_create(tbm_surface_h tbm_surface)
+{
+   int err;
+   tbm_surface_info_s info;
+   pixman_image_t *pix_surface = NULL;
+   pixman_format_code_t pix_format = 0;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(tbm_surface, NULL);
+
+   err = tbm_surface_map(tbm_surface, TBM_SURF_OPTION_WRITE, &info);
+   if (err)
+     return NULL;
+
+   pix_format = _e_rdp_pixman_format_get(info.format);
+   if (pix_format == 0)
+     {
+        ERR("not supported format");
+        goto out;
+     }
+
+   pix_surface = pixman_image_create_bits(pix_format, info.width, info.height, (uint32_t *)info.planes[0].ptr, info.planes[0].stride);
+   if (pix_surface == NULL)
+     ERR("create pixman image failed");
+
+out:
+   tbm_surface_unmap(tbm_surface);
+   return pix_surface;
+}
+
+static void
+e_rdp_peer_refresh_rfx(pixman_region32_t *damage, pixman_image_t *image, freerdp_peer *peer)
+{
+   int width, height, nrects, i;
+   pixman_box32_t *region, *rects;
+   uint32_t *ptr;
+   RFX_RECT *rfxRect;
+   rdpUpdate *update = peer->update;
+   SURFACE_BITS_COMMAND cmd = { 0 };
+   E_Rdp_Peer_Context *context = (E_Rdp_Peer_Context *)peer->context;
+
+   Stream_Clear(context->encode_stream);
+   Stream_SetPosition(context->encode_stream, 0);
+
+   width = (damage->extents.x2 - damage->extents.x1);
+   height = (damage->extents.y2 - damage->extents.y1);
+
+   cmd.skipCompression = TRUE;
+   cmd.cmdType = CMDTYPE_STREAM_SURFACE_BITS;
+   cmd.destLeft = damage->extents.x1;
+   cmd.destTop = damage->extents.y1;
+   cmd.destRight = damage->extents.x2;
+   cmd.destBottom = damage->extents.y2;
+   cmd.bmp.bpp = 32;
+   cmd.bmp.codecID = peer->settings->RemoteFxCodecId;
+   cmd.bmp.width = width;
+   cmd.bmp.height = height;
+
+   ptr = pixman_image_get_data(image) + damage->extents.x1 + damage->extents.y1 * (pixman_image_get_stride(image) / sizeof(uint32_t));
+
+   rects = pixman_region32_rectangles(damage, &nrects);
+   context->rfx_rects = realloc(context->rfx_rects, nrects * sizeof *rfxRect);
+
+   for (i = 0; i < nrects; i++)
+     {
+        region = &rects[i];
+        rfxRect = &context->rfx_rects[i];
+
+        rfxRect->x = (region->x1 - damage->extents.x1);
+        rfxRect->y = (region->y1 - damage->extents.y1);
+        rfxRect->width = (region->x2 - region->x1);
+        rfxRect->height = (region->y2 - region->y1);
+     }
+
+   rfx_compose_message(context->rfx_context, context->encode_stream, context->rfx_rects, nrects, (BYTE *)ptr, width, height, pixman_image_get_stride(image));
+
+   cmd.bmp.bitmapDataLength = Stream_GetPosition(context->encode_stream);
+   cmd.bmp.bitmapData = Stream_Buffer(context->encode_stream);
+
+   update->SurfaceBits(update->context, &cmd);
+}
+
+
+static void
+e_rdp_peer_refresh_nsc(pixman_region32_t *damage, pixman_image_t *image, freerdp_peer *peer)
+{
+   int width, height;
+   uint32_t *ptr;
+   rdpUpdate *update = peer->update;
+   SURFACE_BITS_COMMAND cmd = { 0 };
+   E_Rdp_Peer_Context *context = (E_Rdp_Peer_Context *)peer->context;
+
+   Stream_Clear(context->encode_stream);
+   Stream_SetPosition(context->encode_stream, 0);
+
+   width = (damage->extents.x2 - damage->extents.x1);
+   height = (damage->extents.y2 - damage->extents.y1);
+
+   cmd.cmdType = CMDTYPE_SET_SURFACE_BITS;
+   cmd.skipCompression = TRUE;
+   cmd.destLeft = damage->extents.x1;
+   cmd.destTop = damage->extents.y1;
+   cmd.destRight = damage->extents.x2;
+   cmd.destBottom = damage->extents.y2;
+   cmd.bmp.bpp = 32;
+   cmd.bmp.codecID = peer->settings->NSCodecId;
+   cmd.bmp.width = width;
+   cmd.bmp.height = height;
+
+   ptr = pixman_image_get_data(image) + damage->extents.x1 + damage->extents.y1 * (pixman_image_get_stride(image) / sizeof(uint32_t));
+
+   nsc_compose_message(context->nsc_context, context->encode_stream, (BYTE *)ptr, width, height, pixman_image_get_stride(image));
+
+   cmd.bmp.bitmapDataLength = Stream_GetPosition(context->encode_stream);
+   cmd.bmp.bitmapData = Stream_Buffer(context->encode_stream);
+
+   update->SurfaceBits(update->context, &cmd);
+}
+
+static void
+pixman_image_flipped_subrect(const pixman_box32_t *rect, pixman_image_t *img, BYTE *dest)
+{
+   int stride = pixman_image_get_stride(img);
+   int h;
+   int toCopy = (rect->x2 - rect->x1) * 4;
+   int height = (rect->y2 - rect->y1);
+   const BYTE *src = (const BYTE *)pixman_image_get_data(img);
+   src += ((rect->y2-1) * stride) + (rect->x1 * 4);
+
+   for (h = 0; h < height; h++, src -= stride, dest += toCopy)
+     memcpy(dest, src, toCopy);
+}
+
+static void
+e_rdp_peer_refresh_raw(pixman_region32_t *region, pixman_image_t *image, freerdp_peer *peer)
+{
+   rdpUpdate *update = peer->update;
+   SURFACE_BITS_COMMAND cmd = { 0 };
+   SURFACE_FRAME_MARKER marker;
+   pixman_box32_t *rect, subrect;
+   int nrects, i;
+   int heightIncrement, remainingHeight, top;
+
+   rect = pixman_region32_rectangles(region, &nrects);
+   if (!nrects)
+     return;
+
+   marker.frameId++;
+   marker.frameAction = SURFACECMD_FRAMEACTION_BEGIN;
+   update->SurfaceFrameMarker(peer->context, &marker);
+
+   cmd.cmdType = CMDTYPE_SET_SURFACE_BITS;
+   cmd.bmp.bpp = 32;
+   cmd.bmp.codecID = 0;
+
+   for (i = 0; i < nrects; i++, rect++)
+     {
+        /*INF("rect(%d,%d, %d,%d)\n", rect->x1, rect->y1, rect->x2, rect->y2);*/
+        cmd.destLeft = rect->x1;
+        cmd.destRight = rect->x2;
+        cmd.bmp.width = (rect->x2 - rect->x1);
+
+        heightIncrement = peer->settings->MultifragMaxRequestSize / (16 + cmd.bmp.width * 4);
+        remainingHeight = rect->y2 - rect->y1;
+        top = rect->y1;
+
+        subrect.x1 = rect->x1;
+        subrect.x2 = rect->x2;
+
+        while (remainingHeight)
+          {
+             cmd.bmp.height = (remainingHeight > heightIncrement) ? heightIncrement : remainingHeight;
+             cmd.destTop = top;
+             cmd.destBottom = top + cmd.bmp.height;
+             cmd.bmp.bitmapDataLength = cmd.bmp.width * cmd.bmp.height * 4;
+             cmd.bmp.bitmapData = (BYTE *)realloc(cmd.bmp.bitmapData, cmd.bmp.bitmapDataLength);
+
+             subrect.y1 = top;
+             subrect.y2 = top + cmd.bmp.height;
+             pixman_image_flipped_subrect(&subrect, image, cmd.bmp.bitmapData);
+
+             /*INF("*  sending (%d,%d, %d,%d)\n", subrect.x1, subrect.y1, subrect.x2, subrect.y2); */
+             update->SurfaceBits(peer->context, &cmd);
+
+             remainingHeight -= cmd.bmp.height;
+             top += cmd.bmp.height;
+          }
+     }
+
+   free(cmd.bmp.bitmapData);
+
+   marker.frameAction = SURFACECMD_FRAMEACTION_END;
+   update->SurfaceFrameMarker(peer->context, &marker);
+}
+
+static void
+e_rdp_peer_refresh_region(pixman_region32_t *region, freerdp_peer *peer, tbm_surface_h tbm_surface, pixman_image_t *pix_surface)
+{
+   E_Rdp_Peer_Context *context = (E_Rdp_Peer_Context *)peer->context;
+   E_Rdp_Output *output = context->rdpBackend->output;
+   rdpSettings *settings = peer->settings;
+
+   if (settings->RemoteFxCodec)
+     e_rdp_peer_refresh_rfx(region, pix_surface, peer);
+   else if (settings->NSCodec)
+     e_rdp_peer_refresh_nsc(region, pix_surface, peer);
+   else
+     e_rdp_peer_refresh_raw(region, pix_surface, peer);
+
+   if (output->pix_surface)
+     pixman_image_unref(output->pix_surface);
+   output->pix_surface = pix_surface;
+
+   if (output->tbm_surface)
+     tbm_surface_destroy(output->tbm_surface);
+   output->tbm_surface = tbm_surface;
+}
+
+static void
+_e_rdp_output_image_showing_rect_get(Eina_Rectangle *out_rect, Eina_Rectangle *dst_rect, Eina_Rectangle *showing_rect)
+{
+   showing_rect->x = dst_rect->x;
+   showing_rect->y = dst_rect->y;
+
+   if (dst_rect->x >= out_rect->w)
+     showing_rect->w = 0;
+   else if (dst_rect->x + dst_rect->w > out_rect->w)
+     showing_rect->w = out_rect->w - dst_rect->x;
+   else
+     showing_rect->w = dst_rect->w;
+
+   if (dst_rect->y >= out_rect->h)
+     showing_rect->h = 0;
+   else if (dst_rect->y + dst_rect->h > out_rect->h)
+     showing_rect->h = out_rect->h - dst_rect->y;
+   else
+     showing_rect->h = dst_rect->h;
+}
+
+static void
+_e_rdp_output_image_src_crop_get(E_Hwc_Window *hwc_window, Eina_Rectangle *fit, Eina_Rectangle *showing_rect, int primary_w, int primary_h)
+{
+   float ratio_x, ratio_y;
+   Eina_Rectangle out_rect;
+   Eina_Rectangle dst_rect;
+
+   fit->x = 0;
+   fit->y = 0;
+   fit->w = 0;
+   fit->h = 0;
+
+   out_rect.x = 0;
+   out_rect.y = 0;
+   out_rect.w = primary_w;
+   out_rect.h = primary_h;
+
+   dst_rect.x = hwc_window->current.info.dst_pos.x;
+   dst_rect.y = hwc_window->current.info.dst_pos.y;
+   dst_rect.w = hwc_window->current.info.dst_pos.w;
+   dst_rect.h = hwc_window->current.info.dst_pos.h;
+
+   _e_rdp_output_image_showing_rect_get(&out_rect, &dst_rect, showing_rect);
+
+   fit->x = hwc_window->current.info.src_config.pos.x;
+   fit->y = hwc_window->current.info.src_config.pos.y;
+
+   if (hwc_window->current.info.transform % 2 == 0)
+     {
+        ratio_x = (float)hwc_window->current.info.src_config.pos.w / dst_rect.w;
+        ratio_y = (float)hwc_window->current.info.src_config.pos.h / dst_rect.h;
+
+        fit->w = showing_rect->w * ratio_x;
+        fit->h = showing_rect->h * ratio_y;
+     }
+   else
+     {
+        ratio_x = (float)hwc_window->current.info.src_config.pos.w / dst_rect.h;
+        ratio_y = (float)hwc_window->current.info.src_config.pos.h / dst_rect.w;
+
+        fit->w = showing_rect->h * ratio_x;
+        fit->h = showing_rect->w * ratio_y;
+     }
+}
+
+static void
+_e_rdp_output_center_rect_get (int src_w, int src_h, int dst_w, int dst_h, Eina_Rectangle *fit)
+{
+   float rw, rh;
+
+   if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0 || !fit)
+     return;
+
+   rw = (float)src_w / dst_w;
+   rh = (float)src_h / dst_h;
+
+   if (rw > rh)
+     {
+        fit->w = dst_w;
+        fit->h = src_h / rw;
+        fit->x = 0;
+        fit->y = (dst_h - fit->h) / 2;
+     }
+   else if (rw < rh)
+     {
+        fit->w = src_w / rh;
+        fit->h = dst_h;
+        fit->x = (dst_w - fit->w) / 2;
+        fit->y = 0;
+     }
+   else
+     {
+        fit->w = dst_w;
+        fit->h = dst_h;
+        fit->x = 0;
+        fit->y = 0;
+     }
+
+   if (fit->x % 2)
+     fit->x = fit->x - 1;
+}
+
+static void
+_e_rdp_output_image_dst_crop_get(E_Hwc_Window *hwc_window, int src_w, int src_h, int w, int h,
+                                 Eina_Rectangle *pos, Eina_Rectangle *showing_pos, Eina_Rectangle *dst_crop, int rotate)
+{
+   dst_crop->x = 0;
+   dst_crop->y = 0;
+   dst_crop->w = 0;
+   dst_crop->h = 0;
+
+   if (hwc_window->current.info.src_config.pos.w == w && hwc_window->current.info.src_config.pos.h == h &&
+       pos->x == 0 && pos->y == 0 && pos->w == src_w && pos->h == src_h)
+     {
+        dst_crop->x = pos->x;
+        dst_crop->y = pos->y;
+        dst_crop->w = pos->w;
+        dst_crop->h = pos->h;
+     }
+   else if ((w == pos->w) && (h == pos->h) && (showing_pos->w == pos->w) && (showing_pos->h == pos->h))
+     {
+        dst_crop->x = hwc_window->current.info.dst_pos.x + pos->x;
+        dst_crop->y = hwc_window->current.info.dst_pos.y + pos->y;
+        dst_crop->w = hwc_window->current.info.dst_pos.w;
+        dst_crop->h = hwc_window->current.info.dst_pos.h;
+     }
+   else if (rotate == 0)
+     {
+        dst_crop->x = showing_pos->x * pos->w / w + pos->x;
+        dst_crop->y = showing_pos->y * pos->h / h + pos->y;
+        dst_crop->w = showing_pos->w * pos->w / w;
+        dst_crop->h = showing_pos->h * pos->h / h;
+     }
+   else
+     {
+        dst_crop->x = pos->x;
+        dst_crop->y = pos->y;
+        dst_crop->w = pos->w;
+        dst_crop->h = pos->h;
+     }
+}
+
+static void
+_e_rdp_output_image_composite(pixman_image_t *src_img, pixman_image_t *dst_img,
+                          int sx, int sy, int sw, int sh,
+                          int dx, int dy, int dw, int dh,
+                          Eina_Bool over, int rotate, int hflip, int vflip)
+{
+   double scale_x, scale_y;
+   int rotate_step;
+   pixman_transform_t t;
+   struct pixman_f_transform ft;
+   pixman_op_t op;
+
+   pixman_f_transform_init_identity(&ft);
+
+   if (hflip)
+     {
+        pixman_f_transform_scale(&ft, NULL, -1, 1);
+        pixman_f_transform_translate(&ft, NULL, dw, 0);
+     }
+
+   if (vflip)
+     {
+        pixman_f_transform_scale(&ft, NULL, 1, -1);
+        pixman_f_transform_translate(&ft, NULL, 0, dh);
+     }
+
+   rotate_step = (rotate + 360) / 90 % 4;
+   if (rotate_step > 0)
+     {
+        int c, s, tx = 0, ty = 0;
+        switch (rotate_step)
+          {
+           case 1:
+              c = 0, s = -1, tx = -dw;
+              break;
+           case 2:
+              c = -1, s = 0, tx = -dw, ty = -dh;
+              break;
+           case 3:
+              c = 0, s = 1, ty = -dh;
+              break;
+          }
+        pixman_f_transform_translate(&ft, NULL, tx, ty);
+        pixman_f_transform_rotate(&ft, NULL, c, s);
+     }
+
+   if (rotate_step % 2 == 0)
+     {
+        scale_x = (double)sw / dw;
+        scale_y = (double)sh / dh;
+     }
+   else
+     {
+        scale_x = (double)sw / dh;
+        scale_y = (double)sh / dw;
+     }
+
+   pixman_f_transform_scale(&ft, NULL, scale_x, scale_y);
+   pixman_f_transform_translate(&ft, NULL, sx, sy);
+   pixman_transform_from_pixman_f_transform(&t, &ft);
+   pixman_image_set_transform(src_img, &t);
+
+   if (!over) op = PIXMAN_OP_SRC;
+   else op = PIXMAN_OP_OVER;
+
+   pixman_image_composite(op, src_img, NULL, dst_img, 0, 0, 0, 0,
+                          dx, dy, dw, dh);
+}
+
+static Eina_Bool
+_e_rdp_pixman_output_image_composite(E_Rdp_Output *output, E_Hwc_Window *hwc_window, pixman_image_t *pix_surface, int pix_w, int pix_h, int primary_w, int primary_h)
+{
+   Eina_Rectangle showing_pos = {0, };
+   Eina_Rectangle dst_pos = {0, };
+   Eina_Rectangle src_crop = {0, };
+   Eina_Rectangle dst_crop = {0, };
+   tbm_surface_h tbm_surface = NULL;
+   tbm_surface_info_s info;
+   pixman_image_t *pix_surface_src = NULL;
+   pixman_format_code_t pix_format = 0;
+   int err = 0;
+
+   tbm_surface = hwc_window->display.buffer.tsurface;
+   if (!tbm_surface)
+     return EINA_FALSE;
+
+   tbm_surface_internal_ref(tbm_surface);
+
+   err = tbm_surface_map(tbm_surface, TBM_SURF_OPTION_READ, &info);
+   if (err)
+     return EINA_FALSE;
+
+   pix_format = _e_rdp_pixman_format_get(info.format);
+   if (pix_format == 0)
+     {
+        ERR("not supported format");
+        tbm_surface_unmap(tbm_surface);
+        return EINA_FALSE;
+     }
+
+   pix_surface_src = pixman_image_create_bits(pix_format, info.width, info.height, (uint32_t *)info.planes[0].ptr, info.planes[0].stride);
+   if (pix_surface_src == NULL)
+     {
+        ERR("create pixman image failed");
+        tbm_surface_unmap(tbm_surface);
+        return EINA_FALSE;
+     }
+
+   _e_rdp_output_image_src_crop_get(hwc_window, &src_crop, &showing_pos, primary_w, primary_h);
+   _e_rdp_output_center_rect_get(primary_w, primary_h, pix_w, pix_h, &dst_pos);
+   _e_rdp_output_image_dst_crop_get(hwc_window, info.width, info.height, primary_w, primary_h, &dst_pos, &showing_pos, &dst_crop, 0);
+
+   _e_rdp_output_image_composite(pix_surface_src, pix_surface,
+                                 src_crop.x, src_crop.y, src_crop.w, src_crop.h,
+                                 dst_crop.x, dst_crop.y, dst_crop.w, dst_crop.h,
+                                 EINA_TRUE, 0, 0, 0);
+
+   pixman_image_unref(pix_surface_src);
+   tbm_surface_unmap(tbm_surface);
+   tbm_surface_internal_unref(tbm_surface);
+
+   return EINA_TRUE;
+}
+
+static int
+_e_rdp_cb_hwc_window_sort(const void *d1, const void *d2)
+{
+   E_Hwc_Window *hwc_window1 = (E_Hwc_Window *)d1;
+   E_Hwc_Window *hwc_window2 = (E_Hwc_Window *)d2;
+   int zpos1 = 0, zpos2 = 0;
+
+   if (!hwc_window1) return(1);
+   if (!hwc_window2) return(-1);
+
+   if (hwc_window1->state == E_HWC_WINDOW_STATE_NONE)
+     zpos1 = -999;
+   else
+     zpos1 = hwc_window1->zpos;
+
+   if (hwc_window2->state == E_HWC_WINDOW_STATE_NONE)
+     zpos2 = -999;
+   else
+     zpos2 = hwc_window2->zpos;
+
+   return (zpos1 - zpos2);
+}
+
+static pixman_image_t *
+_e_rdp_pixman_output_image_get(E_Rdp_Output *output, tbm_surface_h tbm_surface)
+{
+   E_Output *e_output = NULL;
+   E_Hwc *hwc = NULL;
+   E_Hwc_Window *hwc_window = NULL;
+   Eina_List *l;
+   Eina_List *visible_list = NULL;
+   Eina_Bool target_window = EINA_FALSE;
+   int err;
+   tbm_surface_info_s info;
+   pixman_image_t *pix_surface = NULL;
+   pixman_format_code_t pix_format = 0;
+   int e_output_w, e_output_h;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(output, NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(tbm_surface, NULL);
+
+   e_output = output->primary_output;
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e_output, NULL);
+
+   hwc = e_output->hwc;
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hwc, EINA_FALSE);
+
+   e_output_w = e_output->config.mode.w;
+   e_output_h = e_output->config.mode.h;
+
+   err = tbm_surface_map(tbm_surface, TBM_SURF_OPTION_WRITE, &info);
+   if (err)
+     return NULL;
+
+   pix_format = _e_rdp_pixman_format_get(info.format);
+   if (pix_format == 0)
+     {
+        ERR("not supported format");
+        goto out;
+     }
+
+   pix_surface = pixman_image_create_bits(pix_format, info.width, info.height, (uint32_t *)info.planes[0].ptr, info.planes[0].stride);
+   if (pix_surface == NULL)
+     {
+        ERR("create pixman image failed");
+        goto out;
+     }
+
+   EINA_LIST_FOREACH(hwc->hwc_windows, l, hwc_window)
+     {
+        if (!hwc_window) continue;
+        if (hwc_window->is_target) continue;
+        if (hwc_window->is_video) continue;
+        if (hwc_window->is_cursor) continue;
+        if (hwc_window->state == E_HWC_WINDOW_STATE_NONE || hwc_window->zpos == -999) continue;
+
+        if (hwc_window->accepted_state == E_HWC_WINDOW_STATE_CLIENT)
+          {
+             target_window = EINA_TRUE;
+             visible_list = eina_list_append(visible_list, hwc_window);
+             continue;
+          }
+
+        if (hwc_window->accepted_state == E_HWC_WINDOW_STATE_DEVICE)
+          visible_list = eina_list_append(visible_list, hwc_window);
+     }
+
+   if (eina_list_count(visible_list) == 0)
+     {
+        ERR("no visible hwc_window for capture");
+        _e_rdp_pixman_output_image_composite(output, (E_Hwc_Window *)hwc->target_hwc_window, pix_surface, info.width, info.height, e_output_w, e_output_h);
+        goto out;
+     }
+
+   visible_list = eina_list_sort(visible_list, eina_list_count(visible_list), _e_rdp_cb_hwc_window_sort);
+
+   EINA_LIST_FOREACH(visible_list, l, hwc_window)
+     {
+        if (hwc_window->accepted_state == E_HWC_WINDOW_STATE_CLIENT)
+          {
+             if (target_window)
+               {
+                  target_window = EINA_FALSE;
+                  _e_rdp_pixman_output_image_composite(output, (E_Hwc_Window *)hwc->target_hwc_window, pix_surface, info.width, info.height, e_output_w, e_output_h);
+               }
+             continue;
+          }
+
+        _e_rdp_pixman_output_image_composite(output, hwc_window, pix_surface, info.width, info.height, e_output_w, e_output_h);
+     }
+
+out:
+   tbm_surface_unmap(tbm_surface);
+
+   return pix_surface;
+}
+
+static Eina_Bool
+_e_rdp_frame_timer(void *data)
+{
+   E_Rdp_Output *output;
+   tbm_surface_h tbm_surface = NULL;
+   pixman_image_t *pix_surface = NULL;
+   pixman_box32_t box;
+   pixman_region32_t damage;
+   E_Rdp_Peer_Item *rdp_peer, *tmp;
+   freerdp_peer *client = NULL;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(data, ECORE_CALLBACK_CANCEL);
+
+   output = (E_Rdp_Output *)data;
+
+   tbm_surface = _e_rdp_tbm_image_create(output, E_RDP_WIDTH, E_RDP_HEIGHT, 0x00000000);
+   if (tbm_surface == NULL)
+     {
+        ERR("create tbm surface failed");
+        return ECORE_CALLBACK_RENEW;
+     }
+
+   pix_surface = _e_rdp_pixman_output_image_get(output, tbm_surface);
+   if (pix_surface == NULL)
+     {
+        ERR("create output pixman surface failed");
+        tbm_surface_destroy(tbm_surface);
+        return ECORE_CALLBACK_RENEW;
+     }
+
+   /* sends a full refresh */
+   box.x1 = 0;
+   box.y1 = 0;
+   box.x2 = E_RDP_WIDTH;
+   box.y2 = E_RDP_HEIGHT;
+   pixman_region32_init_with_extents(&damage, &box);
+
+   wl_list_for_each_safe(rdp_peer, tmp, &output->peers, link)
+     {
+        client = rdp_peer->peer;
+        if (!client)
+          continue;
+
+        e_rdp_peer_refresh_region(&damage, client, tbm_surface, pix_surface);
+     }
+   pixman_region32_fini(&damage);
+
+   return ECORE_CALLBACK_RENEW;
+}
+
+
+static BOOL
+e_rdp_peer_capabilities(freerdp_peer *client)
+{
+   return TRUE;
+}
+
+static BOOL
+e_rdp_peer_post_connect(freerdp_peer *client)
+{
+   return TRUE;
+}
+
+static BOOL
+e_rdp_peer_activate(freerdp_peer *client)
+{
+   E_Rdp_Peer_Context *peerCtx;
+   E_Rdp_Backend *b;
+   E_Rdp_Output *output;
+   rdpSettings *settings;
+   rdpPointerUpdate *pointer;
+   E_Rdp_Peer_Item *peersItem;
+   pixman_box32_t box;
+   pixman_region32_t damage;
+   POINTER_SYSTEM_UPDATE pointer_system;
+   tbm_surface_h tbm_surface = NULL;
+   pixman_image_t *pix_surface = NULL;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client, FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client->context, FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client->settings, FALSE);
+
+   peerCtx = (E_Rdp_Peer_Context *)client->context;
+   EINA_SAFETY_ON_NULL_RETURN_VAL(peerCtx, FALSE);
+
+   b = peerCtx->rdpBackend;
+   EINA_SAFETY_ON_NULL_RETURN_VAL(b, FALSE);
+
+   peersItem = &peerCtx->item;
+   EINA_SAFETY_ON_NULL_RETURN_VAL(peersItem, FALSE);
+
+   output = b->output;
+   settings = client->settings;
+
+   if (!settings->SurfaceCommandsEnabled)
+     {
+        ERR("client doesn't support required SurfaceCommands\n");
+        return FALSE;
+     }
+
+   if (b->force_no_compression && settings->CompressionEnabled)
+     {
+        ERR("Forcing compression off\n");
+        settings->CompressionEnabled = FALSE;
+     }
+
+   if (E_RDP_WIDTH != (int)settings->DesktopWidth || E_RDP_HEIGHT != (int)settings->DesktopHeight)
+     {
+        if (b->no_clients_resize)
+          {
+             /* RDP peers don't dictate their resolution to e */
+             if (!settings->DesktopResize)
+               {
+                  /* peer does not support desktop resize */
+                  ERR("%s: client doesn't support resizing, closing connection\n", __FUNCTION__);
+                  return FALSE;
+               }
+             else
+               {
+                  settings->DesktopWidth = E_RDP_WIDTH;
+                  settings->DesktopHeight = E_RDP_HEIGHT;
+                  client->update->DesktopResize(client->context);
+               }
+          }
+        else
+          {
+             ;
+             /* not supported */
+          }
+     }
+
+   rfx_context_reset(peerCtx->rfx_context, E_RDP_WIDTH, E_RDP_HEIGHT);
+   nsc_context_reset(peerCtx->nsc_context, E_RDP_WIDTH, E_RDP_HEIGHT);
+   if (peersItem->flags & RDP_PEER_ACTIVATED)
+     return TRUE;
+
+   /* when here it's the first reactivation, we need to setup a little more */
+   DBG("kbd_layout:0x%x kbd_type:0x%x kbd_subType:0x%x kbd_functionKeys:0x%x\n",
+       settings->KeyboardLayout, settings->KeyboardType, settings->KeyboardSubType, settings->KeyboardFunctionKey);
+
+   peersItem->flags |= RDP_PEER_ACTIVATED;
+
+   /* disable pointer on the client side */
+   pointer = client->update->pointer;
+   pointer_system.type = SYSPTR_NULL;
+   pointer->PointerSystem(client->context, &pointer_system);
+
+   tbm_surface = _e_rdp_tbm_image_create(output, E_RDP_WIDTH, E_RDP_HEIGHT, 0xFFFFCCFF);
+   if (tbm_surface == NULL)
+     {
+        ERR("create sample tbm surface failed");
+        return TRUE;
+     }
+   pix_surface = _e_rdp_pixman_image_create(tbm_surface);
+   if (pix_surface == NULL)
+     {
+        ERR("create sample pixman failed");
+        tbm_surface_destroy(tbm_surface);
+        return TRUE;
+     }
+
+   /* sends a full refresh */
+   box.x1 = 0;
+   box.y1 = 0;
+   box.x2 = E_RDP_WIDTH;
+   box.y2 = E_RDP_HEIGHT;
+   pixman_region32_init_with_extents(&damage, &box);
+
+   e_rdp_peer_refresh_region(&damage, client, tbm_surface, pix_surface);
+
+   pixman_region32_fini(&damage);
+
+   if (output->frame_timer != NULL)
+     ecore_timer_del(output->frame_timer);
+   output->frame_timer = ecore_timer_add(0.2, _e_rdp_frame_timer, output);
+
+   return TRUE;
+}
+
+static BOOL
+e_rdp_suppress_output(rdpContext *context, BYTE allow, const RECTANGLE_16 *area)
+{
+   E_Rdp_Peer_Context *peerContext = (E_Rdp_Peer_Context *)context;
+
+   if (allow)
+     peerContext->item.flags |= RDP_PEER_OUTPUT_ENABLED;
+   else
+     peerContext->item.flags &= (~RDP_PEER_OUTPUT_ENABLED);
+
+   return TRUE;
+}
+
+static BOOL
+e_rdp_input_synchronize_event(rdpInput *input, UINT32 flags)
+{
+   //To do
+   return TRUE;
+}
+
+static BOOL
+e_rdp_mouse_event(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y)
+{
+   //To do
+   return TRUE;
+}
+
+static BOOL
+e_rdp_extended_mouse_event(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y)
+{
+   //To do
+   return TRUE;
+}
+
+static BOOL
+e_rdp_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code)
+{
+   //To do
+   return TRUE;
+}
+
+static BOOL
+e_rdp_input_unicode_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code)
+{
+   DBG("Client sent a unicode keyboard event (flags:0x%X code:0x%X)\n", flags, code);
+   return TRUE;
+}
+
+static int
+e_rdp_client_activity(int fd, uint32_t mask, void *data)
+{
+   freerdp_peer *client = (freerdp_peer *)data;
+   E_Rdp_Peer_Context *peerCtx;
+   E_Rdp_Backend *b;
+   E_Rdp_Output *output;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client->context, 0);
+
+   if (client->CheckFileDescriptor(client))
+     {
+        DBG("enable client activity %p", client);
+        return 0;
+     }
+
+   INF("unable to checkDescriptor for %p\n", client);
+   peerCtx = (E_Rdp_Peer_Context *)client->context;
+   EINA_SAFETY_ON_NULL_GOTO(peerCtx, out_clean);
+
+   b = peerCtx->rdpBackend;
+   EINA_SAFETY_ON_NULL_GOTO(b, out_clean);
+
+   output = b->output;
+   if (output->frame_timer)
+     {
+        ecore_timer_del(output->frame_timer);
+        output->frame_timer = NULL;
+     }
+out_clean:
+   freerdp_peer_context_free(client);
+   freerdp_peer_free(client);
+   return 0;
+}
+
+static BOOL
+e_rdp_peer_context_new(freerdp_peer *client, E_Rdp_Peer_Context *context)
+{
+   context->item.peer = client;
+   context->item.flags = RDP_PEER_OUTPUT_ENABLED;
+
+   context->rfx_context = rfx_context_new(TRUE);
+   if (!context->rfx_context)
+     return FALSE;
+
+   context->rfx_context->mode = RLGR3;
+   context->rfx_context->width = client->settings->DesktopWidth;
+   context->rfx_context->height = client->settings->DesktopHeight;
+   rfx_context_set_pixel_format(context->rfx_context, DEFAULT_PIXEL_FORMAT);
+
+   context->nsc_context = nsc_context_new();
+   if (!context->nsc_context)
+     goto out_error_nsc;
+
+   nsc_context_set_parameters(context->nsc_context, NSC_COLOR_FORMAT, DEFAULT_PIXEL_FORMAT);
+   context->encode_stream = Stream_New(NULL, 65536);
+   if (!context->encode_stream)
+     goto out_error_stream;
+
+   return TRUE;
+
+out_error_nsc:
+   rfx_context_free(context->rfx_context);
+out_error_stream:
+   nsc_context_free(context->nsc_context);
+   return FALSE;
+}
+
+static void
+e_rdp_peer_context_free(freerdp_peer *client, E_Rdp_Peer_Context *context)
+{
+   int i;
+   if (!context)
+     return;
+
+   wl_list_remove(&context->item.link);
+   for (i = 0; i < MAX_FREERDP_FDS; i++)
+     {
+        if (context->events[i])
+          wl_event_source_remove(context->events[i]);
+     }
+
+   Stream_Free(context->encode_stream, TRUE);
+   nsc_context_free(context->nsc_context);
+   rfx_context_free(context->rfx_context);
+   free(context->rfx_rects);
+}
+
+
+static int
+e_rdp_peer_init(freerdp_peer *client, E_Rdp_Backend *b)
+{
+   int rcount = 0;
+   void *rfds[MAX_FREERDP_FDS];
+   int i, fd;
+   struct wl_event_loop *loop;
+   rdpSettings *settings;
+   rdpInput *input;
+   E_Rdp_Peer_Context *peerCtx;
+
+   client->ContextSize = sizeof(E_Rdp_Peer_Context);
+   client->ContextNew = (psPeerContextNew)e_rdp_peer_context_new;
+   client->ContextFree = (psPeerContextFree)e_rdp_peer_context_free;
+   freerdp_peer_context_new(client);
+
+   peerCtx = (E_Rdp_Peer_Context *)client->context;
+   peerCtx->rdpBackend = b;
+
+   settings = client->settings;
+
+   if (b->tls_enabled)
+     {
+        settings->CertificateFile = strdup(b->server_cert);
+        settings->PrivateKeyFile = strdup(b->server_key);
+     }
+   else
+     {
+        settings->TlsSecurity = FALSE;
+     }
+   settings->NlaSecurity = FALSE;
+
+   if (!client->Initialize(client))
+     {
+        ERR("peer initialization failed\n");
+        return -1;
+     }
+
+   settings->OsMajorType = OSMAJORTYPE_UNIX;
+   settings->OsMinorType = OSMINORTYPE_PSEUDO_XSERVER;
+   settings->ColorDepth = 32;
+   settings->RefreshRect = TRUE;
+   settings->RemoteFxCodec = TRUE;
+   settings->NSCodec = TRUE;
+   settings->FrameMarkerCommandEnabled = TRUE;
+   settings->SurfaceFrameMarkerEnabled = TRUE;
+
+   client->Capabilities = e_rdp_peer_capabilities;
+   client->PostConnect = e_rdp_peer_post_connect;
+   client->Activate = e_rdp_peer_activate;
+
+   client->update->SuppressOutput = (pSuppressOutput)e_rdp_suppress_output;
+
+   input = client->input;
+   input->SynchronizeEvent = e_rdp_input_synchronize_event;
+   input->MouseEvent = e_rdp_mouse_event;
+   input->ExtendedMouseEvent = e_rdp_extended_mouse_event;
+   input->KeyboardEvent = e_rdp_input_keyboard_event;
+   input->UnicodeKeyboardEvent = e_rdp_input_unicode_keyboard_event;
+
+   if (!client->GetFileDescriptor(client, rfds, &rcount))
+     {
+        ERR("unable to retrieve client fds");
+        goto error_initialize;
+     }
+
+   if (rcount > MAX_FREERDP_FDS)
+     {
+        ERR("check fds max count. count:%d, max:%d", rcount, MAX_FREERDP_FDS);
+        goto error_initialize;
+     }
+
+   loop = wl_display_get_event_loop(e_comp_wl->wl.disp);
+   for (i = 0; i < rcount; i++)
+     {
+        fd = (int)(long)(rfds[i]);
+
+        peerCtx->events[i] = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, e_rdp_client_activity, client);
+     }
+   for ( ; i < MAX_FREERDP_FDS; i++)
+     peerCtx->events[i] = 0;
+
+   wl_list_insert(&b->output->peers, &peerCtx->item.link);
+   return 0;
+
+error_initialize:
+   client->Close(client);
+   return -1;
+}
+
+static BOOL
+e_rdp_incoming_peer(freerdp_listener *instance, freerdp_peer *client)
+{
+   E_Rdp_Backend *b = (E_Rdp_Backend *)instance->param4;
+
+   if (e_rdp_peer_init(client, b) < 0)
+     {
+        ERR("error when treating incoming peer\n");
+        return FALSE;
+     }
+
+   return TRUE;
+}
+
+static int
+e_rdp_listener_activity(int fd, uint32_t mask, void *data)
+{
+   freerdp_listener* instance = (freerdp_listener *)data;
+
+   if (!(mask & WL_EVENT_READABLE))
+     return 0;
+
+   if (!instance->CheckFileDescriptor(instance))
+     {
+        ERR("failed to check FreeRDP file descriptor\n");
+        return -1;
+     }
+
+   return 0;
+}
+
+static Eina_Bool
+e_rdp_implant_listener(E_Rdp_Backend *b, freerdp_listener *instance)
+{
+   int i, fd;
+   int rcount = 0;
+   void* rfds[MAX_FREERDP_FDS];
+   struct wl_event_loop *loop;
+
+   if (!instance->GetFileDescriptor(instance, rfds, &rcount))
+     {
+        ERR("Failed to get FreeRDP file descriptor\n");
+        return -1;
+     }
+
+   loop = wl_display_get_event_loop(e_comp_wl->wl.disp);
+   for (i = 0; i < rcount; i++)
+     {
+        fd = (int)(long)(rfds[i]);
+        b->listener_events[i] = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, e_rdp_listener_activity, instance);
+     }
+
+   for ( ; i < MAX_FREERDP_FDS; i++)
+     b->listener_events[i] = 0;
+
+   return 0;
+}
+
+static void
+e_rdp_output_destroy(E_Rdp_Output *output)
+{
+   EINA_SAFETY_ON_NULL_RETURN(output);
+
+   if (output->pix_surface)
+     pixman_image_unref(output->pix_surface);
+   if (output->tbm_surface)
+     tbm_surface_destroy(output->tbm_surface);
+
+   free(output);
+}
+
+E_Rdp_Output *
+e_rdp_output_create(void)
+{
+   E_Rdp_Output *output = NULL;
+
+   output = E_NEW(E_Rdp_Output, 1);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(output, NULL);
+
+   output->primary_output = e_output_find_by_index(0);
+
+   wl_list_init(&output->peers);
+
+   return output;
+}
+
+static void
+e_rdp_backend_destroy(void)
+{
+   E_Rdp_Backend *b = NULL;
+   E_Rdp_Peer_Item *rdp_peer, *tmp;
+   int i;
+
+   b = g_rdp_backend;
+   EINA_SAFETY_ON_NULL_RETURN(b);
+
+   if (b->output->frame_timer)
+     {
+        ecore_timer_del(b->output->frame_timer);
+        b->output->frame_timer = NULL;
+     }
+
+   wl_list_for_each_safe(rdp_peer, tmp, &b->output->peers, link)
+     {
+        freerdp_peer* client = rdp_peer->peer;
+
+        client->Disconnect(client);
+        freerdp_peer_context_free(client);
+        freerdp_peer_free(client);
+     }
+
+   for (i = 0; i < MAX_FREERDP_FDS; i++)
+     if (b->listener_events[i])
+       wl_event_source_remove(b->listener_events[i]);
+
+   freerdp_listener_free(b->listener);
+
+   e_rdp_output_destroy(b->output);
+
+   free(b->server_cert);
+   free(b->server_key);
+   free(b);
+}
+
+
+Eina_Bool
+e_rdp_backend_create(E_Rdp_Config *config)
+{
+   E_Rdp_Backend *b;
+
+   b = E_NEW(E_Rdp_Backend, 1);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(b, EINA_FALSE);
+
+   b->rdp_key = NULL;
+   b->no_clients_resize = config->no_clients_resize;
+   b->force_no_compression = config->force_no_compression;
+
+   /* activate TLS only if certificate/key are available */
+   if (config->server_cert && config->server_key)
+     {
+        DBG("TLS support activated\n");
+        b->server_cert = strdup(config->server_cert);
+        b->server_key = strdup(config->server_key);
+        if (!b->server_cert || !b->server_key)
+          goto err_free_strings;
+        b->tls_enabled = 1;
+     }
+
+   b->output = e_rdp_output_create();
+   if (!b->output)
+     {
+        ERR("output create failed");
+        goto err_free_strings;
+     }
+
+   b->listener = freerdp_listener_new();
+   b->listener->PeerAccepted = e_rdp_incoming_peer;
+   b->listener->param4 = b;
+   if (!b->listener->Open(b->listener, NULL, config->port))
+     {
+        ERR("unable to bind rdp socket\n");
+        goto err_listener;
+     }
+
+   if (e_rdp_implant_listener(b, b->listener) < 0)
+     goto err_listener;
+
+   g_rdp_backend = b;
+
+   return EINA_TRUE;
+
+   err_listener:
+   freerdp_listener_free(b->listener);
+
+   e_rdp_output_destroy(b->output);
+   b->output = NULL;
+
+   err_free_strings:
+   free(b->server_cert);
+   free(b->server_key);
+   free(b);
+
+   return EINA_FALSE;
+}
+
+Eina_Bool
+e_mod_rdp_init(void)
+{
+   E_Rdp_Config config = {NULL, NULL, "/opt/rdp/tls.crt", "/opt/rdp/tls.key", 3389, 0, 0};
+   int major, minor, revision;
+   //TODO : get the tls cert and key name from e-tizen-data
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e_comp_wl, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e_comp_wl->wl.disp, EINA_FALSE);
+
+#if FREERDP_VERSION_MAJOR >= 2
+   winpr_InitializeSSL(0);
+#endif
+
+   freerdp_get_version(&major, &minor, &revision);
+   if (major < E_MOD_RDP_CONFIG_VERSION)
+     {
+        ERR("Not supported version");
+        return EINA_FALSE;
+     }
+   INF("freerdp version : %d.%d.%d", major, minor, revision);
+
+   return e_rdp_backend_create(&config);
+}
+
+void
+e_mod_rdp_deinit(void)
+{
+   e_rdp_backend_destroy();
+}
diff --git a/src/e_mod_rdp.h b/src/e_mod_rdp.h
new file mode 100644 (file)
index 0000000..667897f
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef __E_MOD_RDP_H__
+#define __E_MOD_RDP_H__
+
+#include <e.h>
+
+#ifdef DBG
+# undef DBG
+#endif
+
+#ifdef INF
+# undef INF
+#endif
+
+#ifdef WRN
+# undef WRN
+#endif
+
+#ifdef ERR
+# undef ERR
+#endif
+
+#ifdef CRI
+# undef CRI
+#endif
+
+int _wr_log_dom;
+#define DBG(...)  EINA_LOG_DOM_DBG(_wr_log_dom, __VA_ARGS__)
+#define INF(...)  EINA_LOG_DOM_INFO(_wr_log_dom, __VA_ARGS__)
+#define WRN(...)  EINA_LOG_DOM_WARN(_wr_log_dom, __VA_ARGS__)
+#define ERR(...)  EINA_LOG_DOM_ERR(_wr_log_dom, __VA_ARGS__)
+#define CRI(...)  EINA_LOG_DOM_CRIT(_wr_log_dom, __VA_ARGS__)
+
+Eina_Bool e_mod_rdp_init(void);
+void e_mod_rdp_deinit(void);
+
+#endif