Merge pull request #2310 from matt335672/physical_desktop_size

Parse more physical monitor size information
This commit is contained in:
matt335672 2022-08-01 08:47:25 +01:00 committed by GitHub
commit d43821f11f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 323 additions and 109 deletions

View File

@ -57,6 +57,7 @@
#define SEC_TAG_CLI_CHANNELS 0xc003 /* CS_CHANNELS? */
#define SEC_TAG_CLI_4 0xc004 /* CS_CLUSTER? */
#define SEC_TAG_CLI_MONITOR 0xc005 /* CS_MONITOR */
#define SEC_TAG_CLI_MONITOR_EX 0xc008 /* CS_MONITOR_EX */
/* Client Core Data: colorDepth, postBeta2ColorDepth (2.2.1.3.2) */
#define RNS_UD_COLOR_4BPP 0xCA00

View File

@ -38,7 +38,8 @@ struct monitor_info
int bottom;
int flags;
/* From 2.2.2.2.1 DISPLAYCONTROL_MONITOR_LAYOUT */
/* From [MS-RDPEDISP] 2.2.2.2.1 DISPLAYCONTROL_MONITOR_LAYOUT, or
* [MS-RDPBCGR] 2.2.1.3.9.1 (TS_MONITOR_ATTRIBUTES) */
unsigned int physical_width;
unsigned int physical_height;
unsigned int orientation;
@ -205,6 +206,13 @@ struct xrdp_client_info
/* xrdp.override_* values */
struct xrdp_keyboard_overrides xrdp_keyboard_overrides;
/* These values are optionally send over as part of TS_UD_CS_CORE.
* They can be used as a fallback for a single monitor session
* if physical sizes are not available in the monitor-specific
* data */
unsigned int session_physical_width; /* in mm */
unsigned int session_physical_height; /* in mm */
};
/* yyyymmdd of last incompatible change to xrdp_client_info */

View File

@ -29,6 +29,7 @@
#include "ms-rdpbcgr.h"
#define MAX_BITMAP_BUF_SIZE (16 * 1024) /* 16K */
#define TS_MONITOR_ATTRIBUTES_SIZE 20 /* [MS-RDPBCGR] 2.2.1.3.9 */
/******************************************************************************/
struct xrdp_session *EXPORT_CC
@ -1764,6 +1765,97 @@ libxrdp_send_session_info(struct xrdp_session *session, const char *data,
return xrdp_rdp_send_session_info(rdp, data, data_bytes);
}
/*****************************************************************************/
/*
Sanitise extended monitor attributes
The extended attributes are received from either
[MS-RDPEDISP] 2.2.2.2.1 (DISPLAYCONTROL_MONITOR_LAYOUT), or
[MS-RDPBCGR] 2.2.1.3.9.1 (TS_MONITOR_ATTRIBUTES)
@param monitor_layout struct containing extended attributes
*/
static void
sanitise_extended_monitor_attributes(struct monitor_info *monitor_layout)
{
/* if EITHER physical_width or physical_height are
* out of range, BOTH must be ignored.
*/
if (monitor_layout->physical_width > 10000
|| monitor_layout->physical_width < 10)
{
LOG(LOG_LEVEL_WARNING, "sanitise_extended_monitor_attributes:"
" physical_width is not within valid range."
" Setting physical_width to 0mm,"
" Setting physical_height to 0mm,"
" physical_width was: %d",
monitor_layout->physical_width);
monitor_layout->physical_width = 0;
monitor_layout->physical_height = 0;
}
if (monitor_layout->physical_height > 10000
|| monitor_layout->physical_height < 10)
{
LOG(LOG_LEVEL_WARNING, "sanitise_extended_monitor_attributes:"
" physical_height is not within valid range."
" Setting physical_width to 0mm,"
" Setting physical_height to 0mm,"
" physical_height was: %d",
monitor_layout->physical_height);
monitor_layout->physical_width = 0;
monitor_layout->physical_height = 0;
}
switch (monitor_layout->orientation)
{
case ORIENTATION_LANDSCAPE:
case ORIENTATION_PORTRAIT:
case ORIENTATION_LANDSCAPE_FLIPPED:
case ORIENTATION_PORTRAIT_FLIPPED:
break;
default:
LOG(LOG_LEVEL_WARNING, "sanitise_extended_monitor_attributes:"
" Orientation is not one of %d, %d, %d, or %d."
" Value was %d and ignored and set to default value of LANDSCAPE.",
ORIENTATION_LANDSCAPE,
ORIENTATION_PORTRAIT,
ORIENTATION_LANDSCAPE_FLIPPED,
ORIENTATION_PORTRAIT_FLIPPED,
monitor_layout->orientation);
monitor_layout->orientation = ORIENTATION_LANDSCAPE;
}
int check_desktop_scale_factor
= monitor_layout->desktop_scale_factor < 100
|| monitor_layout->desktop_scale_factor > 500;
if (check_desktop_scale_factor)
{
LOG(LOG_LEVEL_WARNING, "sanitise_extended_monitor_attributes:"
" desktop_scale_factor is not within valid range"
" of [100, 500]. Assuming 100. Value was: %d",
monitor_layout->desktop_scale_factor);
}
int check_device_scale_factor
= monitor_layout->device_scale_factor != 100
&& monitor_layout->device_scale_factor != 140
&& monitor_layout->device_scale_factor != 180;
if (check_device_scale_factor)
{
LOG(LOG_LEVEL_WARNING, "sanitise_extended_monitor_attributes:"
" device_scale_factor a valid value (One of 100, 140, 180)."
" Assuming 100. Value was: %d",
monitor_layout->device_scale_factor);
}
if (check_desktop_scale_factor || check_device_scale_factor)
{
monitor_layout->desktop_scale_factor = 100;
monitor_layout->device_scale_factor = 100;
}
}
/*****************************************************************************/
/*
Process a [MS-RDPBCGR] TS_UD_CS_MONITOR message.
@ -1907,87 +1999,11 @@ libxrdp_process_monitor_stream(struct stream *s,
in_uint32_le(s, monitor_layout->physical_width);
in_uint32_le(s, monitor_layout->physical_height);
/* Per spec (2.2.2.2.1 DISPLAYCONTROL_MONITOR_LAYOUT),
* if EITHER physical_width or physical_height are
* out of range, BOTH must be ignored.
*/
if (monitor_layout->physical_width > 10000
|| monitor_layout->physical_width < 10)
{
LOG(LOG_LEVEL_WARNING, "libxrdp_process_monitor_stream:"
" physical_width is not within valid range."
" Setting physical_width to 0mm,"
" Setting physical_height to 0mm,"
" physical_width was: %d",
monitor_layout->physical_width);
monitor_layout->physical_width = 0;
monitor_layout->physical_height = 0;
}
if (monitor_layout->physical_height > 10000
|| monitor_layout->physical_height < 10)
{
LOG(LOG_LEVEL_WARNING, "libxrdp_process_monitor_stream:"
" physical_height is not within valid range."
" Setting physical_width to 0mm,"
" Setting physical_height to 0mm,"
" physical_height was: %d",
monitor_layout->physical_height);
monitor_layout->physical_width = 0;
monitor_layout->physical_height = 0;
}
in_uint32_le(s, monitor_layout->orientation);
switch (monitor_layout->orientation)
{
case ORIENTATION_LANDSCAPE:
case ORIENTATION_PORTRAIT:
case ORIENTATION_LANDSCAPE_FLIPPED:
case ORIENTATION_PORTRAIT_FLIPPED:
break;
default:
LOG(LOG_LEVEL_WARNING, "libxrdp_process_monitor_stream:"
" Orientation is not one of %d, %d, %d, or %d."
" Value was %d and ignored and set to default value of LANDSCAPE.",
ORIENTATION_LANDSCAPE,
ORIENTATION_PORTRAIT,
ORIENTATION_LANDSCAPE_FLIPPED,
ORIENTATION_PORTRAIT_FLIPPED,
monitor_layout->orientation);
monitor_layout->orientation = ORIENTATION_LANDSCAPE;
}
in_uint32_le(s, monitor_layout->desktop_scale_factor);
int check_desktop_scale_factor
= monitor_layout->desktop_scale_factor < 100
|| monitor_layout->desktop_scale_factor > 500;
if (check_desktop_scale_factor)
{
LOG(LOG_LEVEL_WARNING, "libxrdp_process_monitor_stream:"
" desktop_scale_factor is not within valid range"
" of [100, 500]. Assuming 100. Value was: %d",
monitor_layout->desktop_scale_factor);
}
in_uint32_le(s, monitor_layout->device_scale_factor);
int check_device_scale_factor
= monitor_layout->device_scale_factor != 100
&& monitor_layout->device_scale_factor != 140
&& monitor_layout->device_scale_factor != 180;
if (check_device_scale_factor)
{
LOG(LOG_LEVEL_WARNING, "libxrdp_process_monitor_stream:"
" device_scale_factor a valid value (One of 100, 140, 180)."
" Assuming 100. Value was: %d",
monitor_layout->device_scale_factor);
}
if (check_desktop_scale_factor || check_device_scale_factor)
{
monitor_layout->desktop_scale_factor = 100;
monitor_layout->device_scale_factor = 100;
}
sanitise_extended_monitor_attributes(monitor_layout);
/*
* 2.2.2.2.1 DISPLAYCONTROL_MONITOR_LAYOUT
@ -2137,5 +2153,108 @@ libxrdp_process_monitor_stream(struct stream *s,
monitor_layout->bottom =
monitor_layout->bottom - all_monitors_encompassing_bounds.top;
}
return 0;
}
/*****************************************************************************/
int
libxrdp_process_monitor_ex_stream(struct stream *s,
struct display_size_description *description)
{
uint32_t num_monitor;
uint32_t monitor_index;
uint32_t attribute_size;
struct monitor_info *monitor_layout;
LOG_DEVEL(LOG_LEVEL_TRACE, "libxrdp_process_monitor_ex_stream:");
if (description == NULL)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "libxrdp_process_monitor_ex_stream: "
"description was null. "
" Valid pointer to allocated description expected.");
return SEC_PROCESS_MONITORS_ERR;
}
if (!s_check_rem_and_log(s, 4,
"libxrdp_process_monitor_ex_stream:"
" Parsing [MS-RDPBCGR] TS_UD_CS_MONITOR_EX"))
{
return SEC_PROCESS_MONITORS_ERR;
}
in_uint32_le(s, attribute_size);
if (attribute_size != TS_MONITOR_ATTRIBUTES_SIZE)
{
LOG(LOG_LEVEL_ERROR,
"libxrdp_process_monitor_ex_stream: [MS-RDPBCGR] Protocol"
" error: TS_UD_CS_MONITOR_EX monitorAttributeSize"
" MUST be %d, received: %d",
TS_MONITOR_ATTRIBUTES_SIZE, attribute_size);
return SEC_PROCESS_MONITORS_ERR;
}
in_uint32_le(s, num_monitor);
LOG(LOG_LEVEL_DEBUG, "libxrdp_process_monitor_ex_stream:"
" The number of monitors received is: %d",
num_monitor);
if (num_monitor != description->monitorCount)
{
LOG(LOG_LEVEL_ERROR,
"libxrdp_process_monitor_ex_stream: [MS-RDPBCGR] Protocol"
" error: TS_UD_CS_MONITOR monitorCount"
" MUST be %d, received: %d",
description->monitorCount, num_monitor);
return SEC_PROCESS_MONITORS_ERR;
}
for (monitor_index = 0; monitor_index < num_monitor; ++monitor_index)
{
if (!s_check_rem_and_log(s, attribute_size,
"libxrdp_process_monitor_ex_stream:"
" Parsing TS_UD_CS_MONITOR_EX"))
{
return SEC_PROCESS_MONITORS_ERR;
}
monitor_layout = description->minfo + monitor_index;
in_uint32_le(s, monitor_layout->physical_width);
in_uint32_le(s, monitor_layout->physical_height);
in_uint32_le(s, monitor_layout->orientation);
in_uint32_le(s, monitor_layout->desktop_scale_factor);
in_uint32_le(s, monitor_layout->device_scale_factor);
sanitise_extended_monitor_attributes(monitor_layout);
LOG_DEVEL(LOG_LEVEL_INFO, "libxrdp_process_monitor_ex_stream:"
" Received [MS-RDPBCGR] 2.2.1.3.9.1 "
" TS_MONITOR_ATTRIBUTES"
" Index: %d, PhysicalWidth %d, PhysicalHeight %d,"
" Orientation %d, DesktopScaleFactor %d,"
" DeviceScaleFactor %d",
monitor_index,
monitor_layout->physical_width,
monitor_layout->physical_height,
monitor_layout->orientation,
monitor_layout->desktop_scale_factor,
monitor_layout->device_scale_factor);
}
/* Update non negative monitor info values */
const struct monitor_info *src = description->minfo;
struct monitor_info *dst = description->minfo_wm;
for (monitor_index = 0; monitor_index < num_monitor; ++monitor_index)
{
dst->physical_width = src->physical_width;
dst->physical_height = src->physical_height;
dst->orientation = src->orientation;
dst->desktop_scale_factor = src->desktop_scale_factor;
dst->device_scale_factor = src->device_scale_factor;
++src;
++dst;
}
return 0;
}

View File

@ -364,9 +364,10 @@ xrdp_mcs_disconnect(struct xrdp_mcs *self);
/* xrdp_sec.c */
/*
These are error return codes for both:
These are error return codes for:-
1. xrdp_sec_process_mcs_data_monitors
2. libxrdp_process_monitor_stream
3. libxrdp_process_monitor_ex_stream
To clarify any reason for a non-zero response code.
*/
#define SEC_PROCESS_MONITORS_ERR 1

View File

@ -324,4 +324,19 @@ int EXPORT_CC
libxrdp_process_monitor_stream(struct stream *s, struct display_size_description *description,
int full_parameters);
/**
* Processes a stream that is based on [MS-RDPBCGR] 2.2.1.3.9 Client
* Monitor Extended Data (TS_UD_CS_MONITOR_EX)
*
* Data is stored in a struct which has already been read by
* libxrdp_process_monitor_stream() withj full_parameters set to 0.
*
* @param s Stream to read data from. Stream is read up to monitorAttributeSize
* @param description Result of reading TS_UD_CS_MONITOR PDU
* @return 0 if the data is processed, non-zero if there is an error.
*/
int EXPORT_CC
libxrdp_process_monitor_ex_stream(struct stream *s,
struct display_size_description *description);
#endif

View File

@ -1953,19 +1953,23 @@ xrdp_sec_process_mcs_data_CS_CORE(struct xrdp_sec *self, struct stream *s)
char clientName[INFO_CLIENT_NAME_BYTES / 2] = { '\0' };
UNUSED_VAR(version);
struct xrdp_client_info *client_info = &self->rdp_layer->client_info;
/* Clear physical sizes. These are optional and may not be read later */
client_info->session_physical_width = 0;
client_info->session_physical_height = 0;
/* TS_UD_CS_CORE requiered fields */
/* TS_UD_CS_CORE required fields */
in_uint32_le(s, version);
in_uint16_le(s, self->rdp_layer->client_info.display_sizes.session_width);
in_uint16_le(s, self->rdp_layer->client_info.display_sizes.session_height);
in_uint16_le(s, client_info->display_sizes.session_width);
in_uint16_le(s, client_info->display_sizes.session_height);
in_uint16_le(s, colorDepth);
switch (colorDepth)
{
case RNS_UD_COLOR_4BPP:
self->rdp_layer->client_info.bpp = 4;
client_info->bpp = 4;
break;
case RNS_UD_COLOR_8BPP:
self->rdp_layer->client_info.bpp = 8;
client_info->bpp = 8;
break;
}
in_uint8s(s, 2); /* SASSequence */
@ -1985,8 +1989,8 @@ xrdp_sec_process_mcs_data_CS_CORE(struct xrdp_sec *self, struct stream *s)
"keyboardSubType (ignored), keyboardFunctionKey (ignored), "
"imeFileName (ignored)",
version,
self->rdp_layer->client_info.display_sizes.session_width,
self->rdp_layer->client_info.display_sizes.session_height,
client_info->display_sizes.session_width,
client_info->display_sizes.session_height,
(colorDepth == 0xca00 ? "RNS_UD_COLOR_4BPP" :
colorDepth == 0xca01 ? "RNS_UD_COLOR_8BPP" : "unknown"),
clientName);
@ -2005,19 +2009,19 @@ xrdp_sec_process_mcs_data_CS_CORE(struct xrdp_sec *self, struct stream *s)
switch (postBeta2ColorDepth)
{
case RNS_UD_COLOR_4BPP:
self->rdp_layer->client_info.bpp = 4;
client_info->bpp = 4;
break;
case RNS_UD_COLOR_8BPP :
self->rdp_layer->client_info.bpp = 8;
client_info->bpp = 8;
break;
case RNS_UD_COLOR_16BPP_555:
self->rdp_layer->client_info.bpp = 15;
client_info->bpp = 15;
break;
case RNS_UD_COLOR_16BPP_565:
self->rdp_layer->client_info.bpp = 16;
client_info->bpp = 16;
break;
case RNS_UD_COLOR_24BPP:
self->rdp_layer->client_info.bpp = 24;
client_info->bpp = 24;
break;
}
if (!s_check_rem(s, 2))
@ -2049,7 +2053,7 @@ xrdp_sec_process_mcs_data_CS_CORE(struct xrdp_sec *self, struct stream *s)
highColorDepth == 0x0010 ? "HIGH_COLOR_16BPP" :
highColorDepth == 0x0018 ? "HIGH_COLOR_24BPP" :
"unknown");
self->rdp_layer->client_info.bpp = highColorDepth;
client_info->bpp = highColorDepth;
if (!s_check_rem(s, 2))
{
@ -2069,13 +2073,13 @@ xrdp_sec_process_mcs_data_CS_CORE(struct xrdp_sec *self, struct stream *s)
return 0;
}
in_uint16_le(s, earlyCapabilityFlags);
self->rdp_layer->client_info.mcs_early_capability_flags = earlyCapabilityFlags;
client_info->mcs_early_capability_flags = earlyCapabilityFlags;
LOG_DEVEL(LOG_LEVEL_TRACE, "Received [MS-RDPBCGR] TS_UD_CS_CORE "
"<Optional Field> earlyCapabilityFlags 0x%4.4x",
earlyCapabilityFlags);
if ((earlyCapabilityFlags & 0x0002) && (supportedColorDepths & 0x0008))
{
self->rdp_layer->client_info.bpp = 32;
client_info->bpp = 32;
}
if (!s_check_rem(s, 64))
@ -2090,10 +2094,10 @@ xrdp_sec_process_mcs_data_CS_CORE(struct xrdp_sec *self, struct stream *s)
{
return 0;
}
in_uint8(s, self->rdp_layer->client_info.mcs_connection_type); /* connectionType */
in_uint8(s, client_info->mcs_connection_type); /* connectionType */
LOG_DEVEL(LOG_LEVEL_TRACE, "Received [MS-RDPBCGR] TS_UD_CS_CORE "
"<Optional Field> connectionType 0x%2.2x",
self->rdp_layer->client_info.mcs_connection_type);
client_info->mcs_connection_type);
if (!s_check_rem(s, 1))
{
@ -2111,22 +2115,43 @@ xrdp_sec_process_mcs_data_CS_CORE(struct xrdp_sec *self, struct stream *s)
LOG_DEVEL(LOG_LEVEL_TRACE, "Received [MS-RDPBCGR] TS_UD_CS_CORE "
"<Optional Field> serverSelectedProtocol (ignored)");
/*
* Non-zero values for the desktop physical width and height values
* are only sent if the client has a single monitor. For multiple
* monitors, the physical size of each monitor is sent in the
* TS_UD_CS_MONITOR_EX PDU */
if (!s_check_rem(s, 4))
{
return 0;
}
in_uint8s(s, 4); /* desktopPhysicalWidth */
in_uint32_le(s, client_info->session_physical_width);
LOG_DEVEL(LOG_LEVEL_TRACE, "Received [MS-RDPBCGR] TS_UD_CS_CORE "
"<Optional Field> desktopPhysicalWidth (ignored)");
"<Optional Field> desktopPhysicalWidth %u",
client_info->session_physical_width);
if (!s_check_rem(s, 4))
{
client_info->session_physical_width = 0;
return 0;
}
in_uint8s(s, 4); /* desktopPhysicalHeight */
in_uint32_le(s, client_info->session_physical_height);
LOG_DEVEL(LOG_LEVEL_TRACE, "Received [MS-RDPBCGR] TS_UD_CS_CORE "
"<Optional Field> desktopPhysicalHeight (ignored)");
"<Optional Field> desktopPhysicalHeight %u",
client_info->session_physical_height);
/* MS-RDPBCGR 2.2.1.3.2 */
if (client_info->session_physical_width < 10 ||
client_info->session_physical_width > 10000 ||
client_info->session_physical_height < 10 ||
client_info->session_physical_height > 10000)
{
LOG(LOG_LEVEL_WARNING,
"Physical desktop dimensions (%ux%u) are invalid",
client_info->session_physical_width,
client_info->session_physical_height);
client_info->session_physical_width = 0;
client_info->session_physical_height = 0;
}
if (!s_check_rem(s, 2))
{
return 0;
@ -2407,6 +2432,45 @@ xrdp_sec_process_mcs_data_monitors(struct xrdp_sec *self, struct stream *s)
return error;
}
/*****************************************************************************/
/* Process a [MS-RDPBCGR] TS_UD_CS_MONITOR_EX message.
reads the client monitor's extended data */
int
xrdp_sec_process_mcs_data_monitors_ex(struct xrdp_sec *self, struct stream *s)
{
int flags;
struct xrdp_client_info *client_info = &(self->rdp_layer->client_info);
LOG_DEVEL(LOG_LEVEL_TRACE, "xrdp_sec_process_mcs_data_monitors_ex:");
/* this is an option set in xrdp.ini */
if (client_info->multimon != 1) /* are multi-monitors allowed ? */
{
/* This should already be logged in
xrdp_sec_process_mcs_data_monitors() */
return 0;
}
if (!s_check_rem_and_log(s, 4,
"xrdp_sec_process_mcs_data_monitors_ex:"
" Parsing [MS-RDPBCGR] TS_UD_CS_MONITOR_EX"))
{
return SEC_PROCESS_MONITORS_ERR;
}
in_uint32_le(s, flags); /* flags */
//verify flags - must be 0x0
if (flags != 0)
{
LOG(LOG_LEVEL_ERROR,
"xrdp_sec_process_mcs_data_monitors_ex: [MS-RDPBCGR]"
" Protocol error: TS_UD_CS_MONITOR_EX flags MUST be zero,"
" received: 0x%8.8x", flags);
return SEC_PROCESS_MONITORS_ERR;
}
return libxrdp_process_monitor_ex_stream(s, &client_info->display_sizes);
}
/*****************************************************************************/
/* Process a Client MCS Connect Initial PDU with GCC Conference Create Request.
process client mcs data, we need some things in here to create the server
@ -2418,6 +2482,7 @@ xrdp_sec_process_mcs_data(struct xrdp_sec *self)
char *hold_p = (char *)NULL;
int tag = 0;
int size = 0;
struct xrdp_client_info *client_info = &self->rdp_layer->client_info;
s = &(self->client_mcs_data);
/* set p to beginning */
@ -2486,8 +2551,15 @@ xrdp_sec_process_mcs_data(struct xrdp_sec *self)
return 1;
}
break;
case SEC_TAG_CLI_MONITOR_EX: /* CS_MONITOR_EX 0xC008 */
if (xrdp_sec_process_mcs_data_monitors_ex(self, s) != 0)
{
LOG(LOG_LEVEL_ERROR,
"Processing [MS-RDPBCGR] TS_UD_CS_MONITOR_EX failed");
return 1;
}
break;
/* CS_MCS_MSGCHANNEL 0xC006
CS_MONITOR_EX 0xC008
CS_MULTITRANSPORT 0xC00A
SC_CORE 0x0C01
SC_SECURITY 0x0C02
@ -2504,19 +2576,17 @@ xrdp_sec_process_mcs_data(struct xrdp_sec *self)
s->p = hold_p + size;
}
if (self->rdp_layer->client_info.max_bpp > 0)
if (client_info->max_bpp > 0)
{
if (self->rdp_layer->client_info.bpp >
self->rdp_layer->client_info.max_bpp)
if (client_info->bpp > client_info->max_bpp)
{
LOG(LOG_LEVEL_WARNING, "Client requested %d bpp color depth, "
"but the server configuration is limited to %d bpp. "
"Downgrading the color depth to %d bits-per-pixel.",
self->rdp_layer->client_info.bpp,
self->rdp_layer->client_info.max_bpp,
self->rdp_layer->client_info.max_bpp);
self->rdp_layer->client_info.bpp =
self->rdp_layer->client_info.max_bpp;
client_info->bpp,
client_info->max_bpp,
client_info->max_bpp);
client_info->bpp = client_info->max_bpp;
}
}