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;
}