[gnome-remote-desktop] rdp: Add RDP monitor config
- From: Jonas Ådahl <jadahl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-remote-desktop] rdp: Add RDP monitor config
- Date: Thu, 3 Mar 2022 14:23:09 +0000 (UTC)
commit 51f4595b700fd7950e8ebf0c5aec3e75b3fb473e
Author: Pascal Nowack <Pascal Nowack gmx de>
Date: Tue Dec 7 14:44:20 2021 +0100
rdp: Add RDP monitor config
RDP provides three different ways for clients to submit monitor
configurations:
1. via the Client Core Data (only single-monitor setups possible)
2. via the Client Monitor Data (allows multiple monitors)
3. via the Display Control Channel Extension
The first two ways are only used for the initial monitor configuration,
when connecting to an RDP server.
The third way is used to update the monitor configuration, when the
remote desktop session is already established.
To be able to handle monitor configurations easily, add a new helper
struct with helper functions, which allows to create internal monitor
configurations from any of these three sources.
src/grd-rdp-monitor-config.c | 310 +++++++++++++++++++++++++++++++++++++++++++
src/grd-rdp-monitor-config.h | 80 +++++++++++
src/meson.build | 2 +
3 files changed, 392 insertions(+)
---
diff --git a/src/grd-rdp-monitor-config.c b/src/grd-rdp-monitor-config.c
new file mode 100644
index 00000000..92cb912f
--- /dev/null
+++ b/src/grd-rdp-monitor-config.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2021 Pascal Nowack
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "grd-rdp-monitor-config.h"
+
+#include <cairo/cairo.h>
+
+#define CLAMP_DESKTOP_SIZE(value) MAX (MIN (value, 8192), 200)
+
+static uint32_t
+sanitize_value (uint32_t value,
+ uint32_t lower_bound,
+ uint32_t upper_bound)
+{
+ g_assert (lower_bound < upper_bound);
+ g_assert (lower_bound > 0);
+
+ if (value < lower_bound || value > upper_bound)
+ return 0;
+
+ return value;
+}
+
+GrdRdpMonitorOrientation
+transform_monitor_orientation (uint32_t value)
+{
+ switch (value)
+ {
+ case ORIENTATION_LANDSCAPE:
+ return GRD_RDP_MONITOR_DIRECTION_LANDSCAPE;
+ case ORIENTATION_PORTRAIT:
+ return GRD_RDP_MONITOR_DIRECTION_PORTRAIT;
+ case ORIENTATION_LANDSCAPE_FLIPPED:
+ return GRD_RDP_MONITOR_DIRECTION_LANDSCAPE_FLIPPED;
+ case ORIENTATION_PORTRAIT_FLIPPED:
+ return GRD_RDP_MONITOR_DIRECTION_PORTRAIT_FLIPPED;
+ default:
+ return GRD_RDP_MONITOR_DIRECTION_LANDSCAPE;
+ }
+
+ g_assert_not_reached ();
+}
+
+static void
+write_sanitized_monitor_data (GrdRdpVirtualMonitor *monitor,
+ int32_t pos_x,
+ int32_t pos_y,
+ uint32_t width,
+ uint32_t height,
+ gboolean is_primary,
+ uint32_t physical_width,
+ uint32_t physical_height,
+ uint32_t orientation,
+ uint32_t scale)
+{
+ monitor->pos_x = pos_x;
+ monitor->pos_y = pos_y;
+ monitor->width = CLAMP_DESKTOP_SIZE (width);
+ monitor->height = CLAMP_DESKTOP_SIZE (height);
+ monitor->is_primary = is_primary;
+
+ monitor->physical_width = sanitize_value (physical_width, 10, 10000);
+ monitor->physical_height = sanitize_value (physical_height, 10, 10000);
+ if (monitor->physical_width == 0 || monitor->physical_height == 0)
+ monitor->physical_width = monitor->physical_height = 0;
+
+ monitor->orientation = transform_monitor_orientation (orientation);
+ monitor->scale = sanitize_value (scale, 100, 500);
+}
+
+/**
+ * Create a new monitor config from the Client Core Data data block
+ * ([MS-RDPBCGR] 2.2.1.3.2 Client Core Data (TS_UD_CS_CORE))
+ */
+static GrdRdpMonitorConfig *
+create_monitor_config_from_client_core_data (rdpSettings *rdp_settings)
+{
+ GrdRdpMonitorConfig *monitor_config;
+
+ monitor_config = g_malloc0 (sizeof (GrdRdpMonitorConfig));
+ monitor_config->is_virtual = TRUE;
+ monitor_config->monitor_count = 1;
+ monitor_config->virtual_monitors = g_new0 (GrdRdpVirtualMonitor, 1);
+
+ /* Ignore the DeviceScaleFactor. It is deprecated (Win 8.1 only) */
+ write_sanitized_monitor_data (&monitor_config->virtual_monitors[0],
+ 0, 0,
+ rdp_settings->DesktopWidth,
+ rdp_settings->DesktopHeight,
+ TRUE,
+ rdp_settings->DesktopPhysicalWidth,
+ rdp_settings->DesktopPhysicalHeight,
+ rdp_settings->DesktopOrientation,
+ rdp_settings->DesktopScaleFactor);
+
+ monitor_config->gpo_width = monitor_config->virtual_monitors[0].width;
+ monitor_config->gpo_height = monitor_config->virtual_monitors[0].height;
+
+ return monitor_config;
+}
+
+static void
+determine_primary_monitor (GrdRdpMonitorConfig *monitor_config)
+{
+ uint32_t i;
+
+ for (i = 0; i < monitor_config->monitor_count; ++i)
+ {
+ if (monitor_config->virtual_monitors[i].pos_x == 0 &&
+ monitor_config->virtual_monitors[i].pos_y == 0)
+ {
+ monitor_config->virtual_monitors[i].is_primary = TRUE;
+ return;
+ }
+ }
+
+ monitor_config->virtual_monitors[0].is_primary = TRUE;
+}
+
+static gboolean
+verify_monitor_config (GrdRdpMonitorConfig *monitor_config)
+{
+ cairo_region_t *region;
+ cairo_rectangle_int_t rect;
+ uint32_t i;
+
+ /* Check for overlapping monitors */
+ region = cairo_region_create ();
+ for (i = 0; i < monitor_config->monitor_count; ++i)
+ {
+ rect.x = monitor_config->virtual_monitors[i].pos_x;
+ rect.y = monitor_config->virtual_monitors[i].pos_x;
+ rect.width = monitor_config->virtual_monitors[i].width;
+ rect.height = monitor_config->virtual_monitors[i].height;
+
+ if (cairo_region_contains_rectangle (region, &rect) != CAIRO_REGION_OVERLAP_OUT)
+ {
+ cairo_region_destroy (region);
+ return FALSE;
+ }
+ cairo_region_union_rectangle (region, &rect);
+ }
+
+ /* Calculate size of Graphics Output Buffer ADM element */
+ cairo_region_get_extents (region, &rect);
+ monitor_config->gpo_width = rect.width - rect.x;
+ monitor_config->gpo_height = rect.height - rect.y;
+ cairo_region_destroy (region);
+
+ return TRUE;
+}
+
+/**
+ * Create a new monitor config from the Client Monitor Data packet
+ * ([MS-RDPBCGR] 2.2.1.3.6 Client Monitor Data (TS_UD_CS_MONITOR))
+ */
+static GrdRdpMonitorConfig *
+create_monitor_config_from_client_monitor_data (rdpSettings *rdp_settings)
+{
+ GrdRdpMonitorConfig *monitor_config;
+ uint32_t monitor_count = rdp_settings->MonitorCount;
+ gboolean found_primary_monitor = FALSE;
+ uint32_t i;
+
+ g_assert (monitor_count > 0);
+
+ monitor_config = g_malloc0 (sizeof (GrdRdpMonitorConfig));
+ monitor_config->is_virtual = TRUE;
+ monitor_config->monitor_count = monitor_count;
+ monitor_config->virtual_monitors = g_new0 (GrdRdpVirtualMonitor,
+ monitor_count);
+
+ for (i = 0; i < monitor_count; ++i)
+ {
+ rdpMonitor *monitor = &rdp_settings->MonitorDefArray[i];
+ MONITOR_ATTRIBUTES *monitor_attributes = &monitor->attributes;
+ gboolean is_primary = !!monitor->is_primary;
+ uint32_t physical_width = 0;
+ uint32_t physical_height = 0;
+ uint32_t orientation = 0;
+ uint32_t scale = 0;
+
+ if (found_primary_monitor)
+ is_primary = FALSE;
+ if (!found_primary_monitor && is_primary)
+ found_primary_monitor = TRUE;
+
+ if (rdp_settings->HasMonitorAttributes)
+ {
+ physical_width = monitor_attributes->physicalWidth;
+ physical_height = monitor_attributes->physicalHeight;
+ orientation = monitor_attributes->orientation;
+ scale = monitor_attributes->desktopScaleFactor;
+ }
+
+ /* Ignore the DeviceScaleFactor. It is deprecated (Win 8.1 only) */
+ write_sanitized_monitor_data (&monitor_config->virtual_monitors[i],
+ monitor->x,
+ monitor->y,
+ monitor->width,
+ monitor->height,
+ is_primary,
+ physical_width,
+ physical_height,
+ orientation,
+ scale);
+ }
+ if (!found_primary_monitor)
+ determine_primary_monitor (monitor_config);
+
+ if (!verify_monitor_config (monitor_config))
+ {
+ grd_rdp_monitor_config_free (monitor_config);
+ return NULL;
+ }
+
+ return monitor_config;
+}
+
+GrdRdpMonitorConfig *
+grd_rdp_monitor_config_new_from_client_data (rdpSettings *rdp_settings,
+ uint32_t max_monitor_count)
+{
+ if (rdp_settings->MonitorCount == 0 ||
+ rdp_settings->MonitorCount > max_monitor_count)
+ return create_monitor_config_from_client_core_data (rdp_settings);
+
+ return create_monitor_config_from_client_monitor_data (rdp_settings);
+}
+
+/**
+ * Create a new monitor config from DISPLAYCONTROL_MONITOR_LAYOUT PDU
+ * ([MS-RDPEDISP] 2.2.2.2.1 DISPLAYCONTROL_MONITOR_LAYOUT)
+ */
+GrdRdpMonitorConfig *
+grd_rdp_monitor_config_new_from_disp_monitor_layout (const DISPLAY_CONTROL_MONITOR_LAYOUT_PDU
*monitor_layout)
+{
+ GrdRdpMonitorConfig *monitor_config;
+ uint32_t monitor_count = monitor_layout->NumMonitors;
+ gboolean found_primary_monitor = FALSE;
+ uint32_t i;
+
+ g_assert (monitor_count > 0);
+
+ monitor_config = g_malloc0 (sizeof (GrdRdpMonitorConfig));
+ monitor_config->is_virtual = TRUE;
+ monitor_config->monitor_count = monitor_count;
+ monitor_config->virtual_monitors = g_new0 (GrdRdpVirtualMonitor,
+ monitor_count);
+
+ for (i = 0; i < monitor_count; ++i)
+ {
+ DISPLAY_CONTROL_MONITOR_LAYOUT *monitor = &monitor_layout->Monitors[i];
+ gboolean is_primary;
+
+ is_primary = !!(monitor->Flags & DISPLAY_CONTROL_MONITOR_PRIMARY);
+ if (found_primary_monitor)
+ is_primary = FALSE;
+ if (!found_primary_monitor && is_primary)
+ found_primary_monitor = TRUE;
+
+ /* Ignore the DeviceScaleFactor. It is deprecated (Win 8.1 only) */
+ write_sanitized_monitor_data (&monitor_config->virtual_monitors[i],
+ monitor->Left,
+ monitor->Top,
+ monitor->Width,
+ monitor->Height,
+ is_primary,
+ monitor->PhysicalWidth,
+ monitor->PhysicalHeight,
+ monitor->Orientation,
+ monitor->DesktopScaleFactor);
+ }
+ if (!found_primary_monitor)
+ determine_primary_monitor (monitor_config);
+
+ if (!verify_monitor_config (monitor_config))
+ {
+ grd_rdp_monitor_config_free (monitor_config);
+ return NULL;
+ }
+
+ return monitor_config;
+}
+
+void
+grd_rdp_monitor_config_free (GrdRdpMonitorConfig *monitor_config)
+{
+ g_clear_pointer (&monitor_config->connectors, g_strfreev);
+ g_free (monitor_config->virtual_monitors);
+ g_free (monitor_config);
+}
diff --git a/src/grd-rdp-monitor-config.h b/src/grd-rdp-monitor-config.h
new file mode 100644
index 00000000..13c0dfd4
--- /dev/null
+++ b/src/grd-rdp-monitor-config.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 Pascal Nowack
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef GRD_RDP_MONITOR_CONFIG_H
+#define GRD_RDP_MONITOR_CONFIG_H
+
+#include <freerdp/channels/disp.h>
+#include <gio/gio.h>
+
+typedef enum _GrdRdpMonitorOrientation
+{
+ /* The monitor is not rotated */
+ GRD_RDP_MONITOR_DIRECTION_LANDSCAPE = 0,
+ /* The monitor is rotated clockwise by 90 degrees */
+ GRD_RDP_MONITOR_DIRECTION_PORTRAIT = 1,
+ /* The monitor is rotated clockwise by 180 degrees */
+ GRD_RDP_MONITOR_DIRECTION_LANDSCAPE_FLIPPED = 2,
+ /* The monitor is rotated clockwise by 270 degrees */
+ GRD_RDP_MONITOR_DIRECTION_PORTRAIT_FLIPPED = 3,
+} GrdRdpMonitorOrientation;
+
+typedef struct _GrdRdpVirtualMonitor
+{
+ int32_t pos_x;
+ int32_t pos_y;
+ uint32_t width; /* Valid values are in range of [200, 8192] */
+ uint32_t height; /* Valid values are in range of [200, 8192] */
+ gboolean is_primary;
+
+ /* Physical width of the monitor in millimeters. Ignore, if 0 */
+ uint32_t physical_width; /* Valid values are in range of [10, 10000] */
+ /* Physical height of the monitor in millimeters. Ignore, if 0 */
+ uint32_t physical_height; /* Valid values are in range of [10, 10000] */
+
+ GrdRdpMonitorOrientation orientation;
+
+ /* Monitor scale in percent. Ignore, if 0 */
+ uint32_t scale; /* Valid values are in range of [100, 500] */
+} GrdRdpVirtualMonitor;
+
+typedef struct _GrdRdpMonitorConfig
+{
+ gboolean is_virtual;
+
+ /* Server configs only */
+ char **connectors;
+ /* Client configs only */
+ GrdRdpVirtualMonitor *virtual_monitors;
+ /* Count of items in connectors or virtual_monitors */
+ uint32_t monitor_count;
+
+ /* Size of the Graphics Output Buffer ADM element */
+ uint32_t gpo_width;
+ uint32_t gpo_height;
+} GrdRdpMonitorConfig;
+
+GrdRdpMonitorConfig *grd_rdp_monitor_config_new_from_client_data (rdpSettings *rdp_settings,
+ uint32_t max_monitor_count);
+
+GrdRdpMonitorConfig *grd_rdp_monitor_config_new_from_disp_monitor_layout (const
DISPLAY_CONTROL_MONITOR_LAYOUT_PDU *monitor_layout);
+
+void grd_rdp_monitor_config_free (GrdRdpMonitorConfig *monitor_config);
+
+#endif /* GRD_RDP_MONITOR_CONFIG_H */
diff --git a/src/meson.build b/src/meson.build
index 54e733cc..1e409ab7 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -70,6 +70,8 @@ if have_rdp
'grd-rdp-gfx-surface.h',
'grd-rdp-graphics-pipeline.c',
'grd-rdp-graphics-pipeline.h',
+ 'grd-rdp-monitor-config.c',
+ 'grd-rdp-monitor-config.h',
'grd-rdp-network-autodetection.c',
'grd-rdp-network-autodetection.h',
'grd-rdp-pipewire-stream.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]