/* * 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 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; } static 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 gboolean 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, GError **error) { if (width < 200 || height < 200 || width > 8192 || height > 8192) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid monitor dimensions"); return FALSE; } monitor->pos_x = pos_x; monitor->pos_y = pos_y; monitor->width = width; monitor->height = 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); return TRUE; } /** * 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, GError **error) { g_autoptr (GrdRdpMonitorConfig) monitor_config = NULL; GrdRdpVirtualMonitor *virtual_monitor; uint32_t desktop_width = freerdp_settings_get_uint32 (rdp_settings, FreeRDP_DesktopWidth); uint32_t desktop_height = freerdp_settings_get_uint32 (rdp_settings, FreeRDP_DesktopHeight); uint32_t desktop_physical_width = freerdp_settings_get_uint32 (rdp_settings, FreeRDP_DesktopPhysicalWidth); uint32_t desktop_physical_height = freerdp_settings_get_uint32 (rdp_settings, FreeRDP_DesktopPhysicalHeight); uint32_t desktop_orientation = freerdp_settings_get_uint16 (rdp_settings, FreeRDP_DesktopOrientation); uint32_t desktop_scale_factor = freerdp_settings_get_uint32 (rdp_settings, FreeRDP_DesktopScaleFactor); monitor_config = g_malloc0 (sizeof (GrdRdpMonitorConfig)); monitor_config->is_virtual = TRUE; monitor_config->monitor_count = 1; monitor_config->virtual_monitors = g_new0 (GrdRdpVirtualMonitor, 1); virtual_monitor = &monitor_config->virtual_monitors[0]; /* Ignore the DeviceScaleFactor. It is deprecated (Win 8.1 only) */ if (!write_sanitized_monitor_data (virtual_monitor, 0, 0, desktop_width, desktop_height, TRUE, desktop_physical_width, desktop_physical_height, desktop_orientation, desktop_scale_factor, error)) return NULL; monitor_config->desktop_width = virtual_monitor->width; monitor_config->desktop_height = virtual_monitor->height; return g_steal_pointer (&monitor_config); } static gboolean determine_primary_monitor (GrdRdpMonitorConfig *monitor_config, GError **error) { 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 TRUE; } } g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No suitable primary monitor in monitor layout"); return FALSE; } static gboolean verify_monitor_config (GrdRdpMonitorConfig *monitor_config, GError **error) { 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_y; 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) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Monitor overlaps other monitor in layout"); cairo_region_destroy (region); return FALSE; } cairo_region_union_rectangle (region, &rect); } /* Calculate the size of the desktop and Graphics Output Buffer ADM element */ cairo_region_get_extents (region, &rect); monitor_config->desktop_width = rect.width; monitor_config->desktop_height = rect.height; /* We already checked, that the primary monitor is at (0, 0) */ g_assert (rect.x <= 0); g_assert (rect.y <= 0); /* Determine monitor offset in input- and output-region */ monitor_config->layout_offset_x = rect.x; monitor_config->layout_offset_y = 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, GError **error) { g_autoptr (GrdRdpMonitorConfig) monitor_config = NULL; const rdpMonitor *monitors = freerdp_settings_get_pointer_array (rdp_settings, FreeRDP_MonitorDefArray, 0); uint32_t monitor_count = freerdp_settings_get_uint32 (rdp_settings, FreeRDP_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) { gboolean has_monitor_attributes = freerdp_settings_get_bool (rdp_settings, FreeRDP_HasMonitorAttributes); const rdpMonitor *monitor = &monitors[i]; const 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 || monitor->x != 0 || monitor->y != 0) is_primary = FALSE; if (!found_primary_monitor && is_primary) found_primary_monitor = TRUE; if (has_monitor_attributes) { 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) */ if (!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, error)) return NULL; } if (!found_primary_monitor && !determine_primary_monitor (monitor_config, error)) return NULL; if (!verify_monitor_config (monitor_config, error)) return NULL; return g_steal_pointer (&monitor_config); } GrdRdpMonitorConfig * grd_rdp_monitor_config_new_from_client_data (rdpSettings *rdp_settings, uint32_t max_monitor_count, GError **error) { uint32_t monitor_count = freerdp_settings_get_uint32 (rdp_settings, FreeRDP_MonitorCount); if (monitor_count == 0 || monitor_count > max_monitor_count) return create_monitor_config_from_client_core_data (rdp_settings, error); return create_monitor_config_from_client_monitor_data (rdp_settings, error); } /** * 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, GError **error) { g_autoptr (GrdRdpMonitorConfig) monitor_config = NULL; uint32_t monitor_count = monitor_layout->NumMonitors; gboolean found_primary_monitor = FALSE; uint32_t i; if (monitor_count == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Monitor Layout PDU contains no monitors"); return NULL; } 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 || monitor->Left != 0 || monitor->Top != 0) is_primary = FALSE; if (!found_primary_monitor && is_primary) found_primary_monitor = TRUE; /* Ignore the DeviceScaleFactor. It is deprecated (Win 8.1 only) */ if (!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, error)) return NULL; } if (!found_primary_monitor && !determine_primary_monitor (monitor_config, error)) return NULL; if (!verify_monitor_config (monitor_config, error)) return NULL; return g_steal_pointer (&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); }