From: Junkyeong, Kim Date: Wed, 25 Aug 2021 04:43:25 +0000 (+0900) Subject: Add initial code X-Git-Tag: submit/tizen/20210928.090200~3 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b4e2541430b8839bd5015e85ec5a1ed930d6e59b;p=platform%2Fcore%2Fuifw%2Fe-mod-tizen-rdp.git Add initial code Change-Id: I6af04b464ae932acdee39d5e9a5ff57198277b48 Signed-off-by: Junkyeong, Kim --- diff --git a/COPYING b/COPYING new file mode 100644 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 index 0000000..a30d363 --- /dev/null +++ b/Makefile.am @@ -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 index 0000000..5a30991 --- /dev/null +++ b/autogen.sh @@ -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 index 0000000..a9fb6c6 --- /dev/null +++ b/configure.ac @@ -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 index 0000000..aebaf60 --- /dev/null +++ b/packaging/e-mod-tizen-rdp.spec @@ -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 index 0000000..c674d32 --- /dev/null +++ b/src/Makefile.am @@ -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 index 0000000..e03d3fe --- /dev/null +++ b/src/e_mod_main.c @@ -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 index 0000000..7919e2b --- /dev/null +++ b/src/e_mod_main.h @@ -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 index 0000000..9a26c6d --- /dev/null +++ b/src/e_mod_rdp.c @@ -0,0 +1,1375 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 index 0000000..667897f --- /dev/null +++ b/src/e_mod_rdp.h @@ -0,0 +1,36 @@ +#ifndef __E_MOD_RDP_H__ +#define __E_MOD_RDP_H__ + +#include + +#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