[PATCH] Add a FreeRDP based compositor

hardening rdp.effort at gmail.com
Fri Mar 1 12:32:24 PST 2013


This patch adds a FreeRDP based compositor. This backend
waits for incoming RDP clients and sends frame updates to
connected clients. Each RDP client register his own seat
which make it nice to test multi-seat (with 2 RDP clients
we have 2 pointers on the screen). Frame updates are done
either with surfaces in raw format (that means flipping the
dirty region and cutting in 64x64 tiles), or using the remoteFx
codec (when the client supports it).
---
 configure.ac         |    7 +
 src/Makefile.am      |   16 +-
 src/compositor-rdp.c |  827 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 849 insertions(+), 1 deletion(-)
 create mode 100644 src/compositor-rdp.c

diff --git a/configure.ac b/configure.ac
index 682e7a3..ad4f250 100644
--- a/configure.ac
+++ b/configure.ac
@@ -168,6 +168,13 @@ AS_IF([test x$enable_fbdev_compositor = xyes], [
   PKG_CHECK_MODULES([FBDEV_COMPOSITOR], [libudev >= 136 mtdev >= 1.1.0])
 ])
 
+AC_ARG_ENABLE([rdp-compositor], [  --enable-rdp-compositor],,
+              enable_rdp_compositor=no)
+AM_CONDITIONAL([ENABLE_RDP_COMPOSITOR],
+               [test x$enable_rdp_compositor = xyes])
+if test x$enable_rdp_compositor = xyes; then
+  PKG_CHECK_MODULES(RDP_COMPOSITOR, [freerdp > 1.0.0])
+fi
 
 AC_ARG_WITH(cairo-glesv2,
             AS_HELP_STRING([--with-cairo-glesv2],
diff --git a/src/Makefile.am b/src/Makefile.am
index 2c93a7b..e5833e2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -98,7 +98,8 @@ module_LTLIBRARIES =				\
 	$(drm_backend)				\
 	$(wayland_backend)			\
 	$(headless_backend)			\
-	$(fbdev_backend)
+	$(fbdev_backend)			\
+	$(rdp_backend)
 
 noinst_LTLIBRARIES =
 
@@ -214,6 +215,19 @@ fbdev_backend_la_SOURCES = \
 	launcher-util.c
 endif
 
+if ENABLE_RDP_COMPOSITOR
+rdp_backend = rdp-backend.la
+rdp_backend_la_LDFLAGS = -module -avoid-version
+rdp_backend_la_LIBADD = $(COMPOSITOR_LIBS) \
+	$(RDP_COMPOSITOR_LIBS) \
+	../shared/libshared.la
+rdp_backend_la_CFLAGS =			\
+	$(COMPOSITOR_CFLAGS)			\
+	$(RDP_COMPOSITOR_CFLAGS) \
+	$(GCC_CFLAGS)
+rdp_backend_la_SOURCES = compositor-rdp.c
+endif
+
 if ENABLE_DESKTOP_SHELL
 desktop_shell = desktop-shell.la
 desktop_shell_la_LDFLAGS = -module -avoid-version
diff --git a/src/compositor-rdp.c b/src/compositor-rdp.c
new file mode 100644
index 0000000..8467377
--- /dev/null
+++ b/src/compositor-rdp.c
@@ -0,0 +1,827 @@
+/*
+ * Copyright © 2013 hardening <rdp.effort at gmail.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <linux/input.h>
+
+#include <freerdp/listener.h>
+#include <freerdp/update.h>
+#include <freerdp/input.h>
+#include <freerdp/utils/file.h>
+#include <freerdp/codec/color.h>
+#include <freerdp/codec/rfx.h>
+#include <freerdp/kbd/kbd.h>
+
+#include "compositor.h"
+#include "pixman-renderer.h"
+
+
+struct rdp_output;
+
+struct rdp_compositor {
+	struct weston_compositor base;
+	struct weston_seat main_seat;
+
+	freerdp_listener *listener;
+	struct wl_event_source *listenerEvents[32];
+	struct rdp_output *output;
+};
+
+struct rdp_peer_list {
+	int activated;
+	freerdp_peer *peer;
+	struct weston_seat seat;
+	struct wl_list link;
+};
+
+struct rdp_output {
+	struct weston_output base;
+	struct weston_mode mode;
+	struct wl_event_source *finish_frame_timer;
+	pixman_image_t *shadow_surface;
+
+	struct wl_list peers;
+};
+
+struct rdp_peer_context {
+	rdpContext _p;
+	struct rdp_compositor *rdpCompositor;
+
+	/* file descriptors and associated events */
+	int fds[32];
+	struct wl_event_source *events[32];
+
+	RFX_CONTEXT *rfx_context;
+	STREAM *rfx_stream;
+
+	struct rdp_peer_list item;
+};
+typedef struct rdp_peer_context RdpPeerContext;
+
+
+static int
+finish_frame_handler(void *data)
+{
+	struct weston_output *output = data;
+	uint32_t msec;
+	struct timeval tv;
+
+	gettimeofday(&tv, NULL);
+	msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+	weston_output_finish_frame(output, msec);
+
+	return 1;
+}
+
+
+static void
+rdp_peer_refresh_rfx(pixman_box32_t *region, pixman_image_t *image, freerdp_peer *peer)
+{
+	RFX_RECT rect;
+	int width, height;
+	rdpUpdate *update = peer->update;
+	SURFACE_BITS_COMMAND* cmd = &update->surface_bits_command;
+	RdpPeerContext *context = (RdpPeerContext *)peer->context;
+	uint32 *ptr;
+
+	stream_clear(context->rfx_stream);
+	stream_set_pos(context->rfx_stream, 0);
+
+	width = region->x2 - region->x1;
+	height = region->y2 - region->y1;
+	rect.x = 0;
+	rect.y = 0;
+	rect.width = width;
+	rect.height = height;
+	ptr = pixman_image_get_data(image) + region->x1 +
+			region->y1 * pixman_image_get_width(image);
+	rfx_compose_message(context->rfx_context, context->rfx_stream, &rect, 1,
+			(uint8 *)ptr, width, height,
+			pixman_image_get_stride(image)
+	);
+
+	cmd->destLeft = region->x1;
+	cmd->destTop = region->y1;
+	cmd->destRight = region->x2;
+	cmd->destBottom = region->y2;
+	cmd->bpp = 32;
+	cmd->codecID = peer->settings->rfx_codec_id;
+	cmd->width = width;
+	cmd->height = height;
+	cmd->bitmapDataLength = stream_get_length(context->rfx_stream);
+	cmd->bitmapData = stream_get_head(context->rfx_stream);
+
+	update->SurfaceBits(update->context, cmd);
+}
+
+static void
+rdp_peer_refresh_tiles(pixman_box32_t *region, pixman_image_t *image, freerdp_peer *peer)
+{
+	rdpUpdate *update = peer->update;
+	SURFACE_BITS_COMMAND* cmd = &update->surface_bits_command;
+	int pixelSize, x, y, modx, mody;
+	pixman_image_t *tile;
+
+	cmd->bpp = 32;
+	pixelSize = (cmd->bpp / 8);
+	cmd->codecID = 0;
+	cmd->width = 64;
+	cmd->height = 64;
+	cmd->bitmapDataLength = 64 * 64 * pixelSize;
+
+	tile = pixman_image_create_bits(PIXMAN_x8r8g8b8, 64, 64, 0, 64 * pixelSize);
+	cmd->bitmapData = (uint8 *)pixman_image_get_data(tile);
+
+	/* first we send data that feet in the standard 64x64 tile */
+	modx = cmd->width % 64;
+	mody = cmd->height % 64;
+	for(y = region->y1; y < region->y2 - mody; y += 64) {
+		for(x = region->x1; x < region->x2 - modx; x += 64)	{
+			pixman_image_composite32(PIXMAN_OP_SRC,	image, /* src */
+				NULL 		/* mask */,
+				tile, x, y, /* dest, src_x, src_y */
+				0, 0, /* mask_x, mask_y */
+				0, 0, /* dest_x, dest_y */
+				64, 64 /* width, height */
+			);
+			freerdp_image_flip((uint8 *)pixman_image_get_data(tile),
+					(uint8 *)pixman_image_get_data(tile),
+					64, 64, cmd->bpp
+			);
+
+			cmd->destLeft = x;
+			cmd->destTop = y;
+			cmd->destRight = x+64;
+			cmd->destBottom = y+64;
+			update->SurfaceBits(peer->context, cmd);
+		}
+	}
+	pixman_image_unref(tile);
+
+	if (modx) {
+		/* send remaining bytes on the right */
+		weston_log("%d bytes on the right\n", modx);
+		cmd->width = modx;
+		cmd->destLeft = region->x2 - modx - 1;
+		cmd->destRight = region->x2;
+		tile = pixman_image_create_bits(PIXMAN_x8r8g8b8, modx, 64, 0,
+				modx * pixelSize);
+		cmd->bitmapData = (uint8 *)pixman_image_get_data(tile);
+
+		for(y = region->y1; y < region->y2 - mody; y += 64) {
+			cmd->destTop = y;
+			cmd->destBottom = y+64;
+
+			pixman_image_composite32(PIXMAN_OP_SRC,	image, /* src */
+				NULL /* mask */,
+				tile, /* dest */
+				cmd->destLeft, y, /* src_x, src_y */
+				0, 0, /* mask_x, mask_y */
+				0, 0, /* dest_x, dest_y */
+				modx, 64 /* width, height */
+			);
+			freerdp_image_flip((uint8 *)pixman_image_get_data(tile),
+					(uint8 *)pixman_image_get_data(tile),
+					modx, 64, cmd->bpp
+			);
+
+			update->SurfaceBits(peer->context, cmd);
+		}
+		pixman_image_unref(tile);
+	}
+
+	if (mody) {
+		/* send remaining tiles at the bottom */
+		cmd->width = 64;
+		cmd->height = mody;
+		cmd->destTop = region->y2 - mody - 1;
+		cmd->destBottom = region->y2;
+		tile = pixman_image_create_bits(PIXMAN_x8r8g8b8, 64, mody, 0,
+				64 * pixelSize);
+		cmd->bitmapData = (uint8 *)pixman_image_get_data(tile);
+
+		for(x = region->x1; x < region->x2 - modx; y += 64) {
+			cmd->destLeft = x;
+			cmd->destRight = x+64;
+
+			pixman_image_composite32(PIXMAN_OP_SRC,	image, /* src */
+				NULL /* mask */,
+				tile, x, cmd->destTop, /* dest, src_x, src_y */
+				0, 0, /* mask_x, mask_y */
+				0, 0, /* dest_x, dest_y */
+				64, mody /* width, height */
+			);
+			freerdp_image_flip((uint8 *)pixman_image_get_data(tile),
+					(uint8 *)pixman_image_get_data(tile),
+					64, mody, cmd->bpp
+			);
+
+			update->SurfaceBits(peer->context, cmd);
+		}
+		pixman_image_unref(tile);
+	}
+
+	if(modx && mody) {
+		/* send the remaining bottom right tile */
+		cmd->width = modx;
+		cmd->height = mody;
+		cmd->destLeft = region->x2 - modx - 1;
+		cmd->destRight = region->x2;
+		cmd->destTop = region->y2 - mody - 1;
+		cmd->destBottom = region->y2;
+		tile = pixman_image_create_bits(PIXMAN_x8r8g8b8, modx, mody, 0,
+				modx * pixelSize);
+		cmd->bitmapData = (uint8 *)pixman_image_get_data(tile);
+
+		pixman_image_composite32(PIXMAN_OP_SRC,	image, /* src */
+			NULL, tile, /* mask, dest */
+			cmd->destLeft, cmd->destTop, /* src_x, src_y */
+			0, 0, /* mask_x, mask_y */
+			0, 0, /* dest_x, dest_y */
+			modx, mody /* width, height */
+		);
+		freerdp_image_flip((uint8 *)pixman_image_get_data(tile),
+				(uint8 *)pixman_image_get_data(tile),
+				modx, mody, cmd->bpp
+		);
+
+		update->SurfaceBits(peer->context, cmd);
+		pixman_image_unref(tile);
+	}
+}
+
+static void
+rdp_peer_refresh_region(pixman_box32_t *region, pixman_image_t *image, freerdp_peer *peer)
+{
+	if(peer->settings->rfx_codec &&
+			(region->x2-region->x1 > 64) &&
+			(region->y2-region->y1 > 64))
+		/* use remoteFx only on regions that are bigger than 64x64 */
+		rdp_peer_refresh_rfx(region, image, peer);
+	else
+		rdp_peer_refresh_tiles(region, image, peer);
+}
+
+
+static void
+rdp_output_repaint(struct weston_output *output_base, pixman_region32_t *damage)
+{
+	struct rdp_output *output = container_of(output_base, struct rdp_output, base);
+	struct weston_compositor *ec = output->base.compositor;
+	struct rdp_peer_list *outputPeer;
+	pixman_box32_t *rects;
+	int nrects, i;
+
+	pixman_renderer_output_set_buffer(output_base, output->shadow_surface);
+	ec->renderer->repaint_output(&output->base, damage);
+
+	wl_list_for_each(outputPeer, &output->peers, link) {
+		if (!outputPeer->activated)
+			continue;
+
+		rects = pixman_region32_rectangles(damage, &nrects);
+		for (i = 0; i < nrects; i++) {
+			rdp_peer_refresh_region(&rects[i], output->shadow_surface, outputPeer->peer);
+		}
+	}
+
+	pixman_region32_subtract(&ec->primary_plane.damage,
+				 &ec->primary_plane.damage, damage);
+
+	wl_event_source_timer_update(output->finish_frame_timer, 16);
+}
+
+static void
+rdp_output_destroy(struct weston_output *output_base)
+{
+	struct rdp_output *output = (struct rdp_output *) output_base;
+
+	wl_event_source_remove(output->finish_frame_timer);
+	free(output);
+}
+
+static int
+rdp_compositor_create_output(struct rdp_compositor *c, int width, int height)
+{
+	struct rdp_output *output;
+	struct wl_event_loop *loop;
+
+	output = malloc(sizeof *output);
+	if (output == NULL)
+		return -1;
+	memset(output, 0, sizeof *output);
+
+	wl_list_init(&output->peers);
+	output->mode.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+	output->mode.width = width;
+	output->mode.height = height;
+	output->mode.refresh = 5;
+	wl_list_init(&output->base.mode_list);
+	wl_list_insert(&output->base.mode_list, &output->mode.link);
+
+	output->base.current = &output->mode;
+	weston_output_init(&output->base, &c->base, 0, 0, width, height,
+			   WL_OUTPUT_TRANSFORM_NORMAL);
+
+	output->base.make = "weston";
+	output->base.model = "rdp";
+	output->shadow_surface = pixman_image_create_bits(PIXMAN_x8r8g8b8,
+			width, height,
+		    NULL,
+		    width * 4);
+	if (output->shadow_surface == NULL) {
+		weston_log("Failed to create surface for frame buffer.\n");
+		goto out_output;
+	}
+
+	if (pixman_renderer_output_create(&output->base) < 0)
+		goto out_shadow_surface;
+
+	weston_output_move(&output->base, 0, 0);
+
+	loop = wl_display_get_event_loop(c->base.wl_display);
+	output->finish_frame_timer = wl_event_loop_add_timer(loop, finish_frame_handler, output);
+
+	output->base.origin = output->base.current;
+	output->base.repaint = rdp_output_repaint;
+	output->base.destroy = rdp_output_destroy;
+	output->base.assign_planes = NULL;
+	output->base.set_backlight = NULL;
+	output->base.set_dpms = NULL;
+	output->base.switch_mode = NULL;
+	c->output = output;
+
+	wl_list_insert(c->base.output_list.prev, &output->base.link);
+
+	return 0;
+
+out_shadow_surface:
+	pixman_image_unref(output->shadow_surface);
+out_output:
+	weston_output_destroy(&output->base);
+	free(output);
+	return -1;
+}
+
+static void
+rdp_restore(struct weston_compositor *ec)
+{
+}
+
+static void
+rdp_destroy(struct weston_compositor *ec)
+{
+	struct rdp_compositor *c = (struct rdp_compositor *) ec;
+
+	ec->renderer->destroy(ec);
+
+	weston_seat_release(&c->main_seat);
+	weston_compositor_shutdown(ec);
+
+	free(ec);
+}
+
+static
+int 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))
+	{
+		weston_log("Failed to check FreeRDP file descriptor\n");
+	}
+	return 0;
+}
+
+static
+int rdp_implant_listener(struct rdp_compositor *c, freerdp_listener* instance) {
+	int i, fd;
+	int rcount = 0;
+	void* rfds[32];
+	struct wl_event_loop *loop;
+
+	if (!instance->GetFileDescriptor(instance, rfds, &rcount)) {
+		weston_log("Failed to get FreeRDP file descriptor\n");
+		return -1;
+	}
+
+	loop = wl_display_get_event_loop(c->base.wl_display);
+	for (i = 0; i < rcount; i++) {
+		fd = (int)(long)(rfds[i]);
+		c->listenerEvents[i] = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
+				rdp_listener_activity, instance);
+	}
+
+	for( ; i < 32; i++)
+		c->listenerEvents[i] = 0;
+	return 0;
+}
+
+
+
+
+static void
+rdp_peer_context_new(freerdp_peer* client, RdpPeerContext* context)
+{
+	context->item.peer = client;
+	context->item.activated = 0;
+	context->rfx_context = rfx_context_new();
+	context->rfx_context->mode = RLGR3;
+	context->rfx_context->width = client->settings->width;
+	context->rfx_context->height = client->settings->width;
+	rfx_context_set_pixel_format(context->rfx_context, RFX_PIXEL_FORMAT_BGRA);
+	context->rfx_stream = stream_new(65536);
+}
+
+static void
+rdp_peer_context_free(freerdp_peer* client, RdpPeerContext* context)
+{
+	int i;
+	if(!context)
+		return;
+
+	wl_list_remove(&context->item.link);
+	for(i = 0; i < 32; i++) {
+		if(context->fds[i] != -1)
+			wl_event_source_remove(context->events[i]);
+	}
+
+	weston_seat_release(&context->item.seat);
+	stream_free(context->rfx_stream);
+	rfx_context_free(context->rfx_context);
+}
+
+
+static int
+rdp_client_activity(int fd, uint32_t mask, void *data) {
+	freerdp_peer* client = (freerdp_peer *)data;
+
+	if (mask & WL_EVENT_HANGUP) {
+		weston_log("connection closed with %p\n", client);
+		goto out_clean;
+	}
+
+	if (!client->CheckFileDescriptor(client)) {
+		weston_log("unable to checkDescriptor for %p\n", client);
+		goto out_clean;
+	}
+
+	return 0;
+
+out_clean:
+	freerdp_peer_context_free(client);
+	freerdp_peer_free(client);
+	return 0;
+}
+
+static boolean
+xf_peer_capabilities(freerdp_peer* client)
+{
+	return true;
+}
+
+struct rdp_to_xkb_keyboard_layout {
+	uint32	rdpLayoutCode;
+	char 	*xkbLayout;
+};
+
+/* picked from http://technet.microsoft.com/en-us/library/cc766503(WS.10).aspx */
+static struct rdp_to_xkb_keyboard_layout rdp_keyboards[] = {
+	{0x00000409, "us"},
+	{0x0000040c, "fr"},
+	{0x00000000, 0},
+};
+
+/* taken from 2.2.7.1.6 Input Capability Set (TS_INPUT_CAPABILITYSET) */
+static char *rdp_keyboard_types[] = {
+	"",	/* 0: unused */
+	"", 	/* 1: IBM PC/XT or compatible (83-key) keyboard */
+	"", 	/* 2: Olivetti "ICO" (102-key) keyboard */
+	"", 	/* 3: IBM PC/AT (84-key) or similar keyboard */
+	"pc102",/* 4: IBM enhanced (101- or 102-key) keyboard */
+	"", 	/* 5: Nokia 1050 and similar keyboards */
+	"",	/* 6: Nokia 9140 and similar keyboards */
+	""	/* 7: Japanese keyboard */
+};
+
+static boolean
+xf_peer_post_connect(freerdp_peer* client)
+{
+	struct rdp_output *output;
+	RdpPeerContext *peerCtx;
+	rdpSettings *settings;
+	struct xkb_context *xkbContext;
+	struct xkb_rule_names xkbRuleNames;
+	struct xkb_keymap *keymap;
+	int i;
+
+	/*weston_log("xf_peer_post_connect(%p)\n", client);*/
+	peerCtx = (RdpPeerContext *)client->context;
+	output = peerCtx->rdpCompositor->output;
+
+	if(output->base.width != client->settings->width ||
+			output->base.height != client->settings->height)
+	{
+		if(!client->settings->desktop_resize) {
+			weston_log("client don't support desktopResize() and we don't scale images yet\n");
+			return false;
+		}
+
+		/* force the client size */
+		client->settings->width = output->base.width;
+		client->settings->height = output->base.height;
+		client->update->DesktopResize(client->context);
+	}
+
+	settings = client->settings;
+	weston_log("kbd_layout:%x kbd_type:%x kbd_subType:%x kbd_functionKeys:%x\n",
+			settings->kbd_layout, settings->kbd_type, settings->kbd_subtype,
+			settings->kbd_fn_keys);
+
+	memset(&xkbRuleNames, 0, sizeof(xkbRuleNames));
+	if(settings->kbd_type <= 7)
+		xkbRuleNames.model = rdp_keyboard_types[settings->kbd_type];
+	for(i = 0; rdp_keyboards[i].xkbLayout; i++) {
+		if(rdp_keyboards[i].rdpLayoutCode == settings->kbd_layout) {
+			xkbRuleNames.layout = rdp_keyboards[i].xkbLayout;
+			break;
+		}
+	}
+
+	xkbContext = xkb_context_new(0);
+	if(!xkbContext) {
+		weston_log("unable to create a xkb_context\n");
+		return false;
+	}
+
+	keymap = xkb_keymap_new_from_names(xkbContext, &xkbRuleNames, 0);
+	weston_seat_init_keyboard(&peerCtx->item.seat, keymap);
+	return true;
+}
+
+static boolean
+xf_peer_activate(freerdp_peer* client)
+{
+	return true;
+}
+
+static void
+xf_mouseEvent(rdpInput* input, uint16 flags, uint16 x, uint16 y) {
+	RdpPeerContext *peerContext = (RdpPeerContext *)input->context;
+	uint32_t button = 0;
+
+	if (flags & PTR_FLAGS_MOVE) {
+		notify_motion(&peerContext->item.seat, weston_compositor_get_time(),
+				wl_fixed_from_int(x), wl_fixed_from_int(y)
+		);
+	}
+
+	if (flags & PTR_FLAGS_BUTTON1)
+		button = BTN_LEFT;
+	else if (flags & PTR_FLAGS_BUTTON2)
+		button = BTN_RIGHT;
+	else if (flags & PTR_FLAGS_BUTTON3)
+		button = BTN_MIDDLE;
+
+	if(button) {
+		notify_button(&peerContext->item.seat, weston_compositor_get_time(), button,
+			(flags & PTR_FLAGS_DOWN) ? WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED
+		);
+	}
+}
+
+static void
+xf_extendedMouseEvent(rdpInput* input, uint16 flags, uint16 x, uint16 y) {
+	RdpPeerContext *peerContext = (RdpPeerContext *)input->context;
+
+	notify_motion(&peerContext->item.seat, weston_compositor_get_time(),
+			wl_fixed_from_int(x), wl_fixed_from_int(y)
+	);
+}
+
+
+static void
+xf_input_synchronize_event(rdpInput* input, uint32 flags)
+{
+	RdpPeerContext *peerCtx = (RdpPeerContext *)input->context;
+	freerdp_peer* client = peerCtx->item.peer;
+	struct rdp_output *output = peerCtx->rdpCompositor->output;
+
+	peerCtx->item.activated = 1;
+
+	pixman_box32_t box;
+	box.x1 = 0;
+	box.y1 = 0;
+	box.x2 = client->settings->width;
+	box.y2 = client->settings->height;
+	rdp_peer_refresh_region(&box, output->shadow_surface, client);
+}
+
+static void
+xf_input_keyboard_event(rdpInput* input, uint16 flags, uint16 code)
+{
+	enum wl_keyboard_key_state keyState;
+	RdpPeerContext *peerContext = (RdpPeerContext *)input->context;
+	int notify = 0;
+
+	if (flags & KBD_FLAGS_DOWN) {
+		keyState = WL_KEYBOARD_KEY_STATE_PRESSED;
+		notify = 1;
+	} else if (flags & KBD_FLAGS_RELEASE) {
+		keyState = WL_KEYBOARD_KEY_STATE_RELEASED;
+		notify = 1;
+	}
+
+	if(notify)
+		notify_key(&peerContext->item.seat, weston_compositor_get_time(),
+					code, keyState,	STATE_UPDATE_AUTOMATIC);
+}
+
+static void
+xf_input_unicode_keyboard_event(rdpInput* input, uint16 flags, uint16 code)
+{
+	weston_log("Client sent a unicode keyboard event (flags:0x%X code:0x%X)\n", flags, code);
+}
+
+void update_register_server_callbacks(rdpUpdate* update);
+
+
+static void
+rdp_configure_peer_input(freerdp_peer* client, RdpPeerContext *peerCtx, struct rdp_compositor *c) {
+	rdpInput *input = client->input;
+	input->SynchronizeEvent = xf_input_synchronize_event;
+	input->MouseEvent = xf_mouseEvent;
+	input->ExtendedMouseEvent = xf_extendedMouseEvent;
+	input->KeyboardEvent = xf_input_keyboard_event;
+	input->UnicodeKeyboardEvent = xf_input_unicode_keyboard_event;
+
+	weston_seat_init(&peerCtx->item.seat, &c->base);
+	weston_seat_init_pointer(&peerCtx->item.seat);
+}
+
+static int
+rdp_peer_init(freerdp_peer* client, struct rdp_compositor *c)
+{
+	int rcount = 0;
+	void *rfds[32];
+	int i, fd;
+	char* server_file_path;
+	struct wl_event_loop *loop;
+	rdpSettings		*settings;
+	RdpPeerContext	*peerCtx;
+
+	client->context_size = sizeof(RdpPeerContext);
+	client->ContextNew = (psPeerContextNew)rdp_peer_context_new;
+	client->ContextFree = (psPeerContextFree)rdp_peer_context_free;
+	freerdp_peer_context_new(client);
+
+	peerCtx = (RdpPeerContext *) client->context;
+	peerCtx->rdpCompositor = c;
+
+	settings = client->settings;
+	server_file_path = freerdp_construct_path(settings->config_path, "server");
+
+	if (!freerdp_check_file_exists(server_file_path))
+		freerdp_mkdir(server_file_path);
+
+	settings->cert_file = freerdp_construct_path(server_file_path, "server.crt");
+	settings->privatekey_file = freerdp_construct_path(server_file_path, "server.key");
+	settings->rdp_key_file = freerdp_construct_path(server_file_path, "server.key");
+	settings->nla_security = false;
+
+	client->Capabilities = xf_peer_capabilities;
+	client->PostConnect = xf_peer_post_connect;
+	client->Activate = xf_peer_activate;
+
+	client->Initialize(client);
+	update_register_server_callbacks(client->update);
+	rdp_configure_peer_input(client, peerCtx, c);
+
+	if (!client->GetFileDescriptor(client, rfds, &rcount)) {
+		weston_log("unable to retrieve client fds\n");
+		return -1;
+	}
+
+	loop = wl_display_get_event_loop(c->base.wl_display);
+	for(i = 0; i < rcount; i++) {
+		fd = (int)(long)(rfds[i]);
+
+		peerCtx->fds[i] = fd;
+		peerCtx->events[i] = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE | WL_EVENT_HANGUP,
+				rdp_client_activity, client);
+	}
+	for( ; i < 32; i++) {
+		peerCtx->fds[i] = -1;
+		peerCtx->events[i] = 0;
+	}
+
+	wl_list_insert(&c->output->peers, &peerCtx->item.link);
+	return 0;
+}
+
+
+static void
+rdp_incoming_peer(freerdp_listener* instance, freerdp_peer* client)
+{
+	struct rdp_compositor *c = (struct rdp_compositor *)instance->param4;
+
+	if (rdp_peer_init(client, c) < 0)
+		return;
+}
+
+static struct weston_compositor *
+rdp_compositor_create(struct wl_display *display,
+			  int width, int height, const char *bind_address, int port,
+			  int *argc, char *argv[], const char *config_file)
+{
+	struct rdp_compositor *c;
+
+	c = calloc(1, sizeof *c);
+	if (c == NULL)
+		return NULL;
+
+	memset(c, 0, sizeof *c);
+
+	if (weston_compositor_init(&c->base, display, argc, argv,
+				   config_file) < 0)
+		goto err_free;
+
+	weston_seat_init(&c->main_seat, &c->base);
+	c->base.destroy = rdp_destroy;
+	c->base.restore = rdp_restore;
+
+	c->listener = freerdp_listener_new();
+	c->listener->PeerAccepted = rdp_incoming_peer;
+	c->listener->param4 = c;
+	if(!c->listener->Open(c->listener, bind_address, port)) {
+		weston_log("unable to bind rdp socket");
+		goto err_listener;
+	}
+
+	if (rdp_implant_listener(c, c->listener) < 0)
+		goto err_compositor;
+
+	if (pixman_renderer_init(&c->base) < 0)
+		goto err_compositor;
+
+	if (rdp_compositor_create_output(c, width, height) < 0)
+		goto err_compositor;
+
+
+	return &c->base;
+
+err_compositor:
+	weston_compositor_shutdown(&c->base);
+
+err_listener:
+	freerdp_listener_free(c->listener);
+err_free:
+	free(c);
+	return NULL;
+}
+
+WL_EXPORT struct weston_compositor *
+backend_init(struct wl_display *display, int *argc, char *argv[],
+	     const char *config_file)
+{
+	int width = 640, height = 480, port = 3389;
+	char *bind_address = NULL;
+
+	const struct weston_option rdp_options[] = {
+		{ WESTON_OPTION_INTEGER, "width", 0, &width },
+		{ WESTON_OPTION_INTEGER, "height", 0, &height },
+		{ WESTON_OPTION_STRING,  "address", 0, &bind_address },
+		{ WESTON_OPTION_INTEGER, "port", 0, &port },
+	};
+
+	parse_options(rdp_options, ARRAY_LENGTH(rdp_options), argc, argv);
+
+	return rdp_compositor_create(display, width, height, bind_address, port,
+					 argc, argv, config_file);
+}
-- 
1.7.10.4



More information about the wayland-devel mailing list