From 7ba6c006ca7f1c329fed348200149e54980f3f4a Mon Sep 17 00:00:00 2001 From: Benjamin Hillis <223556219+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 18:16:10 +0000 Subject: [PATCH 1/8] rdp-backend: port to FreeRDP 3.x server API WSLg has been pinned to a FreeRDP 2.4.0-era fork (microsoft/FreeRDP-mirror) since 2023-08. Port the rdp-backend so it also compiles against upstream FreeRDP 3.x (3.26.0 verified). All edits are wrapped in #if FREERDP_VERSION_MAJOR >= 3 guards so this branch still compiles unchanged against FreeRDP 2. Changes (rdp.c, rdp.h, rdpclip.c, rdprail.c): * RFX_CONTEXT became opaque in 3.x. Replace direct field writes (ctx->mode/width/height) with rfx_context_set_mode() and rfx_context_reset(). * Keyboard input event signature changed from (rdpInput*, UINT16 flags, UINT16 code) to (..., UINT8 code), and KBD_FLAGS_DOWN was removed (the absence of KBD_FLAGS_RELEASE now implicitly means key-down). * GetKeycodeFromVirtualKeyCode now takes WINPR_KEYCODE_TYPE_XKB instead of KEYCODE_TYPE_EVDEV. * TLS server cert/key: settings->{Certificate,PrivateKey}{File,Content} and settings->RdpKeyFile were removed in 3.x. Construct rdpPrivateKey* and rdpCertificate* with freerdp_{key,certificate}_new_from_pem() and apply via freerdp_settings_set_pointer_len(FreeRDP_RdpServer{RsaKey, Certificate}, ..., 1). The 3.x line has no rdp4-style key support, so the file-cert path is gated and currently errors out with a clear message (wslg in production uses the session-generated TLS path via Hyper-V vsock, which is fully implemented). * settings->FastPathInput must be explicitly set TRUE; it was the default in 2.x but is no longer. * rdp_client_activity: WTSVirtualChannelManagerCheckFileDescriptor() asserts in 3.x if drdynvc has not yet joined. Add a WTSVirtualChannelManagerIsChannelJoined(vcm,"drdynvc") guard. * rdp_drdynvc_init: 2.x checked state == DRDYNVC_STATE_NONE before waiting; 3.x has additional intermediate states (e.g. INITIALIZED), so check state != DRDYNVC_STATE_READY instead, plus the same IsChannelJoined guard. * rdpclip.c: CLIPRDR PDU headers now nest under .common -- introduce CLIP_MSG_HEADER(s,m) macro to access .common.msgType / .msgFlags / .dataLen in 3.x while keeping the 2.x layout. MultiByteToWideChar /WideCharToMultiByte are replaced by ConvertUtf8NToWChar /ConvertWCharNToUtf8 (return SSIZE_T, negative on error). * AudioPlayback / AudioCapture are explicitly set FALSE on 3.x with a one-time weston_log warning. The audio_server_context APIs were fully rewritten in 3.x (SendVersion/SendFormats/SendOpen/IncomingData PDU-callback model) and the port lands in a follow-up commit. The libfreerdp-server.so dlopen path is already version-conditional (libfreerdp-server3.so on 3.x). Verified end-to-end: wslg system-distro image builds cleanly against upstream FreeRDP 3.26.0 with -DCHANNEL_GFXREDIR=ON; gfxredir and rdpapplist server context constructors are exported. Audio module port (rdpaudio.c + rdpaudioin.c) tracked separately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- libweston/backend-rdp/rdp.c | 81 ++++++++++++++++++++++++++++-- libweston/backend-rdp/rdp.h | 12 ++++- libweston/backend-rdp/rdpclip.c | 89 +++++++++++++++++++++++++-------- libweston/backend-rdp/rdprail.c | 12 +++++ 4 files changed, 166 insertions(+), 28 deletions(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index f135a186e..42f6acdb2 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -736,9 +736,16 @@ rdp_peer_context_new(freerdp_peer* client, RdpPeerContext* context) if (!context->rfx_context) return FALSE; +#if FREERDP_VERSION_MAJOR >= 3 + rfx_context_set_mode(context->rfx_context, RLGR3); + rfx_context_reset(context->rfx_context, + client->context->settings->DesktopWidth, + client->context->settings->DesktopHeight); +#else context->rfx_context->mode = RLGR3; context->rfx_context->width = client->context->settings->DesktopWidth; context->rfx_context->height = client->context->settings->DesktopHeight; +#endif rfx_context_set_pixel_format(context->rfx_context, DEFAULT_PIXEL_FORMAT); context->nsc_context = nsc_context_new(); @@ -839,6 +846,11 @@ rdp_client_activity(int fd, uint32_t mask, void *data) if (peerCtx && peerCtx->vcm) { +#if FREERDP_VERSION_MAJOR >= 3 + /* FreeRDP 3 asserts on calling CheckFileDescriptor before drdynvc joined */ + if (!WTSVirtualChannelManagerIsChannelJoined(peerCtx->vcm, "drdynvc")) + return 0; +#endif if (!WTSVirtualChannelManagerCheckFileDescriptor(peerCtx->vcm)) { rdp_debug_error(rdpBackend, "failed to check FreeRDP WTS VC file descriptor for %p\n", client); goto out_clean; @@ -1113,8 +1125,24 @@ xf_peer_activate(freerdp_peer* client) /* override settings by env variables */ settings->RedirectClipboard = b->redirect_clipboard; +#if FREERDP_VERSION_MAJOR >= 3 + /* TODO: rdpsnd/audin server-side context APIs were rewritten in FreeRDP 3.x to a + * PDU-callback model (SendVersion/SendFormats/SendOpen/IncomingData...). The wslg + * port has not been completed yet, so disable audio negotiation on FreeRDP 3 to + * avoid advertising a capability we cannot service. */ + { + static bool warned; + if (!warned) { + weston_log("RDP audio playback/capture disabled: FreeRDP 3.x port pending\n"); + warned = true; + } + } + settings->AudioPlayback = FALSE; + settings->AudioCapture = FALSE; +#else settings->AudioPlayback = b->audio_out_setup && b->audio_out_teardown; settings->AudioCapture = b->audio_in_setup && b->audio_in_teardown; +#endif if (settings->RemoteApplicationMode || settings->RedirectClipboard || @@ -1582,7 +1610,11 @@ xf_input_synchronize_event(rdpInput *input, UINT32 flags) } static BOOL +#if FREERDP_VERSION_MAJOR >= 3 +xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT8 code) +#else xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code) +#endif { uint32_t scan_code, vk_code, full_code, keyboard_locale; enum wl_keyboard_key_state keyState; @@ -1599,12 +1631,17 @@ xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code) if (!(peerContext->item.flags & RDP_PEER_ACTIVATED)) return TRUE; - if (flags & KBD_FLAGS_DOWN) { - keyState = WL_KEYBOARD_KEY_STATE_PRESSED; - notify = 1; - } else if (flags & KBD_FLAGS_RELEASE) { + if (flags & KBD_FLAGS_RELEASE) { keyState = WL_KEYBOARD_KEY_STATE_RELEASED; notify = 1; + } else { +#if FREERDP_VERSION_MAJOR < 3 + /* KBD_FLAGS_DOWN was removed in FreeRDP 3 — absence of RELEASE + * implicitly means key-down. Keep the old assertion for FreeRDP 2. */ + assert(flags & KBD_FLAGS_DOWN); +#endif + keyState = WL_KEYBOARD_KEY_STATE_PRESSED; + notify = 1; } if (keyboard && notify) { @@ -1653,7 +1690,11 @@ xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code) if (flags & KBD_FLAGS_EXTENDED) vk_code |= KBDEXT; +#if FREERDP_VERSION_MAJOR >= 3 + scan_code = GetKeycodeFromVirtualKeyCode(vk_code, WINPR_KEYCODE_TYPE_XKB); +#else scan_code = GetKeycodeFromVirtualKeyCode(vk_code, KEYCODE_TYPE_EVDEV); +#endif /*weston_log("code=%x ext=%d vk_code=%x scan_code=%x\n", code, (flags & KBD_FLAGS_EXTENDED) ? 1 : 0, vk_code, scan_code);*/ @@ -1822,6 +1863,36 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) settings = client->context->settings; /* configure security settings */ +#if FREERDP_VERSION_MAJOR >= 3 + /* The legacy "rdp4" key path is gone in FreeRDP 3.x; only TLS is supported. */ + if (is_tls_enabled(b)) { + if (using_session_tls(b)) { + rdpPrivateKey *key = freerdp_key_new_from_pem(b->server_key_content); + rdpCertificate *cert = freerdp_certificate_new_from_pem(b->server_cert_content); + if (!key || !cert) { + rdp_debug_error(b, "failed to construct PEM cert/key for RDP TLS\n"); + goto error_initialize; + } + if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerRsaKey, key, 1) || + !freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerCertificate, cert, 1)) { + rdp_debug_error(b, "failed to apply RDP server PEM cert/key\n"); + goto error_initialize; + } + } else { + /* WSLg in production uses the session-generated TLS path via Hyper-V + * vsock (CertificateContent + PrivateKeyContent from the host). The + * file-cert path used to be supported via settings->{Certificate,PrivateKey}File + * but those fields were removed in FreeRDP 3.x. Loading from disk and + * calling freerdp_{key,certificate}_new_from_pem() is left for a follow-up + * once a real consumer for the file-cert path exists. */ + rdp_debug_error(b, "FreeRDP 3.x file-based cert/key path is not yet implemented; use session-generated TLS or run against FreeRDP 2.x\n"); + goto error_initialize; + } + } else { + freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE); + } + freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE); +#else if (b->rdp_key) settings->RdpKeyFile = strdup(b->rdp_key); if (is_tls_enabled(b)) { @@ -1836,6 +1907,7 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) settings->TlsSecurity = FALSE; } settings->NlaSecurity = FALSE; +#endif if (!client->Initialize(client)) { rdp_debug_error(b, "peer initialization failed\n"); @@ -1862,6 +1934,7 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) settings->RedirectClipboard = TRUE; settings->HasExtendedMouseEvent = TRUE; settings->HasHorizontalWheel = TRUE; + settings->FastPathInput = TRUE; client->Capabilities = xf_peer_capabilities; client->PostConnect = xf_peer_post_connect; diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h index cb407bdbf..becc1bcca 100644 --- a/libweston/backend-rdp/rdp.h +++ b/libweston/backend-rdp/rdp.h @@ -28,6 +28,14 @@ #include +#if FREERDP_VERSION_MAJOR >= 3 +/* Temporary workaround for SETTINGS_DEPRECATED attribute warnings. + * Allows the rdp-backend to keep using direct struct member access on + * rdpSettings while the migration to freerdp_settings_get/set_* accessors + * is performed file-by-file. Remove once all direct accesses are gone. */ +#define FREERDP_SETTINGS_INTERNAL_USE +#endif + #include #include #include @@ -144,7 +152,7 @@ struct rdp_backend { struct weston_surface *proxy_surface; #ifdef HAVE_FREERDP_RDPAPPLIST_H - /* import from libfreerdp-server2.so */ + /* import from librdpapplist-server.so (built from wslg/rdpapplist/) */ RdpAppListServerContext *(*rdpapplist_server_context_new)(HANDLE vcm); void (*rdpapplist_server_context_free)(RdpAppListServerContext* context); @@ -153,7 +161,7 @@ struct rdp_backend { #endif // HAVE_FREERDP_RDPAPPLIST_H #ifdef HAVE_FREERDP_GFXREDIR_H - /* import from libfreerdp-server2.so */ + /* import from libfreerdp-server{2,3}.so */ GfxRedirServerContext *(*gfxredir_server_context_new)(HANDLE vcm); void (*gfxredir_server_context_free)(GfxRedirServerContext* context); diff --git a/libweston/backend-rdp/rdpclip.c b/libweston/backend-rdp/rdpclip.c index d7ab56232..ece7ba7cf 100644 --- a/libweston/backend-rdp/rdpclip.c +++ b/libweston/backend-rdp/rdpclip.c @@ -39,6 +39,16 @@ #include "libweston-internal.h" +#if FREERDP_VERSION_MAJOR >= 3 +/* In FreeRDP 3 the CLIPRDR_* PDU structs nest the common header (msgType, + * msgFlags, dataLen) under a `.common` sub-struct. This macro lets the + * existing access pattern keep compiling against both 2.x and 3.x without + * rewriting every site. */ +#define CLIP_MSG_HEADER(s,m) (s).common.m +#else +#define CLIP_MSG_HEADER(s,m) (s).m +#endif + /* From MSDN, RegisterClipboardFormat API. Registered clipboard formats are identified by values in the range 0xC000 through 0xFFFF. */ #define CF_PRIVATE_RTF 49309 /* fake format ID for "Rich Text Format". */ @@ -185,7 +195,12 @@ clipboard_process_text_utf8(struct rdp_clipboard_data_source *source, bool is_se if (is_send) { char *data = source->data_contents.data; +#if FREERDP_VERSION_MAJOR >= 3 + /* WinPR's Convert*N functions return SSIZE_T (-1 on error) */ + SSIZE_T data_size, data_size_in_char; +#else size_t data_size, data_size_in_char; +#endif /* Linux to Windows (convert utf-8 to UNICODE) */ /* Include terminating NULL in size */ @@ -194,10 +209,16 @@ clipboard_process_text_utf8(struct rdp_clipboard_data_source *source, bool is_se source->data_contents.size++; /* obtain size in UNICODE */ +#if FREERDP_VERSION_MAJOR >= 3 + data_size = ConvertUtf8NToWChar(data, + source->data_contents.size, + NULL, 0); +#else data_size = MultiByteToWideChar(CP_UTF8, 0, data, source->data_contents.size, NULL, 0); +#endif if (data_size < 1) goto error_return; @@ -206,17 +227,28 @@ clipboard_process_text_utf8(struct rdp_clipboard_data_source *source, bool is_se goto error_return; /* convert to UNICODE */ +#if FREERDP_VERSION_MAJOR >= 3 + data_size_in_char = ConvertUtf8NToWChar(data, + source->data_contents.size, + data_contents.data, + data_size); +#else data_size_in_char = MultiByteToWideChar(CP_UTF8, 0, data, source->data_contents.size, data_contents.data, data_size); - assert(data_contents.size == (data_size_in_char * 2)); +#endif + assert(data_contents.size == (size_t)(data_size_in_char * 2)); } else { /* Windows to Linux (UNICODE to utf-8) */ - size_t data_size; LPWSTR data = source->data_contents.data; +#if FREERDP_VERSION_MAJOR >= 3 + SSIZE_T data_size, data_size_in_char = source->data_contents.size / 2; +#else + size_t data_size; size_t data_size_in_char = source->data_contents.size / 2; +#endif /* Windows's data has trailing chars, which Linux doesn't expect. */ while (data_size_in_char && @@ -226,11 +258,17 @@ clipboard_process_text_utf8(struct rdp_clipboard_data_source *source, bool is_se goto error_return; /* obtain size in utf-8 */ +#if FREERDP_VERSION_MAJOR >= 3 + data_size = ConvertWCharNToUtf8(source->data_contents.data, + data_size_in_char, + NULL, 0); +#else data_size = WideCharToMultiByte(CP_UTF8, 0, source->data_contents.data, data_size_in_char, NULL, 0, NULL, NULL); +#endif if (data_size < 1) goto error_return; @@ -238,13 +276,20 @@ clipboard_process_text_utf8(struct rdp_clipboard_data_source *source, bool is_se goto error_return; /* convert to utf-8 */ +#if FREERDP_VERSION_MAJOR >= 3 + data_size = ConvertWCharNToUtf8(source->data_contents.data, + data_size_in_char, + data_contents.data, + data_size); +#else data_size = WideCharToMultiByte(CP_UTF8, 0, source->data_contents.data, data_size_in_char, data_contents.data, data_size, NULL, NULL); - if (data_contents.size != data_size) +#endif + if (data_contents.size != (size_t)data_size) goto error_return; } @@ -791,9 +836,9 @@ clipboard_client_send_format_data_response(RdpPeerContext *ctx, struct rdp_clipb clipboard_supported_formats[source->format_index].mime_type, source->processed_data_size); - formatDataResponse.msgType = CB_FORMAT_DATA_RESPONSE; - formatDataResponse.msgFlags = CB_RESPONSE_OK; - formatDataResponse.dataLen = source->processed_data_size; + CLIP_MSG_HEADER(formatDataResponse, msgType) = CB_FORMAT_DATA_RESPONSE; + CLIP_MSG_HEADER(formatDataResponse, msgFlags) = CB_RESPONSE_OK; + CLIP_MSG_HEADER(formatDataResponse, dataLen) = source->processed_data_size; formatDataResponse.requestedFormatData = source->processed_data_start; ctx->clipboard_server_context->ServerFormatDataResponse(ctx->clipboard_server_context, &formatDataResponse); /* if here failed to send response, what can we do ? */ @@ -815,9 +860,9 @@ clipboard_client_send_format_data_response_fail(RdpPeerContext *ctx, struct rdp_ source->data_response_fail_count++; } - formatDataResponse.msgType = CB_FORMAT_DATA_RESPONSE; - formatDataResponse.msgFlags = CB_RESPONSE_FAIL; - formatDataResponse.dataLen = 0; + CLIP_MSG_HEADER(formatDataResponse, msgType) = CB_FORMAT_DATA_RESPONSE; + CLIP_MSG_HEADER(formatDataResponse, msgFlags) = CB_RESPONSE_FAIL; + CLIP_MSG_HEADER(formatDataResponse, dataLen) = 0; formatDataResponse.requestedFormatData = NULL; ctx->clipboard_server_context->ServerFormatDataResponse(ctx->clipboard_server_context, &formatDataResponse); /* if here failed to send response, what can we do ? */ @@ -1166,8 +1211,8 @@ clipboard_data_source_send(struct weston_data_source *base, /* update requesting format property */ source->format_index = index; /* request clipboard data from client */ - formatDataRequest.msgType = CB_FORMAT_DATA_REQUEST; - formatDataRequest.dataLen = 4; + CLIP_MSG_HEADER(formatDataRequest, msgType) = CB_FORMAT_DATA_REQUEST; + CLIP_MSG_HEADER(formatDataRequest, dataLen) = 4; formatDataRequest.requestedFormatId = source->client_format_id_table[index]; source->state = RDP_CLIPBOARD_SOURCE_REQUEST_DATA; rdp_debug_clipboard(b, "RDP %s (%p:%s) request data \"%s\" index:%d formatId:%d %s\n", @@ -1457,7 +1502,7 @@ clipboard_set_selection(struct wl_listener *listener, void *data) if (num_supported_format) { /* let client knows formats are available in server clipboard */ - formatList.msgType = CB_FORMAT_LIST; + CLIP_MSG_HEADER(formatList, msgType) = CB_FORMAT_LIST; formatList.numFormats = num_supported_format; formatList.formats = &format[0]; ctx->clipboard_server_context->ServerFormatList(ctx->clipboard_server_context, &formatList); @@ -1595,9 +1640,9 @@ clipboard_client_format_list(CliprdrServerContext *context, const CLIPRDR_FORMAT rdp_dispatch_task_to_display_loop(ctx, clipboard_data_source_publish, &source->task_base); fail: - formatListResponse.msgType = CB_FORMAT_LIST_RESPONSE; - formatListResponse.msgFlags = source ? CB_RESPONSE_OK : CB_RESPONSE_FAIL; - formatListResponse.dataLen = 0; + CLIP_MSG_HEADER(formatListResponse, msgType) = CB_FORMAT_LIST_RESPONSE; + CLIP_MSG_HEADER(formatListResponse, msgFlags) = source ? CB_RESPONSE_OK : CB_RESPONSE_FAIL; + CLIP_MSG_HEADER(formatListResponse, dataLen) = 0; if (ctx->clipboard_server_context->ServerFormatListResponse(ctx->clipboard_server_context, &formatListResponse) != 0) { source->state = RDP_CLIPBOARD_SOURCE_FAILED; weston_log("Client: %s (%p:%s) ServerFormatListResponse failed\n", @@ -1623,8 +1668,8 @@ clipboard_client_format_data_response(CliprdrServerContext *context, const CLIPR rdp_debug_clipboard(b, "Client: %s (%p:%s) flags:%d dataLen:%d\n", __func__, source, clipboard_data_source_state_to_string(source), - formatDataResponse->msgFlags, - formatDataResponse->dataLen); + CLIP_MSG_HEADER(*formatDataResponse, msgFlags), + CLIP_MSG_HEADER(*formatDataResponse, dataLen)); assert_not_compositor_thread(b); @@ -1642,13 +1687,13 @@ clipboard_client_format_data_response(CliprdrServerContext *context, const CLIPR return -1; } - if (formatDataResponse->msgFlags == CB_RESPONSE_OK) { + if (CLIP_MSG_HEADER(*formatDataResponse, msgFlags) == CB_RESPONSE_OK) { /* Recieved data from client, cache to data source */ - if (wl_array_add(&source->data_contents, formatDataResponse->dataLen+1)) { + if (wl_array_add(&source->data_contents, CLIP_MSG_HEADER(*formatDataResponse, dataLen)+1)) { memcpy(source->data_contents.data, formatDataResponse->requestedFormatData, - formatDataResponse->dataLen); - source->data_contents.size = formatDataResponse->dataLen; + CLIP_MSG_HEADER(*formatDataResponse, dataLen)); + source->data_contents.size = CLIP_MSG_HEADER(*formatDataResponse, dataLen); /* regardless data type, make sure it ends with NULL */ ((char *)source->data_contents.data)[source->data_contents.size] = '\0'; /* data is ready, waiting to be written to destination */ @@ -1693,7 +1738,7 @@ clipboard_client_format_list_response(CliprdrServerContext *context, RdpPeerContext *ctx = (RdpPeerContext *)client->context; struct rdp_backend *b = ctx->rdpBackend; - rdp_debug_clipboard(b, "Client: %s msgFlags:0x%x\n", __func__, formatListResponse->msgFlags); + rdp_debug_clipboard(b, "Client: %s msgFlags:0x%x\n", __func__, CLIP_MSG_HEADER(*formatListResponse, msgFlags)); assert_not_compositor_thread(b); return 0; } diff --git a/libweston/backend-rdp/rdprail.c b/libweston/backend-rdp/rdprail.c index de9c03cdb..857ee5007 100644 --- a/libweston/backend-rdp/rdprail.c +++ b/libweston/backend-rdp/rdprail.c @@ -4076,7 +4076,13 @@ rdp_drdynvc_init(freerdp_peer *client) peer_ctx->drdynvc_server_context = vc_ctx; /* Force Dynamic virtual channel to exchange caps */ +#if FREERDP_VERSION_MAJOR >= 3 + /* In FreeRDP 3 the initial drdynvc state may be other than NONE before + * READY is reached (e.g. INITIALIZED); compare against READY directly. */ + if (WTSVirtualChannelManagerGetDrdynvcState(peer_ctx->vcm) != DRDYNVC_STATE_READY) { +#else if (WTSVirtualChannelManagerGetDrdynvcState(peer_ctx->vcm) == DRDYNVC_STATE_NONE) { +#endif int waitRetry = 0; client->activated = TRUE; @@ -4088,7 +4094,13 @@ rdp_drdynvc_init(freerdp_peer *client) } usleep(10000); /* wait 0.01 sec. */ client->CheckFileDescriptor(client); +#if FREERDP_VERSION_MAJOR >= 3 + /* FreeRDP 3 asserts on calling CheckFileDescriptor before drdynvc has joined */ + if (WTSVirtualChannelManagerIsChannelJoined(peer_ctx->vcm, "drdynvc")) + WTSVirtualChannelManagerCheckFileDescriptor(peer_ctx->vcm); +#else WTSVirtualChannelManagerCheckFileDescriptor(peer_ctx->vcm); +#endif } } From 2ee5dca27d90ee25ed3731ac354478e1e3482cb7 Mon Sep 17 00:00:00 2001 From: Benjamin Hillis <223556219+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 18:16:21 +0000 Subject: [PATCH 2/8] compositor: stub rdp audio modules for FreeRDP 3.x build Wrap rdpaudio.c and rdpaudioin.c implementations with #if FREERDP_VERSION_MAJOR < 3 so the wslg compositor can link against FreeRDP 3.x without a full audio port. On FreeRDP 3, rdp_audio_{out,in} _init() return NULL (treated by the rdp-backend as "continue without audio") and rdp_audio_{out,in}_destroy() are no-ops. The audin_server_context and rdpsnd_server_context APIs were rewritten in FreeRDP 3.x: the field-based model (Activated, ConfirmBlock, {num_,}server_formats, src_format, dst_format, frames_per_packet, Opening, OpenResult, ReceiveSamples, data, Initialize) was replaced by a PDU-callback model (SendVersion, SendFormats, SendOpen, SendFormatChange, Open, Close, IsOpen, plus ChannelIdAssigned and IncomingData callbacks). Porting the full state machine is a substantial rewrite tracked as a follow-up. The companion change in libweston/backend-rdp/rdp.c explicitly sets settings->AudioPlayback = FALSE and settings->AudioCapture = FALSE on FreeRDP 3, so the protocol does not advertise an audio capability the server cannot service. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- compositor/rdpaudio.c | 23 +++++++++++++++++++++++ compositor/rdpaudioin.c | 22 ++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/compositor/rdpaudio.c b/compositor/rdpaudio.c index bfb7638a2..70e67c6b5 100644 --- a/compositor/rdpaudio.c +++ b/compositor/rdpaudio.c @@ -41,9 +41,13 @@ #include #include +#include + +#if FREERDP_VERSION_MAJOR < 3 static AUDIO_FORMAT rdp_audio_supported_audio_formats[] = { { WAVE_FORMAT_PCM, 2, 44100, 176400, 4, 16, 0, NULL }, }; +#endif #define AUDIO_LATENCY 5 #define AUDIO_FRAMES_PER_RDP_PACKET (44100 * AUDIO_LATENCY / 1000) @@ -67,6 +71,7 @@ typedef struct _rdp_audio_cmd_header { }; } rdp_audio_cmd_header; +#if FREERDP_VERSION_MAJOR < 3 static char* AUDIO_FORMAT_to_String(UINT16 format) { @@ -669,10 +674,21 @@ rdp_audio_client_activated(RdpsndServerContext* context) weston_log("RDPAudio - No agreeded format.\n"); } } +#endif /* FREERDP_VERSION_MAJOR < 3 */ void * rdp_audio_out_init(struct weston_compositor *c, HANDLE vcm) { +#if FREERDP_VERSION_MAJOR >= 3 + /* TODO: Port to the FreeRDP 3.x RdpsndServerContext API. The 2.x server + * API used here (num_server_formats, server_formats, Activated callback, + * src_format, ConfirmBlock, etc.) was fully replaced in 3.x. Returning + * NULL is the supported "audio output unavailable" sentinel; the + * compositor will continue without RDP audio playback. */ + (void)c; + (void)vcm; + return NULL; +#else struct audio_out_private *priv; char *s; @@ -746,11 +762,17 @@ rdp_audio_out_init(struct weston_compositor *c, HANDLE vcm) free(priv); return NULL; +#endif /* FREERDP_VERSION_MAJOR >= 3 */ } void rdp_audio_out_destroy(void *audio_out_private) { +#if FREERDP_VERSION_MAJOR >= 3 + /* Paired with the NULL-returning init: nothing to tear down. */ + (void)audio_out_private; + return; +#else struct audio_out_private *priv = audio_out_private; if (priv->rdpsnd_server_context) { @@ -796,4 +818,5 @@ rdp_audio_out_destroy(void *audio_out_private) priv->rdpsnd_server_context = NULL; } free(priv); +#endif /* FREERDP_VERSION_MAJOR >= 3 */ } diff --git a/compositor/rdpaudioin.c b/compositor/rdpaudioin.c index af367705f..8891387f6 100644 --- a/compositor/rdpaudioin.c +++ b/compositor/rdpaudioin.c @@ -42,6 +42,9 @@ #include #include +#include + +#if FREERDP_VERSION_MAJOR < 3 static AUDIO_FORMAT rdp_audioin_supported_audio_formats[] = { { WAVE_FORMAT_PCM, 1, 44100, 88200, 2, 16, 0, NULL }, }; @@ -462,10 +465,22 @@ rdp_audioin_source_thread(void *context) return NULL; } +#endif /* FREERDP_VERSION_MAJOR < 3 */ void * rdp_audio_in_init(struct weston_compositor *c, HANDLE vcm) { +#if FREERDP_VERSION_MAJOR >= 3 + /* TODO: Port to the FreeRDP 3.x audin_server_context PDU-based API + * (SendVersion/SendFormats/SendOpen/SendFormatChange + callbacks). + * The 2.x context members (num_server_formats, server_formats, + * Opening, OpenResult, ReceiveSamples, dst_format, frames_per_packet) + * have all been removed. NULL signals "no audio in" which the + * compositor handles gracefully. */ + (void)c; + (void)vcm; + return NULL; +#else struct audio_in_private *priv; priv = xzalloc(sizeof *priv); @@ -541,11 +556,17 @@ rdp_audio_in_init(struct weston_compositor *c, HANDLE vcm) free(priv); return NULL; // Continue without audio +#endif /* FREERDP_VERSION_MAJOR >= 3 */ } void rdp_audio_in_destroy(void *audio_in_private) { +#if FREERDP_VERSION_MAJOR >= 3 + /* Paired with the NULL-returning init: nothing to tear down. */ + (void)audio_in_private; + return; +#else struct audio_in_private *priv = audio_in_private; if (priv->audin_server_context) { @@ -577,4 +598,5 @@ rdp_audio_in_destroy(void *audio_in_private) priv->audin_server_context = NULL; } free(priv); +#endif /* FREERDP_VERSION_MAJOR >= 3 */ } From e8e58d1616ebad8288a9c26d9dccc56b2829e243 Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Tue, 19 May 2026 19:34:13 +0000 Subject: [PATCH 3/8] rdp-backend: implement file-based TLS cert/key on FreeRDP 3.x Previously the FreeRDP 3.x file-cert path was a TODO no-op that erred out. Implement it using freerdp_{certificate,key}_new_from_file() which exist alongside the *_new_from_pem() variants in 3.x. Loading order: 1. Try whichever source (PEM content from session-TLS, or file path from --rdp-tls-cert/--rdp-tls-key) is configured. 2. Wrap into the FreeRDP-3 settings via FreeRDP_RdpServer{RsaKey,Certificate}. 3. Free key/cert on any failure path to avoid leaks before error_initialize falls through. Verified via Linux loopback smoke test that file-based cert/key loads cleanly and TLS support activates against FreeRDP 3.26.0. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- libweston/backend-rdp/rdp.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 42f6acdb2..09d56ff72 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -1866,26 +1866,26 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) #if FREERDP_VERSION_MAJOR >= 3 /* The legacy "rdp4" key path is gone in FreeRDP 3.x; only TLS is supported. */ if (is_tls_enabled(b)) { + rdpPrivateKey *key = NULL; + rdpCertificate *cert = NULL; if (using_session_tls(b)) { - rdpPrivateKey *key = freerdp_key_new_from_pem(b->server_key_content); - rdpCertificate *cert = freerdp_certificate_new_from_pem(b->server_cert_content); - if (!key || !cert) { - rdp_debug_error(b, "failed to construct PEM cert/key for RDP TLS\n"); - goto error_initialize; - } - if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerRsaKey, key, 1) || - !freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerCertificate, cert, 1)) { - rdp_debug_error(b, "failed to apply RDP server PEM cert/key\n"); - goto error_initialize; - } + key = freerdp_key_new_from_pem(b->server_key_content); + cert = freerdp_certificate_new_from_pem(b->server_cert_content); } else { - /* WSLg in production uses the session-generated TLS path via Hyper-V - * vsock (CertificateContent + PrivateKeyContent from the host). The - * file-cert path used to be supported via settings->{Certificate,PrivateKey}File - * but those fields were removed in FreeRDP 3.x. Loading from disk and - * calling freerdp_{key,certificate}_new_from_pem() is left for a follow-up - * once a real consumer for the file-cert path exists. */ - rdp_debug_error(b, "FreeRDP 3.x file-based cert/key path is not yet implemented; use session-generated TLS or run against FreeRDP 2.x\n"); + key = freerdp_key_new_from_file(b->server_key); + cert = freerdp_certificate_new_from_file(b->server_cert); + } + if (!key || !cert) { + rdp_debug_error(b, "failed to load RDP server TLS cert/key\n"); + freerdp_key_free(key); + freerdp_certificate_free(cert); + goto error_initialize; + } + if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerRsaKey, key, 1) || + !freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerCertificate, cert, 1)) { + rdp_debug_error(b, "failed to apply RDP server PEM cert/key\n"); + freerdp_key_free(key); + freerdp_certificate_free(cert); goto error_initialize; } } else { From 9cf2d6a639dcd7da9934848d8792ac2fc8931d3c Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Tue, 19 May 2026 20:28:27 +0000 Subject: [PATCH 4/8] rdp-backend: disable new FreeRDP 3.x server-side connect-time steps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FreeRDP 3.x added two unconditional connect-time steps to the server state machine that did not exist in 2.x and that WSLg's rdp-backend never instrumented: 1. NetworkAutoDetect — SECURE_SETTINGS_EXCHANGE goes through CONNECT_TIME_AUTO_DETECT_REQUEST/RESPONSE before LICENSING, with the server sending AUTODETECT_REQ and waiting for a response. The 3.x default for FreeRDP_NetworkAutoDetect is TRUE so this step is always taken. WSLg does not register autodetect callbacks, so the server gets stuck waiting. 2. SupportMultitransport (with the UDPFECR flag) — after LICENSING the server emits a SEC_TRANSPORT_REQ on the message channel before transitioning to CAPABILITIES_EXCHANGE. The 3.x defaults for FreeRDP_SupportMultitransport (TRUE) and FreeRDP_MultitransportFlags (TRANSPORT_TYPE_UDP_FECR) both engage this step. WSLg runs over TCP-only Hyper-V vsock with no UDP path, so the multitransport bootstrap is unusable here. Force both off in rdp_peer_init so the 3.x state machine collapses back to the 2.x topology of SECURE_SETTINGS_EXCHANGE -> LICENSING -> CAPABILITIES_EXCHANGE. No-op on 2.x (settings exist but are ignored at connect-time on that branch). Verified via TRACE-level WLog: with these settings the server log shows CONNECT_TIME_AUTO_DETECT_REQUEST -> LICENSING transitioning in a single tick, and LICENSING -> CAPABILITIES_EXCHANGE_DEMAND_ACTIVE following without a transport-request being emitted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- libweston/backend-rdp/rdp.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 09d56ff72..4bc649f9e 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -1936,6 +1936,30 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) settings->HasHorizontalWheel = TRUE; settings->FastPathInput = TRUE; + /* FreeRDP 3.x added a connect-time NetworkAutoDetect step to the server + * state machine that did not exist in 2.x: SECURE_SETTINGS_EXCHANGE -> + * CONNECT_TIME_AUTO_DETECT_REQUEST -> CONNECT_TIME_AUTO_DETECT_RESPONSE + * -> LICENSING. Leaving the 3.x default (TRUE) causes the server to send + * AUTODETECT_REQ and wait for a response that WSLg's rdp-backend never + * services (we don't register autodetect callbacks). WSLg always runs + * over a local Hyper-V vsock, so bandwidth/RTT autodetect has no value + * here. Force FALSE so the state machine skips straight to LICENSING, + * matching the FreeRDP 2.x behavior we relied on. No-op on 2.x (which + * ignored the setting at connect-time anyway). */ + settings->NetworkAutoDetect = FALSE; + + /* Likewise FreeRDP 3.x server unconditionally walks an extra + * MULTITRANSPORT bootstrap step (LICENSING -> + * MULTITRANSPORT_BOOTSTRAPPING_REQUEST -> CAPABILITIES_EXCHANGE) that did + * not exist on the 2.x server. When SupportMultitransport+UDPFECR are + * both set (3.x defaults) the server sends a SEC_TRANSPORT_REQ on the + * message channel right after the license PDU. UDP multitransport carries + * no benefit on WSLg's TCP-only vsock/loopback transport, so disable it: + * the server then transitions straight to CAPABILITIES_EXCHANGE without + * emitting a transport request. No-op on 2.x. */ + settings->SupportMultitransport = FALSE; + settings->MultitransportFlags = 0; + client->Capabilities = xf_peer_capabilities; client->PostConnect = xf_peer_post_connect; client->Activate = xf_peer_activate; From e758643f9606ac739274a0d86f8c78e0a90c5b7a Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Tue, 19 May 2026 21:07:07 +0000 Subject: [PATCH 5/8] rdp-backend: migrate to freerdp_settings_get/set_* accessors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace 128 direct `settings->FieldName` reads/writes across rdp.c, rdpdisp.c and rdprail.c with the canonical `freerdp_settings_get_*` and `freerdp_settings_set_*` accessor functions, and drop the FREERDP_SETTINGS_INTERNAL_USE workaround that was suppressing the SETTINGS_DEPRECATED warnings while the migration was in flight. - Reads: `expr->settings->X` → `freerdp_settings_get_TYPE(expr->settings, FreeRDP_X)` - Writes: `expr->settings->X = V;` → `(void)freerdp_settings_set_TYPE(expr->settings, FreeRDP_X, V);` - MonitorDefArray pointer: `settings->MonitorDefArray` → `freerdp_settings_get_pointer(settings, FreeRDP_MonitorDefArray)` The 5 remaining `settings->X` accesses in rdp.c (RdpKeyFile, {Certificate,PrivateKey}{File,Content}) are inside the `#if FREERDP_VERSION_MAJOR < 3` else-branch and reference fields that exist only in 2.x's rdpSettings struct, so they are intentionally left alone — 2.x has no SETTINGS_DEPRECATED attribute and the 3.x build never sees them. The accessor APIs exist in both FreeRDP 2.x and 3.x with compatible signatures (the 2.x version takes `size_t id` whereas 3.x takes a typed enum, but both accept the `FreeRDP_` enum constants), so this change compiles cleanly under either FreeRDP major version. Verified end-to-end Docker build succeeds against FreeRDP 3.26.0 with zero SETTINGS_DEPRECATED warnings, and the rdp-backend.so module loads and initializes correctly (TLS support active, head added, output enabled, plugin APIs registered). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- libweston/backend-rdp/rdp.c | 218 ++++++++++++++++---------------- libweston/backend-rdp/rdp.h | 8 -- libweston/backend-rdp/rdpdisp.c | 4 +- libweston/backend-rdp/rdprail.c | 38 +++--- 4 files changed, 130 insertions(+), 138 deletions(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 4bc649f9e..63dd8ab1b 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -114,7 +114,7 @@ rdp_peer_refresh_rfx(pixman_region32_t *damage, pixman_image_t *image, freerdp_p cmd.destRight = damage->extents.x2; cmd.destBottom = damage->extents.y2; cmd.bmp.bpp = 32; - cmd.bmp.codecID = peer->context->settings->RemoteFxCodecId; + cmd.bmp.codecID = freerdp_settings_get_uint32(peer->context->settings, FreeRDP_RemoteFxCodecId); cmd.bmp.width = width; cmd.bmp.height = height; @@ -168,7 +168,7 @@ rdp_peer_refresh_nsc(pixman_region32_t *damage, pixman_image_t *image, freerdp_p cmd.destRight = damage->extents.x2; cmd.destBottom = damage->extents.y2; cmd.bmp.bpp = 32; - cmd.bmp.codecID = peer->context->settings->NSCodecId; + cmd.bmp.codecID = freerdp_settings_get_uint32(peer->context->settings, FreeRDP_NSCodecId); cmd.bmp.width = width; cmd.bmp.height = height; @@ -227,7 +227,7 @@ rdp_peer_refresh_raw(pixman_region32_t *region, pixman_image_t *image, freerdp_p cmd.destRight = rect->x2; cmd.bmp.width = rect->x2 - rect->x1; - heightIncrement = peer->context->settings->MultifragMaxRequestSize / (16 + cmd.bmp.width * 4); + heightIncrement = freerdp_settings_get_uint32(peer->context->settings, FreeRDP_MultifragMaxRequestSize) / (16 + cmd.bmp.width * 4); remainingHeight = rect->y2 - rect->y1; top = rect->y1; @@ -266,9 +266,9 @@ rdp_peer_refresh_region(pixman_region32_t *region, freerdp_peer *peer) struct rdp_output *output = rdp_get_first_output(context->rdpBackend); rdpSettings *settings = peer->context->settings; - if (settings->RemoteFxCodec) + if (freerdp_settings_get_bool(settings, FreeRDP_RemoteFxCodec)) rdp_peer_refresh_rfx(region, output->shadow_surface, peer); - else if (settings->NSCodec) + else if (freerdp_settings_get_bool(settings, FreeRDP_NSCodec)) rdp_peer_refresh_nsc(region, output->shadow_surface, peer); else rdp_peer_refresh_raw(region, output->shadow_surface, peer); @@ -310,7 +310,7 @@ rdp_output_repaint(struct weston_output *output_base, pixman_region32_t *damage, } if (b->rdp_peer && - b->rdp_peer->context->settings->HiDefRemoteApp) { + freerdp_settings_get_bool(b->rdp_peer->context->settings, FreeRDP_HiDefRemoteApp)) { /* RAIL mode, repaint RAIL window */ rdp_rail_output_repaint(output_base, damage); } else if (output->shadow_surface && @@ -396,7 +396,7 @@ rdp_output_set_mode(struct weston_output *base, struct weston_mode *mode) base->current_mode = cur; base->native_mode = cur; - if (b->rdp_peer && b->rdp_peer->context->settings->HiDefRemoteApp) + if (b->rdp_peer && freerdp_settings_get_bool(b->rdp_peer->context->settings, FreeRDP_HiDefRemoteApp)) return; if (base->enabled) { @@ -411,23 +411,23 @@ rdp_output_set_mode(struct weston_output *base, struct weston_mode *mode) rdpOutput->shadow_surface = new_shadow_buffer; } - /* Apparently settings->DesktopWidth is supposed to be primary only, + /* Apparently freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) is supposed to be primary only, * but we don't hit this path for RAIL, and we don't have more than * one head for non-RAIL. */ wl_list_for_each(rdpPeer, &b->peers, link) { settings = rdpPeer->peer->context->settings; - if (settings->DesktopWidth == (UINT32)mode->width && - settings->DesktopHeight == (UINT32)mode->height) + if (freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) == (UINT32)mode->width && + freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) == (UINT32)mode->height) continue; - if (!settings->DesktopResize) { + if (!freerdp_settings_get_bool(settings, FreeRDP_DesktopResize)) { /* too bad this peer does not support desktop resize */ rdp_debug_error(b, "%s: desktop resize is not allowed\n", __func__); rdpPeer->peer->Close(rdpPeer->peer); } else { - settings->DesktopWidth = mode->width; - settings->DesktopHeight = mode->height; + (void)freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, mode->width); + (void)freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, mode->height); rdpPeer->peer->context->update->DesktopResize(rdpPeer->peer->context); } } @@ -477,7 +477,7 @@ rdp_output_enable(struct weston_output *base) }; bool HiDefRemoteApp = false; - if (b->rdp_peer && b->rdp_peer->context->settings->HiDefRemoteApp) + if (b->rdp_peer && freerdp_settings_get_bool(b->rdp_peer->context->settings, FreeRDP_HiDefRemoteApp)) HiDefRemoteApp = true; if (!HiDefRemoteApp) { @@ -739,12 +739,12 @@ rdp_peer_context_new(freerdp_peer* client, RdpPeerContext* context) #if FREERDP_VERSION_MAJOR >= 3 rfx_context_set_mode(context->rfx_context, RLGR3); rfx_context_reset(context->rfx_context, - client->context->settings->DesktopWidth, - client->context->settings->DesktopHeight); + freerdp_settings_get_uint32(client->context->settings, FreeRDP_DesktopWidth), + freerdp_settings_get_uint32(client->context->settings, FreeRDP_DesktopHeight)); #else context->rfx_context->mode = RLGR3; - context->rfx_context->width = client->context->settings->DesktopWidth; - context->rfx_context->height = client->context->settings->DesktopHeight; + context->rfx_context->width = freerdp_settings_get_uint32(client->context->settings, FreeRDP_DesktopWidth); + context->rfx_context->height = freerdp_settings_get_uint32(client->context->settings, FreeRDP_DesktopHeight); #endif rfx_context_set_pixel_format(context->rfx_context, DEFAULT_PIXEL_FORMAT); @@ -814,7 +814,7 @@ rdp_peer_context_free(freerdp_peer* client, RdpPeerContext* context) weston_seat_release_keyboard(context->item.seat); weston_seat_release_pointer(context->item.seat); /* save current seat for future use only when it's not saved yet */ - if (settings->RemoteApplicationMode && + if (freerdp_settings_get_bool(settings, FreeRDP_RemoteApplicationMode) && b->enable_persistent_rail_seat) { assert(!b->persistent_rail_seat); b->persistent_rail_seat = context->item.seat; @@ -1086,24 +1086,24 @@ xf_peer_activate(freerdp_peer* client) peersItem = &peerCtx->item; settings = client->context->settings; - if (!settings->SurfaceCommandsEnabled) { + if (!freerdp_settings_get_bool(settings, FreeRDP_SurfaceCommandsEnabled)) { rdp_debug_error(b, "client doesn't support required SurfaceCommands\n"); return FALSE; } - if (b->force_no_compression && settings->CompressionEnabled) { + if (b->force_no_compression && freerdp_settings_get_bool(settings, FreeRDP_CompressionEnabled)) { rdp_debug_error(b, "Forcing compression off\n"); - settings->CompressionEnabled = FALSE; + (void)freerdp_settings_set_bool(settings, FreeRDP_CompressionEnabled, FALSE); } /* in RAIL mode, only one peer per backend can be activated */ - if (settings->RemoteApplicationMode) { + if (freerdp_settings_get_bool(settings, FreeRDP_RemoteApplicationMode)) { if (b->rdp_peer != client) { rdp_debug_error(b, "Another RAIL connection active, only one connection is allowed.\n"); return FALSE; } - if (!settings->HiDefRemoteApp) { + if (!freerdp_settings_get_bool(settings, FreeRDP_HiDefRemoteApp)) { /* HiDef is required for RAIL mode. Cookie-cutter window remoting is not supported. */ rdp_debug_error(b, "HiDef-RAIL is required for RAIL.\n"); return FALSE; @@ -1124,7 +1124,7 @@ xf_peer_activate(freerdp_peer* client) } /* override settings by env variables */ - settings->RedirectClipboard = b->redirect_clipboard; + (void)freerdp_settings_set_bool(settings, FreeRDP_RedirectClipboard, b->redirect_clipboard); #if FREERDP_VERSION_MAJOR >= 3 /* TODO: rdpsnd/audin server-side context APIs were rewritten in FreeRDP 3.x to a * PDU-callback model (SendVersion/SendFormats/SendOpen/IncomingData...). The wslg @@ -1137,17 +1137,17 @@ xf_peer_activate(freerdp_peer* client) warned = true; } } - settings->AudioPlayback = FALSE; - settings->AudioCapture = FALSE; + (void)freerdp_settings_set_bool(settings, FreeRDP_AudioPlayback, FALSE); + (void)freerdp_settings_set_bool(settings, FreeRDP_AudioCapture, FALSE); #else - settings->AudioPlayback = b->audio_out_setup && b->audio_out_teardown; - settings->AudioCapture = b->audio_in_setup && b->audio_in_teardown; + (void)freerdp_settings_set_bool(settings, FreeRDP_AudioPlayback, b->audio_out_setup && b->audio_out_teardown); + (void)freerdp_settings_set_bool(settings, FreeRDP_AudioCapture, b->audio_in_setup && b->audio_in_teardown); #endif - if (settings->RemoteApplicationMode || - settings->RedirectClipboard || - settings->AudioPlayback || - settings->AudioCapture) { + if (freerdp_settings_get_bool(settings, FreeRDP_RemoteApplicationMode) || + freerdp_settings_get_bool(settings, FreeRDP_RedirectClipboard) || + freerdp_settings_get_bool(settings, FreeRDP_AudioPlayback) || + freerdp_settings_get_bool(settings, FreeRDP_AudioCapture)) { if (!peerCtx->vcm) { rdp_debug_error(b, "Virtual channel is required for RAIL, clipboard, audio playback/capture\n"); @@ -1158,19 +1158,19 @@ xf_peer_activate(freerdp_peer* client) if (!rdp_drdynvc_init(client)) goto error_exit; - if (settings->RemoteApplicationMode) + if (freerdp_settings_get_bool(settings, FreeRDP_RemoteApplicationMode)) if (!rdp_rail_peer_activate(client)) goto error_exit; /* Audio setup will return NULL on failure, and we'll proceed without audio */ - if (settings->AudioPlayback) + if (freerdp_settings_get_bool(settings, FreeRDP_AudioPlayback)) peerCtx->audio_out_private = b->audio_out_setup(b->compositor, peerCtx->vcm); - if (settings->AudioCapture) + if (freerdp_settings_get_bool(settings, FreeRDP_AudioCapture)) peerCtx->audio_in_private = b->audio_in_setup(b->compositor, peerCtx->vcm); } - if (settings->HiDefRemoteApp) { + if (freerdp_settings_get_bool(settings, FreeRDP_HiDefRemoteApp)) { /* single monitor case, FreeRDP doesn't call AdjustMonitorsLayout callback, so call now */ xf_peer_adjust_monitor_layout(client); output = NULL; @@ -1179,18 +1179,18 @@ xf_peer_activate(freerdp_peer* client) output = rdp_get_first_output(b); /* multiple monitor is not supported in non-HiDef */ rdp_debug_error(b, "%s: DesktopWidth:%d, DesktopHeigh:%d, DesktopScaleFactor:%d\n", __FUNCTION__, - settings->DesktopWidth, settings->DesktopHeight, settings->DesktopScaleFactor); - if (output->base.width != (int)settings->DesktopWidth || - output->base.height != (int)settings->DesktopHeight) { + freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth), freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight), freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor)); + if (output->base.width != (int)freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) || + output->base.height != (int)freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight)) { if (b->no_clients_resize) { /* RDP peers don't dictate their resolution to weston */ - if (!settings->DesktopResize) { + if (!freerdp_settings_get_bool(settings, FreeRDP_DesktopResize)) { /* peer does not support desktop resize */ rdp_debug_error(b, "%s: client doesn't support resizing, closing connection\n", __FUNCTION__); goto error_exit; } else { - settings->DesktopWidth = output->base.width; - settings->DesktopHeight = output->base.height; + (void)freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, output->base.width); + (void)freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, output->base.height); client->context->update->DesktopResize(client->context); } } else { @@ -1207,7 +1207,7 @@ xf_peer_activate(freerdp_peer* client) nsc_context_reset(peerCtx->nsc_context, weston_output->width, weston_output->height); } - if (settings->RemoteApplicationMode) + if (freerdp_settings_get_bool(settings, FreeRDP_RemoteApplicationMode)) rdp_rail_sync_window_status(client); if (peersItem->flags & RDP_PEER_ACTIVATED) @@ -1215,12 +1215,12 @@ xf_peer_activate(freerdp_peer* client) /* when here it's the first reactivation, we need to setup a little more */ rdp_debug(b, "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); + freerdp_settings_get_uint32(settings, FreeRDP_KeyboardLayout), freerdp_settings_get_uint32(settings, FreeRDP_KeyboardType), freerdp_settings_get_uint32(settings, FreeRDP_KeyboardSubType), + freerdp_settings_get_uint32(settings, FreeRDP_KeyboardFunctionKey)); - convert_rdp_keyboard_to_xkb_rule_names(settings->KeyboardType, - settings->KeyboardSubType, - settings->KeyboardLayout, + convert_rdp_keyboard_to_xkb_rule_names(freerdp_settings_get_uint32(settings, FreeRDP_KeyboardType), + freerdp_settings_get_uint32(settings, FreeRDP_KeyboardSubType), + freerdp_settings_get_uint32(settings, FreeRDP_KeyboardLayout), &xkbRuleNames); keymap = NULL; @@ -1229,14 +1229,14 @@ xf_peer_activate(freerdp_peer* client) &xkbRuleNames, 0); } - if (settings->RemoteApplicationMode) + if (freerdp_settings_get_bool(settings, FreeRDP_RemoteApplicationMode)) snprintf(seat_name, sizeof(seat_name), "RDP Remote Application Client"); - if (settings->ClientHostname) - snprintf(seat_name, sizeof(seat_name), "RDP %s", settings->ClientHostname); + if (freerdp_settings_get_string(settings, FreeRDP_ClientHostname)) + snprintf(seat_name, sizeof(seat_name), "RDP %s", freerdp_settings_get_string(settings, FreeRDP_ClientHostname)); else - snprintf(seat_name, sizeof(seat_name), "RDP peer @%s", settings->ClientAddress); + snprintf(seat_name, sizeof(seat_name), "RDP peer @%s", freerdp_settings_get_string(settings, FreeRDP_ClientAddress)); - if (settings->RemoteApplicationMode && + if (freerdp_settings_get_bool(settings, FreeRDP_RemoteApplicationMode) && b->persistent_rail_seat) { /* reuse persistent seat for RAIL connection */ peersItem->seat = b->persistent_rail_seat; @@ -1257,13 +1257,13 @@ xf_peer_activate(freerdp_peer* client) peersItem->seat->led_update = rdp_peer_seat_led_update; /* Initialize RDP clipboard after seat is initialized */ - if (settings->RedirectClipboard) + if (freerdp_settings_get_bool(settings, FreeRDP_RedirectClipboard)) if (rdp_clipboard_init(client) != 0) goto error_exit; peersItem->flags |= RDP_PEER_ACTIVATED; - if (!settings->HiDefRemoteApp && output) { + if (!freerdp_settings_get_bool(settings, FreeRDP_HiDefRemoteApp) && output) { /* disable pointer on the client side */ pointer = client->context->update->pointer; pointer_system.type = SYSPTR_NULL; @@ -1286,9 +1286,9 @@ xf_peer_activate(freerdp_peer* client) error_exit: rdp_clipboard_destroy(peerCtx); - if (settings->AudioPlayback && peerCtx->audio_out_private) + if (freerdp_settings_get_bool(settings, FreeRDP_AudioPlayback) && peerCtx->audio_out_private) b->audio_out_teardown(peerCtx->audio_out_private); - if (settings->AudioCapture && peerCtx->audio_in_private) + if (freerdp_settings_get_bool(settings, FreeRDP_AudioCapture) && peerCtx->audio_in_private) b->audio_in_teardown(peerCtx->audio_in_private); rdp_rail_peer_context_free(client, peerCtx); rdp_drdynvc_destroy(peerCtx); @@ -1588,7 +1588,7 @@ xf_input_synchronize_event(rdpInput *input, UINT32 flags) value); } - if (client->context->settings->HiDefRemoteApp) + if (freerdp_settings_get_bool(client->context->settings, FreeRDP_HiDefRemoteApp)) return TRUE; /* sends a full refresh */ @@ -1648,7 +1648,7 @@ xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code) full_code = code; /* On Windows 10 client, certain locale's keyboard layout reports extended bit for right shift key (scancode 0x36) due to bug, so drop the bit here. */ - keyboard_locale = client->context->settings->KeyboardLayout & 0xFFFF; + keyboard_locale = freerdp_settings_get_uint32(client->context->settings, FreeRDP_KeyboardLayout) & 0xFFFF; if (code == 0x36 && /* Right shift key */ (keyboard_locale == KBD_CHINESE_TRADITIONAL_US || keyboard_locale == KBD_CHINESE_SIMPLIFIED_US || @@ -1664,8 +1664,8 @@ xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code) /* From Linux's keyboard driver at drivers/input/keyboard/atkbd.c */ #define ATKBD_RET_HANJA 0xf1 #define ATKBD_RET_HANGEUL 0xf2 - if (client->context->settings->KeyboardType == 8 && - client->context->settings->KeyboardSubType == 6 && + if (freerdp_settings_get_uint32(client->context->settings, FreeRDP_KeyboardType) == 8 && + freerdp_settings_get_uint32(client->context->settings, FreeRDP_KeyboardSubType) == 6 && ((full_code == (KBD_FLAGS_EXTENDED | ATKBD_RET_HANJA)) || (full_code == (KBD_FLAGS_EXTENDED | ATKBD_RET_HANGEUL)))) { if (full_code == (KBD_FLAGS_EXTENDED | ATKBD_RET_HANJA)) @@ -1682,7 +1682,7 @@ xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code) assert(keyState == WL_KEYBOARD_KEY_STATE_PRESSED); send_release_key = true; } else { - vk_code = GetVirtualKeyCodeFromVirtualScanCode(full_code, client->context->settings->KeyboardType); + vk_code = GetVirtualKeyCodeFromVirtualScanCode(full_code, freerdp_settings_get_uint32(client->context->settings, FreeRDP_KeyboardType)); } /* Korean keyboard support */ /* WinPR's GetKeycodeFromVirtualKeyCode() expects no extended bit for VK_HANGUL and VK_HANJA */ @@ -1772,35 +1772,36 @@ xf_peer_adjust_monitor_layout(freerdp_peer *client) unsigned int i; rdp_debug(b, "%s:\n", __func__); - rdp_debug(b, " DesktopWidth:%d, DesktopHeight:%d\n", settings->DesktopWidth, settings->DesktopHeight); - rdp_debug(b, " UseMultimon:%d\n", settings->UseMultimon); - rdp_debug(b, " ForceMultimon:%d\n", settings->ForceMultimon); - rdp_debug(b, " MonitorCount:%d\n", settings->MonitorCount); - rdp_debug(b, " HasMonitorAttributes:%d\n", settings->HasMonitorAttributes); - rdp_debug(b, " HiDefRemoteApp:%d\n", settings->HiDefRemoteApp); + rdp_debug(b, " DesktopWidth:%d, DesktopHeight:%d\n", freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth), freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight)); + rdp_debug(b, " UseMultimon:%d\n", freerdp_settings_get_bool(settings, FreeRDP_UseMultimon)); + rdp_debug(b, " ForceMultimon:%d\n", freerdp_settings_get_bool(settings, FreeRDP_ForceMultimon)); + rdp_debug(b, " MonitorCount:%d\n", freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount)); + rdp_debug(b, " HasMonitorAttributes:%d\n", freerdp_settings_get_bool(settings, FreeRDP_HasMonitorAttributes)); + rdp_debug(b, " HiDefRemoteApp:%d\n", freerdp_settings_get_bool(settings, FreeRDP_HiDefRemoteApp)); /* these settings must have no impact in RAIL mode */ /* In RAIL mode, it must mirror client's monitor settings */ /* If not in RAIL mode, or RAIL-shell is not used, only signle mon is allowed */ - if (!settings->HiDefRemoteApp || b->rdprail_shell_api == NULL) { - if (settings->MonitorCount > 1) { + if (!freerdp_settings_get_bool(settings, FreeRDP_HiDefRemoteApp) || b->rdprail_shell_api == NULL) { + if (freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount) > 1) { rdp_debug_error(b, "\nWARNING\nWARNING\nWARNING: multiple monitor is not supported in non HiDef RAIL mode\nWARNING\nWARNING\n"); fallback = true; } } - if (settings->MonitorCount > RDP_MAX_MONITOR) { + if (freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount) > RDP_MAX_MONITOR) { rdp_debug_error(b, "\nWARNING\nWARNING\nWARNING: client reports more monitors then expected:(%d)\nWARNING\nWARNING\n", - settings->MonitorCount); + freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount)); return FALSE; } - if ((settings->MonitorCount > 0 && settings->MonitorDefArray) && !fallback) { - rdpMonitor *rdp_monitor = settings->MonitorDefArray; - monitor_count = settings->MonitorCount; + const rdpMonitor *rdp_monitor = + freerdp_settings_get_pointer(settings, FreeRDP_MonitorDefArray); + if ((freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount) > 0 && rdp_monitor) && !fallback) { + monitor_count = freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount); monitors = xmalloc(sizeof(*monitors) * monitor_count); for (i = 0; i < monitor_count; i++) { monitors[i] = rdp_monitor[i]; - if (!settings->HasMonitorAttributes) { + if (!freerdp_settings_get_bool(settings, FreeRDP_HasMonitorAttributes)) { monitors[i].attributes.physicalWidth = 0; monitors[i].attributes.physicalHeight = 0; monitors[i].attributes.orientation = ORIENTATION_LANDSCAPE; @@ -1812,16 +1813,16 @@ xf_peer_adjust_monitor_layout(freerdp_peer *client) monitor_count = 1; monitors = xmalloc(sizeof(*monitors) * monitor_count); /* when no monitor array provided, generate from desktop settings */ - monitors[0].x = 0; // settings->DesktopPosX; - monitors[0].y = 0; // settings->DesktopPosY; - monitors[0].width = settings->DesktopWidth; - monitors[0].height = settings->DesktopHeight; + monitors[0].x = 0; // freerdp_settings_get_uint32(settings, FreeRDP_DesktopPosX); + monitors[0].y = 0; // freerdp_settings_get_uint32(settings, FreeRDP_DesktopPosY); + monitors[0].width = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth); + monitors[0].height = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight); monitors[0].is_primary = 1; - monitors[0].attributes.physicalWidth = settings->DesktopPhysicalWidth; - monitors[0].attributes.physicalHeight = settings->DesktopPhysicalHeight; - monitors[0].attributes.orientation = settings->DesktopOrientation; - monitors[0].attributes.desktopScaleFactor = settings->DesktopScaleFactor; - monitors[0].attributes.deviceScaleFactor = settings->DeviceScaleFactor; + monitors[0].attributes.physicalWidth = freerdp_settings_get_uint32(settings, FreeRDP_DesktopPhysicalWidth); + monitors[0].attributes.physicalHeight = freerdp_settings_get_uint32(settings, FreeRDP_DesktopPhysicalHeight); + monitors[0].attributes.orientation = freerdp_settings_get_uint16(settings, FreeRDP_DesktopOrientation); + monitors[0].attributes.desktopScaleFactor = freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor); + monitors[0].attributes.deviceScaleFactor = freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor); monitors[0].orig_screen = 0; } success = handle_adjust_monitor_layout(client, monitor_count, monitors); @@ -1904,9 +1905,9 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) settings->PrivateKeyFile = strdup(b->server_key); } } else { - settings->TlsSecurity = FALSE; + (void)freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE); } - settings->NlaSecurity = FALSE; + (void)freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE); #endif if (!client->Initialize(client)) { @@ -1914,27 +1915,26 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) goto error_initialize; } - settings->OsMajorType = OSMAJORTYPE_UNIX; - settings->OsMinorType = OSMINORTYPE_PSEUDO_XSERVER; - settings->ColorDepth = 32; - settings->RefreshRect = TRUE; - settings->RemoteFxCodec = FALSE; // TODO: - settings->NSCodec = TRUE; - settings->FrameMarkerCommandEnabled = TRUE; - settings->SurfaceFrameMarkerEnabled = TRUE; - settings->RemoteApplicationMode = TRUE; - settings->RemoteApplicationSupportLevel = - RAIL_LEVEL_SUPPORTED | + (void)freerdp_settings_set_uint32(settings, FreeRDP_OsMajorType, OSMAJORTYPE_UNIX); + (void)freerdp_settings_set_uint32(settings, FreeRDP_OsMinorType, OSMINORTYPE_PSEUDO_XSERVER); + (void)freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth, 32); + (void)freerdp_settings_set_bool(settings, FreeRDP_RefreshRect, TRUE); + (void)freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, FALSE); // TODO: + (void)freerdp_settings_set_bool(settings, FreeRDP_NSCodec, TRUE); + (void)freerdp_settings_set_bool(settings, FreeRDP_FrameMarkerCommandEnabled, TRUE); + (void)freerdp_settings_set_bool(settings, FreeRDP_SurfaceFrameMarkerEnabled, TRUE); + (void)freerdp_settings_set_bool(settings, FreeRDP_RemoteApplicationMode, TRUE); + (void)freerdp_settings_set_uint32(settings, FreeRDP_RemoteApplicationSupportLevel, RAIL_LEVEL_SUPPORTED | RAIL_LEVEL_SHELL_INTEGRATION_SUPPORTED | RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED | RAIL_LEVEL_SERVER_TO_CLIENT_IME_SYNC_SUPPORTED | - RAIL_LEVEL_HANDSHAKE_EX_SUPPORTED; - settings->SupportGraphicsPipeline = TRUE; - settings->SupportMonitorLayoutPdu = TRUE; - settings->RedirectClipboard = TRUE; - settings->HasExtendedMouseEvent = TRUE; - settings->HasHorizontalWheel = TRUE; - settings->FastPathInput = TRUE; + RAIL_LEVEL_HANDSHAKE_EX_SUPPORTED); + (void)freerdp_settings_set_bool(settings, FreeRDP_SupportGraphicsPipeline, TRUE); + (void)freerdp_settings_set_bool(settings, FreeRDP_SupportMonitorLayoutPdu, TRUE); + (void)freerdp_settings_set_bool(settings, FreeRDP_RedirectClipboard, TRUE); + (void)freerdp_settings_set_bool(settings, FreeRDP_HasExtendedMouseEvent, TRUE); + (void)freerdp_settings_set_bool(settings, FreeRDP_HasHorizontalWheel, TRUE); + (void)freerdp_settings_set_bool(settings, FreeRDP_FastPathInput, TRUE); /* FreeRDP 3.x added a connect-time NetworkAutoDetect step to the server * state machine that did not exist in 2.x: SECURE_SETTINGS_EXCHANGE -> @@ -1946,7 +1946,7 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) * here. Force FALSE so the state machine skips straight to LICENSING, * matching the FreeRDP 2.x behavior we relied on. No-op on 2.x (which * ignored the setting at connect-time anyway). */ - settings->NetworkAutoDetect = FALSE; + (void)freerdp_settings_set_bool(settings, FreeRDP_NetworkAutoDetect, FALSE); /* Likewise FreeRDP 3.x server unconditionally walks an extra * MULTITRANSPORT bootstrap step (LICENSING -> @@ -1957,8 +1957,8 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) * no benefit on WSLg's TCP-only vsock/loopback transport, so disable it: * the server then transitions straight to CAPABILITIES_EXCHANGE without * emitting a transport request. No-op on 2.x. */ - settings->SupportMultitransport = FALSE; - settings->MultitransportFlags = 0; + (void)freerdp_settings_set_bool(settings, FreeRDP_SupportMultitransport, FALSE); + (void)freerdp_settings_set_uint32(settings, FreeRDP_MultitransportFlags, 0); client->Capabilities = xf_peer_capabilities; client->PostConnect = xf_peer_post_connect; diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h index becc1bcca..b299e9b79 100644 --- a/libweston/backend-rdp/rdp.h +++ b/libweston/backend-rdp/rdp.h @@ -28,14 +28,6 @@ #include -#if FREERDP_VERSION_MAJOR >= 3 -/* Temporary workaround for SETTINGS_DEPRECATED attribute warnings. - * Allows the rdp-backend to keep using direct struct member access on - * rdpSettings while the migration to freerdp_settings_get/set_* accessors - * is performed file-by-file. Remove once all direct accesses are gone. */ -#define FREERDP_SETTINGS_INTERNAL_USE -#endif - #include #include #include diff --git a/libweston/backend-rdp/rdpdisp.c b/libweston/backend-rdp/rdpdisp.c index 693e2f0ea..153c7811f 100644 --- a/libweston/backend-rdp/rdpdisp.c +++ b/libweston/backend-rdp/rdpdisp.c @@ -226,8 +226,8 @@ disp_start_monitor_layout_change(freerdp_peer *client, rdpMonitor *config, UINT3 /* accumulate monitor layout */ if (config[i].is_primary) { /* it looks settings's desktopWidth/Height only represents primary */ - settings->DesktopWidth = config[i].width; - settings->DesktopHeight = config[i].height; + (void)freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, config[i].width); + (void)freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, config[i].height); } pixman_region32_union_rect(&desktop, &desktop, config[i].x, diff --git a/libweston/backend-rdp/rdprail.c b/libweston/backend-rdp/rdprail.c index 857ee5007..9fd0d4c84 100644 --- a/libweston/backend-rdp/rdprail.c +++ b/libweston/backend-rdp/rdprail.c @@ -1019,9 +1019,9 @@ rail_client_LanguageImeInfo_callback(bool freeOnly, void *arg) } if (new_keyboard_layout && - (new_keyboard_layout != settings->KeyboardLayout)) { - convert_rdp_keyboard_to_xkb_rule_names(settings->KeyboardType, - settings->KeyboardSubType, + (new_keyboard_layout != freerdp_settings_get_uint32(settings, FreeRDP_KeyboardLayout))) { + convert_rdp_keyboard_to_xkb_rule_names(freerdp_settings_get_uint32(settings, FreeRDP_KeyboardType), + freerdp_settings_get_uint32(settings, FreeRDP_KeyboardSubType), new_keyboard_layout, &xkbRuleNames); if (xkbRuleNames.layout) { @@ -1030,7 +1030,7 @@ rail_client_LanguageImeInfo_callback(bool freeOnly, void *arg) if (keymap) { weston_seat_update_keymap(peer_ctx->item.seat, keymap); xkb_keymap_unref(keymap); - settings->KeyboardLayout = new_keyboard_layout; + (void)freerdp_settings_set_uint32(settings, FreeRDP_KeyboardLayout, new_keyboard_layout); rdp_debug(b, "%s: new keyboard layout: 0x%x\n", __func__, new_keyboard_layout); } @@ -1038,8 +1038,8 @@ rail_client_LanguageImeInfo_callback(bool freeOnly, void *arg) if (!keymap) { rdp_debug_error(b, "%s: Failed to switch to kbd_layout:0x%x kbd_type:0x%x kbd_subType:0x%x\n", __func__, new_keyboard_layout, - settings->KeyboardType, - settings->KeyboardSubType); + freerdp_settings_get_uint32(settings, FreeRDP_KeyboardType), + freerdp_settings_get_uint32(settings, FreeRDP_KeyboardSubType)); rdp_debug_error(b, "%s: Resetting default keymap\n", __func__); keymap = xkb_keymap_new_from_names(peer_ctx->item.seat->compositor->xkb_context, @@ -1047,7 +1047,7 @@ rail_client_LanguageImeInfo_callback(bool freeOnly, void *arg) 0); weston_seat_update_keymap(peer_ctx->item.seat, keymap); xkb_keymap_unref(keymap); - settings->KeyboardLayout = new_keyboard_layout; + (void)freerdp_settings_set_uint32(settings, FreeRDP_KeyboardLayout, new_keyboard_layout); } } } @@ -1526,7 +1526,7 @@ rdp_rail_create_window(struct wl_listener *listener, void *data) return; } - if (!b->rdp_peer->context->settings->HiDefRemoteApp) + if (!freerdp_settings_get_bool(b->rdp_peer->context->settings, FreeRDP_HiDefRemoteApp)) return; if (!b->rdp_peer->context) { @@ -3285,7 +3285,7 @@ disp_client_monitor_layout_change(DispServerContext *context, const DISPLAY_CONT rdp_debug(b, "Client: DisplayLayoutChange: monitor count:0x%x\n", display_control->NumMonitors); - assert(settings->HiDefRemoteApp); + assert(freerdp_settings_get_bool(settings, FreeRDP_HiDefRemoteApp)); data = xmalloc(sizeof(*data) + (sizeof(rdpMonitor) * display_control->NumMonitors)); @@ -3379,13 +3379,13 @@ rdp_rail_peer_activate(freerdp_peer* client) /* In RAIL mode, client must not be resized */ assert(b->no_clients_resize == 0); /* Server must not ask client to resize */ - settings->DesktopResize = FALSE; + (void)freerdp_settings_set_bool(settings, FreeRDP_DesktopResize, FALSE); /* HiDef requires graphics pipeline to be supported */ - if (settings->SupportGraphicsPipeline == FALSE) { - if (settings->HiDefRemoteApp) { + if (freerdp_settings_get_bool(settings, FreeRDP_SupportGraphicsPipeline) == FALSE) { + if (freerdp_settings_get_bool(settings, FreeRDP_HiDefRemoteApp)) { rdp_debug_error(b, "HiDef remoting is going to be disabled because client doesn't support graphics pipeline\n"); - settings->HiDefRemoteApp = FALSE; + (void)freerdp_settings_set_bool(settings, FreeRDP_HiDefRemoteApp, FALSE); } } @@ -3413,7 +3413,7 @@ rdp_rail_peer_activate(freerdp_peer* client) rail_server_started = true; /* send handshake to client */ - if (settings->RemoteApplicationSupportLevel & RAIL_LEVEL_HANDSHAKE_EX_SUPPORTED) { + if (freerdp_settings_get_uint32(settings, FreeRDP_RemoteApplicationSupportLevel) & RAIL_LEVEL_HANDSHAKE_EX_SUPPORTED) { RAIL_HANDSHAKE_EX_ORDER handshakeEx = {}; uint32_t railHandshakeFlags = TS_RAIL_ORDER_HANDSHAKEEX_FLAGS_HIDEF | TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_EXTENDED_SPI_SUPPORTED; @@ -3759,7 +3759,7 @@ rdp_rail_send_window_minmax_info( RAIL_MINMAXINFO_ORDER minmax_order; int dummyX = 0, dummyY = 0; - if (!b->rdp_peer || !b->rdp_peer->context->settings->HiDefRemoteApp) { + if (!b->rdp_peer || !freerdp_settings_get_bool(b->rdp_peer->context->settings, FreeRDP_HiDefRemoteApp)) { return; } @@ -3833,7 +3833,7 @@ rdp_rail_start_window_move( struct weston_view* view; RailServerContext *rail_ctx; - if (!b->rdp_peer || !b->rdp_peer->context->settings->HiDefRemoteApp) + if (!b->rdp_peer || !freerdp_settings_get_bool(b->rdp_peer->context->settings, FreeRDP_HiDefRemoteApp)) return; peer_ctx = (RdpPeerContext *)b->rdp_peer->context; @@ -3915,7 +3915,7 @@ rdp_rail_end_window_move(struct weston_surface *surface) int numViews = 0; struct weston_view *view; - if (!b->rdp_peer || !b->rdp_peer->context->settings->HiDefRemoteApp) { + if (!b->rdp_peer || !freerdp_settings_get_bool(b->rdp_peer->context->settings, FreeRDP_HiDefRemoteApp)) { return; } @@ -4511,7 +4511,7 @@ rdp_rail_set_window_icon(struct weston_surface *surface, pixman_image_t *icon) peer_ctx = (RdpPeerContext *)b->rdp_peer->context; update = b->rdp_peer->context->update; - if (!b->rdp_peer->context->settings->HiDefRemoteApp) + if (!freerdp_settings_get_bool(b->rdp_peer->context->settings, FreeRDP_HiDefRemoteApp)) return; assert_compositor_thread(b); @@ -4687,7 +4687,7 @@ rdp_rail_notify_app_list(void *rdp_backend, return false; } - if (!b->rdp_peer->context->settings->HiDefRemoteApp) + if (!freerdp_settings_get_bool(b->rdp_peer->context->settings, FreeRDP_HiDefRemoteApp)) return true; peer_ctx = (RdpPeerContext *)b->rdp_peer->context; From 5fd0638eb33016cd956027891a75fb601175f3b7 Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Tue, 19 May 2026 21:27:23 +0000 Subject: [PATCH 6/8] rdp-backend: fix const-qualifier warnings from 3.x rail.h API drift MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In FreeRDP 2.x the `RAIL_EXEC_ORDER` struct held `char *` for the RemoteApplication{Program,WorkingDir,Arguments} fields; in 3.x those were changed to `const char *`. The original 2.x rdp-backend code freed and strcpy'd into those fields directly, which produces three classes of `-Wdiscarded-qualifiers` warnings under the 3.x headers: * `char *remoteProgramAndArgs = exec->RemoteApplicationProgram` in rail_client_Exec_callback (rdprail.c:186) * `free(exec->RemoteApplicationProgram)` × 3 (rdprail.c:247-249) * `strcpy(exec_order.RemoteApplicationProgram, ...)` × 3 in rail_client_Exec (rdprail.c:262/267/272) In all of these the buffer is one we own — rail_client_Exec receives the const arg from FreeRDP and creates fresh xmalloc'd copies before dispatching the work to the display loop — so the const-cast is safe. * Write side (rail_client_Exec): xmalloc into a local `char *buf`, strcpy into it, then assign to the const field. The cast is implicit at the assignment and the compiler is happy. * Read side (rail_client_Exec_callback): explicit `(char *)` cast on the free() calls and on the initial `remoteProgramAndArgs` initializer, with comments explaining why. End-to-end build now emits zero `-Wdiscarded-qualifiers` warnings for backend-rdp. Remaining backend-rdp warnings are all pre-existing and unrelated to the FreeRDP 3.x port: * OpenSSL 3.0 RSA_* deprecation (rdp.c:2077-2082, in the 2.x-only `#else` TLS path). * `-Wformat-truncation` in rdp_log_timestamp's snprintf. * `-Warray-bounds` in clipboard_data_source_send's debug log. * `-Wformat-overflow` from xkbRuleNames->options being NULL in convert_rdp_keyboard_to_xkb_rule_names's debug log. Smoke-tested: backend loads, reports FreeRDP 3.26.0, TLS activates, head added, output enabled, plugin APIs registered. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- libweston/backend-rdp/rdprail.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/libweston/backend-rdp/rdprail.c b/libweston/backend-rdp/rdprail.c index 9fd0d4c84..be504f3ab 100644 --- a/libweston/backend-rdp/rdprail.c +++ b/libweston/backend-rdp/rdprail.c @@ -183,7 +183,11 @@ rail_client_Exec_callback(bool freeOnly, void *arg) const struct weston_rdprail_shell_api *api = b->rdprail_shell_api; UINT result = RAIL_EXEC_E_FAIL; RAIL_EXEC_RESULT_ORDER orderResult = {}; - char *remoteProgramAndArgs = exec->RemoteApplicationProgram; + /* RemoteApplicationProgram is `const char *` in the 3.x rail.h struct + * even though the buffer we receive here is one we own (allocated and + * strcpy'd by rail_client_Exec below before dispatch). Track it as a + * mutable pointer so we can free/replace it without warnings. */ + char *remoteProgramAndArgs = (char *)exec->RemoteApplicationProgram; rdp_debug(b, "Client ExecOrder:0x%08X, Program:%s, WorkingDir:%s, RemoteApplicationArguments:%s\n", (UINT)exec->flags, @@ -244,9 +248,12 @@ rail_client_Exec_callback(bool freeOnly, void *arg) free(orderResult.exeOrFile.string); if (remoteProgramAndArgs != exec->RemoteApplicationProgram) free(remoteProgramAndArgs); - free(exec->RemoteApplicationProgram); - free(exec->RemoteApplicationWorkingDir); - free(exec->RemoteApplicationArguments); + /* These buffers were xmalloc'd by rail_client_Exec; the const-ness of + * the struct fields is a 3.x rail.h contract for the caller, not for + * ourselves as the owner. Cast away const to free. */ + free((char *)exec->RemoteApplicationProgram); + free((char *)exec->RemoteApplicationWorkingDir); + free((char *)exec->RemoteApplicationArguments); free(data); } @@ -258,19 +265,19 @@ rail_client_Exec(RailServerContext *context, const RAIL_EXEC_ORDER *arg) exec_order.flags = arg->flags; if (arg->RemoteApplicationProgram) { - exec_order.RemoteApplicationProgram = xmalloc(strlen(arg->RemoteApplicationProgram) + 1); - strcpy(exec_order.RemoteApplicationProgram, - arg->RemoteApplicationProgram); + char *buf = xmalloc(strlen(arg->RemoteApplicationProgram) + 1); + strcpy(buf, arg->RemoteApplicationProgram); + exec_order.RemoteApplicationProgram = buf; } if (arg->RemoteApplicationWorkingDir) { - exec_order.RemoteApplicationWorkingDir = xmalloc(strlen(arg->RemoteApplicationWorkingDir) + 1); - strcpy(exec_order.RemoteApplicationWorkingDir, - arg->RemoteApplicationWorkingDir); + char *buf = xmalloc(strlen(arg->RemoteApplicationWorkingDir) + 1); + strcpy(buf, arg->RemoteApplicationWorkingDir); + exec_order.RemoteApplicationWorkingDir = buf; } if (arg->RemoteApplicationArguments) { - exec_order.RemoteApplicationArguments = xmalloc(strlen(arg->RemoteApplicationArguments) + 1); - strcpy(exec_order.RemoteApplicationArguments, - arg->RemoteApplicationArguments); + char *buf = xmalloc(strlen(arg->RemoteApplicationArguments) + 1); + strcpy(buf, arg->RemoteApplicationArguments); + exec_order.RemoteApplicationArguments = buf; } RDP_DISPATCH_TO_DISPLAY_LOOP(context, exec, &exec_order, rail_client_Exec_callback); From 5bf746394e3583011ed42fae8b1d218a3be0cb3e Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Tue, 19 May 2026 22:26:42 +0000 Subject: [PATCH 7/8] rdp-backend: port rdpsnd / audin server modules to FreeRDP 3.x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 2.x RdpsndServerContext and audin_server_context APIs were stubbed to no-op on FreeRDP 3.x in the prior port to avoid advertising audio capabilities we could not service. This commit completes the audio port: rdpsnd (audio output) — vendor/weston/compositor/rdpaudio.c ======================================================== The 3.x RdpsndServerContext public struct is field-for-field compatible with 2.x (Activated/ConfirmBlock callbacks, Initialize/SelectFormat/ SendSamples/SetVolume/Close APIs, server_formats/num_server_formats/ src_format/client_formats/num_client_formats/block_no/data fields). The only API-surface changes are WINPR_ATTR_NODISCARD on the function pointers and minor int->size_t / int->UINT32 / int->UINT16 widening on SendSamples/latency/SetVolume. * Remove the outer #if FREERDP_VERSION_MAJOR < 3 guards on the format table and helper functions so they compile under both versions * Remove the version-branch in rdp_audio_out_init / rdp_audio_out_destroy — the 2.x body works as-is for 3.x * Check SelectFormat()'s return value (per rubber-duck feedback — a silent failure would create a 'connected but no audio' state). Cast SetVolume and Close/Stop returns to (void) since those failures are non-fatal. audin (audio input) — vendor/weston/compositor/rdpaudioin.c ========================================================== The 3.x audin_server_context was reworked to a PDU-callback model (SendVersion/SendFormats/SendOpen + ReceiveVersion/ReceiveFormats/ Data callbacks), but FreeRDP provides convenience helpers (audin_server_set_formats) that hide most of the new mechanics. The shared helpers (format table, AUDIO_FORMAT_to_String, source thread, listener setup) are ungated. * Keep 2.x-specific callbacks (rdp_audioin_client_opening/ open_result/receive_samples) under #if FREERDP_VERSION_MAJOR < 3 * Add 3.x-specific callbacks under #if FREERDP_VERSION_MAJOR >= 3: - rdp_audioin_client_receive_formats — overrides the default ReceiveFormats handler. The default in FreeRDP 3.x (audin_server_receive_formats_default in channels/audin/server/audin.c) negotiates a compatible format but then sends a hardcoded stereo 44.1kHz 16-bit captureFormat in the SNDIN_OPEN PDU. WSLg's pulseaudio source pipe expects mono, so we override to send the negotiated (mono) format. - rdp_audioin_client_data — replaces ReceiveSamples; reads Stream_Length / Stream_Buffer from the SNDIN_DATA wStream and forwards to the pulseaudio source fd. * In rdp_audio_in_init, split the context-setup block by version. The shared source-thread that calls Open()/Close() works for both. backend-rdp/rdp.c ================= * Remove the temporary stub block (#if FREERDP_VERSION_MAJOR >= 3 in xf_peer_activate) that forced AudioPlayback = FALSE and AudioCapture = FALSE on 3.x. The full audio_out_setup / audio_out_teardown gate is now used for both versions. Validation ========== * Full docker build clean — both rdpaudio.c and rdpaudioin.c compile with zero new warnings under FreeRDP 3.26.0 * Smoke test passes: backend loads, no more 'audio playback/capture disabled: FreeRDP 3.x port pending' warning. End-to-end audio handshake validation requires mstsc.exe over the production vsock path (the xfreerdp3 loopback path still has the pre-existing licensing framing issue tracked separately). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- compositor/rdpaudio.c | 36 +++------ compositor/rdpaudioin.c | 147 +++++++++++++++++++++++++++++++----- libweston/backend-rdp/rdp.c | 16 ---- 3 files changed, 137 insertions(+), 62 deletions(-) diff --git a/compositor/rdpaudio.c b/compositor/rdpaudio.c index 70e67c6b5..ef25a1c2e 100644 --- a/compositor/rdpaudio.c +++ b/compositor/rdpaudio.c @@ -43,11 +43,9 @@ #include -#if FREERDP_VERSION_MAJOR < 3 static AUDIO_FORMAT rdp_audio_supported_audio_formats[] = { { WAVE_FORMAT_PCM, 2, 44100, 176400, 4, 16, 0, NULL }, }; -#endif #define AUDIO_LATENCY 5 #define AUDIO_FRAMES_PER_RDP_PACKET (44100 * AUDIO_LATENCY / 1000) @@ -71,7 +69,6 @@ typedef struct _rdp_audio_cmd_header { }; } rdp_audio_cmd_header; -#if FREERDP_VERSION_MAJOR < 3 static char* AUDIO_FORMAT_to_String(UINT16 format) { @@ -661,8 +658,11 @@ rdp_audio_client_activated(RdpsndServerContext* context) rdp_audio_debug(priv, "rdp_audio_server_activated: bytesPerFrame:%d, latency:%d\n", priv->bytesPerFrame, context->latency); - context->SelectFormat(context, format); - context->SetVolume(context, 0x7FFF, 0x7FFF); + if (context->SelectFormat(context, format) != CHANNEL_RC_OK) { + weston_log("RDPAudio - SelectFormat failed; audio output disabled.\n"); + return; + } + (void)context->SetVolume(context, 0x7FFF, 0x7FFF); priv->pulseAudioSinkListenerFd = rdp_audio_setup_listener(); if (priv->pulseAudioSinkListenerFd < 0) { @@ -674,21 +674,10 @@ rdp_audio_client_activated(RdpsndServerContext* context) weston_log("RDPAudio - No agreeded format.\n"); } } -#endif /* FREERDP_VERSION_MAJOR < 3 */ void * rdp_audio_out_init(struct weston_compositor *c, HANDLE vcm) { -#if FREERDP_VERSION_MAJOR >= 3 - /* TODO: Port to the FreeRDP 3.x RdpsndServerContext API. The 2.x server - * API used here (num_server_formats, server_formats, Activated callback, - * src_format, ConfirmBlock, etc.) was fully replaced in 3.x. Returning - * NULL is the supported "audio output unavailable" sentinel; the - * compositor will continue without RDP audio playback. */ - (void)c; - (void)vcm; - return NULL; -#else struct audio_out_private *priv; char *s; @@ -722,7 +711,7 @@ rdp_audio_out_init(struct weston_compositor *c, HANDLE vcm) goto Error_Exit; } memcpy(audio_formats, rdp_audio_supported_audio_formats, sizeof rdp_audio_supported_audio_formats); - + priv->rdpsnd_server_context->data = (void*)priv; priv->rdpsnd_server_context->Activated = rdp_audio_client_activated; priv->rdpsnd_server_context->ConfirmBlock = rdp_audio_client_confirm_block; @@ -738,6 +727,8 @@ rdp_audio_out_init(struct weston_compositor *c, HANDLE vcm) weston_log("RDPAudio - force static channel.\n"); } } +#else + (void)s; #endif // HAVE_RDPAUDIO_DYNAMIC_VIRTUAL_CHANNEL /* Calling Initialize does Start as well */ @@ -762,17 +753,11 @@ rdp_audio_out_init(struct weston_compositor *c, HANDLE vcm) free(priv); return NULL; -#endif /* FREERDP_VERSION_MAJOR >= 3 */ } void rdp_audio_out_destroy(void *audio_out_private) { -#if FREERDP_VERSION_MAJOR >= 3 - /* Paired with the NULL-returning init: nothing to tear down. */ - (void)audio_out_private; - return; -#else struct audio_out_private *priv = audio_out_private; if (priv->rdpsnd_server_context) { @@ -806,8 +791,8 @@ rdp_audio_out_destroy(void *audio_out_private) assert(priv->pulseAudioSinkFd < 0); assert(priv->audioBuffer == NULL); - priv->rdpsnd_server_context->Close(priv->rdpsnd_server_context); - priv->rdpsnd_server_context->Stop(priv->rdpsnd_server_context); + (void)priv->rdpsnd_server_context->Close(priv->rdpsnd_server_context); + (void)priv->rdpsnd_server_context->Stop(priv->rdpsnd_server_context); if (priv->audioSem != -1) { close(priv->audioSem); @@ -818,5 +803,4 @@ rdp_audio_out_destroy(void *audio_out_private) priv->rdpsnd_server_context = NULL; } free(priv); -#endif /* FREERDP_VERSION_MAJOR >= 3 */ } diff --git a/compositor/rdpaudioin.c b/compositor/rdpaudioin.c index 8891387f6..bdb12619a 100644 --- a/compositor/rdpaudioin.c +++ b/compositor/rdpaudioin.c @@ -43,8 +43,8 @@ #include #include +#include -#if FREERDP_VERSION_MAJOR < 3 static AUDIO_FORMAT rdp_audioin_supported_audio_formats[] = { { WAVE_FORMAT_PCM, 1, 44100, 88200, 2, 16, 0, NULL }, }; @@ -311,6 +311,7 @@ rdp_audioin_setup_listener(struct audio_in_private *priv) return fd; } +#if FREERDP_VERSION_MAJOR < 3 static UINT rdp_audioin_client_opening(audin_server_context* context) { @@ -405,6 +406,112 @@ rdp_audioin_client_receive_samples( return 0; } +#else /* FREERDP_VERSION_MAJOR >= 3 */ + +/* + * In FreeRDP 3.x the audin server API was reworked into a PDU-callback + * model. The default ReceiveFormats handler in libfreerdp-server picks a + * compatible format from the client's list but then calls SendOpen with a + * hardcoded stereo 44.1kHz 16-bit captureFormat (see + * channels/audin/server/audin.c:send_open in FreeRDP). WSLg's pulse audio + * source pipe expects mono PCM, so we override ReceiveFormats to send the + * negotiated format (mono) back to the client in the SNDIN_OPEN PDU. + */ +static UINT +rdp_audioin_client_receive_formats(audin_server_context* context, + const SNDIN_FORMATS* formats) +{ + struct audio_in_private *priv = context->userdata; + const AUDIO_FORMAT *server_format = &rdp_audioin_supported_audio_formats[0]; + UINT32 matched_index = UINT32_MAX; + SNDIN_OPEN open = { 0 }; + + rdp_audio_debug(priv, "RDP AudioIn ReceiveFormats: %u client formats.\n", + formats->NumFormats); + + for (UINT32 i = 0; i < formats->NumFormats; i++) { + const AUDIO_FORMAT *cf = &formats->SoundFormats[i]; + + rdp_audio_debug(priv, "\t[%u] - Format(%s) - Bits(%d), Channels(%d), Frequency(%d)\n", + i, + AUDIO_FORMAT_to_String(cf->wFormatTag), + cf->wBitsPerSample, + cf->nChannels, + cf->nSamplesPerSec); + + if (cf->wFormatTag == server_format->wFormatTag && + cf->nChannels == server_format->nChannels && + cf->nSamplesPerSec == server_format->nSamplesPerSec && + cf->wBitsPerSample == server_format->wBitsPerSample) { + matched_index = i; + rdp_audio_debug(priv, "RDPAudioIn - Agreed on format %u.\n", i); + break; + } + } + + if (matched_index == UINT32_MAX) { + weston_log("RDPAudioIn - No agreeded format.\n"); + return ERROR_INVALID_DATA; + } + + /* + * 10ms of audio per packet — matches the 2.x frames_per_packet + * setting (nSamplesPerSec / 100). + */ + open.FramesPerPacket = server_format->nSamplesPerSec / 100; + open.initialFormat = matched_index; + open.captureFormat = *server_format; + + priv->isAudioInStreamOpened = TRUE; + + return context->SendOpen(context, &open); +} + +static UINT +rdp_audioin_client_data(audin_server_context* context, + const SNDIN_DATA* data) +{ + struct audio_in_private *priv = context->userdata; + const AUDIO_FORMAT *server_format = &rdp_audioin_supported_audio_formats[0]; + const BYTE *buffer; + size_t bytes; + ssize_t sent; + + if (!priv->isAudioInStreamOpened || priv->pulseAudioSourceFd == -1) { + weston_log("RDPAudioIn - audio stream is not opened.\n"); + return CHANNEL_RC_OK; + } + + bytes = Stream_Length(data->Data); + if (bytes == 0) + return CHANNEL_RC_OK; + + buffer = Stream_Buffer(data->Data); + assert(buffer != NULL); + assert(server_format->wFormatTag == WAVE_FORMAT_PCM); + assert(server_format->nChannels == 1); + assert(server_format->nSamplesPerSec == 44100); + assert(server_format->wBitsPerSample == 16); + + sent = send(priv->pulseAudioSourceFd, buffer, bytes, 0); + if (sent != (ssize_t)bytes) { + rdp_audio_debug(priv, "RDP AudioIn source send failed (sent:%zd, bytes:%zu) %s\n", + sent, bytes, strerror(errno)); + + /* Unblock worker thread to close pipe to pulseaudio */ + uint64_t one = 1; + if (write(priv->closeAudioSourceFd, &one, sizeof(one)) != sizeof(uint64_t)) { + weston_log("RDP AudioIn error at Data while writing to closeAudioSourceFd (%s)\n", strerror(errno)); + return ERROR_INTERNAL_ERROR; + } + + if (sent <= 0) + return ERROR_INTERNAL_ERROR; + } + + return CHANNEL_RC_OK; +} +#endif /* FREERDP_VERSION_MAJOR >= 3 */ static void* rdp_audioin_source_thread(void *context) @@ -465,22 +572,10 @@ rdp_audioin_source_thread(void *context) return NULL; } -#endif /* FREERDP_VERSION_MAJOR < 3 */ void * rdp_audio_in_init(struct weston_compositor *c, HANDLE vcm) { -#if FREERDP_VERSION_MAJOR >= 3 - /* TODO: Port to the FreeRDP 3.x audin_server_context PDU-based API - * (SendVersion/SendFormats/SendOpen/SendFormatChange + callbacks). - * The 2.x context members (num_server_formats, server_formats, - * Opening, OpenResult, ReceiveSamples, dst_format, frames_per_packet) - * have all been removed. NULL signals "no audio in" which the - * compositor handles gracefully. */ - (void)c; - (void)vcm; - return NULL; -#else struct audio_in_private *priv; priv = xzalloc(sizeof *priv); @@ -499,6 +594,24 @@ rdp_audio_in_init(struct weston_compositor *c, HANDLE vcm) priv->pulseAudioSourceFd = -1; priv->closeAudioSourceFd = -1; +#if FREERDP_VERSION_MAJOR >= 3 + priv->audin_server_context->userdata = (void*)priv; + /* + * Override ReceiveFormats to send our negotiated (mono) captureFormat + * in the SNDIN_OPEN PDU instead of the default-handler's hardcoded + * stereo. The default ReceiveVersion / ReceiveFormatChange handlers + * installed by audin_server_context_new are fine to keep. + */ + priv->audin_server_context->ReceiveFormats = rdp_audioin_client_receive_formats; + priv->audin_server_context->Data = rdp_audioin_client_data; + + if (!audin_server_set_formats(priv->audin_server_context, + ARRAYSIZE(rdp_audioin_supported_audio_formats), + rdp_audioin_supported_audio_formats)) { + weston_log("RDPAudioIn - audin_server_set_formats failed.\n"); + goto Error_Exit; + } +#else // this will be freed by FreeRDP at audin_server_context_free. AUDIO_FORMAT *audio_formats = malloc(sizeof rdp_audioin_supported_audio_formats); if (!audio_formats) { @@ -515,6 +628,7 @@ rdp_audio_in_init(struct weston_compositor *c, HANDLE vcm) priv->audin_server_context->server_formats = audio_formats; priv->audin_server_context->dst_format = &rdp_audioin_supported_audio_formats[0]; priv->audin_server_context->frames_per_packet = rdp_audioin_supported_audio_formats[0].nSamplesPerSec / 100; // 10ms per packet +#endif /* FREERDP_VERSION_MAJOR >= 3 */ priv->closeAudioSourceFd = eventfd(0, EFD_CLOEXEC); if (priv->closeAudioSourceFd < 0) { @@ -556,17 +670,11 @@ rdp_audio_in_init(struct weston_compositor *c, HANDLE vcm) free(priv); return NULL; // Continue without audio -#endif /* FREERDP_VERSION_MAJOR >= 3 */ } void rdp_audio_in_destroy(void *audio_in_private) { -#if FREERDP_VERSION_MAJOR >= 3 - /* Paired with the NULL-returning init: nothing to tear down. */ - (void)audio_in_private; - return; -#else struct audio_in_private *priv = audio_in_private; if (priv->audin_server_context) { @@ -598,5 +706,4 @@ rdp_audio_in_destroy(void *audio_in_private) priv->audin_server_context = NULL; } free(priv); -#endif /* FREERDP_VERSION_MAJOR >= 3 */ } diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 63dd8ab1b..0c0dbcd97 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -1125,24 +1125,8 @@ xf_peer_activate(freerdp_peer* client) /* override settings by env variables */ (void)freerdp_settings_set_bool(settings, FreeRDP_RedirectClipboard, b->redirect_clipboard); -#if FREERDP_VERSION_MAJOR >= 3 - /* TODO: rdpsnd/audin server-side context APIs were rewritten in FreeRDP 3.x to a - * PDU-callback model (SendVersion/SendFormats/SendOpen/IncomingData...). The wslg - * port has not been completed yet, so disable audio negotiation on FreeRDP 3 to - * avoid advertising a capability we cannot service. */ - { - static bool warned; - if (!warned) { - weston_log("RDP audio playback/capture disabled: FreeRDP 3.x port pending\n"); - warned = true; - } - } - (void)freerdp_settings_set_bool(settings, FreeRDP_AudioPlayback, FALSE); - (void)freerdp_settings_set_bool(settings, FreeRDP_AudioCapture, FALSE); -#else (void)freerdp_settings_set_bool(settings, FreeRDP_AudioPlayback, b->audio_out_setup && b->audio_out_teardown); (void)freerdp_settings_set_bool(settings, FreeRDP_AudioCapture, b->audio_in_setup && b->audio_in_teardown); -#endif if (freerdp_settings_get_bool(settings, FreeRDP_RemoteApplicationMode) || freerdp_settings_get_bool(settings, FreeRDP_RedirectClipboard) || From 01dc3633d5a9e5a6f443c2c1a647d44efcec8ecf Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Tue, 19 May 2026 23:11:38 +0000 Subject: [PATCH 8/8] rdp-backend: cast nodiscard Close() return in audin source thread FreeRDP 3.x marks audin_server_context->Close as WINPR_ATTR_NODISCARD (see include/freerdp/server/audin.h). The two call sites in rdp_audioin_source_thread were discarding the return value without a (void) cast, producing -Wunused-result noise. rdpaudio.c already follows the (void)-cast pattern for the same nodiscard methods on RdpsndServerContext; apply the same pattern here for consistency. No behavioral change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- compositor/rdpaudioin.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compositor/rdpaudioin.c b/compositor/rdpaudioin.c index bdb12619a..7e005ff8b 100644 --- a/compositor/rdpaudioin.c +++ b/compositor/rdpaudioin.c @@ -551,7 +551,7 @@ rdp_audioin_source_thread(void *context) weston_log("RDP AudioIn wait on eventfd failed. thread exiting. %s\n", strerror(errno)); break; } - priv->audin_server_context->Close(priv->audin_server_context); + (void)priv->audin_server_context->Close(priv->audin_server_context); rdp_audio_debug(priv, "RDP AudioIn closed.\n"); } else { weston_log("Failed to open audio in connection with RDP client.\n"); @@ -563,7 +563,7 @@ rdp_audioin_source_thread(void *context) } if (priv->audin_server_context->IsOpen(priv->audin_server_context)) - priv->audin_server_context->Close(priv->audin_server_context); + (void)priv->audin_server_context->Close(priv->audin_server_context); if (priv->pulseAudioSourceFd != -1) { close(priv->pulseAudioSourceFd);