Add Dynamic Virtual Channels and basic RDPEDISP support

Fix issue #192

Signed-off-by: Henrik Andersson <hean01@cendio.com>
Signed-off-by: Karl Mikaelsson <derfian@cendio.se>
This commit is contained in:
Cendio 2017-11-08 09:29:41 +01:00
parent d3d6613c78
commit 2f03f65efe
14 changed files with 740 additions and 14 deletions

View File

@ -25,7 +25,7 @@ SOUNDOBJ = @SOUNDOBJ@
SCARDOBJ = @SCARDOBJ@
CREDSSPOBJ = @CREDSSPOBJ@
RDPOBJ = tcp.o asn.o iso.o mcs.o secure.o licence.o rdp.o orders.o bitmap.o cache.o rdp5.o channels.o rdpdr.o serial.o printer.o disk.o parallel.o printercache.o mppc.o pstcache.o lspci.o seamless.o ssl.o utils.o stream.o
RDPOBJ = tcp.o asn.o iso.o mcs.o secure.o licence.o rdp.o orders.o bitmap.o cache.o rdp5.o channels.o rdpdr.o serial.o printer.o disk.o parallel.o printercache.o mppc.o pstcache.o lspci.o seamless.o ssl.o utils.o stream.o dvc.o rdpedisp.o
X11OBJ = rdesktop.o xwin.o xkeymap.o ewmhints.o xclip.o cliprdr.o ctrl.o
.PHONY: all

View File

@ -92,7 +92,7 @@ channel_send(STREAM s, VCHANNEL * channel)
s_pop_layer(s, channel_hdr);
length = s->end - s->p - 8;
logger(Protocol, Debug, "channel_send(), length = %d", length);
logger(Protocol, Debug, "channel_send(), channel = %d, length = %d", channel->mcs_id, length);
thislength = MIN(length, CHANNEL_CHUNK_LENGTH);
/* Note: In the original clipboard implementation, this number was

View File

@ -17,6 +17,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _CONSTANTS_H
#define _CONSTANTS_H
/* TCP port for Remote Desktop Protocol */
#define TCP_PORT_RDP 3389
@ -800,3 +803,5 @@ enum RDP_SUPPRESS_STATUS
SUPPRESS_DISPLAY_UPDATES = 0x00,
ALLOW_DISPLAY_UPDATES = 0x01
};
#endif /* _CONSTANTS_H */

455
dvc.c Normal file
View File

@ -0,0 +1,455 @@
/* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client.
Dynamic Channel Virtual Channel Extension.
Copyright 2017 Henrik Andersson <hean01@cendio.com> for Cendio AB
Copyright 2017 Karl Mikaelsson <derfian@cendio.se> for Cendio AB
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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "rdesktop.h"
#define MAX_DVC_CHANNELS 20
#define INVALID_CHANNEL ((uint32)-1)
#define DYNVC_CREATE_REQ 0x01
#define DYNVC_DATA_FIRST 0x02
#define DYNVC_DATA 0x03
#define DYNVC_CLOSE 0x04
#define DYNVC_CAPABILITIES 0x05
#define DYNVC_DATA_FIRST_COMPRESSED 0x06
#define DYNVC_DATA_COMPRESSED 0x07
#define DYNVC_SOFT_SYNC_REQUEST 0x08
#define DYNVC_SOFT_SYNC_RESPONSE 0x09
static VCHANNEL *dvc_channel;
static uint32 dvc_in_channelid(STREAM s, dvc_hdr_t hdr);
typedef struct dvc_channel_t
{
uint32 hash;
uint32 channel_id;
dvc_channel_process_fn handler;
} dvc_channel_t;
dvc_channel_t channels[MAX_DVC_CHANNELS];
static RD_BOOL
dvc_channels_exists(const char *name)
{
int i;
uint32 hash;
hash = utils_djb2_hash(name);
for (i = 0; i < MAX_DVC_CHANNELS; i++)
{
if (channels[i].hash == hash)
return True;
}
return False;
}
static const dvc_channel_t *
dvc_channels_get_by_id(uint32 id)
{
int i;
for (i = 0; i < MAX_DVC_CHANNELS; i++)
{
if (channels[i].channel_id == id)
{
return &channels[i];
}
}
return NULL;
}
static uint32
dvc_channels_get_id(const char *name)
{
int i;
uint32 hash;
hash = utils_djb2_hash(name);
for (i = 0; i < MAX_DVC_CHANNELS; i++)
{
if (channels[i].hash == hash)
{
return channels[i].channel_id;
}
}
return INVALID_CHANNEL;
}
static RD_BOOL
dvc_channels_remove_by_id(uint32 channelid)
{
int i;
for (i = 0; i < MAX_DVC_CHANNELS; i++)
{
if (channels[i].channel_id == channelid)
{
memset(&channels[i], 0, sizeof(dvc_channel_t));
return True;
}
}
return False;
}
static RD_BOOL
dvc_channels_add(const char *name, dvc_channel_process_fn handler, uint32 channel_id)
{
int i;
uint32 hash;
if (dvc_channels_exists(name) == True)
{
logger(Core, Warning, "dvc_channels_add(), channel with name '%s' already exists",
name);
return False;
}
for (i = 0; i < MAX_DVC_CHANNELS; i++)
{
if (channels[i].hash == 0)
{
hash = utils_djb2_hash(name);
channels[i].hash = hash;
channels[i].handler = handler;
channels[i].channel_id = channel_id;
logger(Core, Debug,
"dvc_channels_add(), Added hash=%x, channel_id=%d, name=%s, handler=%p",
hash, channel_id, name, handler);
return True;
}
}
logger(Core, Warning, "dvc_channels_add(), Failed to add channel, maximum number of channels are being used");
return False;
}
static int
dvc_channels_set_id(const char *name, uint32 channel_id)
{
int i;
uint32 hash;
hash = utils_djb2_hash(name);
for (i = 0; i < MAX_DVC_CHANNELS; i++)
{
if (channels[i].hash == hash)
{
logger(Core, Debug, "dvc_channels_set_id(), name = '%s', channel_id = %d",
name, channel_id);
channels[i].channel_id = channel_id;
return 0;
}
}
return -1;
}
RD_BOOL
dvc_channels_is_available(const char *name)
{
int i;
uint32 hash;
hash = utils_djb2_hash(name);
for (i = 0; i < MAX_DVC_CHANNELS; i++)
{
if (channels[i].hash == hash)
{
return (channels[i].channel_id != INVALID_CHANNEL);
}
}
return False;
}
RD_BOOL
dvc_channels_register(const char *name, dvc_channel_process_fn handler)
{
return dvc_channels_add(name, handler, INVALID_CHANNEL);
}
static STREAM
dvc_init_packet(dvc_hdr_t hdr, uint32 channelid, size_t length)
{
STREAM s;
length += 1; /* add 1 byte hdr */
if (channelid != INVALID_CHANNEL)
{
if (hdr.hdr.cbid == 0)
length += 1;
else if (hdr.hdr.cbid == 1)
length += 2;
else if (hdr.hdr.cbid == 2)
length += 4;
}
s = channel_init(dvc_channel, length);
out_uint8(s, hdr.data); /* DVC header */
if (channelid != INVALID_CHANNEL)
{
if (hdr.hdr.cbid == 0)
{
out_uint8(s, channelid);
}
else if (hdr.hdr.cbid == 1)
{
out_uint16_le(s, channelid);
}
else if (hdr.hdr.cbid == 2)
{
out_uint32_le(s, channelid);
}
}
return s;
}
void
dvc_send(const char *name, STREAM s)
{
STREAM ls;
dvc_hdr_t hdr;
uint32 channel_id;
channel_id = dvc_channels_get_id(name);
if (channel_id == INVALID_CHANNEL)
{
logger(Core, Error, "dvc_send(), Trying to send data on invalid channel '%s'",
name);
return;
}
/* FIXME: we assume length is less than 1600 */
hdr.hdr.cmd = DYNVC_DATA;
hdr.hdr.cbid = 2;
hdr.hdr.sp = 0;
ls = dvc_init_packet(hdr, channel_id, s_length(s));
out_stream(ls, s);
s_mark_end(ls);
channel_send(ls, dvc_channel);
}
static void
dvc_send_capabilities_response()
{
STREAM s;
dvc_hdr_t hdr;
uint16 supportedversion = 0x01;
hdr.hdr.cbid = 0x00;
hdr.hdr.sp = 0x00;
hdr.hdr.cmd = DYNVC_CAPABILITIES;
logger(Protocol, Debug,
"dvc_send_capabilities_response(), offering support for dvc %d", supportedversion);
s = dvc_init_packet(hdr, -1, 3);
out_uint8(s, 0x00); /* pad */
out_uint16_le(s, supportedversion); /* version */
s_mark_end(s);
channel_send(s, dvc_channel);
}
static void
dvc_process_caps_pdu(STREAM s)
{
uint16 version;
/* VERSION1 */
in_uint8s(s, 1); /* pad */
in_uint16_le(s, version); /* version */
logger(Protocol, Debug, "dvc_process_caps(), server supports dvc %d", version);
dvc_send_capabilities_response();
}
static void
dvc_send_create_response(RD_BOOL success, dvc_hdr_t hdr, uint32 channelid)
{
STREAM s;
logger(Protocol, Debug, "dvc_send_create_response(), %s request to create channelid %d",
(success ? "granted" : "denied"), channelid);
s = dvc_init_packet(hdr, channelid, 4);
out_uint32_le(s, success ? 0 : -1);
s_mark_end(s);
channel_send(s, dvc_channel);
}
static void
dvc_process_create_pdu(STREAM s, dvc_hdr_t hdr)
{
char name[512];
uint32 channelid;
channelid = dvc_in_channelid(s, hdr);
in_ansi_string(s, name, sizeof(name));
logger(Protocol, Debug, "dvc_process_create(), server requests channelid = %d, name = '%s'",
channelid, name);
if (dvc_channels_exists(name))
{
logger(Core, Verbose, "Established dynamic virtual channel '%s'", name);
dvc_channels_set_id(name, channelid);
dvc_send_create_response(True, hdr, channelid);
}
else
{
dvc_send_create_response(False, hdr, channelid);
}
}
static uint32
dvc_in_channelid(STREAM s, dvc_hdr_t hdr)
{
uint32 id;
id = (uint32) - 1;
switch (hdr.hdr.cbid)
{
case 0:
in_uint8(s, id);
break;
case 1:
in_uint16_le(s, id);
break;
case 2:
in_uint32_le(s, id);
break;
}
return id;
}
static void
dvc_process_data_pdu(STREAM s, dvc_hdr_t hdr)
{
const dvc_channel_t *ch;
uint32 channelid;
channelid = dvc_in_channelid(s, hdr);
ch = dvc_channels_get_by_id(channelid);
if (ch == NULL)
{
logger(Protocol, Warning,
"dvc_process_data(), Received data on unregistered channel %d", channelid);
return;
}
/* dispatch packet to channel handler */
ch->handler(s);
}
static void
dvc_process_close_pdu(STREAM s, dvc_hdr_t hdr)
{
uint32 channelid;
channelid = dvc_in_channelid(s, hdr);
logger(Protocol, Debug, "dvc_process_close_pdu(), close channel %d", channelid);
if (!dvc_channels_remove_by_id(channelid))
{
logger(Protocol, Warning,
"dvc_process_close_pdu(), Received close request for unregistered channel %d",
channelid);
return;
}
}
static void
dvc_process_pdu(STREAM s)
{
dvc_hdr_t hdr;
in_uint8(s, hdr.data);
switch (hdr.hdr.cmd)
{
case DYNVC_CAPABILITIES:
dvc_process_caps_pdu(s);
break;
case DYNVC_CREATE_REQ:
dvc_process_create_pdu(s, hdr);
break;
case DYNVC_DATA:
dvc_process_data_pdu(s, hdr);
break;
case DYNVC_CLOSE:
dvc_process_close_pdu(s, hdr);
break;
#if 0 /* Unimplemented */
case DYNVC_DATA_FIRST:
break;
case DYNVC_DATA_FIRST_COMPRESSED:
break;
case DYNVC_DATA_COMPRESSED:
break;
case DYNVC_SOFT_SYNC_REQUEST:
break;
case DYNVC_SOFT_SYNC_RESPONSE:
break;
#endif
default:
logger(Protocol, Warning, "dvc_process_pdu(), Unhandled command type 0x%x",
hdr.hdr.cmd);
break;
}
}
RD_BOOL
dvc_init()
{
memset(channels, 0, sizeof(channels));
dvc_channel = channel_register("drdynvc",
CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP,
dvc_process_pdu);
return (dvc_channel != NULL);
}

View File

@ -327,6 +327,15 @@ void ui_seamless_syncbegin(unsigned long flags);
void ui_seamless_ack(unsigned int serial);
/* lspci.c */
RD_BOOL lspci_init(void);
/* rdpedisp.c */
void rdpedisp_init(void);
void rdpedisp_set_session_size(uint32 width, uint32 height);
/* dvc.c */
typedef void (*dvc_channel_process_fn) (STREAM s);
RD_BOOL dvc_init(void);
RD_BOOL dvc_channels_register(const char *name, dvc_channel_process_fn handler);
RD_BOOL dvc_channels_is_available(const char *name);
void dvc_send(const char *name, STREAM s);
/* seamless.c */
RD_BOOL seamless_init(void);
void seamless_reset_state(void);

View File

@ -74,6 +74,8 @@ int g_sizeopt = 0; /* If non-zero, a special size has been
int g_dpi = 0; /* device DPI: default not set */
int g_width = 800;
int g_height = 600;
uint32 g_windowed_width = 800;
uint32 g_windowed_height = 600;
int g_xpos = 0;
int g_ypos = 0;
int g_pos = 0; /* 0 position unspecified,
@ -766,6 +768,9 @@ main(int argc, char *argv[])
g_ypos = strtol(p, NULL, 10);
}
g_windowed_height = g_height;
g_windowed_width = g_width;
break;
case 'f':
@ -1208,6 +1213,10 @@ main(int argc, char *argv[])
lspci_init();
rdpdr_init();
dvc_init();
rdpedisp_init();
g_reconnect_loop = False;
while (1)
{

138
rdpedisp.c Normal file
View File

@ -0,0 +1,138 @@
/* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client.
Display Update Virtual Channel Extension.
Copyright 2017 Henrik Andersson <hean01@cendio.com> for Cendio AB
Copyright 2017 Karl Mikaelsson <derfian@cendio.se> for Cendio AB
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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "rdesktop.h"
#define DISPLAYCONTROL_PDU_TYPE_CAPS 0x5
#define DISPLAYCONTROL_PDU_TYPE_MONITOR_LAYOUT 0x2
#define DISPLAYCONTROL_MONITOR_PRIMARY 0x1
#define RDPEDISP_CHANNEL_NAME "Microsoft::Windows::RDS::DisplayControl"
static void rdpedisp_send(STREAM s);
static void rdpedisp_init_packet(STREAM s, uint32 type, uint32 length);
static void
rdpedisp_process_caps_pdu(STREAM s)
{
uint32 tmp[3];
in_uint32_le(s, tmp[0]); /* MaxNumMonitors */
in_uint32_le(s, tmp[1]); /* MaxMonitorAreaFactorA */
in_uint32_le(s, tmp[2]); /* MaxMonitorAreaFactorB */
logger(Protocol, Debug,
"rdpedisp_process_caps_pdu(), Max supported monitor area (square pixels) is %d",
tmp[0] * tmp[1] * tmp[2]);
}
static void
rdpedisp_process_pdu(STREAM s)
{
uint32 type;
/* Read DISPLAYCONTROL_HEADER */
in_uint32_le(s, type); /* type */
in_skip(s, 4); /* length */
logger(Protocol, Debug, "rdpedisp_process_pdu(), Got PDU type %d", type);
switch (type)
{
case DISPLAYCONTROL_PDU_TYPE_CAPS:
rdpedisp_process_caps_pdu(s);
break;
default:
logger(Protocol, Warning, "rdpedisp_process_pdu(), Unhandled PDU type %d",
type);
break;
}
}
static void
rdpedisp_send_monitor_layout_pdu(uint32 width, uint32 height)
{
struct stream s;
uint32 physwidth, physheight, desktopscale, devicescale;
memset(&s, 0, sizeof(s));
logger(Protocol, Debug, "rdpedisp_send_monitor_layout_pdu(), width = %d, height = %d", width,
height);
rdpedisp_init_packet(&s, DISPLAYCONTROL_PDU_TYPE_MONITOR_LAYOUT, 16 + 1 * 40);
out_uint32_le(&s, 40); /* MonitorLayoutSize - spec mandates 40 */
out_uint32_le(&s, 1); /* NumMonitors */
out_uint32_le(&s, DISPLAYCONTROL_MONITOR_PRIMARY); /* flags */
out_uint32_le(&s, 0); /* left */
out_uint32_le(&s, 0); /* top */
out_uint32_le(&s, width); /* width */
out_uint32_le(&s, height); /* height */
utils_calculate_dpi_scale_factors(&physwidth, &physheight, &desktopscale, &devicescale);
out_uint32_le(&s, physwidth); /* physicalwidth */
out_uint32_le(&s, physheight); /* physicalheight */
out_uint32_le(&s, ORIENTATION_LANDSCAPE); /* Orientation */
out_uint32_le(&s, desktopscale); /* DesktopScaleFactor */
out_uint32_le(&s, devicescale); /* DeviceScaleFactor */
s_mark_end(&s);
rdpedisp_send(&s);
}
static void
rdpedisp_init_packet(STREAM s, uint32 type, uint32 length)
{
s_realloc(s, length);
s_reset(s);
out_uint32_le(s, type);
out_uint32_le(s, length);
}
static void
rdpedisp_send(STREAM s)
{
dvc_send(RDPEDISP_CHANNEL_NAME, s);
}
static RD_BOOL
rdpedisp_is_available()
{
return dvc_channels_is_available(RDPEDISP_CHANNEL_NAME);
}
void
rdpedisp_set_session_size(uint32 width, uint32 height)
{
if (rdpedisp_is_available() == False)
return;
rdpedisp_send_monitor_layout_pdu(width, height);
}
void
rdpedisp_init(void)
{
dvc_channels_register(RDPEDISP_CHANNEL_NAME, rdpedisp_process_pdu);
}

View File

@ -397,6 +397,7 @@ sec_out_mcs_connect_initial_pdu(STREAM s, uint32 selected_protocol)
uint32 rdpversion = RDP_40;
uint16 capflags = RNS_UD_CS_SUPPORT_ERRINFO_PDU;
uint16 colorsupport = RNS_UD_24BPP_SUPPORT | RNS_UD_16BPP_SUPPORT | RNS_UD_32BPP_SUPPORT;
uint32 physwidth, physheight, desktopscale, devicescale;
if (g_rdp_version >= RDP_V5)
rdpversion = RDP_50;
@ -458,13 +459,13 @@ sec_out_mcs_connect_initial_pdu(STREAM s, uint32 selected_protocol)
if (g_dpi > 0)
{
/* Extended client info describing monitor geometry */
out_uint32_le(s, g_width * 254 / (g_dpi * 10)); /* desktop physical width */
out_uint32_le(s, g_height * 254 / (g_dpi * 10)); /* desktop physical height */
out_uint16_le(s, ORIENTATION_LANDSCAPE);
out_uint32_le(s, g_dpi < 96 ? 100 : (g_dpi * 100 + 48) / 96); /* desktop scale factor */
/* the spec calls this out as being valid for range 100-500 but I doubt the upper range is accurate */
out_uint32_le(s, g_dpi < 134 ? 100 : (g_dpi < 173 ? 140 : 180)); /* device scale factor */
/* the only allowed values for device scale factor are 100, 140, and 180. */
utils_calculate_dpi_scale_factors(&physwidth, &physheight,
&desktopscale, &devicescale);
out_uint32_le(s, physwidth); /* physicalwidth */
out_uint32_le(s, physheight); /* physicalheight */
out_uint16_le(s, ORIENTATION_LANDSCAPE); /* Orientation */
out_uint32_le(s, desktopscale); /* DesktopScaleFactor */
out_uint32_le(s, devicescale); /* DeviceScaleFactor */
}
/* Write a Client Cluster Data (TS_UD_CS_CLUSTER) */

View File

@ -153,3 +153,30 @@ out_utf16s_no_eos(STREAM s, const char *string)
{
_out_utf16s(s, 0, string);
}
/* Read bytes from STREAM s into *string until a null terminator is
found, or len bytes are read from the stream. Returns the number of
bytes read. */
size_t
in_ansi_string(STREAM s, char *string, size_t len)
{
char *ps;
size_t left;
ps = string;
left = len;
while(left--)
{
if (left == 0)
break;
in_uint8(s, *ps);
if (*ps == '\0')
break;
ps++;
}
return len - left;
}

View File

@ -47,6 +47,9 @@ void out_utf16s(STREAM s, const char *string);
void out_utf16s_padded(STREAM s, const char *string, size_t width, unsigned char pad);
void out_utf16s_no_eos(STREAM s, const char *string);
size_t in_ansi_string(STREAM s, char *string, size_t len);
#define s_push_layer(s,h,n) { (s)->h = (s)->p; (s)->p += n; }
#define s_pop_layer(s,h) (s)->p = (s)->h;
#define s_mark_end(s) (s)->end = (s)->p;
@ -100,6 +103,7 @@ void out_utf16s_no_eos(STREAM s, const char *string);
#define in_uint8p(s,v,n) { v = (s)->p; (s)->p += n; }
#define in_uint8a(s,v,n) { memcpy(v,(s)->p,n); (s)->p += n; }
#define in_uint8s(s,n) (s)->p += n;
#define in_skip(s,n) in_uint8s(s,n)
#define out_uint8(s,v) *((s)->p++) = v;
#define out_uint8p(s,v,n) { memcpy((s)->p,v,n); (s)->p += n; }
#define out_uint8a(s,v,n) out_uint8p(s,v,n);

18
types.h
View File

@ -19,6 +19,12 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _TYPES_H
#define _TYPES_H
#include "constants.h"
#include "stream.h"
typedef int RD_BOOL;
#ifndef True
@ -300,4 +306,16 @@ typedef struct fileinfo
}
FILEINFO;
typedef union dvc_hdr_t {
uint8 data;
struct {
uint8 cbid:2;
uint8 sp:2;
uint8 cmd:4;
} hdr;
} dvc_hdr_t;
typedef RD_BOOL(*str_handle_lines_t) (const char *line, void *data);
#endif /* _TYPES_H */

42
utils.c
View File

@ -28,8 +28,27 @@
#include "utils.h"
extern char g_codepage[16];
extern int g_dpi;
extern int g_width;
extern int g_height;
static RD_BOOL g_iconv_works = True;
uint32
utils_djb2_hash(const char *str)
{
uint8 c;
uint8 *pstr;
uint32 hash = 5381;
pstr = (uint8*)str;
while ((c = *pstr++))
{
hash = ((hash << 5) + hash) + c;
}
return hash;
}
char *
utils_string_escape(const char *str)
{
@ -220,6 +239,29 @@ utils_locale_to_utf8(const char *src, size_t is, char *dest, size_t os)
}
void
utils_calculate_dpi_scale_factors(uint32 *physwidth, uint32 *physheight,
uint32 *desktopscale, uint32 *devicescale)
{
*physwidth = *physheight = *desktopscale = *devicescale = 0;
if (g_dpi > 0)
{
*physwidth = g_width * 254 / (g_dpi * 10);
*physheight = g_height * 254 / (g_dpi * 10);
/* the spec calls this out as being valid for range
100-500 but I doubt the upper range is accurate */
*desktopscale = g_dpi < 96 ? 100 : (g_dpi * 100 + 48) / 96;
/* the only allowed values for device scale factor are
100, 140, and 180. */
*devicescale = g_dpi < 134 ? 100 : (g_dpi < 173 ? 140 : 180);
}
}
/*
* component logging
*

View File

@ -20,11 +20,16 @@
#ifndef _utils_h
#define _utils_h
#include "types.h"
unsigned int utils_djb2_hash(const char *str);
char *utils_string_escape(const char *str);
char *utils_string_unescape(const char *str);
int utils_locale_to_utf8(const char *src, size_t is, char *dest, size_t os);
int utils_mkdir_safe(const char *path, int mask);
int utils_mkdir_p(const char *path, int mask);
void utils_calculate_dpi_scale_factors(uint32 *physwidth, uint32 *physheight,
uint32 *desktopscale, uint32 *devicescale);
typedef enum log_level_t
{

23
xwin.c
View File

@ -45,6 +45,8 @@
extern int g_sizeopt;
extern int g_width;
extern int g_height;
extern uint32 g_windowed_width;
extern uint32 g_windowed_height;
extern int g_xpos;
extern int g_ypos;
extern int g_pos;
@ -2058,12 +2060,21 @@ ui_create_window(void)
XSetWindowAttributes attribs;
XClassHint *classhints;
XSizeHints *sizehints;
int wndwidth, wndheight;
long input_mask, ic_input_mask;
XEvent xevent;
wndwidth = g_fullscreen ? WidthOfScreen(g_screen) : g_width;
wndheight = g_fullscreen ? HeightOfScreen(g_screen) : g_height;
if (g_fullscreen)
{
g_width = WidthOfScreen(g_screen);
g_height = HeightOfScreen(g_screen);
}
else
{
g_width = g_windowed_width;
g_height = g_windowed_height;
}
logger(GUI, Debug, "ui_create_window() width = %d, height = %d", g_width, g_height);
/* Handle -x-y portion of geometry string */
if (g_xpos < 0 || (g_xpos == 0 && (g_pos & 2)))
@ -2073,8 +2084,8 @@ ui_create_window(void)
get_window_attribs(&attribs);
g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, wndwidth,
wndheight, 0, g_depth, InputOutput, g_visual,
g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, g_width,
g_height, 0, g_depth, InputOutput, g_visual,
CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap |
CWBorderPixel, &attribs);
ewmh_set_wm_pid(g_wnd, getpid());
@ -2180,6 +2191,8 @@ ui_create_window(void)
seamless_restack_test();
}
rdpedisp_set_session_size(g_width, g_height);
return True;
}