mirror of
https://github.com/morgan9e/grd
synced 2026-04-15 00:44:04 +09:00
371 lines
13 KiB
C
371 lines
13 KiB
C
|
|
/*
|
||
|
|
* 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>
|
||
|
|
|
||
|
|
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);
|
||
|
|
}
|