Skip to content

feat(acceptor): expose the client's keyboard layout on AcceptorResult#1397

Merged
Benoît Cortier (CBenoit) merged 1 commit into
Devolutions:masterfrom
clintcan:feat/acceptor-expose-keyboard-layout
Jul 1, 2026
Merged

feat(acceptor): expose the client's keyboard layout on AcceptorResult#1397
Benoît Cortier (CBenoit) merged 1 commit into
Devolutions:masterfrom
clintcan:feat/acceptor-expose-keyboard-layout

Conversation

@clintcan

@clintcan clintcan commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

What

The client announces its keyboard layout identifier (KLID) in the GCC Client Core Data (MS-RDPBCGR 2.2.1.3.2, keyboardLayout). ironrdp-acceptor already decodes that block in BasicSettingsWaitInitial but keeps only the early-capability flags, so a server built on the acceptor has no way to learn the client's layout.

This adds AcceptorResult::keyboard_layout: u32 (the announced KLID; 0 when the client didn't send one), captured during BasicSettingsWaitInitial and stored on Acceptor so it survives a deactivation-reactivation (same as desktop_size).

Why

A server can use it to select a matching server-side keyboard layout without touching any local input state — e.g. translating positional scancodes through the client's announced layout for non-US clients. Today the KLID is decoded and then thrown away.

This mirrors the existing pattern of surfacing already-parsed client data on AcceptorResult:

The motivating downstream is a macOS RDP server (macrdp) that auto-detects the client's keyboard layout; this is the last piece it vendors a fork of the acceptor for on this front.

Notes

  • Additive field on a result struct that only get_result constructs internally — labeled feat(acceptor): to match feat(acceptor): expose received client credentials in AcceptorResult #1155/feat(acceptor): negotiate the MCS message channel #1347 (same shape).
  • No new tests: the acceptor lib sets test = false (Cargo.toml) and there's no acceptor harness in ironrdp-testsuite-core, so there's no in-tree place to assert it (the value is a straight passthrough of the already-decoded gcc_blocks.core.keyboard_layout). Happy to add coverage wherever you'd prefer it to live.
  • cargo build / cargo fmt --check / cargo clippy -p ironrdp-acceptor --all-targets -- -D warnings clean (only the pre-existing workspace MSRV-config notice).

The client announces its keyboard layout identifier (KLID) in the GCC
Client Core Data (MS-RDPBCGR 2.2.1.3.2, `keyboardLayout`). The acceptor
already decodes that block but keeps only the early-capability flags, so
a server built on ironrdp-acceptor has no way to learn the client's
layout.

Surface it on `AcceptorResult::keyboard_layout` (0 when the client did
not announce one), mirroring the existing `credentials` (Devolutions#1155) and
`message_channel_id` (Devolutions#1347) additions. It is captured in
BasicSettingsWaitInitial and stored on `Acceptor`, so it survives a
deactivation-reactivation like `desktop_size`.

A server can use it to select a matching server-side keyboard layout
without touching any local input state — e.g. translating positional
scancodes through the announced layout for non-US clients.
@clintcan

clintcan commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Flagging this small one for your queue 🙂 — CI green and mergeable. It's a companion to #1373: just surfaces the client's already-decoded KLID on AcceptorResult (same additive shape as #1155 credentials and #1347 message_channel_id), so a server can pick a matching layout without a vendored acceptor fork.

Only open point is testing: the acceptor lib is test = false and there's no acceptor harness in ironrdp-testsuite-core, so I left it untested (it's a straight passthrough of gcc_blocks.core.keyboard_layout). Happy to add coverage wherever you'd prefer it.

@CBenoit Benoît Cortier (CBenoit) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Simple enough 🙂

@CBenoit Benoît Cortier (CBenoit) merged commit 5ca84a5 into Devolutions:master Jul 1, 2026
22 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR exposes the client-announced keyboard layout identifier (KLID) from the GCC Client Core Data on ironrdp-acceptor’s AcceptorResult, so server implementations can select a matching server-side keyboard layout (and keep the value across deactivation/reactivation).

Changes:

  • Add keyboard_layout: u32 storage on Acceptor and propagate it through deactivation/reactivation.
  • Expose keyboard_layout: u32 on AcceptorResult.
  • Capture the value during BasicSettingsWaitInitial from gcc_blocks.core.keyboard_layout.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@@ -54,6 +55,14 @@ pub struct AcceptorResult {
/// channel. `None` when the client did not request it.
Comment on lines +61 to +64
/// This is the low word of a Windows locale identifier (e.g. `0x0000_0409`
/// for US English, `0x0000_040C` for French). `0` when the client did not
/// announce one. Servers can use it to pick a server-side keyboard layout
/// matching the client without changing any local input state.
Comment on lines 450 to 454
let gcc_blocks = settings_initial.conference_create_request.into_gcc_blocks();
let early_capability = gcc_blocks.core.optional_data.early_capability_flags;
let client_wants_message_channel = gcc_blocks.message_channel.is_some();
self.keyboard_layout = gcc_blocks.core.keyboard_layout;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants