Merge pull request #2951 from metalefty/v0.10-monitor-hotplug

[v0.10] Fixes some problems with monitor hotplug
This commit is contained in:
metalefty 2024-02-21 00:41:30 +09:00 committed by GitHub
commit c3cb8554b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 770 additions and 588 deletions

View File

@ -74,6 +74,7 @@
/* Client Core Data: earlyCapabilityFlags (2.2.1.3.2) */
#define RNS_UD_CS_WANT_32BPP_SESSION 0x0002
#define RNS_UD_CS_SUPPORT_MONITOR_LAYOUT_PDU 0x0040
#define RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL 0x0100
/* Client Core Data: connectionType (2.2.1.3.2) */
@ -448,6 +449,7 @@
#define RDP_DATA_PDU_LOGON 38
#define RDP_DATA_PDU_FONT2 39
#define RDP_DATA_PDU_DISCONNECT 47
#define PDUTYPE2_MONITOR_LAYOUT_PDU 55
/* TS_SECURITY_HEADER: flags (2.2.8.1.1.2.1) */
/* TODO: to be renamed */

View File

@ -67,6 +67,13 @@ struct display_size_description
unsigned int session_height;
};
enum client_resize_mode
{
CRMODE_NONE,
CRMODE_SINGLE_SCREEN,
CRMODE_MULTI_SCREEN
};
/**
* Information about the xrdp client
*
@ -218,6 +225,9 @@ struct xrdp_client_info
int large_pointer_support_flags;
int gfx;
// Can we resize the desktop by using a Deactivation-Reactivation Sequence?
enum client_resize_mode client_resize_mode;
};
enum xrdp_encoder_flags

View File

@ -1136,43 +1136,10 @@ libxrdp_orders_send_font(struct xrdp_session *session,
}
/*****************************************************************************/
/* Note : if this is called on a multimon setup, the client is resized
* to a single monitor */
int EXPORT_CC
libxrdp_reset(struct xrdp_session *session,
unsigned int width, unsigned int height, int bpp)
libxrdp_reset(struct xrdp_session *session)
{
LOG_DEVEL(LOG_LEVEL_TRACE, "libxrdp_reset:");
if (session->client_info != 0)
{
struct xrdp_client_info *client_info = session->client_info;
/* older client can't resize */
if (client_info->build <= 419)
{
return 0;
}
/* if same (and only one monitor on client) don't need to do anything */
if (client_info->display_sizes.session_width == width &&
client_info->display_sizes.session_height == height &&
client_info->bpp == bpp &&
(client_info->display_sizes.monitorCount == 0 || client_info->multimon == 0))
{
return 0;
}
client_info->display_sizes.session_width = width;
client_info->display_sizes.session_height = height;
client_info->display_sizes.monitorCount = 0;
client_info->bpp = bpp;
client_info->multimon = 0;
}
else
{
LOG(LOG_LEVEL_ERROR, "libxrdp_reset: session->client_info is NULL");
return 1;
}
/* this will send any lingering orders */
if (xrdp_orders_reset((struct xrdp_orders *)session->orders) != 0)
@ -1801,6 +1768,19 @@ libxrdp_send_session_info(struct xrdp_session *session, const char *data,
static void
sanitise_extended_monitor_attributes(struct monitor_info *monitor_layout)
{
if (monitor_layout->physical_width == 0
&& monitor_layout->physical_width == 0
&& monitor_layout->orientation == 0
&& monitor_layout->desktop_scale_factor == 0
&& monitor_layout->device_scale_factor == 0)
{
/* Module expects us to provide defaults */
monitor_layout->orientation = ORIENTATION_LANDSCAPE;
monitor_layout->desktop_scale_factor = 100;
monitor_layout->device_scale_factor = 100;
return;
}
/* if EITHER physical_width or physical_height are
* out of range, BOTH must be ignored.
*/
@ -1891,16 +1871,16 @@ libxrdp_process_monitor_stream(struct stream *s,
{
uint32_t num_monitor;
uint32_t monitor_index;
struct monitor_info monitors[CLIENT_MONITOR_DATA_MAXIMUM_MONITORS];
struct monitor_info *monitor_layout;
struct xrdp_rect all_monitors_encompassing_bounds = {0};
int got_primary = 0;
int monitor_struct_stream_check_bytes;
const char *monitor_struct_stream_check_message;
LOG_DEVEL(LOG_LEVEL_TRACE, "libxrdp_process_monitor_stream:");
if (description == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "libxrdp_process_monitor_stream: description was"
LOG_DEVEL(LOG_LEVEL_ERROR,
"libxrdp_process_monitor_stream: description was"
" null. Valid pointer to allocated description expected.");
return SEC_PROCESS_MONITORS_ERR;
}
@ -1948,7 +1928,7 @@ libxrdp_process_monitor_stream(struct stream *s,
" from [MS-RDPEDISP] 2.2.2.2.1 DISPLAYCONTROL_MONITOR_LAYOUT.";
}
description->monitorCount = num_monitor;
memset(monitors, 0, sizeof(monitors[0]) * num_monitor);
for (monitor_index = 0; monitor_index < num_monitor; ++monitor_index)
{
@ -1960,7 +1940,8 @@ libxrdp_process_monitor_stream(struct stream *s,
return SEC_PROCESS_MONITORS_ERR;
}
monitor_layout = description->minfo + monitor_index;
monitor_layout = &monitors[monitor_index];
if (full_parameters != 0)
{
in_uint32_le(s, monitor_layout->flags);
@ -2026,8 +2007,6 @@ libxrdp_process_monitor_stream(struct stream *s,
in_uint32_le(s, monitor_layout->desktop_scale_factor);
in_uint32_le(s, monitor_layout->device_scale_factor);
sanitise_extended_monitor_attributes(monitor_layout);
/*
* 2.2.2.2.1 DISPLAYCONTROL_MONITOR_LAYOUT
*/
@ -2056,130 +2035,10 @@ libxrdp_process_monitor_stream(struct stream *s,
monitor_layout->is_primary = TS_MONITOR_PRIMARY;
}
}
if (monitor_index == 0)
{
all_monitors_encompassing_bounds.left = monitor_layout->left;
all_monitors_encompassing_bounds.top = monitor_layout->top;
all_monitors_encompassing_bounds.right = monitor_layout->right;
all_monitors_encompassing_bounds.bottom = monitor_layout->bottom;
}
else
{
all_monitors_encompassing_bounds.left =
MIN(monitor_layout->left,
all_monitors_encompassing_bounds.left);
all_monitors_encompassing_bounds.top =
MIN(monitor_layout->top,
all_monitors_encompassing_bounds.top);
all_monitors_encompassing_bounds.right =
MAX(all_monitors_encompassing_bounds.right,
monitor_layout->right);
all_monitors_encompassing_bounds.bottom =
MAX(all_monitors_encompassing_bounds.bottom,
monitor_layout->bottom);
}
if (monitor_layout->is_primary == TS_MONITOR_PRIMARY)
{
got_primary = 1;
}
}
if (!got_primary)
{
/* no primary monitor was set,
* choose the leftmost monitor as primary.
*/
for (monitor_index = 0; monitor_index < num_monitor; ++monitor_index)
{
monitor_layout = description->minfo + monitor_index;
if (monitor_layout->left
== all_monitors_encompassing_bounds.left
&& monitor_layout->top
== all_monitors_encompassing_bounds.top)
{
monitor_layout->is_primary = TS_MONITOR_PRIMARY;
break;
}
}
}
/* set wm geometry if the encompassing area is well formed.
Otherwise, log and return an error.
*/
if (all_monitors_encompassing_bounds.right
> all_monitors_encompassing_bounds.left
&& all_monitors_encompassing_bounds.bottom
> all_monitors_encompassing_bounds.top)
{
description->session_width =
all_monitors_encompassing_bounds.right
- all_monitors_encompassing_bounds.left + 1;
description->session_height =
all_monitors_encompassing_bounds.bottom
- all_monitors_encompassing_bounds.top + 1;
}
else
{
LOG(LOG_LEVEL_ERROR, "libxrdp_process_monitor_stream:"
" The area encompassing the monitors is not a"
" well-formed rectangle. Received"
" (top: %d, left: %d, right: %d, bottom: %d)."
" This will prevent initialization.",
all_monitors_encompassing_bounds.top,
all_monitors_encompassing_bounds.left,
all_monitors_encompassing_bounds.right,
all_monitors_encompassing_bounds.bottom);
return SEC_PROCESS_MONITORS_ERR_INVALID_DESKTOP;
}
/* Make sure virtual desktop size is OK
* 2.2.1.3.6 Client Monitor Data (TS_UD_CS_MONITOR)
*/
if (description->session_width
> CLIENT_MONITOR_DATA_MAXIMUM_VIRTUAL_DESKTOP_WIDTH
|| description->session_width
< CLIENT_MONITOR_DATA_MINIMUM_VIRTUAL_DESKTOP_WIDTH
|| description->session_height
> CLIENT_MONITOR_DATA_MAXIMUM_VIRTUAL_DESKTOP_HEIGHT
|| description->session_height
< CLIENT_MONITOR_DATA_MINIMUM_VIRTUAL_DESKTOP_HEIGHT)
{
LOG(LOG_LEVEL_ERROR,
"libxrdp_process_monitor_stream: Client supplied virtual"
" desktop width or height is invalid."
" Allowed width range: min %d, max %d. Width received: %d."
" Allowed height range: min %d, max %d. Height received: %d",
CLIENT_MONITOR_DATA_MINIMUM_VIRTUAL_DESKTOP_WIDTH,
CLIENT_MONITOR_DATA_MAXIMUM_VIRTUAL_DESKTOP_WIDTH,
description->session_width,
CLIENT_MONITOR_DATA_MINIMUM_VIRTUAL_DESKTOP_HEIGHT,
CLIENT_MONITOR_DATA_MAXIMUM_VIRTUAL_DESKTOP_HEIGHT,
description->session_width);
return SEC_PROCESS_MONITORS_ERR_INVALID_DESKTOP;
}
/* keep a copy of non negative monitor info values for xrdp_wm usage */
for (monitor_index = 0; monitor_index < num_monitor; ++monitor_index)
{
monitor_layout = description->minfo_wm + monitor_index;
g_memcpy(monitor_layout,
description->minfo + monitor_index,
sizeof(struct monitor_info));
monitor_layout->left =
monitor_layout->left - all_monitors_encompassing_bounds.left;
monitor_layout->top =
monitor_layout->top - all_monitors_encompassing_bounds.top;
monitor_layout->right =
monitor_layout->right - all_monitors_encompassing_bounds.left;
monitor_layout->bottom =
monitor_layout->bottom - all_monitors_encompassing_bounds.top;
}
return 0;
return libxrdp_init_display_size_description(
num_monitor, monitors, description);
}
/*****************************************************************************/
@ -2283,6 +2142,163 @@ libxrdp_process_monitor_ex_stream(struct stream *s,
return 0;
}
/*****************************************************************************/
int
libxrdp_init_display_size_description(
unsigned int num_monitor,
const struct monitor_info *monitors,
struct display_size_description *description)
{
unsigned int monitor_index;
struct monitor_info *monitor_layout;
struct xrdp_rect all_monitors_encompassing_bounds = {0};
int got_primary = 0;
/* Caller should have checked this, so don't log an error */
if (num_monitor > CLIENT_MONITOR_DATA_MAXIMUM_MONITORS)
{
return SEC_PROCESS_MONITORS_ERR_TOO_MANY_MONITORS;
}
description->monitorCount = num_monitor;
for (monitor_index = 0 ; monitor_index < num_monitor; ++monitor_index)
{
monitor_layout = &description->minfo[monitor_index];
*monitor_layout = monitors[monitor_index];
sanitise_extended_monitor_attributes(monitor_layout);
if (monitor_index == 0)
{
all_monitors_encompassing_bounds.left = monitor_layout->left;
all_monitors_encompassing_bounds.top = monitor_layout->top;
all_monitors_encompassing_bounds.right = monitor_layout->right;
all_monitors_encompassing_bounds.bottom = monitor_layout->bottom;
}
else
{
all_monitors_encompassing_bounds.left =
MIN(monitor_layout->left,
all_monitors_encompassing_bounds.left);
all_monitors_encompassing_bounds.top =
MIN(monitor_layout->top,
all_monitors_encompassing_bounds.top);
all_monitors_encompassing_bounds.right =
MAX(all_monitors_encompassing_bounds.right,
monitor_layout->right);
all_monitors_encompassing_bounds.bottom =
MAX(all_monitors_encompassing_bounds.bottom,
monitor_layout->bottom);
}
if (monitor_layout->is_primary == TS_MONITOR_PRIMARY)
{
if (got_primary)
{
// Already got one - don't have two
monitor_layout->is_primary = 0;
}
else
{
got_primary = 1;
}
}
}
if (!got_primary)
{
/* no primary monitor was set,
* choose the leftmost monitor as primary.
*/
for (monitor_index = 0; monitor_index < num_monitor; ++monitor_index)
{
monitor_layout = description->minfo + monitor_index;
if (monitor_layout->left
== all_monitors_encompassing_bounds.left
&& monitor_layout->top
== all_monitors_encompassing_bounds.top)
{
monitor_layout->is_primary = TS_MONITOR_PRIMARY;
break;
}
}
}
/* set wm geometry if the encompassing area is well formed.
Otherwise, log and return an error.
*/
if (all_monitors_encompassing_bounds.right
> all_monitors_encompassing_bounds.left
&& all_monitors_encompassing_bounds.bottom
> all_monitors_encompassing_bounds.top)
{
description->session_width =
all_monitors_encompassing_bounds.right
- all_monitors_encompassing_bounds.left + 1;
description->session_height =
all_monitors_encompassing_bounds.bottom
- all_monitors_encompassing_bounds.top + 1;
}
else
{
LOG(LOG_LEVEL_ERROR, "libxrdp_init_display_size_description:"
" The area encompassing the monitors is not a"
" well-formed rectangle. Received"
" (top: %d, left: %d, right: %d, bottom: %d)."
" This will prevent initialization.",
all_monitors_encompassing_bounds.top,
all_monitors_encompassing_bounds.left,
all_monitors_encompassing_bounds.right,
all_monitors_encompassing_bounds.bottom);
return SEC_PROCESS_MONITORS_ERR_INVALID_DESKTOP;
}
/* Make sure virtual desktop size is OK
* 2.2.1.3.6 Client Monitor Data (TS_UD_CS_MONITOR)
*/
if (description->session_width
> CLIENT_MONITOR_DATA_MAXIMUM_VIRTUAL_DESKTOP_WIDTH
|| description->session_width
< CLIENT_MONITOR_DATA_MINIMUM_VIRTUAL_DESKTOP_WIDTH
|| description->session_height
> CLIENT_MONITOR_DATA_MAXIMUM_VIRTUAL_DESKTOP_HEIGHT
|| description->session_height
< CLIENT_MONITOR_DATA_MINIMUM_VIRTUAL_DESKTOP_HEIGHT)
{
LOG(LOG_LEVEL_ERROR,
"libxrdp_init_display_size_description: Calculated virtual"
" desktop width or height is invalid."
" Allowed width range: min %d, max %d. Width received: %d."
" Allowed height range: min %d, max %d. Height received: %d",
CLIENT_MONITOR_DATA_MINIMUM_VIRTUAL_DESKTOP_WIDTH,
CLIENT_MONITOR_DATA_MAXIMUM_VIRTUAL_DESKTOP_WIDTH,
description->session_width,
CLIENT_MONITOR_DATA_MINIMUM_VIRTUAL_DESKTOP_HEIGHT,
CLIENT_MONITOR_DATA_MAXIMUM_VIRTUAL_DESKTOP_HEIGHT,
description->session_width);
return SEC_PROCESS_MONITORS_ERR_INVALID_DESKTOP;
}
/* keep a copy of non negative monitor info values for xrdp_wm usage */
for (monitor_index = 0; monitor_index < num_monitor; ++monitor_index)
{
monitor_layout = description->minfo_wm + monitor_index;
*monitor_layout = description->minfo[monitor_index];
monitor_layout->left =
monitor_layout->left - all_monitors_encompassing_bounds.left;
monitor_layout->top =
monitor_layout->top - all_monitors_encompassing_bounds.top;
monitor_layout->right =
monitor_layout->right - all_monitors_encompassing_bounds.left;
monitor_layout->bottom =
monitor_layout->bottom - all_monitors_encompassing_bounds.top;
}
return 0;
}
/*****************************************************************************/
int EXPORT_CC
libxrdp_planar_compress(char *in_data, int width, int height,

View File

@ -437,6 +437,10 @@ int
xrdp_rdp_send_data(struct xrdp_rdp *self, struct stream *s,
int data_pdu_type);
int
xrdp_rdp_send_data_from_channel(struct xrdp_rdp *self, struct stream *s,
int data_pdu_type, int channel_id,
int compress);
int
xrdp_rdp_send_fastpath(struct xrdp_rdp *self, struct stream *s,
int data_pdu_type);
int

View File

@ -24,6 +24,7 @@
#include "xrdp_rail.h"
struct list;
struct monitor_info;
/* struct xrdp_client_info moved to xrdp_client_info.h */
@ -195,8 +196,7 @@ libxrdp_orders_send_font(struct xrdp_session *session,
struct xrdp_font_char *font_char,
int font_index, int char_index);
int
libxrdp_reset(struct xrdp_session *session,
unsigned int width, unsigned int height, int bpp);
libxrdp_reset(struct xrdp_session *session);
int
libxrdp_orders_send_raw_bitmap2(struct xrdp_session *session,
int width, int height, int bpp, char *data,
@ -346,4 +346,21 @@ int EXPORT_CC
libxrdp_process_monitor_ex_stream(struct stream *s,
struct display_size_description *description);
/**
* Convert a list of monitors into a full description
*
* Monitor data is sanitised during the conversion
*
* @param num_monitor Monitor count (> 0)
* @param monitors List of monitors
* @param[out] description Display size description
*
* @return 0 if the data is processed, non-zero if there is an error.
*/
int
libxrdp_init_display_size_description(
unsigned int num_monitor,
const struct monitor_info *monitors,
struct display_size_description *description);
#endif

View File

@ -48,6 +48,7 @@ xrdp_caps_send_monitorlayout(struct xrdp_rdp *self)
struct stream *s;
uint32_t i;
struct display_size_description *description;
int rv = 0;
make_stream(s);
init_stream(s, 8192);
@ -74,14 +75,13 @@ xrdp_caps_send_monitorlayout(struct xrdp_rdp *self)
s_mark_end(s);
if (xrdp_rdp_send_data(self, s, 0x37) != 0)
{
free_stream(s);
return 1;
}
// [MS-RDPBCGR]
// - 2.2.12.1 - ...the pduSource field MUST be set to zero.
// - 3.3.5.12.1 - The contents of this PDU SHOULD NOT be compressed
rv = xrdp_rdp_send_data_from_channel(self, s,
PDUTYPE2_MONITOR_LAYOUT_PDU, 0, 0);
free_stream(s);
return 0;
return rv;
}
/*****************************************************************************/
@ -120,6 +120,45 @@ xrdp_caps_process_general(struct xrdp_rdp *self, struct stream *s,
return 0;
}
/*****************************************************************************/
static int
xrdp_caps_process_bitmap(struct xrdp_rdp *self, struct stream *s,
int len)
{
/* [MS-RDPBCGR] 2.2.7.1.2 */
int desktopResizeFlag;
if (len < 14 + 2)
{
LOG(LOG_LEVEL_ERROR, "Not enough bytes in the stream: "
"len 16, remaining %d", len);
return 1;
}
in_uint8s(s, 14);
in_uint16_le(s, desktopResizeFlag);
/* Work out what kind of client resizing we can do from the server */
int early_cap_flags = self->client_info.mcs_early_capability_flags;
if (desktopResizeFlag == 0)
{
self->client_info.client_resize_mode = CRMODE_NONE;
LOG(LOG_LEVEL_INFO, "Client cannot be resized by xrdp");
}
else if ((early_cap_flags & RNS_UD_CS_SUPPORT_MONITOR_LAYOUT_PDU) == 0)
{
self->client_info.client_resize_mode = CRMODE_SINGLE_SCREEN;
LOG(LOG_LEVEL_INFO, "Client supports single-screen resizes by xrdp");
}
else
{
self->client_info.client_resize_mode = CRMODE_MULTI_SCREEN;
LOG(LOG_LEVEL_INFO, "Client supports multi-screen resizes by xrdp");
}
return 0;
}
/*****************************************************************************/
/*
* Process [MS-RDPBCGR] TS_ORDER_CAPABILITYSET (2.2.7.1.3) message.
@ -766,7 +805,8 @@ xrdp_caps_process_confirm_active(struct xrdp_rdp *self, struct stream *s)
break;
case CAPSTYPE_BITMAP:
LOG_DEVEL(LOG_LEVEL_INFO, "Received [MS-RDPBCGR] TS_CONFIRM_ACTIVE_PDU - TS_CAPS_SET "
"capabilitySetType = CAPSTYPE_BITMAP - Ignored");
"capabilitySetType = CAPSTYPE_BITMAP");
xrdp_caps_process_bitmap(self, s, len);
break;
case CAPSTYPE_ORDER:
LOG_DEVEL(LOG_LEVEL_INFO, "Received [MS-RDPBCGR] TS_CONFIRM_ACTIVE_PDU - TS_CAPS_SET "
@ -1337,8 +1377,11 @@ xrdp_caps_send_demand_active(struct xrdp_rdp *self)
return 1;
}
/* send Monitor Layout PDU for dual monitor */
if (self->client_info.display_sizes.monitorCount > 0 &&
/* send Monitor Layout PDU for multi-monitor */
int early_cap_flags = self->client_info.mcs_early_capability_flags;
if ((early_cap_flags & RNS_UD_CS_SUPPORT_MONITOR_LAYOUT_PDU) != 0 &&
self->client_info.display_sizes.monitorCount > 0 &&
self->client_info.multimon == 1)
{
LOG_DEVEL(LOG_LEVEL_TRACE, "xrdp_caps_send_demand_active: sending monitor layout pdu");

View File

@ -585,11 +585,13 @@ xrdp_rdp_send(struct xrdp_rdp *self, struct stream *s, int pdu_type)
}
/*****************************************************************************/
/* Send a [MS-RDPBCGR] Data PDU with for the given pduType2 with the headers
/* Send a [MS-RDPBCGR] Data PDU for the given pduType2 from
* the specified source with the headers
added and data compressed */
int
xrdp_rdp_send_data(struct xrdp_rdp *self, struct stream *s,
int data_pdu_type)
xrdp_rdp_send_data_from_channel(struct xrdp_rdp *self, struct stream *s,
int data_pdu_type, int channel_id,
int compress)
{
int len;
int ctype;
@ -614,7 +616,8 @@ xrdp_rdp_send_data(struct xrdp_rdp *self, struct stream *s,
clen = len;
tocomplen = pdulen - 18;
if (self->client_info.rdp_compression && self->session->up_and_running)
if (compress && self->client_info.rdp_compression &&
self->session->up_and_running)
{
mppc_enc = self->mppc_enc;
if (compress_rdp(mppc_enc, (tui8 *)(s->p + 18), tocomplen))
@ -643,7 +646,8 @@ xrdp_rdp_send_data(struct xrdp_rdp *self, struct stream *s,
else
{
LOG_DEVEL(LOG_LEVEL_TRACE,
"xrdp_rdp_send_data: compress_rdp failed, sending "
"xrdp_rdp_send_data_from_channel: "
"compress_rdp failed, sending "
"uncompressed data. type %d, flags %d",
mppc_enc->protocol_type, mppc_enc->flags);
}
@ -652,11 +656,11 @@ xrdp_rdp_send_data(struct xrdp_rdp *self, struct stream *s,
/* TS_SHARECONTROLHEADER */
out_uint16_le(s, pdulen); /* totalLength */
out_uint16_le(s, pdutype); /* pduType */
out_uint16_le(s, self->mcs_channel); /* pduSource */
out_uint16_le(s, channel_id); /* pduSource */
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding header [MS-RDPBCGR] TS_SHARECONTROLHEADER "
"totalLength %d, pduType.type %s (%d), pduType.PDUVersion %d, "
"pduSource %d", pdulen, PDUTYPE_TO_STR(pdutype & 0xf),
pdutype & 0xf, ((pdutype & 0xfff0) >> 4), self->mcs_channel);
pdutype & 0xf, ((pdutype & 0xfff0) >> 4), channel_id);
/* TS_SHAREDATAHEADER */
out_uint32_le(s, self->share_id);
@ -673,13 +677,26 @@ xrdp_rdp_send_data(struct xrdp_rdp *self, struct stream *s,
if (xrdp_sec_send(self->sec_layer, s, MCS_GLOBAL_CHANNEL) != 0)
{
LOG(LOG_LEVEL_ERROR, "xrdp_rdp_send_data: xrdp_sec_send failed");
LOG(LOG_LEVEL_ERROR, "xrdp_rdp_send_data_from_channel: "
"xrdp_sec_send failed");
return 1;
}
return 0;
}
/*****************************************************************************/
/* Send a [MS-RDPBCGR] Data PDU on the MCS channel for the given pduType2
* with the headers added and data compressed */
int
xrdp_rdp_send_data(struct xrdp_rdp *self, struct stream *s,
int data_pdu_type)
{
return xrdp_rdp_send_data_from_channel(self, s, data_pdu_type,
self->mcs_channel, 1);
}
/*****************************************************************************/
/* returns the fastpath rdp byte count */
int

10
mc/mc.h
View File

@ -31,6 +31,9 @@
struct source_info;
/* Defined in xrdp_client_info.h */
struct monitor_info;
struct mod
{
int size; /* size of this struct */
@ -81,7 +84,10 @@ struct mod
int box_left, int box_top,
int box_right, int box_bottom,
int x, int y, char *data, int data_len);
int (*server_reset)(struct mod *v, int width, int height, int bpp);
int (*client_monitor_resize)(struct mod *v, int width, int height,
int num_monitors,
const struct monitor_info *monitors);
int (*server_monitor_resize_done)(struct mod *v);
int (*server_get_channel_count)(struct mod *v);
int (*server_query_channel)(struct mod *v, int index,
char *channel_name,
@ -92,7 +98,7 @@ struct mod
int total_data_len, int flags);
int (*server_bell_trigger)(struct mod *v);
int (*server_chansrv_in_use)(struct mod *v);
tintptr server_dumby[100 - 27]; /* align, 100 minus the number of server
tintptr server_dumby[100 - 28]; /* align, 100 minus the number of server
functions above */
/* common */
tintptr handle; /* pointer to self as long */

View File

@ -877,8 +877,12 @@ lxrdp_server_version_message(struct mod *mod)
/******************************************************************************/
static int
lxrdp_server_monitor_resize(struct mod *mod, int width, int height)
lxrdp_server_monitor_resize(struct mod *mod, int width, int height,
int num_monitors,
const struct monitor_info *monitors,
int *in_progress)
{
*in_progress = 0;
return 0;
}

View File

@ -88,7 +88,10 @@ struct mod
int (*mod_suppress_output)(struct mod *mod, int suppress,
int left, int top, int right, int bottom);
int (*mod_server_monitor_resize)(struct mod *mod,
int width, int height);
int width, int height,
int num_monitors,
const struct monitor_info *monitors,
int *in_progress);
int (*mod_server_monitor_full_invalidate)(struct mod *mod,
int width, int height);
int (*mod_server_version_message)(struct mod *mod);
@ -126,7 +129,10 @@ struct mod
int box_left, int box_top,
int box_right, int box_bottom,
int x, int y, char *data, int data_len);
int (*server_reset)(struct mod *v, int width, int height, int bpp);
int (*client_monitor_resize)(struct mod *v, int width, int height,
int num_monitors,
const struct monitor_info *monitors);
int (*server_monitor_resize_done)(struct mod *v);
int (*server_get_channel_count)(struct mod *v);
int (*server_query_channel)(struct mod *v, int index,
char *channel_name,
@ -191,7 +197,7 @@ struct mod
int flags, int frame_id);
int (*server_session_info)(struct mod *v, const char *data,
int data_bytes);
tintptr server_dumby[100 - 46]; /* align, 100 minus the number of server
tintptr server_dumby[100 - 47]; /* align, 100 minus the number of server
functions above */
/* common */
tintptr handle; /* pointer to self as long */

431
vnc/vnc.c
View File

@ -149,17 +149,13 @@ log_screen_layout(const enum logLevels lvl, const char *source,
* @param a First structure
* @param b Second structure
*
* @return Suitable for sorting structures with ID as the primary key
* @return Suitable for sorting structures on (x, y, width, height)
*/
static int cmp_vnc_screen(const struct vnc_screen *a,
const struct vnc_screen *b)
{
int result = 0;
if (a->id != b->id)
{
result = a->id - b->id;
}
else if (a->x != b->x)
if (a->x != b->x)
{
result = a->x - b->x;
}
@ -211,9 +207,7 @@ static int vnc_screen_layouts_equal(const struct vnc_screen_layout *a,
* @return != 0 for error
*
* @pre The next octet read from v->trans is the number of screens
* @pre layout is not already allocated
*
* @post if call is successful, layout->s must be freed after use.
* @post Returned structure is in increasing ID order
* @post layout->total_width is untouched
* @post layout->total_height is untouched
@ -225,10 +219,8 @@ read_extended_desktop_size_rect(struct vnc *v,
struct stream *s;
int error;
unsigned int count;
struct vnc_screen *screens;
layout->count = 0;
layout->s = NULL;
make_stream(s);
init_stream(s, 8192);
@ -239,46 +231,41 @@ read_extended_desktop_size_rect(struct vnc *v,
{
/* Get the number of screens */
in_uint8(s, count);
in_uint8s(s, 3);
error = trans_force_read_s(v->trans, s, 16 * count);
if (error == 0)
if (count <= 0 || count > CLIENT_MONITOR_DATA_MAXIMUM_MONITORS)
{
screens = g_new(struct vnc_screen, count);
if (screens == NULL)
{
LOG(LOG_LEVEL_ERROR,
"VNC : Can't alloc for %d screens", count);
error = 1;
}
else
LOG(LOG_LEVEL_ERROR,
"Bad monitor count %d in ExtendedDesktopSize rectangle",
count);
error = 1;
}
else
{
in_uint8s(s, 3);
error = trans_force_read_s(v->trans, s, 16 * count);
if (error == 0)
{
unsigned int i;
for (i = 0 ; i < count ; ++i)
{
in_uint32_be(s, screens[i].id);
in_uint16_be(s, screens[i].x);
in_uint16_be(s, screens[i].y);
in_uint16_be(s, screens[i].width);
in_uint16_be(s, screens[i].height);
in_uint32_be(s, screens[i].flags);
in_uint32_be(s, layout->s[i].id);
in_uint16_be(s, layout->s[i].x);
in_uint16_be(s, layout->s[i].y);
in_uint16_be(s, layout->s[i].width);
in_uint16_be(s, layout->s[i].height);
in_uint32_be(s, layout->s[i].flags);
}
/* sort monitors in increasing ID order */
qsort(screens, count, sizeof(screens[0]),
/* sort monitors in increasing (x,y) order */
qsort(layout->s, count, sizeof(layout->s[0]),
(int (*)(const void *, const void *))cmp_vnc_screen);
layout->count = count;
}
}
}
free_stream(s);
if (error == 0)
{
layout->count = count;
layout->s = screens;
}
return error;
}
@ -326,88 +313,76 @@ send_set_desktop_size(struct vnc *v, const struct vnc_screen_layout *layout)
}
/**************************************************************************//**
* Sets up a single-screen vnc_screen_layout structure
*
* @param layout Structure to set up
* @param width New client width
* @param height New client height
*
* @pre layout->count must be valid
* @pre layout->s must be valid
* Initialises a vnc_screen_layout as a single screen
* @param[in] width Screen Width
* @param[in] height Screen Height
* @param[out] layout Layout to initialise
*/
static void
set_single_screen_layout(struct vnc_screen_layout *layout,
int width, int height)
init_single_screen_layout(int width, int height,
struct vnc_screen_layout *layout)
{
int id = 0;
int flags = 0;
layout->total_width = width;
layout->total_height = height;
if (layout->count == 0)
{
/* No previous layout */
layout->s = g_new(struct vnc_screen, 1);
}
else
{
/* Keep the ID and flags from the previous first screen */
id = layout->s[0].id;
flags = layout->s[0].flags;
if (layout->count > 1)
{
g_free(layout->s);
layout->s = g_new(struct vnc_screen, 1);
}
}
layout->count = 1;
layout->s[0].id = id;
layout->s[0].id = 0;
layout->s[0].x = 0;
layout->s[0].y = 0;
layout->s[0].width = width;
layout->s[0].height = height;
layout->s[0].flags = flags;
layout->s[0].flags = 0;
}
/**************************************************************************//**
* Resize the client as a single screen
* Resize the client to match the server_layout
*
* @param v VNC object
* @param update_in_progress True if there's a painter update in progress
* @param width New client width
* @param height New client height
* @return != 0 for error
*
* The new client layout is recorded in v->client_layout. If the client was
* multi-screen before this call, it won't be afterwards.
* The new client layout is recorded in v->client_layout.
*/
static int
resize_client(struct vnc *v, int update_in_progress, int width, int height)
resize_client_to_server(struct vnc *v, int update_in_progress)
{
int error = 0;
unsigned int i;
const struct vnc_screen_layout *sl = &v->server_layout;
struct monitor_info client_mons[CLIENT_MONITOR_DATA_MAXIMUM_MONITORS] = {0};
if (v->client_layout.count != 1 ||
v->client_layout.total_width != width ||
v->client_layout.total_height != height)
if (sl->count <= 0 ||
sl->count > CLIENT_MONITOR_DATA_MAXIMUM_MONITORS)
{
if (update_in_progress)
{
error = v->server_end_update(v);
}
LOG(LOG_LEVEL_ERROR, "%s: Programming error. Bad monitors %d",
__func__, sl->count);
return 1;
}
// Convert the server monitors into client monitors
for (i = 0; i < sl->count; ++i)
{
client_mons[i].left = sl->s[i].x;
client_mons[i].top = sl->s[i].y;
client_mons[i].right = sl->s[i].x + sl->s[i].width - 1;
client_mons[i].bottom = sl->s[i].y + sl->s[i].height - 1;
}
if (update_in_progress && v->server_end_update(v) != 0)
{
error = 1;
}
else
{
error = v->client_monitor_resize(v, sl->total_width, sl->total_height,
sl->count, client_mons);
if (error == 0)
{
error = v->server_reset(v, width, height, v->server_bpp);
if (error == 0)
{
set_single_screen_layout(&v->client_layout, width, height);
if (update_in_progress)
{
error = v->server_begin_update(v);
}
}
v->client_layout = *sl;
}
if (update_in_progress && v->server_begin_update(v) != 0)
{
error = 1;
}
}
@ -416,50 +391,53 @@ resize_client(struct vnc *v, int update_in_progress, int width, int height)
/**************************************************************************//**
* Resize the attached client from a layout
* Resize the server to the client layout
*
* @param v VNC object
* @param update_in_progress True if there's a painter update in progress
* @param layout Desired layout from server
* @return != 0 for error
*
* This has some limitations. We have no way to move multiple screens about
* on a connected client, and so we are not able to change the client unless
* we're changing to a single screen layout.
* The new client layout is recorded in v->client_layout.
*/
static int
resize_client_from_layout(struct vnc *v,
int update_in_progress,
const struct vnc_screen_layout *layout)
resize_server_to_client_layout(struct vnc *v)
{
int error = 0;
if (!vnc_screen_layouts_equal(&v->client_layout, layout))
if (v->resize_supported != VRSS_SUPPORTED)
{
LOG(LOG_LEVEL_ERROR, "%s: Asked to resize server, but not possible",
__func__);
error = 1;
}
else if (vnc_screen_layouts_equal(&v->server_layout, &v->client_layout))
{
LOG(LOG_LEVEL_DEBUG, "Server layout is the same "
"as the client layout");
v->resize_status = VRS_DONE;
}
else
{
/*
* we don't have the capability to resize to anything other
* than a single screen.
* If we've only got one screen, and the other side has
* only got one screen, we will preserve their screen ID
* and any flags. This may prevent us sending an unwanted
* SetDesktopSize message if the screen dimensions are
* a match. We can't do this with more than one screen,
* as we have no way to map different IDs
*/
if (layout->count != 1)
if (v->server_layout.count == 1 && v->client_layout.count == 1)
{
LOG(LOG_LEVEL_ERROR,
"VNC Resize to %d screen(s) from %d screen(s) "
"not implemented",
v->client_layout.count, layout->count);
LOG(LOG_LEVEL_DEBUG, "VNC "
"setting screen id to %d from server",
v->server_layout.s[0].id);
/* Dump some useful info, in case we get here when we don't
* need to */
log_screen_layout(LOG_LEVEL_ERROR, "OldLayout", &v->client_layout);
log_screen_layout(LOG_LEVEL_ERROR, "NewLayout", layout);
error = 1;
}
else
{
error = resize_client(v,
update_in_progress,
layout->total_width,
layout->total_height);
v->client_layout.s[0].id = v->server_layout.s[0].id;
v->client_layout.s[0].flags = v->server_layout.s[0].flags;
}
LOG(LOG_LEVEL_DEBUG, "Changing server layout");
error = send_set_desktop_size(v, &v->client_layout);
v->resize_status = VRS_WAITING_FOR_RESIZE_CONFIRM;
}
return error;
@ -927,7 +905,6 @@ skip_encoding(struct vnc *v, int x, int y, int cx, int cy,
"x=%d, y=%d geom=%dx%d",
x, y, cx, cy);
error = read_extended_desktop_size_rect(v, &layout);
g_free(layout.s);
}
break;
@ -944,7 +921,7 @@ skip_encoding(struct vnc *v, int x, int y, int cx, int cy,
* Parses an entire framebuffer update message from the wire, and returns the
* first matching ExtendedDesktopSize encoding if found.
*
* Caller can check for a match by examining match_layout.s after the call
* Caller can check for a match by examining match_layout.count after the call
*
* @param v VNC object
* @param match Function to call to check for a match
@ -952,8 +929,6 @@ skip_encoding(struct vnc *v, int x, int y, int cx, int cy,
* @param [out] match_y Matching y parameter for an encoding (if needed)
* @param [out] match_layout Returned layout for the encoding
* @return != 0 for error
*
* @post After a successful call, match_layout.s must be free'd
*/
static int
find_matching_extended_rect(struct vnc *v,
@ -971,8 +946,7 @@ find_matching_extended_rect(struct vnc *v,
int cx;
int cy;
encoding_type encoding;
match_layout->s = NULL;
int found = 0;
make_stream(s);
init_stream(s, 8192);
@ -1002,14 +976,14 @@ find_matching_extended_rect(struct vnc *v,
in_uint32_be(s, encoding);
if (encoding == RFB_ENC_EXTENDED_DESKTOP_SIZE &&
match_layout->s == NULL &&
!found &&
match(x, y, cx, cy))
{
LOG(LOG_LEVEL_DEBUG,
"VNC matched ExtendedDesktopSize rectangle "
"x=%d, y=%d geom=%dx%d",
x, y, cx, cy);
found = 1;
error = read_extended_desktop_size_rect(v, match_layout);
if (match_x)
{
@ -1065,6 +1039,7 @@ send_update_request_for_resize_status(struct vnc *v)
switch (v->resize_status)
{
case VRS_WAITING_FOR_FIRST_UPDATE:
case VRS_WAITING_FOR_RESIZE_CONFIRM:
/*
* Ask for an immediate, minimal update.
*/
@ -1078,20 +1053,6 @@ send_update_request_for_resize_status(struct vnc *v)
error = lib_send_copy(v, s);
break;
case VRS_WAITING_FOR_RESIZE_CONFIRM:
/*
* Ask for a deferred minimal update.
*/
out_uint8(s, RFB_C2S_FRAMEBUFFER_UPDATE_REQUEST);
out_uint8(s, 1); /* incremental == 1 : Changes only */
out_uint16_be(s, 0);
out_uint16_be(s, 0);
out_uint16_be(s, 1);
out_uint16_be(s, 1);
s_mark_end(s);
error = lib_send_copy(v, s);
break;
default:
/*
* Ask for a full update from the server
@ -1102,8 +1063,8 @@ send_update_request_for_resize_status(struct vnc *v)
out_uint8(s, 0); /* incremental == 0 : Full update */
out_uint16_be(s, 0);
out_uint16_be(s, 0);
out_uint16_be(s, v->server_width);
out_uint16_be(s, v->server_height);
out_uint16_be(s, v->server_layout.total_width);
out_uint16_be(s, v->server_layout.total_height);
s_mark_end(s);
error = lib_send_copy(v, s);
}
@ -1159,12 +1120,15 @@ lib_framebuffer_first_update(struct vnc *v)
&layout);
if (error == 0)
{
if (layout.s != NULL)
if (layout.count > 0)
{
LOG(LOG_LEVEL_DEBUG, "VNC server supports resizing");
v->resize_supported = VRSS_SUPPORTED;
v->server_layout = layout;
/* Force the client geometry over to the server */
log_screen_layout(LOG_LEVEL_INFO, "OldLayout", &layout);
log_screen_layout(LOG_LEVEL_INFO, "ClientLayout", &v->client_layout);
log_screen_layout(LOG_LEVEL_INFO, "OldServerLayout", &layout);
/*
* If we've only got one screen, and the other side has
@ -1184,32 +1148,19 @@ lib_framebuffer_first_update(struct vnc *v)
v->client_layout.s[0].flags = layout.s[0].flags;
}
if (vnc_screen_layouts_equal(&layout, &v->client_layout))
{
LOG(LOG_LEVEL_DEBUG, "Server layout is the same "
"as the client layout");
v->resize_status = VRS_DONE;
}
else
{
LOG(LOG_LEVEL_DEBUG, "Server layout differs from "
"the client layout. Changing server layout");
error = send_set_desktop_size(v, &v->client_layout);
v->resize_status = VRS_WAITING_FOR_RESIZE_CONFIRM;
}
resize_server_to_client_layout(v);
}
else
{
LOG(LOG_LEVEL_DEBUG, "VNC server does not support resizing");
v->resize_supported = VRSS_NOT_SUPPORTED;
/* Force client to same size as server */
LOG(LOG_LEVEL_DEBUG, "Resizing client to server %dx%d",
v->server_width, v->server_height);
error = resize_client(v, 0, v->server_width, v->server_height);
v->server_layout.total_width, v->server_layout.total_height);
error = resize_client_to_server(v, 0);
v->resize_status = VRS_DONE;
}
g_free(layout.s);
}
if (error == 0)
@ -1224,7 +1175,7 @@ lib_framebuffer_first_update(struct vnc *v)
* Looks for a resize confirm in a framebuffer update request
*
* If the server supports resizes from us, this is used to find the
* reply to our initial resize request. See The RFB community wiki for details.
* reply to our resize request. See The RFB community wiki for details.
*
* @param v VNC object
* @return != 0 for error
@ -1243,18 +1194,17 @@ lib_framebuffer_waiting_for_resize_confirm(struct vnc *v)
&layout);
if (error == 0)
{
if (layout.s != NULL)
if (layout.count > 0)
{
if (response_code == 0)
{
LOG(LOG_LEVEL_DEBUG, "VNC server successfully resized");
log_screen_layout(LOG_LEVEL_INFO, "NewLayout", &layout);
v->server_layout = layout;
// If this resize was requested by the client mid-session
// (dynamic resize), we need to tell xrdp_mm that
// it's OK to continue with the resize state machine.
// We do this by sending a reset with bpp == 0
error = v->server_reset(v, v->server_width,
v->server_height, 0);
error = v->server_monitor_resize_done(v);
}
else
{
@ -1263,14 +1213,11 @@ lib_framebuffer_waiting_for_resize_confirm(struct vnc *v)
response_code,
rfb_get_eds_status_msg(response_code));
/* Force client to same size as server */
LOG(LOG_LEVEL_WARNING, "Resizing client to server %dx%d",
v->server_width, v->server_height);
error = resize_client(v, 0, v->server_width, v->server_height);
LOG(LOG_LEVEL_WARNING, "Resizing client to server");
error = resize_client_to_server(v, 0);
}
v->resize_status = VRS_DONE;
}
g_free(layout.s);
}
if (error == 0)
@ -1415,9 +1362,8 @@ lib_framebuffer_update(struct vnc *v)
else if (encoding == RFB_ENC_DESKTOP_SIZE)
{
/* Server end has resized */
v->server_width = cx;
v->server_height = cy;
error = resize_client(v, 1, cx, cy);
init_single_screen_layout(cx, cy, &v->server_layout);
error = resize_client_to_server(v, 1);
}
else if (encoding == RFB_ENC_EXTENDED_DESKTOP_SIZE)
{
@ -1427,11 +1373,14 @@ lib_framebuffer_update(struct vnc *v)
/* If this is a reply to a request from us, x == 1 */
if (error == 0 && x != 1)
{
v->server_width = layout.total_width;
v->server_height = layout.total_height;
error = resize_client_from_layout(v, 1, &layout);
if (!vnc_screen_layouts_equal(&v->server_layout, &layout))
{
v->server_layout = layout;
log_screen_layout(LOG_LEVEL_INFO, "NewServerLayout",
&v->server_layout);
error = resize_client_to_server(v, 1);
}
}
g_free(layout.s);
}
else
{
@ -1456,8 +1405,8 @@ lib_framebuffer_update(struct vnc *v)
out_uint8(s, 1); /* incremental == 1 : Changes only */
out_uint16_be(s, 0);
out_uint16_be(s, 0);
out_uint16_be(s, v->server_width);
out_uint16_be(s, v->server_height);
out_uint16_be(s, v->server_layout.total_width);
out_uint16_be(s, v->server_layout.total_height);
s_mark_end(s);
error = lib_send_copy(v, s);
}
@ -1831,8 +1780,11 @@ lib_mod_connect(struct vnc *v)
if (error == 0)
{
in_uint16_be(s, v->server_width);
in_uint16_be(s, v->server_height);
int width;
int height;
in_uint16_be(s, width);
in_uint16_be(s, height);
init_single_screen_layout(width, height, &v->server_layout);
init_stream(pixel_format, 8192);
v->server_msg(v, "VNC receiving pixel format", 0);
@ -2000,6 +1952,7 @@ lib_mod_connect(struct vnc *v)
if (error == 0)
{
v->resize_supported = VRSS_UNKNOWN;
v->resize_status = VRS_WAITING_FOR_FIRST_UPDATE;
error = send_update_request_for_resize_status(v);
}
@ -2061,33 +2014,40 @@ lib_mod_end(struct vnc *v)
/**************************************************************************//**
* Initialises the client layout from the Windows monitor definition.
*
* @param [out] layout Our layout
* @param [in] client_info WM info
* @param v VNC module
* @param [in] width session width
* @param [in] height session height
* @param [in] num_monitors (can be 0, meaning one monitor)
* @param [in] monitors Monitor definitions for num_monitors > 0
* @param [in] multimon_configured Whether multimon is configured
*/
static void
init_client_layout(struct vnc_screen_layout *layout,
const struct xrdp_client_info *client_info)
init_client_layout(struct vnc *v,
int width, int height,
int num_monitors,
const struct monitor_info *monitors)
{
uint32_t i;
layout->total_width = client_info->display_sizes.session_width;
layout->total_height = client_info->display_sizes.session_height;
layout->count = client_info->display_sizes.monitorCount;
layout->s = g_new(struct vnc_screen, layout->count);
for (i = 0 ; i < client_info->display_sizes.monitorCount ; ++i)
struct vnc_screen_layout *layout = &v->client_layout;
if (!v->multimon_configured || num_monitors < 1)
{
/* Use minfo_wm, as this is normalised for a top-left of (0,0)
* as required by RFC6143 */
layout->s[i].id = i;
layout->s[i].x = client_info->display_sizes.minfo_wm[i].left;
layout->s[i].y = client_info->display_sizes.minfo_wm[i].top;
layout->s[i].width = client_info->display_sizes.minfo_wm[i].right -
client_info->display_sizes.minfo_wm[i].left + 1;
layout->s[i].height = client_info->display_sizes.minfo_wm[i].bottom -
client_info->display_sizes.minfo_wm[i].top + 1;
layout->s[i].flags = 0;
init_single_screen_layout(width, height, layout);
}
else
{
layout->total_width = width;
layout->total_height = height;
layout->count = num_monitors;
unsigned int i;
for (i = 0 ; i < layout->count; ++i)
{
layout->s[i].id = i;
layout->s[i].x = monitors[i].left;
layout->s[i].y = monitors[i].top;
layout->s[i].width = monitors[i].right - monitors[i].left + 1;
layout->s[i].height = monitors[i].bottom - monitors[i].top + 1;
layout->s[i].flags = 0;
}
}
}
@ -2132,19 +2092,16 @@ lib_mod_set_param(struct vnc *v, const char *name, const char *value)
const struct xrdp_client_info *client_info =
(const struct xrdp_client_info *) value;
g_free(v->client_layout.s);
v->multimon_configured = client_info->multimon;
/* Save monitor information from the client */
if (!client_info->multimon || client_info->display_sizes.monitorCount < 1)
{
set_single_screen_layout(&v->client_layout,
client_info->display_sizes.session_width,
client_info->display_sizes.session_height);
}
else
{
init_client_layout(&v->client_layout, client_info);
}
/* Save monitor information from the client
* Use minfo_wm, as this is normalised for a top-left of (0,0)
* as required by RFC6143 */
init_client_layout(v,
client_info->display_sizes.session_width,
client_info->display_sizes.session_height,
client_info->display_sizes.monitorCount,
client_info->display_sizes.minfo_wm);
log_screen_layout(LOG_LEVEL_DEBUG, "client_info", &v->client_layout);
}
@ -2185,6 +2142,10 @@ lib_mod_check_wait_objs(struct vnc *v)
if (v->trans != 0)
{
rv = trans_check_wait_objs(v->trans);
if (rv != 0)
{
LOG(LOG_LEVEL_ERROR, "VNC server closed connection");
}
}
}
return rv;
@ -2217,8 +2178,8 @@ lib_mod_suppress_output(struct vnc *v, int suppress,
out_uint8(s, 0); /* incremental == 0 : Full contents */
out_uint16_be(s, 0);
out_uint16_be(s, 0);
out_uint16_be(s, v->server_width);
out_uint16_be(s, v->server_height);
out_uint16_be(s, v->server_layout.total_width);
out_uint16_be(s, v->server_layout.total_height);
s_mark_end(s);
error = lib_send_copy(v, s);
free_stream(s);
@ -2237,12 +2198,29 @@ lib_mod_server_version_message(struct vnc *v)
/******************************************************************************/
/* return error */
int
lib_mod_server_monitor_resize(struct vnc *v, int width, int height)
lib_mod_server_monitor_resize(struct vnc *v, int width, int height,
int num_monitors,
const struct monitor_info *monitors,
int *in_progress)
{
int error = 0;
set_single_screen_layout(&v->client_layout, width, height);
v->resize_status = VRS_WAITING_FOR_FIRST_UPDATE;
error = send_update_request_for_resize_status(v);
int error;
*in_progress = 0;
init_client_layout(v, width, height, num_monitors, monitors);
if ((error = resize_server_to_client_layout(v)) == 0)
{
// If we're waiting for a confirmation, send an update request.
// According to the spec this should not be needed, but
// it works around a buggy VNC server not sending an
// ExtendedDesktopSize rectangle if the desktop change is
// small (eg. same dimensions, but 2 monitors -> 1 monitor)
if (v->resize_status == VRS_WAITING_FOR_RESIZE_CONFIRM &&
(error = send_update_request_for_resize_status(v)) == 0)
{
*in_progress = 1;
}
}
return error;
}
@ -2298,7 +2276,6 @@ mod_exit(tintptr handle)
return 0;
}
trans_delete(v->trans);
g_free(v->client_layout.s);
vnc_clip_exit(v);
g_free(v);
return 0;

View File

@ -27,6 +27,7 @@
#include "os_calls.h"
#include "defines.h"
#include "guid.h"
#include "ms-rdpbcgr.h"
#define CURRENT_MOD_VER 4
@ -46,8 +47,8 @@ struct vnc_screen_layout
int total_width;
int total_height;
unsigned int count;
/* For comparison, screens are sorted in increasing order of ID */
struct vnc_screen *s;
/* For comparison, screens are sorted in x, y, width, height) order */
struct vnc_screen s[CLIENT_MONITOR_DATA_MAXIMUM_MONITORS];
};
/**
@ -60,11 +61,21 @@ enum vnc_resize_status
VRS_DONE
};
enum vnc_resize_support_status
{
VRSS_NOT_SUPPORTED,
VRSS_SUPPORTED,
VRSS_UNKNOWN
};
struct source_info;
/* Defined in vnc_clip.c */
struct vnc_clipboard_data;
/* Defined in xrdp_client_info.h */
struct monitor_info;
struct vnc
{
int size; /* size of this struct */
@ -85,7 +96,10 @@ struct vnc
int (*mod_suppress_output)(struct vnc *v, int suppress,
int left, int top, int right, int bottom);
int (*mod_server_monitor_resize)(struct vnc *v,
int width, int height);
int width, int height,
int num_monitors,
const struct monitor_info *monitors,
int *in_progress);
int (*mod_server_monitor_full_invalidate)(struct vnc *v,
int width, int height);
int (*mod_server_version_message)(struct vnc *v);
@ -123,7 +137,10 @@ struct vnc
int box_left, int box_top,
int box_right, int box_bottom,
int x, int y, char *data, int data_len);
int (*server_reset)(struct vnc *v, int width, int height, int bpp);
int (*client_monitor_resize)(struct vnc *v, int width, int height,
int num_monitors,
const struct monitor_info *monitors);
int (*server_monitor_resize_done)(struct vnc *v);
int (*server_get_channel_count)(struct vnc *v);
int (*server_query_channel)(struct vnc *v, int index,
char *channel_name,
@ -134,7 +151,7 @@ struct vnc
int total_data_len, int flags);
int (*server_bell_trigger)(struct vnc *v);
int (*server_chansrv_in_use)(struct vnc *v);
tintptr server_dumby[100 - 27]; /* align, 100 minus the number of server
tintptr server_dumby[100 - 28]; /* align, 100 minus the number of server
functions above */
/* common */
tintptr handle; /* pointer to self as long */
@ -142,8 +159,6 @@ struct vnc
tintptr painter;
struct source_info *si;
/* mod data */
int server_width;
int server_height;
int server_bpp;
char mod_name[256];
int mod_mouse_state;
@ -164,8 +179,11 @@ struct vnc
int suppress_output;
unsigned int enabled_encodings_mask;
/* Resizeable support */
int multimon_configured;
struct vnc_screen_layout client_layout;
struct vnc_screen_layout server_layout;
enum vnc_resize_status resize_status;
enum vnc_resize_support_status resize_supported;
};
/*

View File

@ -502,6 +502,9 @@ int
xrdp_mm_check_wait_objs(struct xrdp_mm *self);
int
xrdp_mm_frame_ack(struct xrdp_mm *self, int frame_id);
void
xrdp_mm_efgx_add_dirty_region_to_planar_list(struct xrdp_mm *self,
struct xrdp_region *dirty_region);
int
xrdp_mm_egfx_send_planar_bitmap(struct xrdp_mm *self,
struct xrdp_bitmap *bitmap,
@ -591,7 +594,10 @@ server_draw_text(struct xrdp_mod *mod, int font,
int box_right, int box_bottom,
int x, int y, char *data, int data_len);
int
server_reset(struct xrdp_mod *mod, int width, int height, int bpp);
client_monitor_resize(struct xrdp_mod *mod, int width, int height,
int num_monitors, const struct monitor_info *monitors);
int
server_monitor_resize_done(struct xrdp_mod *mod);
int
is_channel_allowed(struct xrdp_wm *wm, int channel_id);
int

View File

@ -301,8 +301,6 @@ xrdp_bitmap_resize(struct xrdp_bitmap *self, int width, int height)
return 1;
}
self->width = width;
self->height = height;
Bpp = 4;
switch (self->bpp)
@ -318,8 +316,28 @@ xrdp_bitmap_resize(struct xrdp_bitmap *self, int width, int height)
break;
}
g_free(self->data);
self->data = (char *)g_malloc(width * height * Bpp, 0);
/* To prevent valgrind errors (particularly on a screen resize),
clear extra memory */
unsigned long old_size = self->width * self->height * Bpp;
unsigned long new_size = width * height * Bpp;
char *new_data = (char *)realloc(self->data, new_size);
if (new_data == NULL)
{
return 1;
}
self->width = width;
self->height = height;
if (new_data != self->data)
{
self->data = new_data;
memset(self->data, 0, new_size);
}
else if (new_size > old_size)
{
memset(self->data + old_size, 0, new_size - old_size);
}
self->line_size = width * Bpp;
return 0;
}

View File

@ -1121,6 +1121,10 @@ xrdp_egfx_shutdown_close_connection(struct xrdp_egfx *egfx)
return error;
}
// Ignore any messages we haven't processed yet
egfx->caps_advertise = NULL;
egfx->frame_ack = NULL;
return error;
}

View File

@ -396,7 +396,8 @@ xrdp_mm_setup_mod1(struct xrdp_mm *self)
self->mod->server_draw_line = server_draw_line;
self->mod->server_add_char = server_add_char;
self->mod->server_draw_text = server_draw_text;
self->mod->server_reset = server_reset;
self->mod->client_monitor_resize = client_monitor_resize;
self->mod->server_monitor_resize_done = server_monitor_resize_done;
self->mod->server_get_channel_count = server_get_channel_count;
self->mod->server_query_channel = server_query_channel;
self->mod->server_get_channel_id = server_get_channel_id;
@ -1094,25 +1095,32 @@ cleanup:
/******************************************************************************/
static int
xrdp_mm_egfx_invalidate_all(struct xrdp_mm *self)
xrdp_mm_egfx_invalidate_wm_screen(struct xrdp_mm *self)
{
struct xrdp_rect xr_rect;
struct xrdp_bitmap *screen;
int error;
int error = 0;
LOG(LOG_LEVEL_INFO, "xrdp_mm_egfx_invalidate_all:");
LOG(LOG_LEVEL_INFO, "xrdp_mm_egfx_invalidate_wm_screen:");
screen = self->wm->screen;
xr_rect.left = 0;
xr_rect.top = 0;
xr_rect.right = screen->width;
xr_rect.bottom = screen->height;
if (self->wm->screen_dirty_region == NULL)
// Only invalidate the WM screen if the module is using the WM screen,
// or we haven't loaded the module yet.
//
// Otherwise we may send client updates which conflict with the
// updates sent directly from the module via the encoder.
if (self->mod_uses_wm_screen_for_gfx || self->mod_handle == 0)
{
self->wm->screen_dirty_region = xrdp_region_create(self->wm);
struct xrdp_bitmap *screen = self->wm->screen;
struct xrdp_rect xr_rect;
xr_rect.left = 0;
xr_rect.top = 0;
xr_rect.right = screen->width;
xr_rect.bottom = screen->height;
if (self->wm->screen_dirty_region == NULL)
{
self->wm->screen_dirty_region = xrdp_region_create(self->wm);
}
error = xrdp_region_add_rect(self->wm->screen_dirty_region, &xr_rect);
}
error = xrdp_region_add_rect(self->wm->screen_dirty_region, &xr_rect);
return error;
}
@ -1369,7 +1377,7 @@ xrdp_mm_egfx_caps_advertise(void *user, int caps_count,
self->egfx_up = 1;
xrdp_mm_egfx_create_surfaces(self);
self->encoder = xrdp_encoder_create(self);
xrdp_mm_egfx_invalidate_all(self);
xrdp_mm_egfx_invalidate_wm_screen(self);
if (self->resize_data != NULL
&& self->resize_data->state == WMRZ_EGFX_INITALIZING)
@ -1635,6 +1643,7 @@ process_display_control_monitor_layout_data(struct xrdp_wm *wm)
struct xrdp_rdp *rdp;
struct xrdp_sec *sec;
struct xrdp_channel *chan;
int in_progress;
LOG_DEVEL(LOG_LEVEL_TRACE, "process_display_control_monitor_layout_data:");
@ -1697,6 +1706,7 @@ process_display_control_monitor_layout_data(struct xrdp_wm *wm)
if (error == 0 && module != 0)
{
xrdp_egfx_shutdown_close_connection(wm->mm->egfx);
mm->egfx_up = 0;
}
advance_resize_state_machine(mm, WMRZ_EGFX_CONN_CLOSING);
break;
@ -1725,13 +1735,15 @@ process_display_control_monitor_layout_data(struct xrdp_wm *wm)
{
xrdp_egfx_shutdown_delete(wm->mm->egfx);
mm->egfx = NULL;
mm->egfx_up = 0;
}
advance_resize_state_machine(mm, WMRZ_SERVER_MONITOR_RESIZE);
break;
case WMRZ_SERVER_MONITOR_RESIZE:
error = module->mod_server_monitor_resize(
module, desc_width, desc_height);
module, desc_width, desc_height,
description->description.monitorCount,
description->description.minfo,
&in_progress);
if (error != 0)
{
LOG_DEVEL(LOG_LEVEL_INFO,
@ -1739,30 +1751,27 @@ process_display_control_monitor_layout_data(struct xrdp_wm *wm)
" mod_server_monitor_resize failed %d", error);
return advance_error(error, mm);
}
advance_resize_state_machine(
mm, WMRZ_SERVER_VERSION_MESSAGE_START);
break;
case WMRZ_SERVER_VERSION_MESSAGE_START:
error = module->mod_server_version_message(module);
if (error != 0)
else if (in_progress)
{
LOG_DEVEL(LOG_LEVEL_INFO,
"process_display_control_monitor_layout_data:"
" mod_server_version_message failed %d", error);
return advance_error(error, mm);
// Call is proceeding asynchronously
advance_resize_state_machine(
mm, WMRZ_SERVER_MONITOR_MESSAGE_PROCESSING);
}
else
{
// Call is done
advance_resize_state_machine(
mm, WMRZ_SERVER_MONITOR_MESSAGE_PROCESSED);
}
advance_resize_state_machine(
mm, WMRZ_SERVER_MONITOR_MESSAGE_PROCESSING);
break;
// Not processed here. Processed in server_reset
// Not processed here. Processed in client_monitor_resize
// case WMRZ_SERVER_MONITOR_MESSAGE_PROCESSING:
case WMRZ_SERVER_MONITOR_MESSAGE_PROCESSED:
advance_resize_state_machine(mm, WMRZ_XRDP_CORE_RESET);
break;
case WMRZ_XRDP_CORE_RESET:
// TODO: Unify this logic with server_reset
error = libxrdp_reset(
wm->session, desc_width, desc_height, wm->screen->bpp);
sync_dynamic_monitor_data(wm, &(description->description));
error = libxrdp_reset(wm->session);
if (error != 0)
{
LOG_DEVEL(LOG_LEVEL_INFO,
@ -1813,7 +1822,6 @@ process_display_control_monitor_layout_data(struct xrdp_wm *wm)
" xrdp_bitmap_resize failed %d", error);
return advance_error(error, mm);
}
sync_dynamic_monitor_data(wm, &(description->description));
advance_resize_state_machine(mm, WMRZ_EGFX_INITIALIZE);
break;
case WMRZ_EGFX_INITIALIZE:
@ -1845,20 +1853,13 @@ process_display_control_monitor_layout_data(struct xrdp_wm *wm)
// Ack all frames to speed up resize.
module->mod_frame_ack(module, 0, INT_MAX);
}
// Restart module output
// Restart module output after invalidating
// the screen. This causes an automatic redraw.
xrdp_bitmap_invalidate(wm->screen, 0);
rdp = (struct xrdp_rdp *) (wm->session->rdp);
xrdp_rdp_suppress_output(rdp,
0, XSO_REASON_DYNAMIC_RESIZE,
0, 0, desc_width, desc_height);
/* redraw */
error = xrdp_bitmap_invalidate(wm->screen, 0);
if (error != 0)
{
LOG_DEVEL(LOG_LEVEL_INFO,
"process_display_control_monitor_layout_data:"
" xrdp_bitmap_invalidate failed %d", error);
return advance_error(error, mm);
}
advance_resize_state_machine(mm, WMRZ_COMPLETE);
break;
default:
@ -3534,6 +3535,39 @@ xrdp_mm_process_enc_done(struct xrdp_mm *self)
return 0;
}
/*****************************************************************************/
void
xrdp_mm_efgx_add_dirty_region_to_planar_list(struct xrdp_mm *self,
struct xrdp_region *dirty_region)
{
int jndex = 0;
struct xrdp_rect rect;
int error = xrdp_region_get_rect(dirty_region, jndex, &rect);
if (error == 0)
{
if (self->wm->screen_dirty_region == NULL)
{
self->wm->screen_dirty_region = xrdp_region_create(self->wm);
}
do
{
xrdp_region_add_rect(self->wm->screen_dirty_region, &rect);
jndex++;
error = xrdp_region_get_rect(dirty_region, jndex, &rect);
}
while (error == 0);
if (self->mod_handle != 0)
{
// Module has been writing to WM screen using GFX
self->mod_uses_wm_screen_for_gfx = 1;
}
}
}
/*****************************************************************************/
static int
xrdp_mm_draw_dirty(struct xrdp_mm *self)
@ -4432,17 +4466,70 @@ server_draw_text(struct xrdp_mod *mod, int font,
x, y, data, data_len);
}
/*****************************************************************************/
int
client_monitor_resize(struct xrdp_mod *mod, int width, int height,
int num_monitors, const struct monitor_info *monitors)
{
int error = 0;
struct xrdp_wm *wm;
struct display_size_description *display_size_data;
LOG_DEVEL(LOG_LEVEL_TRACE, "client_monitor_resize:");
wm = (struct xrdp_wm *)(mod->wm);
if (wm == 0 || wm->mm == 0 || wm->client_info == 0)
{
return 1;
}
if (wm->client_info->client_resize_mode == CRMODE_NONE)
{
LOG(LOG_LEVEL_WARNING, "Server is not allowed to resize this client");
return 1;
}
if (wm->client_info->client_resize_mode == CRMODE_SINGLE_SCREEN &&
num_monitors > 1)
{
LOG(LOG_LEVEL_WARNING,
"Server cannot resize this client with multiple monitors");
return 1;
}
display_size_data = g_new0(struct display_size_description, 1);
if (display_size_data == NULL)
{
LOG(LOG_LEVEL_ERROR, "client_monitor_resize: Out of memory");
return 1;
}
error = libxrdp_init_display_size_description(num_monitors,
monitors,
display_size_data);
if (error)
{
LOG(LOG_LEVEL_ERROR, "client_monitor_resize:"
" libxrdp_init_display_size_description"
" failed with error %d.", error);
free(display_size_data);
return error;
}
list_add_item(wm->mm->resize_queue, (tintptr)display_size_data);
g_set_wait_obj(wm->mm->resize_ready);
return 0;
}
/*****************************************************************************/
/* Note : if this is called on a multimon setup, the client is resized
* to a single monitor */
int
server_reset(struct xrdp_mod *mod, int width, int height, int bpp)
server_monitor_resize_done(struct xrdp_mod *mod)
{
struct xrdp_wm *wm;
struct xrdp_mm *mm;
LOG(LOG_LEVEL_TRACE, "server_reset:");
LOG(LOG_LEVEL_TRACE, "server_monitor_resize_done:");
wm = (struct xrdp_wm *)(mod->wm);
if (wm == 0)
@ -4450,78 +4537,25 @@ server_reset(struct xrdp_mod *mod, int width, int height, int bpp)
return 1;
}
mm = wm->mm;
if (mm == 0)
{
return 1;
}
if (wm->client_info == 0)
{
return 1;
}
/* older client can't resize */
if (wm->client_info->build <= 419)
if (mm->resize_data != NULL
&& mm->resize_data->state
== WMRZ_SERVER_MONITOR_MESSAGE_PROCESSING)
{
return 0;
LOG(LOG_LEVEL_INFO,
"server_monitor_resize_done: Advancing server monitor resized.");
advance_resize_state_machine(
mm, WMRZ_SERVER_MONITOR_MESSAGE_PROCESSED);
}
// bpp of zero is impossible.
// This is a signal from xup that
// It is finished resizing.
if (bpp == 0)
{
if (mm == 0)
{
return 1;
}
if (!xrdp_wm_can_resize(wm))
{
return 1;
}
if (mm->resize_data == NULL)
{
mm->mod->mod_server_monitor_full_invalidate(mm->mod, width, height);
return 0;
}
if (mm->resize_data != NULL
&& mm->resize_data->state
== WMRZ_SERVER_MONITOR_MESSAGE_PROCESSING)
{
LOG(LOG_LEVEL_INFO,
"server_reset: Advancing server monitor resized.");
advance_resize_state_machine(
mm, WMRZ_SERVER_MONITOR_MESSAGE_PROCESSED);
}
else if (mm->resize_data != NULL
&& mm->resize_data->description.session_height == 0
&& mm->resize_data->description.session_width == 0)
{
mm->mod->mod_server_monitor_full_invalidate(mm->mod, width, height);
}
return 0;
}
/* if same (and only one monitor on client) don't need to do anything */
if (wm->client_info->display_sizes.session_width == (uint32_t)width &&
wm->client_info->display_sizes.session_height == (uint32_t)height &&
wm->client_info->bpp == bpp &&
(wm->client_info->display_sizes.monitorCount == 0 ||
wm->client_info->multimon == 0))
{
return 0;
}
/* reset lib, client_info gets updated in libxrdp_reset */
if (libxrdp_reset(wm->session, width, height, bpp) != 0)
{
return 1;
}
/* reset cache */
xrdp_cache_reset(wm->cache, wm->client_info);
/* resize the main window */
xrdp_bitmap_resize(wm->screen, wm->client_info->display_sizes.session_width,
wm->client_info->display_sizes.session_height);
/* load some stuff */
xrdp_wm_load_static_colors_plus(wm, 0);
xrdp_wm_load_static_pointers(wm);
return 0;
}

View File

@ -95,18 +95,8 @@ xrdp_painter_send_dirty(struct xrdp_painter *self)
if (self->session->client_info->gfx)
{
if (self->wm->screen_dirty_region == NULL)
{
self->wm->screen_dirty_region = xrdp_region_create(self->wm);
}
jndex = 0;
error = xrdp_region_get_rect(self->dirty_region, jndex, &rect);
while (error == 0)
{
xrdp_region_add_rect(self->wm->screen_dirty_region, &rect);
jndex++;
error = xrdp_region_get_rect(self->dirty_region, jndex, &rect);
}
xrdp_mm_efgx_add_dirty_region_to_planar_list(self->wm->mm,
self->dirty_region);
}
else
{

View File

@ -64,7 +64,10 @@ struct xrdp_mod
int (*mod_suppress_output)(struct xrdp_mod *v, int suppress,
int left, int top, int right, int bottom);
int (*mod_server_monitor_resize)(struct xrdp_mod *v,
int width, int height);
int width, int height,
int num_monitors,
const struct monitor_info *monitors,
int *in_progress);
int (*mod_server_monitor_full_invalidate)(struct xrdp_mod *v,
int width, int height);
int (*mod_server_version_message)(struct xrdp_mod *v);
@ -106,7 +109,10 @@ struct xrdp_mod
int box_left, int box_top,
int box_right, int box_bottom,
int x, int y, char *data, int data_len);
int (*server_reset)(struct xrdp_mod *v, int width, int height, int bpp);
int (*client_monitor_resize)(struct xrdp_mod *v, int width, int height,
int num_monitors,
const struct monitor_info *monitors);
int (*server_monitor_resize_done)(struct xrdp_mod *v);
int (*server_get_channel_count)(struct xrdp_mod *v);
int (*server_query_channel)(struct xrdp_mod *v, int index,
char *channel_name,
@ -185,7 +191,7 @@ struct xrdp_mod
int (*server_egfx_cmd)(struct xrdp_mod *v,
char *cmd, int cmd_bytes,
char *data, int data_bytes);
tintptr server_dumby[100 - 49]; /* align, 100 minus the number of server
tintptr server_dumby[100 - 50]; /* align, 100 minus the number of server
functions above */
/* common */
tintptr handle; /* pointer to self as int */
@ -347,7 +353,6 @@ enum display_resize_state
WMRZ_EGFX_CONN_CLOSED,
WRMZ_EGFX_DELETE,
WMRZ_SERVER_MONITOR_RESIZE,
WMRZ_SERVER_VERSION_MESSAGE_START,
WMRZ_SERVER_MONITOR_MESSAGE_PROCESSING,
WMRZ_SERVER_MONITOR_MESSAGE_PROCESSED,
WMRZ_XRDP_CORE_RESET,
@ -370,8 +375,6 @@ enum display_resize_state
(status) == WMRZ_EGFX_CONN_CLOSED ? "WMRZ_EGFX_CONN_CLOSED" : \
(status) == WRMZ_EGFX_DELETE ? "WMRZ_EGFX_DELETE" : \
(status) == WMRZ_SERVER_MONITOR_RESIZE ? "WMRZ_SERVER_MONITOR_RESIZE" : \
(status) == WMRZ_SERVER_VERSION_MESSAGE_START ? \
"WMRZ_SERVER_VERSION_MESSAGE_START" : \
(status) == WMRZ_SERVER_MONITOR_MESSAGE_PROCESSING ? \
"WMRZ_SERVER_MONITOR_MESSAGE_PROCESSING" : \
(status) == WMRZ_SERVER_MONITOR_MESSAGE_PROCESSED ? \
@ -436,6 +439,7 @@ struct xrdp_mm
int egfx_up;
enum xrdp_egfx_flags egfx_flags;
int gfx_delay_autologin;
int mod_uses_wm_screen_for_gfx;
/* Resize on-the-fly control */
struct display_control_monitor_layout_data *resize_data;
struct list *resize_queue;

View File

@ -28,8 +28,10 @@
#include "string_calls.h"
static int
send_server_monitor_resize(
struct mod *mod, struct stream *s, int width, int height, int bpp);
send_server_monitor_update(struct mod *v, struct stream *s,
int width, int height,
int num_monitors,
const struct monitor_info *monitors);
static int
send_server_monitor_full_invalidate(
@ -224,13 +226,6 @@ lib_mod_connect(struct mod *mod)
error = send_server_version_message(mod, s);
}
if (error == 0)
{
/* send screen size message */
error = send_server_monitor_resize(
mod, s, mod->width, mod->height, mod->bpp);
}
if (error == 0)
{
error = send_server_monitor_full_invalidate(
@ -1466,33 +1461,30 @@ send_server_version_message(struct mod *mod, struct stream *s)
/******************************************************************************/
/* return error */
static int
send_server_monitor_resize(
struct mod *mod, struct stream *s, int width, int height, int bpp)
send_server_monitor_update(struct mod *mod, struct stream *s,
int width, int height,
int num_monitors,
const struct monitor_info *monitors)
{
/* send screen size message */
/* send monitor update message */
init_stream(s, 8192);
s_push_layer(s, iso_hdr, 4);
out_uint16_le(s, 103);
out_uint32_le(s, 300);
out_uint32_le(s, 302);
out_uint32_le(s, width);
out_uint32_le(s, height);
/*
TODO: The bpp here is only necessary for initial creation. We should
modify XUP to require this only on server initialization, but not on
resize. Microsoft's RDP protocol does not support changing
the bpp on resize.
*/
out_uint32_le(s, bpp);
out_uint32_le(s, num_monitors);
out_uint32_le(s, 0);
out_uint8a(s, monitors, sizeof(monitors[0]) * num_monitors);
s_mark_end(s);
int len = (int)(s->end - s->data);
s_pop_layer(s, iso_hdr);
out_uint32_le(s, len);
int rv = lib_send_copy(mod, s);
LOG_DEVEL(LOG_LEVEL_DEBUG, "send_server_monitor_resize:"
" sent resize message with following properties to"
" xorgxrdp backend width=%d, height=%d, bpp=%d, return value=%d",
width, height, bpp, rv);
LOG_DEVEL(LOG_LEVEL_DEBUG, "send_server_monitor_update:"
" sent monitor updsate message with following properties to"
" xorgxrdp backend width=%d, height=%d, num=%d, return value=%d",
width, height, num_monitors, rv);
return rv;
}
@ -1542,12 +1534,16 @@ lib_send_server_version_message(struct mod *mod)
/******************************************************************************/
/* return error */
static int
lib_send_server_monitor_resize(struct mod *mod, int width, int height)
lib_send_server_monitor_resize(struct mod *mod, int width, int height,
int num_monitors,
const struct monitor_info *monitors,
int *in_progress)
{
/* send screen size message */
struct stream *s;
make_stream(s);
int rv = send_server_monitor_resize(mod, s, width, height, mod->bpp);
int rv = send_server_monitor_update(mod, s, width, height,
num_monitors, monitors);
*in_progress = (rv == 0);
free_stream(s);
return rv;
}
@ -1802,7 +1798,7 @@ lib_mod_process_message(struct mod *mod, struct stream *s)
LOG(LOG_LEVEL_INFO, "Received memory_allocation_complete"
" command. width: %d, height: %d",
width, height);
rv = mod->server_reset(mod, width, height, 0);
rv = mod->server_monitor_resize_done(mod);
break;
}
s->p = phold + len;
@ -1897,6 +1893,10 @@ lib_mod_check_wait_objs(struct mod *mod)
if (mod->trans != 0)
{
rv = trans_check_wait_objs(mod->trans);
if (rv != 0)
{
LOG(LOG_LEVEL_ERROR, "Xorg server closed connection");
}
}
}

View File

@ -55,7 +55,10 @@ struct mod
int (*mod_suppress_output)(struct mod *v, int suppress,
int left, int top, int right, int bottom);
int (*mod_server_monitor_resize)(struct mod *v,
int width, int height);
int width, int height,
int num_monitors,
const struct monitor_info *monitors,
int *in_progress);
int (*mod_server_monitor_full_invalidate)(struct mod *v,
int width, int height);
int (*mod_server_version_message)(struct mod *v);
@ -94,7 +97,10 @@ struct mod
int box_left, int box_top,
int box_right, int box_bottom,
int x, int y, char *data, int data_len);
int (*server_reset)(struct mod *v, int width, int height, int bpp);
int (*client_monitor_resize)(struct mod *v, int width, int height,
int num_monitors,
const struct monitor_info *monitors);
int (*server_monitor_resize_done)(struct mod *v);
int (*server_get_channel_count)(struct mod *v);
int (*server_query_channel)(struct mod *v, int index,
char *channel_name,
@ -170,7 +176,7 @@ struct mod
int (*server_egfx_cmd)(struct mod *v,
char *cmd, int cmd_bytes,
char *data, int data_bytes);
tintptr server_dumby[100 - 49]; /* align, 100 minus the number of server
tintptr server_dumby[100 - 50]; /* align, 100 minus the number of server
functions above */
/* common */
tintptr handle; /* pointer to self as long */