Add FreeRDP backend for remote desktop support

This commit is contained in:
Drew DeVault 2019-02-22 13:58:52 -05:00
parent 9faea17c73
commit fd0d7d0907
16 changed files with 1335 additions and 10 deletions

View file

@ -1,6 +1,7 @@
image: alpine/edge image: alpine/edge
packages: packages:
- eudev-dev - eudev-dev
- freerdp-dev
- ffmpeg-dev - ffmpeg-dev
- libcap-dev - libcap-dev
- libinput-dev - libinput-dev

View file

@ -1,6 +1,7 @@
image: archlinux image: archlinux
packages: packages:
- clang - clang
- freerdp
- ffmpeg - ffmpeg
- libcap - libcap
- libinput - libinput

View file

@ -21,6 +21,9 @@
#if WLR_HAS_X11_BACKEND #if WLR_HAS_X11_BACKEND
#include <wlr/backend/x11.h> #include <wlr/backend/x11.h>
#endif #endif
#if WLR_HAS_RDP_BACKEND
#include <wlr/backend/rdp.h>
#endif
void wlr_backend_init(struct wlr_backend *backend, void wlr_backend_init(struct wlr_backend *backend,
const struct wlr_backend_impl *impl) { const struct wlr_backend_impl *impl) {
@ -134,6 +137,38 @@ static struct wlr_backend *attempt_headless_backend(
return backend; return backend;
} }
#if WLR_HAS_RDP_BACKEND
static struct wlr_backend *attempt_rdp_backend(struct wl_display *display,
wlr_renderer_create_func_t create_renderer_func) {
const char *cert_path = getenv("WLR_RDP_TLS_CERT_PATH");
const char *key_path = getenv("WLR_RDP_TLS_KEY_PATH");
if (!cert_path || !key_path) {
wlr_log(WLR_ERROR, "The RDP backend requires WLR_RDP_TLS_CERT_PATH "
"and WLR_RDP_TLS_KEY_PATH to be set.");
return NULL;
}
struct wlr_backend *backend = wlr_rdp_backend_create(
display, create_renderer_func, cert_path, key_path);
const char *address = getenv("WLR_RDP_ADDRESS");
if (address) {
wlr_rdp_backend_set_address(backend, address);
}
const char *_port = getenv("WLR_RDP_PORT");
if (_port) {
char *endptr;
int port = strtol(_port, &endptr, 10);
if (*endptr || port <= 0 || port >= 1024) {
wlr_log(WLR_ERROR, "Expected WLR_RDP_PORT to be a "
"positive integer less than 1024");
wlr_backend_destroy(backend);
return NULL;
}
wlr_rdp_backend_set_port(backend, port);
}
return backend;
}
#endif
static struct wlr_backend *attempt_noop_backend(struct wl_display *display) { static struct wlr_backend *attempt_noop_backend(struct wl_display *display) {
struct wlr_backend *backend = wlr_noop_backend_create(display); struct wlr_backend *backend = wlr_noop_backend_create(display);
if (backend == NULL) { if (backend == NULL) {
@ -185,6 +220,10 @@ static struct wlr_backend *attempt_backend_by_name(struct wl_display *display,
#endif #endif
} else if (strcmp(name, "headless") == 0) { } else if (strcmp(name, "headless") == 0) {
return attempt_headless_backend(display, create_renderer_func); return attempt_headless_backend(display, create_renderer_func);
#if WLR_HAS_RDP_BACKEND
} else if (strcmp(name, "rdp") == 0) {
return attempt_rdp_backend(display, create_renderer_func);
#endif
} else if (strcmp(name, "noop") == 0) { } else if (strcmp(name, "noop") == 0) {
return attempt_noop_backend(display); return attempt_noop_backend(display);
} else if (strcmp(name, "drm") == 0 || strcmp(name, "libinput") == 0) { } else if (strcmp(name, "drm") == 0 || strcmp(name, "libinput") == 0) {

View file

@ -53,6 +53,22 @@ if logind.found()
backend_deps += logind backend_deps += logind
endif endif
if freerdp.found() and winpr2.found()
backend_files += files(
'rdp/backend.c',
'rdp/keyboard.c',
'rdp/listener.c',
'rdp/output.c',
'rdp/peer.c',
'rdp/pointer.c',
)
backend_deps += [
freerdp,
winpr2
]
conf_data.set10('WLR_HAS_RDP_BACKEND', true)
endif
subdir('x11') subdir('x11')
lib_wlr_backend = static_library( lib_wlr_backend = static_library(

134
backend/rdp/backend.c Normal file
View file

@ -0,0 +1,134 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <wlr/render/egl.h>
#include <wlr/render/gles2.h>
#include <wlr/util/log.h>
#include "backend/rdp.h"
#include "glapi.h"
#include "util/signal.h"
struct wlr_rdp_backend *rdp_backend_from_backend(
struct wlr_backend *wlr_backend) {
assert(wlr_backend_is_rdp(wlr_backend));
return (struct wlr_rdp_backend *)wlr_backend;
}
static bool backend_start(struct wlr_backend *wlr_backend) {
struct wlr_rdp_backend *backend =
rdp_backend_from_backend(wlr_backend);
assert(backend->listener == NULL);
wlr_log(WLR_INFO, "Starting RDP backend");
if (!rdp_configure_listener(backend)) {
return false;
}
return true;
}
void wlr_rdp_backend_set_address(struct wlr_backend *wlr_backend,
const char *address) {
struct wlr_rdp_backend *backend =
rdp_backend_from_backend(wlr_backend);
assert(backend->listener == NULL);
backend->address = strdup(address);
}
void wlr_rdp_backend_set_port(struct wlr_backend *wlr_backend, int port) {
struct wlr_rdp_backend *backend =
rdp_backend_from_backend(wlr_backend);
assert(backend->listener == NULL);
backend->port = port;
}
static void backend_destroy(struct wlr_backend *wlr_backend) {
struct wlr_rdp_backend *backend =
rdp_backend_from_backend(wlr_backend);
if (!wlr_backend) {
return;
}
wl_list_remove(&backend->display_destroy.link);
struct wlr_rdp_peer_context *client;
wl_list_for_each(client, &backend->clients, link) {
freerdp_peer_context_free(client->peer);
freerdp_peer_free(client->peer);
}
wlr_signal_emit_safe(&wlr_backend->events.destroy, backend);
wlr_renderer_destroy(backend->renderer);
wlr_egl_finish(&backend->egl);
free(backend->address);
free(backend);
}
static struct wlr_renderer *backend_get_renderer(
struct wlr_backend *wlr_backend) {
struct wlr_rdp_backend *backend =
rdp_backend_from_backend(wlr_backend);
return backend->renderer;
}
static const struct wlr_backend_impl backend_impl = {
.start = backend_start,
.destroy = backend_destroy,
.get_renderer = backend_get_renderer,
};
static void handle_display_destroy(struct wl_listener *listener, void *data) {
struct wlr_rdp_backend *backend =
wl_container_of(listener, backend, display_destroy);
backend_destroy(&backend->backend);
}
struct wlr_backend *wlr_rdp_backend_create(struct wl_display *display,
wlr_renderer_create_func_t create_renderer_func,
const char *tls_cert_path, const char *tls_key_path) {
wlr_log(WLR_INFO, "Creating RDP backend");
struct wlr_rdp_backend *backend =
calloc(1, sizeof(struct wlr_rdp_backend));
if (!backend) {
wlr_log(WLR_ERROR, "Failed to allocate wlr_rdp_backend");
return NULL;
}
wlr_backend_init(&backend->backend, &backend_impl);
backend->display = display;
backend->tls_cert_path = tls_cert_path;
backend->tls_key_path = tls_key_path;
backend->address = strdup("127.0.0.1");
backend->port = 3389;
wl_list_init(&backend->clients);
static const EGLint config_attribs[] = {
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_ALPHA_SIZE, 0,
EGL_BLUE_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_RED_SIZE, 1,
EGL_NONE,
};
if (!create_renderer_func) {
create_renderer_func = wlr_renderer_autocreate;
}
backend->renderer = create_renderer_func(&backend->egl,
EGL_PLATFORM_SURFACELESS_MESA, NULL, (EGLint*)config_attribs, 0);
if (!backend->renderer) {
wlr_log(WLR_ERROR, "Failed to create renderer");
free(backend);
return NULL;
}
backend->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &backend->display_destroy);
return &backend->backend;
}
bool wlr_backend_is_rdp(struct wlr_backend *backend) {
return backend->impl == &backend_impl;
}

219
backend/rdp/keyboard.c Normal file
View file

@ -0,0 +1,219 @@
#include <stdlib.h>
#include <wayland-server.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/interfaces/wlr_keyboard.h>
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
#include "backend/rdp.h"
#include "util/signal.h"
struct rdp_to_xkb_keyboard_layout {
UINT32 rdp_layout_code;
const char *xkb_layout;
const char *xkb_variant;
};
/* table reversed from
https://github.com/awakecoding/FreeRDP/blob/master/libfreerdp/locale/xkb_layout_ids.c#L811 */
static struct rdp_to_xkb_keyboard_layout rdp_keyboards[] = {
{KBD_ARABIC_101, "ara", 0},
{KBD_BULGARIAN, 0, 0},
{KBD_CHINESE_TRADITIONAL_US, 0, 0},
{KBD_CZECH, "cz", 0},
{KBD_CZECH_PROGRAMMERS, "cz", "bksl"},
{KBD_CZECH_QWERTY, "cz", "qwerty"},
{KBD_DANISH, "dk", 0},
{KBD_GERMAN, "de", 0},
{KBD_GERMAN_NEO, "de", "neo"},
{KBD_GERMAN_IBM, "de", "qwerty"},
{KBD_GREEK, "gr", 0},
{KBD_GREEK_220, "gr", "simple"},
{KBD_GREEK_319, "gr", "extended"},
{KBD_GREEK_POLYTONIC, "gr", "polytonic"},
{KBD_US, "us", 0},
{KBD_US_ENGLISH_TABLE_FOR_IBM_ARABIC_238_L, "ara", "buckwalter"},
{KBD_SPANISH, "es", 0},
{KBD_SPANISH_VARIATION, "es", "nodeadkeys"},
{KBD_FINNISH, "fi", 0},
{KBD_FRENCH, "fr", 0},
{KBD_HEBREW, "il", 0},
{KBD_HUNGARIAN, "hu", 0},
{KBD_HUNGARIAN_101_KEY, "hu", "standard"},
{KBD_ICELANDIC, "is", 0},
{KBD_ITALIAN, "it", 0},
{KBD_ITALIAN_142, "it", "nodeadkeys"},
{KBD_JAPANESE, "jp", 0},
{KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002, "jp", "kana"},
{KBD_KOREAN, "kr", 0},
{KBD_KOREAN_INPUT_SYSTEM_IME_2000, "kr", "kr104"},
{KBD_DUTCH, "nl", 0},
{KBD_NORWEGIAN, "no", 0},
{KBD_POLISH_PROGRAMMERS, "pl", 0},
{KBD_POLISH_214, "pl", "qwertz"},
{KBD_ROMANIAN, "ro", 0},
{KBD_RUSSIAN, "ru", 0},
{KBD_RUSSIAN_TYPEWRITER, "ru", "typewriter"},
{KBD_CROATIAN, "hr", 0},
{KBD_SLOVAK, "sk", 0},
{KBD_SLOVAK_QWERTY, "sk", "qwerty"},
{KBD_ALBANIAN, 0, 0},
{KBD_SWEDISH, "se", 0},
{KBD_THAI_KEDMANEE, "th", 0},
{KBD_THAI_KEDMANEE_NON_SHIFTLOCK, "th", "tis"},
{KBD_TURKISH_Q, "tr", 0},
{KBD_TURKISH_F, "tr", "f"},
{KBD_URDU, "in", "urd-phonetic3"},
{KBD_UKRAINIAN, "ua", 0},
{KBD_BELARUSIAN, "by", 0},
{KBD_SLOVENIAN, "si", 0},
{KBD_ESTONIAN, "ee", 0},
{KBD_LATVIAN, "lv", 0},
{KBD_LITHUANIAN_IBM, "lt", "ibm"},
{KBD_FARSI, "af", 0},
{KBD_VIETNAMESE, "vn", 0},
{KBD_ARMENIAN_EASTERN, "am", 0},
{KBD_AZERI_LATIN, 0, 0},
{KBD_FYRO_MACEDONIAN, "mk", 0},
{KBD_GEORGIAN, "ge", 0},
{KBD_FAEROESE, 0, 0},
{KBD_DEVANAGARI_INSCRIPT, 0, 0},
{KBD_MALTESE_47_KEY, 0, 0},
{KBD_NORWEGIAN_WITH_SAMI, "no", "smi"},
{KBD_KAZAKH, "kz", 0},
{KBD_KYRGYZ_CYRILLIC, "kg", "phonetic"},
{KBD_TATAR, "ru", "tt"},
{KBD_BENGALI, "bd", 0},
{KBD_BENGALI_INSCRIPT, "bd", "probhat"},
{KBD_PUNJABI, 0, 0},
{KBD_GUJARATI, "in", "guj"},
{KBD_TAMIL, "in", "tam"},
{KBD_TELUGU, "in", "tel"},
{KBD_KANNADA, "in", "kan"},
{KBD_MALAYALAM, "in", "mal"},
{KBD_HINDI_TRADITIONAL, "in", 0},
{KBD_MARATHI, 0, 0},
{KBD_MONGOLIAN_CYRILLIC, "mn", 0},
{KBD_UNITED_KINGDOM_EXTENDED, "gb", "intl"},
{KBD_SYRIAC, "syc", 0},
{KBD_SYRIAC_PHONETIC, "syc", "syc_phonetic"},
{KBD_NEPALI, "np", 0},
{KBD_PASHTO, "af", "ps"},
{KBD_DIVEHI_PHONETIC, 0, 0},
{KBD_LUXEMBOURGISH, 0, 0},
{KBD_MAORI, "mao", 0},
{KBD_CHINESE_SIMPLIFIED_US, 0, 0},
{KBD_SWISS_GERMAN, "ch", "de_nodeadkeys"},
{KBD_UNITED_KINGDOM, "gb", 0},
{KBD_LATIN_AMERICAN, "latam", 0},
{KBD_BELGIAN_FRENCH, "be", 0},
{KBD_BELGIAN_PERIOD, "be", "oss_sundeadkeys"},
{KBD_PORTUGUESE, "pt", 0},
{KBD_SERBIAN_LATIN, "rs", 0},
{KBD_AZERI_CYRILLIC, "az", "cyrillic"},
{KBD_SWEDISH_WITH_SAMI, "se", "smi"},
{KBD_UZBEK_CYRILLIC, "af", "uz"},
{KBD_INUKTITUT_LATIN, "ca", "ike"},
{KBD_CANADIAN_FRENCH_LEGACY, "ca", "fr-legacy"},
{KBD_SERBIAN_CYRILLIC, "rs", 0},
{KBD_CANADIAN_FRENCH, "ca", "fr-legacy"},
{KBD_SWISS_FRENCH, "ch", "fr"},
{KBD_BOSNIAN, "ba", 0},
{KBD_IRISH, 0, 0},
{KBD_BOSNIAN_CYRILLIC, "ba", "us"},
{KBD_UNITED_STATES_DVORAK, "us", "dvorak"},
{KBD_PORTUGUESE_BRAZILIAN_ABNT2, "br", "nativo"},
{KBD_CANADIAN_MULTILINGUAL_STANDARD, "ca", "multix"},
{KBD_GAELIC, "ie", "CloGaelach"},
{0x00000000, 0, 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 void keyboard_destroy(struct wlr_input_device *wlr_device) {
struct wlr_rdp_keyboard *keyboard =
(struct wlr_rdp_keyboard *)wlr_device->keyboard;
xkb_keymap_unref(keyboard->keymap);
free(keyboard);
}
static struct wlr_input_device_impl input_device_impl = {
.destroy = keyboard_destroy,
};
struct wlr_rdp_input_device *wlr_rdp_keyboard_create(
struct wlr_rdp_backend *backend, rdpSettings *settings) {
struct wlr_rdp_input_device *device =
calloc(1, sizeof(struct wlr_rdp_input_device));
if (!device) {
wlr_log(WLR_ERROR, "Failed to allcoate RDP input device");
return NULL;
}
int vendor = 0;
int product = 0;
const char *name = "rdp";
struct wlr_input_device *wlr_device = &device->wlr_input_device;
wlr_input_device_init(wlr_device, WLR_INPUT_DEVICE_KEYBOARD,
&input_device_impl, name, vendor, product);
struct wlr_rdp_keyboard *keyboard =
calloc(1, sizeof(struct wlr_rdp_keyboard));
if (!keyboard) {
wlr_log(WLR_ERROR, "Failed to allocate RDP pointer device");
goto error;
}
wlr_device->keyboard = (struct wlr_keyboard *)keyboard;
wlr_keyboard_init(wlr_device->keyboard, NULL);
wlr_log(WLR_DEBUG, "RDP keyboard layout: 0x%x type: 0x%x subtype: 0x%x "
"function_keys 0x%x", settings->KeyboardLayout,
settings->KeyboardType, settings->KeyboardSubType,
settings->KeyboardFunctionKey);
// We need to set up an XKB context and jump through some hoops to convert
// RDP input events into scancodes
struct xkb_rule_names xkb_rule_names = { 0 };
if (settings->KeyboardType <= 7) {
xkb_rule_names.model = rdp_keyboard_types[settings->KeyboardType];
}
for (int i = 0; rdp_keyboards[i].rdp_layout_code; ++i) {
if (rdp_keyboards[i].rdp_layout_code == settings->KeyboardLayout) {
xkb_rule_names.layout = rdp_keyboards[i].xkb_layout;
xkb_rule_names.variant = rdp_keyboards[i].xkb_variant;
wlr_log(WLR_DEBUG, "Mapped RDP keyboard to xkb layout %s variant "
"%s", xkb_rule_names.layout, xkb_rule_names.variant);
break;
}
}
if (xkb_rule_names.layout) {
struct xkb_context *xkb_context = xkb_context_new(0);
if (!xkb_context) {
wlr_log(WLR_DEBUG, "Failed to allocate xkb context");
goto error;
}
keyboard->keymap =
xkb_keymap_new_from_names(xkb_context, &xkb_rule_names, 0);
xkb_context_unref(xkb_context);
}
wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap);
wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_device);
return device;
error:
wlr_input_device_destroy(wlr_device);
return NULL;
}

61
backend/rdp/listener.c Normal file
View file

@ -0,0 +1,61 @@
#include <stdlib.h>
#include <wlr/util/log.h>
#include "backend/rdp.h"
static int rdp_incoming_peer(
freerdp_listener *listener, freerdp_peer *client) {
struct wlr_rdp_backend *backend =
(struct wlr_rdp_backend *)listener->param4;
if (rdp_peer_init(client, backend) < 0) {
wlr_log(WLR_ERROR, "Error initializing incoming peer");
return false;
}
return true;
}
static int rdp_listener_activity(int fd, uint32_t mask, void *data) {
freerdp_listener *listener = data;
if (!(mask & WL_EVENT_READABLE)) {
return 0;
}
if (!listener->CheckFileDescriptor(listener)) {
wlr_log(WLR_ERROR, "Failed to check FreeRDP file descriptor");
return -1;
}
return 0;
}
bool rdp_configure_listener(struct wlr_rdp_backend *backend) {
backend->listener = freerdp_listener_new();
if (!backend->listener) {
wlr_log(WLR_ERROR, "Failed to allocate FreeRDP listener");
return false;
}
backend->listener->PeerAccepted = rdp_incoming_peer;
backend->listener->param4 = backend;
if (!backend->listener->Open(backend->listener,
backend->address, backend->port)) {
wlr_log(WLR_ERROR, "Failed to bind to RDP socket");
return false;
}
int rcount = 0;
void *rfds[MAX_FREERDP_FDS];
if (!backend->listener->GetFileDescriptor(
backend->listener, rfds, &rcount)) {
wlr_log(WLR_ERROR, "Failed to get FreeRDP file descriptors");
return false;
}
struct wl_event_loop *event_loop =
wl_display_get_event_loop(backend->display);
int i;
for (i = 0; i < rcount; ++i) {
int fd = (int)(long)(rfds[i]);
backend->listener_events[i] = wl_event_loop_add_fd(
event_loop, fd, WL_EVENT_READABLE, rdp_listener_activity,
backend->listener);
}
for (; i < MAX_FREERDP_FDS; ++i) {
backend->listener_events[i] = NULL;
}
return true;
}

288
backend/rdp/output.c Normal file
View file

@ -0,0 +1,288 @@
#include <assert.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <stdlib.h>
#include <wayland-server.h>
#include <wlr/types/wlr_output.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/util/log.h>
#include <wlr/render/wlr_renderer.h>
#include "backend/rdp.h"
#include "util/signal.h"
static struct wlr_rdp_output *rdp_output_from_output(
struct wlr_output *wlr_output) {
assert(wlr_output_is_rdp(wlr_output));
return (struct wlr_rdp_output *)wlr_output;
}
static EGLSurface egl_create_surface(struct wlr_egl *egl, unsigned int width,
unsigned int height) {
EGLint attribs[] = {
EGL_WIDTH, width,
EGL_HEIGHT, height,
EGL_NONE,
};
EGLSurface surf = eglCreatePbufferSurface(egl->display, egl->config, attribs);
if (surf == EGL_NO_SURFACE) {
wlr_log(WLR_ERROR, "Failed to create EGL surface");
return EGL_NO_SURFACE;
}
return surf;
}
static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width,
int32_t height, int32_t refresh) {
struct wlr_rdp_output *output =
rdp_output_from_output(wlr_output);
struct wlr_rdp_backend *backend = output->backend;
if (refresh <= 0) {
refresh = 60 * 1000; // 60 Hz
}
wlr_egl_destroy_surface(&backend->egl, output->egl_surface);
output->egl_surface = egl_create_surface(&backend->egl, width, height);
if (output->egl_surface == EGL_NO_SURFACE) {
wlr_log(WLR_ERROR, "Failed to recreate EGL surface");
wlr_output_destroy(wlr_output);
return false;
}
output->frame_delay = 1000000 / refresh;
if (output->shadow_surface) {
pixman_image_unref(output->shadow_surface);
}
output->shadow_surface = pixman_image_create_bits(PIXMAN_x8r8g8b8,
width, height, NULL, width * 4);
wlr_output_update_custom_mode(&output->wlr_output, width, height, refresh);
return true;
}
static void output_transform(struct wlr_output *wlr_output,
enum wl_output_transform transform) {
struct wlr_rdp_output *output =
rdp_output_from_output(wlr_output);
output->wlr_output.transform = transform;
}
static bool output_make_current(struct wlr_output *wlr_output, int *buffer_age) {
struct wlr_rdp_output *output =
rdp_output_from_output(wlr_output);
return wlr_egl_make_current(&output->backend->egl, output->egl_surface,
buffer_age);
}
static bool rfx_swap_buffers(
struct wlr_rdp_output *output, pixman_region32_t *damage) {
struct wlr_rdp_peer_context *context = output->context;
freerdp_peer *peer = context->peer;
rdpUpdate *update = peer->update;
Stream_Clear(context->encode_stream);
Stream_SetPosition(context->encode_stream, 0);
int width = damage->extents.x2 - damage->extents.x1;
int height = damage->extents.y2 - damage->extents.y1;
SURFACE_BITS_COMMAND cmd;
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 = pixman_image_get_depth(output->shadow_surface);
cmd.bmp.codecID = peer->settings->RemoteFxCodecId;
cmd.bmp.width = width;
cmd.bmp.height = height;
uint32_t *ptr = pixman_image_get_data(output->shadow_surface) +
damage->extents.x1 + damage->extents.y1 *
(pixman_image_get_stride(output->shadow_surface) / sizeof(uint32_t));
RFX_RECT *rfx_rect;
int nrects;
pixman_box32_t *rects =
pixman_region32_rectangles(damage, &nrects);
rfx_rect = realloc(context->rfx_rects, nrects * sizeof(*rfx_rect));
if (rfx_rect == NULL) {
wlr_log(WLR_ERROR, "RDP swap buffers failed: could not realloc rects");
return false;
}
context->rfx_rects = rfx_rect;
for (int i = 0; i < nrects; ++i) {
pixman_box32_t *region = &rects[i];
rfx_rect = &context->rfx_rects[i];
rfx_rect->x = region->x1 - damage->extents.x1;
rfx_rect->y = region->y1 - damage->extents.y1;
rfx_rect->width = region->x2 - region->x1;
rfx_rect->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(output->shadow_surface));
cmd.bmp.bitmapDataLength = Stream_GetPosition(context->encode_stream);
cmd.bmp.bitmapData = Stream_Buffer(context->encode_stream);
update->SurfaceBits(update->context, &cmd);
return true;
}
static bool nsc_swap_buffers(
struct wlr_rdp_output *output, pixman_region32_t *damage) {
struct wlr_rdp_peer_context *context = output->context;
freerdp_peer *peer = context->peer;
rdpUpdate *update = peer->update;
Stream_Clear(context->encode_stream);
Stream_SetPosition(context->encode_stream, 0);
int width = damage->extents.x2 - damage->extents.x1;
int height = damage->extents.y2 - damage->extents.y1;
SURFACE_BITS_COMMAND cmd;
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 = pixman_image_get_depth(output->shadow_surface);
cmd.bmp.codecID = peer->settings->NSCodecId;
cmd.bmp.width = width;
cmd.bmp.height = height;
uint32_t *ptr = pixman_image_get_data(output->shadow_surface) +
damage->extents.x1 + damage->extents.y1 *
(pixman_image_get_stride(output->shadow_surface) / sizeof(uint32_t));
nsc_compose_message(context->nsc_context, context->encode_stream,
(BYTE *)ptr, width, height,
pixman_image_get_stride(output->shadow_surface));
cmd.bmp.bitmapDataLength = Stream_GetPosition(context->encode_stream);
cmd.bmp.bitmapData = Stream_Buffer(context->encode_stream);
update->SurfaceBits(update->context, &cmd);
return true;
}
static bool output_swap_buffers(
struct wlr_output *wlr_output, pixman_region32_t *damage) {
if (!pixman_region32_not_empty(damage)) {
return true;
}
struct wlr_rdp_output *output =
rdp_output_from_output(wlr_output);
// Update shadow buffer
int width = damage->extents.x2 - damage->extents.x1;
int height = damage->extents.y2 - damage->extents.y1;
struct wlr_renderer *renderer =
wlr_backend_get_renderer(&output->backend->backend);
// TODO performance: add support for flags
if (!wlr_renderer_read_pixels(renderer, WL_SHM_FORMAT_XRGB8888,
NULL, pixman_image_get_stride(output->shadow_surface),
width, height, damage->extents.x1, damage->extents.y1,
damage->extents.x1, damage->extents.y1,
pixman_image_get_data(output->shadow_surface))) {
return false;
}
// Send along to clients
bool ret = false;
rdpSettings *settings = output->context->peer->settings;
if (settings->RemoteFxCodec) {
ret = rfx_swap_buffers(output, damage);
} else if (settings->NSCodec) {
ret = nsc_swap_buffers(output, damage);
} else {
// This would perform like ass so why bother
wlr_log(WLR_ERROR, "Raw updates are not supported; use rfx or nsc");
}
wlr_output_send_present(wlr_output, NULL);
return ret;
}
static void output_destroy(struct wlr_output *wlr_output) {
struct wlr_rdp_output *output =
rdp_output_from_output(wlr_output);
if (output->frame_timer) {
wl_event_source_remove(output->frame_timer);
}
wlr_egl_destroy_surface(&output->backend->egl, output->egl_surface);
if (output->shadow_surface) {
pixman_image_unref(output->shadow_surface);
}
free(output);
}
static const struct wlr_output_impl output_impl = {
.set_custom_mode = output_set_custom_mode,
.transform = output_transform,
.destroy = output_destroy,
.make_current = output_make_current,
.swap_buffers = output_swap_buffers,
};
bool wlr_output_is_rdp(struct wlr_output *wlr_output) {
return wlr_output->impl == &output_impl;
}
static int signal_frame(void *data) {
struct wlr_rdp_output *output = data;
wlr_output_send_frame(&output->wlr_output);
wl_event_source_timer_update(output->frame_timer, output->frame_delay);
return 0;
}
struct wlr_rdp_output *wlr_rdp_output_create(struct wlr_rdp_backend *backend,
struct wlr_rdp_peer_context *context, unsigned int width,
unsigned int height) {
struct wlr_rdp_output *output =
calloc(1, sizeof(struct wlr_rdp_output));
if (output == NULL) {
wlr_log(WLR_ERROR, "Failed to allocate wlr_rdp_output");
return NULL;
}
output->backend = backend;
output->context = context;
wlr_output_init(&output->wlr_output, &backend->backend, &output_impl,
backend->display);
struct wlr_output *wlr_output = &output->wlr_output;
output->egl_surface = egl_create_surface(&backend->egl, width, height);
if (output->egl_surface == EGL_NO_SURFACE) {
wlr_log(WLR_ERROR, "Failed to create EGL surface");
goto error;
}
output_set_custom_mode(wlr_output, width, height, 0);
strncpy(wlr_output->make, "RDP", sizeof(wlr_output->make));
strncpy(wlr_output->model, "RDP", sizeof(wlr_output->model));
snprintf(wlr_output->name, sizeof(wlr_output->name), "RDP-%d",
wl_list_length(&backend->clients));
if (!wlr_egl_make_current(&output->backend->egl, output->egl_surface,
NULL)) {
goto error;
}
wlr_renderer_begin(backend->renderer, wlr_output->width, wlr_output->height);
wlr_renderer_clear(backend->renderer, (float[]){ 1.0, 1.0, 1.0, 1.0 });
wlr_renderer_end(backend->renderer);
struct wl_event_loop *ev = wl_display_get_event_loop(backend->display);
output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output);
wl_event_source_timer_update(output->frame_timer, output->frame_delay);
wlr_output_update_enabled(wlr_output, true);
wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output);
return output;
error:
wlr_output_destroy(&output->wlr_output);
return NULL;
}

360
backend/rdp/peer.c Normal file
View file

@ -0,0 +1,360 @@
#define _POSIX_C_SOURCE 200809L
#include <linux/input.h>
#include <stdlib.h>
#include <string.h>
#include <wlr/types/wlr_output.h>
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/interfaces/wlr_keyboard.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/util/log.h>
#include "backend/rdp.h"
#include "util/signal.h"
static BOOL xf_peer_capabilities(freerdp_peer *client) {
return TRUE;
}
static BOOL xf_peer_post_connect(freerdp_peer *client) {
return TRUE;
}
static BOOL xf_peer_activate(freerdp_peer *client) {
struct wlr_rdp_peer_context *context =
(struct wlr_rdp_peer_context *)client->context;
struct wlr_rdp_backend *backend = context->backend;
rdpSettings *settings = client->settings;
if (!settings->SurfaceCommandsEnabled) {
wlr_log(WLR_ERROR, "RDP peer does not support SurfaceCommands");
return FALSE;
}
context->output = wlr_rdp_output_create(backend, context,
(int)settings->DesktopWidth, (int)settings->DesktopHeight);
if (!context->output) {
wlr_log(WLR_ERROR, "Failed to allcoate output for RDP peer");
return FALSE;
}
rfx_context_reset(context->rfx_context,
context->output->wlr_output.width,
context->output->wlr_output.height);
nsc_context_reset(context->nsc_context,
context->output->wlr_output.width,
context->output->wlr_output.height);
if (context->flags & RDP_PEER_ACTIVATED) {
return TRUE;
}
context->pointer = wlr_rdp_pointer_create(backend, context);
if (!context->pointer) {
wlr_log(WLR_ERROR, "Failed to allocate pointer for RDP peer");
return FALSE;
}
// Use wlroots' software cursors instead of remote cursors
POINTER_SYSTEM_UPDATE pointer_system;
rdpPointerUpdate *pointer = client->update->pointer;
pointer_system.type = SYSPTR_NULL;
pointer->PointerSystem(client->context, &pointer_system);
context->keyboard = wlr_rdp_keyboard_create(backend, settings);
if (!context->keyboard) {
wlr_log(WLR_ERROR, "Failed to allocate keyboard for RDP peer");
return FALSE;
}
context->flags |= RDP_PEER_ACTIVATED;
return TRUE;
}
static int xf_suppress_output(rdpContext *context,
BYTE allow, const RECTANGLE_16 *area) {
struct wlr_rdp_peer_context *peer_context =
(struct wlr_rdp_peer_context *)context;
if (allow) {
peer_context->flags |= RDP_PEER_OUTPUT_ENABLED;
} else {
peer_context->flags &= ~RDP_PEER_OUTPUT_ENABLED;
}
return true;
}
static int xf_input_synchronize_event(rdpInput *input, UINT32 flags) {
struct wlr_rdp_peer_context *context =
(struct wlr_rdp_peer_context *)input->context;
wlr_output_damage_whole(&context->output->wlr_output);
return true;
}
static inline int64_t timespec_to_msec(const struct timespec *a) {
return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000;
}
static int xf_input_mouse_event(rdpInput *input,
UINT16 flags, UINT16 x, UINT16 y) {
struct wlr_rdp_peer_context *context =
(struct wlr_rdp_peer_context *)input->context;
struct wlr_input_device *wlr_device = &context->pointer->wlr_input_device;
struct wlr_pointer *pointer = wlr_device->pointer;
bool frame = false;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
if (flags & PTR_FLAGS_MOVE) {
struct wlr_event_pointer_motion_absolute event = { 0 };
event.device = wlr_device;
event.time_msec = timespec_to_msec(&now);
event.x = x / (double)context->output->wlr_output.width;
event.y = y / (double)context->output->wlr_output.height;
wlr_signal_emit_safe(&pointer->events.motion_absolute, &event);
frame = true;
}
uint32_t button = 0;
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) {
struct wlr_event_pointer_button event = { 0 };
event.device = wlr_device;
event.time_msec = timespec_to_msec(&now);
event.button = button;
event.state = (flags & PTR_FLAGS_DOWN) ?
WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED;
wlr_signal_emit_safe(&pointer->events.button, &event);
frame = true;
}
if (flags & PTR_FLAGS_WHEEL) {
double value = -(flags & 0xFF) / 120.0;
if (flags & PTR_FLAGS_WHEEL_NEGATIVE) {
value = -value;
}
struct wlr_event_pointer_axis event = { 0 };
event.device = &context->pointer->wlr_input_device;
event.time_msec = timespec_to_msec(&now);
event.source = WLR_AXIS_SOURCE_WHEEL;
event.orientation = WLR_AXIS_ORIENTATION_VERTICAL;
event.delta = value;
event.delta_discrete = (int32_t)value;
wlr_signal_emit_safe(&pointer->events.axis, &event);
frame = true;
}
if (frame) {
wlr_signal_emit_safe(&pointer->events.frame, pointer);
}
return true;
}
static int xf_input_extended_mouse_event(
rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) {
struct wlr_rdp_peer_context *context =
(struct wlr_rdp_peer_context *)input->context;
struct wlr_input_device *wlr_device = &context->pointer->wlr_input_device;
struct wlr_pointer *pointer = wlr_device->pointer;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
struct wlr_event_pointer_motion_absolute event = { 0 };
event.device = wlr_device;
event.time_msec = timespec_to_msec(&now);
event.x = x / (double)context->output->wlr_output.width;
event.y = y / (double)context->output->wlr_output.height;
wlr_signal_emit_safe(&pointer->events.motion_absolute, &event);
wlr_signal_emit_safe(&pointer->events.frame, pointer);
return true;
}
static int xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code) {
struct wlr_rdp_peer_context *context =
(struct wlr_rdp_peer_context *)input->context;
struct wlr_input_device *wlr_device = &context->keyboard->wlr_input_device;
struct wlr_keyboard *keyboard = wlr_device->keyboard;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
if (!(context->flags & RDP_PEER_ACTIVATED)) {
return true;
}
bool notify = false;
enum wlr_key_state state;
if ((flags & KBD_FLAGS_DOWN)) {
state = WLR_KEY_PRESSED;
notify = true;
} else if ((flags & KBD_FLAGS_RELEASE)) {
state = WLR_KEY_RELEASED;
notify = true;
}
if (notify) {
uint32_t full_code = code;
if (flags & KBD_FLAGS_EXTENDED) {
full_code |= KBD_FLAGS_EXTENDED;
}
uint32_t vk_code = GetVirtualKeyCodeFromVirtualScanCode(full_code, 4);
if (flags & KBD_FLAGS_EXTENDED) {
vk_code |= KBDEXT;
}
uint32_t scan_code = GetKeycodeFromVirtualKeyCode(
vk_code, KEYCODE_TYPE_EVDEV);
struct wlr_event_keyboard_key event = { 0 };
event.time_msec = timespec_to_msec(&now);
event.keycode = scan_code - 8;
event.state = state;
event.update_state = true;
wlr_keyboard_notify_key(keyboard, &event);
}
return true;
}
static int xf_input_unicode_keyboard_event(rdpInput *input,
UINT16 flags, UINT16 code) {
wlr_log(WLR_DEBUG, "Unhandled RDP unicode keyboard event "
"(flags:0x%X code:0x%X)\n", flags, code);
return true;
}
static int rdp_client_activity(int fd, uint32_t mask, void *data) {
freerdp_peer *client = (freerdp_peer *)data;
if (!client->CheckFileDescriptor(client)) {
wlr_log(WLR_ERROR,
"Unable to check client file descriptor for %p", client);
freerdp_peer_context_free(client);
freerdp_peer_free(client);
}
return 0;
}
static int rdp_peer_context_new(
freerdp_peer *client, struct wlr_rdp_peer_context *context) {
context->peer = client;
context->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, PIXEL_FORMAT_BGRA32);
context->nsc_context = nsc_context_new();
if (!context->nsc_context) {
rfx_context_free(context->rfx_context);
return false;
}
nsc_context_set_pixel_format(context->nsc_context, PIXEL_FORMAT_BGRA32);
context->encode_stream = Stream_New(NULL, 65536);
if (!context->encode_stream) {
nsc_context_free(context->nsc_context);
rfx_context_free(context->rfx_context);
return false;
}
return true;
}
static void rdp_peer_context_free(
freerdp_peer *client, struct wlr_rdp_peer_context *context) {
if (!context) {
return;
}
for (int i = 0; i < MAX_FREERDP_FDS; ++i) {
if (context->events[i]) {
wl_event_source_remove(context->events[i]);
}
}
if (context->flags & RDP_PEER_ACTIVATED) {
wlr_output_destroy(&context->output->wlr_output);
wlr_input_device_destroy(&context->pointer->wlr_input_device);
wlr_input_device_destroy(&context->keyboard->wlr_input_device);
}
wl_list_remove(&context->link);
wlr_output_destroy(&context->output->wlr_output);
Stream_Free(context->encode_stream, TRUE);
nsc_context_free(context->nsc_context);
rfx_context_free(context->rfx_context);
free(context->rfx_rects);
}
int rdp_peer_init(freerdp_peer *client,
struct wlr_rdp_backend *backend) {
client->ContextSize = sizeof(struct wlr_rdp_peer_context);
client->ContextNew = (psPeerContextNew)rdp_peer_context_new;
client->ContextFree = (psPeerContextFree)rdp_peer_context_free;
freerdp_peer_context_new(client);
struct wlr_rdp_peer_context *peer_context =
(struct wlr_rdp_peer_context *)client->context;
peer_context->backend = backend;
client->settings->CertificateFile = strdup(backend->tls_cert_path);
client->settings->PrivateKeyFile = strdup(backend->tls_key_path);
client->settings->NlaSecurity = FALSE;
if (!client->Initialize(client)) {
wlr_log(WLR_ERROR, "Failed to initialize FreeRDP peer");
goto err_init;
}
client->settings->OsMajorType = OSMAJORTYPE_UNIX;
client->settings->OsMinorType = OSMINORTYPE_PSEUDO_XSERVER;
client->settings->ColorDepth = 32;
client->settings->RefreshRect = TRUE;
client->settings->RemoteFxCodec = TRUE;
client->settings->NSCodec = TRUE;
client->settings->FrameMarkerCommandEnabled = TRUE;
client->settings->SurfaceFrameMarkerEnabled = TRUE;
client->Capabilities = xf_peer_capabilities;
client->PostConnect = xf_peer_post_connect;
client->Activate = xf_peer_activate;
client->update->SuppressOutput = (pSuppressOutput)xf_suppress_output;
client->input->SynchronizeEvent = xf_input_synchronize_event;
client->input->MouseEvent = xf_input_mouse_event;
client->input->ExtendedMouseEvent = xf_input_extended_mouse_event;
client->input->KeyboardEvent = xf_input_keyboard_event;
client->input->UnicodeKeyboardEvent = xf_input_unicode_keyboard_event;
int rcount = 0;
void *rfds[MAX_FREERDP_FDS];
if (!client->GetFileDescriptor(client, rfds, &rcount)) {
wlr_log(WLR_ERROR, "Unable to retrieve client file descriptors");
goto err_init;
}
struct wl_event_loop *event_loop =
wl_display_get_event_loop(backend->display);
int i;
for (i = 0; i < rcount; ++i) {
int fd = (int)(long)(rfds[i]);
peer_context->events[i] = wl_event_loop_add_fd(
event_loop, fd, WL_EVENT_READABLE, rdp_client_activity,
client);
}
for (; i < MAX_FREERDP_FDS; ++i) {
peer_context->events[i] = NULL;
}
wl_list_insert(&backend->clients, &peer_context->link);
return 0;
err_init:
client->Close(client);
return -1;
}

40
backend/rdp/pointer.c Normal file
View file

@ -0,0 +1,40 @@
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <wayland-server.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/interfaces/wlr_pointer.h>
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/util/log.h>
#include "backend/rdp.h"
#include "util/signal.h"
static struct wlr_input_device_impl input_device_impl = { 0 };
struct wlr_rdp_input_device *wlr_rdp_pointer_create(
struct wlr_rdp_backend *backend, struct wlr_rdp_peer_context *context) {
struct wlr_rdp_input_device *device =
calloc(1, sizeof(struct wlr_rdp_input_device));
if (!device) {
wlr_log(WLR_ERROR, "Failed to allocate RDP input device");
return NULL;
}
int vendor = 0;
int product = 0;
const char *name = "rdp";
struct wlr_input_device *wlr_device = &device->wlr_input_device;
wlr_input_device_init(wlr_device, WLR_INPUT_DEVICE_POINTER,
&input_device_impl, name, vendor, product);
if (!(wlr_device->pointer = calloc(1, sizeof(struct wlr_pointer)))) {
wlr_log(WLR_ERROR, "Failed to allocate RDP pointer device");
return NULL;
}
wlr_device->output_name = strdup(context->output->wlr_output.name);
wlr_pointer_init(wlr_device->pointer, NULL);
wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_device);
return device;
}

View file

@ -1,7 +1,7 @@
wlroots reads these environment variables wlroots reads these environment variables
wlroots specific # wlroots specific
----------------
* *WLR_DRM_DEVICES*: specifies the DRM devices (as a colon separated list) * *WLR_DRM_DEVICES*: specifies the DRM devices (as a colon separated list)
instead of auto probing them. The first existing device in this list is instead of auto probing them. The first existing device in this list is
considered the primary DRM device. considered the primary DRM device.
@ -12,23 +12,54 @@ wlroots specific
* *WLR_LIBINPUT_NO_DEVICES*: set to 1 to not fail without any input devices * *WLR_LIBINPUT_NO_DEVICES*: set to 1 to not fail without any input devices
* *WLR_BACKENDS*: comma-separated list of backends to use (available backends: * *WLR_BACKENDS*: comma-separated list of backends to use (available backends:
wayland, x11, headless, noop) wayland, x11, headless, noop)
* *WLR_WL_OUTPUTS*: when using the wayland backend specifies the number of outputs
* *WLR_X11_OUTPUTS*: when using the X11 backend specifies the number of outputs
* *WLR_HEADLESS_OUTPUTS*: when using the headless backend specifies the number
of outputs
* *WLR_NO_HARDWARE_CURSORS*: set to 1 to use software cursors instead of * *WLR_NO_HARDWARE_CURSORS*: set to 1 to use software cursors instead of
hardware cursors hardware cursors
* *WLR_SESSION*: specifies the wlr\_session to be used (available sessions: * *WLR_SESSION*: specifies the wlr\_session to be used (available sessions:
logind/systemd, direct) logind/systemd, direct)
* *WLR_DIRECT_TTY*: specifies the tty to be used (instead of using /dev/tty) * *WLR_DIRECT_TTY*: specifies the tty to be used (instead of using /dev/tty)
rootston specific # Headless backend
------------------
* *WLR_HEADLESS_OUTPUTS*: when using the headless backend specifies the number
of outputs
# RDP backend
* *WLR_RDP_TLS_CERT_PATH*: required when using `wlr_backend_autocreate`,
specifies the path to the TLS certificate to use for encrypting connections
* *WLR_RDP_TLS_KEY_PATH*: required when using `wlr_backend_autocreate`,
specifies the path to the TLS private key to use for encrypting connections
* *WLR_RDP_ADDRESS*: the IP address to bind to, defaults to `127.0.0.1`
* *WLR_RDP_PORT*: the port to bind to, defaults to 3389.
Note: a TLS certificate and key can be generated like so:
```
$ openssl genrsa -out tls.key 2048
$ openssl req -new -key tls.key -out tls.csr
$ openssl x509 -req -days 365 -signkey tls.key -in tls.csr -out tls.crt
```
`tls.csr` can be discarded. Connecting to the RDP backend with xfreedrp can be
done like so:
xfreerdp -v localhost --bpp 32 --size 1920x1080 --rfx
# Wayland backend
* *WLR_WL_OUTPUTS*: when using the wayland backend specifies the number of outputs
# X11 backend
* *WLR_X11_OUTPUTS*: when using the X11 backend specifies the number of outputs
# Rootston specific
* *XKB_DEFAULT_RULES*, *XKB_DEFAULT_MODEL*, *XKB_DEFAULT_LAYOUT*, * *XKB_DEFAULT_RULES*, *XKB_DEFAULT_MODEL*, *XKB_DEFAULT_LAYOUT*,
*XKB_DEFAULT_VARIANT*, *XKB_DEFAULT_OPTIONS*: xkb setup *XKB_DEFAULT_VARIANT*, *XKB_DEFAULT_OPTIONS*: xkb setup
generic # Generic
-------
* *DISPLAY*: if set probe X11 backend in *wlr_backend_autocreate* * *DISPLAY*: if set probe X11 backend in *wlr_backend_autocreate*
* *WAYLAND_DISPLAY*, *_WAYLAND_DISPLAY*, *WAYLAND_SOCKET*: if set probe Wayland * *WAYLAND_DISPLAY*, *_WAYLAND_DISPLAY*, *WAYLAND_SOCKET*: if set probe Wayland
backend in *wlr_backend_autocreate* backend in *wlr_backend_autocreate*

98
include/backend/rdp.h Normal file
View file

@ -0,0 +1,98 @@
#ifndef BACKEND_RDP_H
#define BACKEND_RDP_H
#include <freerdp/codec/color.h>
#include <freerdp/codec/nsc.h>
#include <freerdp/codec/rfx.h>
#include <freerdp/freerdp.h>
#include <freerdp/input.h>
#include <freerdp/listener.h>
#include <freerdp/locale/keyboard.h>
#include <freerdp/update.h>
#include <pixman.h>
#include <wlr/backend/interface.h>
#include <wlr/backend/rdp.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_pointer.h>
#include <xkbcommon/xkbcommon.h>
#define MAX_FREERDP_FDS 64
struct wlr_rdp_peer_context;
struct wlr_rdp_output {
struct wlr_output wlr_output;
struct wlr_rdp_backend *backend;
struct wlr_rdp_peer_context *context;
void *egl_surface;
pixman_image_t *shadow_surface;
struct wl_event_source *frame_timer;
int frame_delay; // ms
};
struct wlr_rdp_input_device {
struct wlr_input_device wlr_input_device;
};
struct wlr_rdp_keyboard {
struct wlr_keyboard keyboard;
struct xkb_keymap *keymap;
};
enum wlr_rdp_peer_flags {
RDP_PEER_ACTIVATED = 1 << 0,
RDP_PEER_OUTPUT_ENABLED = 1 << 1,
};
struct wlr_rdp_peer_context {
rdpContext _p;
struct wlr_rdp_backend *backend;
struct wl_event_source *events[MAX_FREERDP_FDS];
freerdp_peer *peer;
uint32_t flags;
RFX_CONTEXT *rfx_context;
wStream *encode_stream;
RFX_RECT *rfx_rects;
NSC_CONTEXT *nsc_context;
struct wlr_rdp_output *output;
struct wlr_rdp_input_device *pointer;
struct wlr_rdp_input_device *keyboard;
struct wl_list link;
};
struct wlr_rdp_backend {
struct wlr_backend backend;
struct wlr_egl egl;
struct wlr_renderer *renderer;
struct wl_display *display;
struct wl_listener display_destroy;
const char *tls_cert_path;
const char *tls_key_path;
char *address;
int port;
freerdp_listener *listener;
struct wl_event_source *listener_events[MAX_FREERDP_FDS];
struct wl_list clients;
};
struct wlr_rdp_backend *rdp_backend_from_backend(
struct wlr_backend *wlr_backend);
bool rdp_configure_listener(struct wlr_rdp_backend *backend);
int rdp_peer_init(freerdp_peer *client, struct wlr_rdp_backend *backend);
struct wlr_rdp_output *wlr_rdp_output_create(struct wlr_rdp_backend *backend,
struct wlr_rdp_peer_context *context, unsigned int width,
unsigned int height);
struct wlr_rdp_input_device *wlr_rdp_pointer_create(
struct wlr_rdp_backend *backend, struct wlr_rdp_peer_context *context);
struct wlr_rdp_input_device *wlr_rdp_keyboard_create(
struct wlr_rdp_backend *backend, rdpSettings *settings);
#endif

31
include/wlr/backend/rdp.h Normal file
View file

@ -0,0 +1,31 @@
/*
* This an unstable interface of wlroots. No guarantees are made regarding the
* future consistency of this API.
*/
#ifndef WLR_USE_UNSTABLE
#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
#endif
#ifndef WLR_BACKEND_RDP_H
#define WLR_BACKEND_RDP_H
#include <wlr/backend.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_output.h>
/**
* Creates an RDP backend. An RDP backend will create one output, keyboard, and
* pointer for each client that connects.
*/
struct wlr_backend *wlr_rdp_backend_create(struct wl_display *display,
wlr_renderer_create_func_t create_renderer_func,
const char *tls_cert_path, const char *tls_key_path);
void wlr_rdp_backend_set_address(struct wlr_backend *wlr_backend,
const char *address);
void wlr_rdp_backend_set_port(struct wlr_backend *wlr_backend, int port);
bool wlr_backend_is_rdp(struct wlr_backend *backend);
bool wlr_input_device_is_rdp(struct wlr_input_device *device);
bool wlr_output_is_rdp(struct wlr_output *output);
#endif

View file

@ -7,6 +7,7 @@
#mesondefine WLR_HAS_ELOGIND #mesondefine WLR_HAS_ELOGIND
#mesondefine WLR_HAS_X11_BACKEND #mesondefine WLR_HAS_X11_BACKEND
#mesondefine WLR_HAS_RDP_BACKEND
#mesondefine WLR_HAS_XWAYLAND #mesondefine WLR_HAS_XWAYLAND

View file

@ -45,6 +45,7 @@ conf_data = configuration_data()
conf_data.set10('WLR_HAS_LIBCAP', false) conf_data.set10('WLR_HAS_LIBCAP', false)
conf_data.set10('WLR_HAS_SYSTEMD', false) conf_data.set10('WLR_HAS_SYSTEMD', false)
conf_data.set10('WLR_HAS_ELOGIND', false) conf_data.set10('WLR_HAS_ELOGIND', false)
conf_data.set10('WLR_HAS_RDP_BACKEND', false)
conf_data.set10('WLR_HAS_X11_BACKEND', false) conf_data.set10('WLR_HAS_X11_BACKEND', false)
conf_data.set10('WLR_HAS_XWAYLAND', false) conf_data.set10('WLR_HAS_XWAYLAND', false)
conf_data.set10('WLR_HAS_XCB_ERRORS', false) conf_data.set10('WLR_HAS_XCB_ERRORS', false)
@ -67,6 +68,8 @@ wayland_client = dependency('wayland-client')
wayland_egl = dependency('wayland-egl') wayland_egl = dependency('wayland-egl')
wayland_protos = dependency('wayland-protocols', version: '>=1.17') wayland_protos = dependency('wayland-protocols', version: '>=1.17')
egl = dependency('egl') egl = dependency('egl')
freerdp = dependency('freerdp2', required: get_option('freerdp'))
winpr2 = dependency('winpr2', required: get_option('freerdp'))
glesv2 = dependency('glesv2') glesv2 = dependency('glesv2')
drm = dependency('libdrm', version: '>=2.4.95') drm = dependency('libdrm', version: '>=2.4.95')
gbm = dependency('gbm', version: '>=17.1.0') gbm = dependency('gbm', version: '>=17.1.0')
@ -156,6 +159,7 @@ summary = [
' systemd: @0@'.format(conf_data.get('WLR_HAS_SYSTEMD', false)), ' systemd: @0@'.format(conf_data.get('WLR_HAS_SYSTEMD', false)),
' elogind: @0@'.format(conf_data.get('WLR_HAS_ELOGIND', false)), ' elogind: @0@'.format(conf_data.get('WLR_HAS_ELOGIND', false)),
' xwayland: @0@'.format(conf_data.get('WLR_HAS_XWAYLAND', false)), ' xwayland: @0@'.format(conf_data.get('WLR_HAS_XWAYLAND', false)),
' rdp_backend: @0@'.format(conf_data.get('WLR_HAS_RDP_BACKEND', false)),
' x11_backend: @0@'.format(conf_data.get('WLR_HAS_X11_BACKEND', false)), ' x11_backend: @0@'.format(conf_data.get('WLR_HAS_X11_BACKEND', false)),
' xcb-icccm: @0@'.format(conf_data.get('WLR_HAS_XCB_ICCCM', false)), ' xcb-icccm: @0@'.format(conf_data.get('WLR_HAS_XCB_ICCCM', false)),
' xcb-errors: @0@'.format(conf_data.get('WLR_HAS_XCB_ERRORS', false)), ' xcb-errors: @0@'.format(conf_data.get('WLR_HAS_XCB_ERRORS', false)),

View file

@ -1,3 +1,4 @@
option('freerdp', type: 'feature', value: 'auto', description: 'Enable support for the RDP backend with freerdp')
option('libcap', type: 'feature', value: 'auto', description: 'Enable support for rootless session via capabilities (cap_sys_admin)') option('libcap', type: 'feature', value: 'auto', description: 'Enable support for rootless session via capabilities (cap_sys_admin)')
option('logind', type: 'feature', value: 'auto', description: 'Enable support for rootless session via logind') option('logind', type: 'feature', value: 'auto', description: 'Enable support for rootless session via logind')
option('logind-provider', type: 'combo', choices: ['systemd', 'elogind'], value: 'systemd', description: 'Provider of logind support library') option('logind-provider', type: 'combo', choices: ['systemd', 'elogind'], value: 'systemd', description: 'Provider of logind support library')