Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions src/protocols/rdp/channels/disp.c
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,23 @@ void guac_rdp_disp_update_size(guac_rdp_disp* disp,
}

else if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE) {

/*
* Forward any requested desktop scale factor to the server so the
* remote session keeps scaling its UI across dynamic resizes. Both
* fields default to 0 (no scaling requested), which the server ignores
* per [MS-RDPEDISP]. When set, the desktop scale factor MUST be paired
* with a protocol-valid device scale factor (100, 140, or 180) or both
* are ignored.
*/
UINT32 desktop_scale_factor = 0;
UINT32 device_scale_factor = 0;
if (settings->desktop_scale_factor != 0) {
desktop_scale_factor = settings->desktop_scale_factor;
device_scale_factor =
guac_rdp_get_device_scale_factor(settings->desktop_scale_factor);
}

DISPLAY_CONTROL_MONITOR_LAYOUT monitors[1] = {{
.Flags = 0x1, /* DISPLAYCONTROL_MONITOR_PRIMARY */
.Left = 0,
Expand All @@ -233,8 +250,8 @@ void guac_rdp_disp_update_size(guac_rdp_disp* disp,
.PhysicalWidth = 0,
.PhysicalHeight = 0,
.Orientation = 0,
.DesktopScaleFactor = 0,
.DeviceScaleFactor = 0
.DesktopScaleFactor = desktop_scale_factor,
.DeviceScaleFactor = device_scale_factor
}};

/* Send display update notification if display channel is connected */
Expand Down
98 changes: 98 additions & 0 deletions src/protocols/rdp/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ const char* GUAC_RDP_CLIENT_ARGS[] = {

"force-lossless",
"normalize-clipboard",
"desktop-scale-factor",
NULL
};

Expand Down Expand Up @@ -740,6 +741,17 @@ enum RDP_ARGS_IDX {
*/
IDX_NORMALIZE_CLIPBOARD,

/**
* The desktop scale factor to request from the RDP server, as a percentage
* between 100 and 500. When set, the remote Windows session is asked to
* scale its UI accordingly (the equivalent of the Windows display scaling
* setting), keeping UI and text comfortably sized even when a
* high-resolution (device-pixel) framebuffer is requested. If omitted or
* set to 0, no scale factor is sent and the server uses its default (no
* scaling).
*/
IDX_DESKTOP_SCALE_FACTOR,

RDP_ARGS_COUNT
};

Expand Down Expand Up @@ -911,6 +923,45 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
settings->height,
settings->resolution);

/*
* Desktop scale factor to request from the RDP server, as a percentage. A
* value of 0 (the default) disables the feature entirely, preserving the
* historical behavior of never asking the server to scale its session UI.
* Any non-zero value is clamped to the range permitted by [MS-RDPBCGR]
* (100-500), as values outside that range are silently ignored by the
* server.
*/
settings->desktop_scale_factor =
guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_DESKTOP_SCALE_FACTOR, 0);

if (settings->desktop_scale_factor != 0) {

if (settings->desktop_scale_factor < 100) {
guac_user_log(user, GUAC_LOG_WARNING,
"Requested desktop scale factor of %i%% is below the "
"minimum supported by RDP. Clamping to 100%%.",
settings->desktop_scale_factor);
settings->desktop_scale_factor = 100;
}

else if (settings->desktop_scale_factor > 500) {
guac_user_log(user, GUAC_LOG_WARNING,
"Requested desktop scale factor of %i%% is above the "
"maximum supported by RDP. Clamping to 500%%.",
settings->desktop_scale_factor);
settings->desktop_scale_factor = 500;
}

guac_user_log(user, GUAC_LOG_DEBUG,
"Requesting desktop scale factor of %i%% "
"(device scale factor %i%%)",
settings->desktop_scale_factor,
guac_rdp_get_device_scale_factor(
settings->desktop_scale_factor));

}

/* Lossless compression */
settings->lossless =
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
Expand Down Expand Up @@ -1544,6 +1595,27 @@ int guac_rdp_get_depth(freerdp* rdp) {
#endif
}

int guac_rdp_get_device_scale_factor(int desktop_scale_factor) {

/*
* Per [MS-RDPBCGR] and [MS-RDPEDISP], the device scale factor MUST be one
* of exactly 100, 140, or 180 percent. Any other value causes both the
* device scale factor AND the accompanying desktop scale factor to be
* ignored by the server, so we always map to one of these three values.
* The thresholds mirror the behavior of native RDP clients (mstsc /
* FreeRDP): scales below 120% use 100, 120%-160% use 140, and anything
* higher uses 180.
*/
if (desktop_scale_factor < 120)
return 100;

if (desktop_scale_factor <= 160)
return 140;

return 180;

}

void guac_rdp_push_settings(guac_client* client,
guac_rdp_settings* guac_settings, freerdp* rdp) {

Expand All @@ -1567,6 +1639,19 @@ void guac_rdp_push_settings(guac_client* client,
freerdp_settings_set_string(rdp_settings, FreeRDP_AlternateShell, guac_strdup(guac_settings->initial_program));
freerdp_settings_set_uint32(rdp_settings, FreeRDP_KeyboardLayout, guac_settings->server_layout->freerdp_keyboard_layout);

/*
* Request server-side UI scaling if a desktop scale factor was specified.
* These values are sent to the server within the client core data
* ([MS-RDPBCGR] TS_UD_CS_CORE) during connection negotiation. The desktop
* scale factor MUST be paired with a protocol-valid device scale factor
* (100, 140, or 180), otherwise the server ignores both values.
*/
if (guac_settings->desktop_scale_factor != 0) {
freerdp_settings_set_uint32(rdp_settings, FreeRDP_DesktopScaleFactor,
guac_settings->desktop_scale_factor);
freerdp_settings_set_uint32(rdp_settings, FreeRDP_DeviceScaleFactor,
guac_rdp_get_device_scale_factor(guac_settings->desktop_scale_factor));
}

/* Performance flags */
/* Explicitly set flag value */
Expand Down Expand Up @@ -1803,6 +1888,19 @@ void guac_rdp_push_settings(guac_client* client,
rdp_settings->AlternateShell = guac_strdup(guac_settings->initial_program);
rdp_settings->KeyboardLayout = guac_settings->server_layout->freerdp_keyboard_layout;

/*
* Request server-side UI scaling if a desktop scale factor was specified.
* These values are sent to the server within the client core data
* ([MS-RDPBCGR] TS_UD_CS_CORE) during connection negotiation. The desktop
* scale factor MUST be paired with a protocol-valid device scale factor
* (100, 140, or 180), otherwise the server ignores both values.
*/
if (guac_settings->desktop_scale_factor != 0) {
rdp_settings->DesktopScaleFactor = guac_settings->desktop_scale_factor;
rdp_settings->DeviceScaleFactor =
guac_rdp_get_device_scale_factor(guac_settings->desktop_scale_factor);
}

/* Performance flags */
/* Explicitly set flag value */
rdp_settings->PerformanceFlags = guac_rdp_get_performance_flags(guac_settings);
Expand Down
32 changes: 32 additions & 0 deletions src/protocols/rdp/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,19 @@ typedef struct guac_rdp_settings {
*/
int resolution;

/**
* The desktop scale factor to request from the RDP server, as a
* percentage. When set to a value between 100 and 500, the remote Windows
* session is asked to scale its UI accordingly (the equivalent of the
* Windows "Scale and layout" / display scaling setting). This value is sent
* to the server as the [MS-RDPBCGR] desktopScaleFactor (paired with a
* protocol-valid deviceScaleFactor), allowing a high-resolution
* (device-pixel) framebuffer to remain razor sharp while keeping UI and
* text at a comfortable size. A value of 0 (the default) disables the
* feature, and no scale factor is sent to the server.
*/
int desktop_scale_factor;

/**
* Whether all graphical updates for this connection should use lossless
* compression only.
Expand Down Expand Up @@ -753,6 +766,25 @@ extern const char* GUAC_RDP_CLIENT_ARGS[];
void guac_rdp_push_settings(guac_client* client,
guac_rdp_settings* guac_settings, freerdp* rdp);

/**
* Returns the RDP device scale factor that should accompany the given desktop
* scale factor. Per [MS-RDPBCGR] (Client Core Data) and [MS-RDPEDISP]
* (DISPLAYCONTROL_MONITOR_LAYOUT), the device scale factor MUST be exactly
* 100, 140, or 180 percent, otherwise both the device scale factor AND the
* accompanying desktop scale factor are ignored by the server. The mapping
* used here mirrors the behavior of native clients (mstsc / FreeRDP):
* a desktop scale below 120% maps to 100, 120%-160% maps to 140, and anything
* higher maps to 180.
*
* @param desktop_scale_factor
* The desktop scale factor, as a percentage (typically 100-500).
*
* @return
* The protocol-valid device scale factor (100, 140, or 180) to pair with
* the given desktop scale factor.
*/
int guac_rdp_get_device_scale_factor(int desktop_scale_factor);

/**
* Returns the width of the RDP session display.
*
Expand Down