From 2f03f65efe785907376654a1e93f51a81b6cf134 Mon Sep 17 00:00:00 2001 From: Cendio Date: Wed, 8 Nov 2017 09:29:41 +0100 Subject: [PATCH] Add Dynamic Virtual Channels and basic RDPEDISP support Fix issue #192 Signed-off-by: Henrik Andersson Signed-off-by: Karl Mikaelsson --- Makefile.in | 2 +- channels.c | 2 +- constants.h | 5 + dvc.c | 455 ++++++++++++++++++++++++++++++++++++++++++++++++++++ proto.h | 9 ++ rdesktop.c | 9 ++ rdpedisp.c | 138 ++++++++++++++++ secure.c | 15 +- stream.c | 27 ++++ stream.h | 4 + types.h | 18 +++ utils.c | 42 +++++ utils.h | 5 + xwin.c | 23 ++- 14 files changed, 740 insertions(+), 14 deletions(-) create mode 100644 dvc.c create mode 100644 rdpedisp.c diff --git a/Makefile.in b/Makefile.in index 11d53a5..a7e0405 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 diff --git a/channels.c b/channels.c index fd81b59..cffffb4 100644 --- a/channels.c +++ b/channels.c @@ -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 diff --git a/constants.h b/constants.h index 15889bc..dfb118e 100644 --- a/constants.h +++ b/constants.h @@ -17,6 +17,9 @@ along with this program. If not, see . */ +#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 */ diff --git a/dvc.c b/dvc.c new file mode 100644 index 0000000..f512371 --- /dev/null +++ b/dvc.c @@ -0,0 +1,455 @@ +/* -*- c-basic-offset: 8 -*- + rdesktop: A Remote Desktop Protocol client. + Dynamic Channel Virtual Channel Extension. + Copyright 2017 Henrik Andersson for Cendio AB + Copyright 2017 Karl Mikaelsson 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 . +*/ + +#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); +} diff --git a/proto.h b/proto.h index 33be4be..2f12350 100644 --- a/proto.h +++ b/proto.h @@ -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); diff --git a/rdesktop.c b/rdesktop.c index 65831ac..0cf0ab6 100644 --- a/rdesktop.c +++ b/rdesktop.c @@ -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) { diff --git a/rdpedisp.c b/rdpedisp.c new file mode 100644 index 0000000..8770856 --- /dev/null +++ b/rdpedisp.c @@ -0,0 +1,138 @@ +/* -*- c-basic-offset: 8 -*- + rdesktop: A Remote Desktop Protocol client. + Display Update Virtual Channel Extension. + Copyright 2017 Henrik Andersson for Cendio AB + Copyright 2017 Karl Mikaelsson 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 . +*/ + +#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); +} diff --git a/secure.c b/secure.c index b2b9279..d0286cb 100644 --- a/secure.c +++ b/secure.c @@ -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) */ diff --git a/stream.c b/stream.c index 028cd80..bf94302 100644 --- a/stream.c +++ b/stream.c @@ -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; +} diff --git a/stream.h b/stream.h index cbf3606..3359efb 100644 --- a/stream.h +++ b/stream.h @@ -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); diff --git a/types.h b/types.h index 3e8089d..94df8e5 100644 --- a/types.h +++ b/types.h @@ -19,6 +19,12 @@ along with this program. If not, see . */ +#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 */ diff --git a/utils.c b/utils.c index 08fa321..77b72c1 100644 --- a/utils.c +++ b/utils.c @@ -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 * diff --git a/utils.h b/utils.h index c6b779f..1d0b04c 100644 --- a/utils.h +++ b/utils.h @@ -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 { diff --git a/xwin.c b/xwin.c index 5051756..1df1a8a 100644 --- a/xwin.c +++ b/xwin.c @@ -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; }