Merge pull request #206 from derfian/dynamic-resize

Dynamic session resize
This commit is contained in:
Karl Mikaelsson 2017-12-12 12:56:52 +01:00 committed by GitHub
commit 3d9dccbc70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 2506 additions and 224 deletions

2
.gitignore vendored
View File

@ -1,6 +1,6 @@
rdesktop
autom4te.cache
Makefile
/Makefile
config.log
config.status
configure

View File

@ -259,12 +259,12 @@ RD_BOOL get_key_state(unsigned int state, uint32 keysym);
RD_BOOL ui_init(void);
void ui_init_connection(void);
void ui_deinit(void);
RD_BOOL ui_create_window(void);
void ui_resize_window(void);
RD_BOOL ui_create_window(uint32 width, uint32 height);
void ui_resize_window(uint32 width, uint32 height);
void ui_destroy_window(void);
RD_BOOL ui_have_window(void);
void xwin_toggle_fullscreen(void);
int ui_select(int rdp_socket);
void ui_select(int rdp_socket);
void ui_move_pointer(int x, int y);
RD_HBITMAP ui_create_bitmap(int width, int height, uint8 * data);
void ui_paint_bitmap(int x, int y, int cx, int cy, int width, int height, uint8 * data);
@ -329,6 +329,7 @@ void ui_seamless_ack(unsigned int serial);
RD_BOOL lspci_init(void);
/* rdpedisp.c */
void rdpedisp_init(void);
RD_BOOL rdpedisp_is_available();
void rdpedisp_set_session_size(uint32 width, uint32 height);
/* dvc.c */
typedef void (*dvc_channel_process_fn) (STREAM s);

View File

@ -72,10 +72,13 @@ int g_sizeopt = 0; /* If non-zero, a special size has been
from _NET_WORKAREA. If negative, absolute value
specifies the percent of the whole screen. */
int g_dpi = 0; /* device DPI: default not set */
int g_width = 1024;
int g_height = 768;
uint32 g_windowed_width = 1024;
uint32 g_windowed_height = 768;
/* Following variables holds the initial width and height for a
rdesktop window, this is sent upon connect and tells the server
what size of session we want to have. Set to decent defaults. */
uint32 g_initial_width = 1024;
uint32 g_initial_height = 768;
int g_xpos = 0;
int g_ypos = 0;
int g_pos = 0; /* 0 position unspecified,
@ -712,17 +715,17 @@ main(int argc, char *argv[])
break;
}
g_width = strtol(optarg, &p, 10);
if (g_width <= 0)
g_initial_width = strtol(optarg, &p, 10);
if (g_initial_width <= 0)
{
logger(Core, Error, "invalid geometry width specified");
return EX_USAGE;
}
if (*p == 'x')
g_height = strtol(p + 1, &p, 10);
g_initial_height = strtol(p + 1, &p, 10);
if (g_height <= 0)
if (g_initial_height <= 0)
{
logger(Core, Error, "invalid geometry height specified");
return EX_USAGE;
@ -730,16 +733,16 @@ main(int argc, char *argv[])
if (*p == '%')
{
g_sizeopt = -g_width;
g_width = g_sizeopt;
g_sizeopt = -g_initial_width;
g_initial_width = g_sizeopt;
if (*(p + 1) == 'x')
{
g_height = -strtol(p + 2, &p, 10);
g_initial_height = -strtol(p + 2, &p, 10);
}
else
{
g_height = g_sizeopt;
g_initial_height = g_sizeopt;
}
p++;
@ -767,9 +770,6 @@ main(int argc, char *argv[])
g_ypos = strtol(p, NULL, 10);
}
g_windowed_height = g_height;
g_windowed_width = g_width;
break;
case 'f':
@ -1240,6 +1240,8 @@ main(int argc, char *argv[])
}
ui_init_connection();
utils_apply_session_size_limitations(&g_initial_width, &g_initial_height);
if (!rdp_connect
(server, flags, domain, g_password, shell, directory, g_reconnect_loop))
{
@ -1307,6 +1309,8 @@ main(int argc, char *argv[])
/* Enter a reconnect loop if we have a pending resize request */
if (g_pending_resize)
{
logger(Core, Verbose, "Resize reconnect loop triggered, new size %dx%d",
g_initial_width, g_initial_height);
g_pending_resize = False;
g_reconnect_loop = True;
continue;
@ -1839,7 +1843,7 @@ rd_create_ui()
if (!ui_have_window())
{
/* create a window if we don't have one initialized */
if (!ui_create_window())
if (!ui_create_window(g_initial_width, g_initial_height))
exit(EX_OSERR);
}
else

70
rdp.c
View File

@ -43,13 +43,16 @@ extern RDP_VERSION g_rdp_version;
extern uint16 g_server_rdp_version;
extern uint32 g_rdp5_performanceflags;
extern int g_server_depth;
extern int g_width;
extern int g_height;
extern uint32 g_initial_width;
extern uint32 g_initial_height;
extern RD_BOOL g_bitmap_cache;
extern RD_BOOL g_bitmap_cache_persist_enable;
extern RD_BOOL g_numlock_sync;
extern RD_BOOL g_pending_resize;
extern RD_BOOL g_network_error;
extern time_t g_wait_for_deactivate_ts;
RD_BOOL g_exit_mainloop = False;
uint8 *g_next_packet;
uint32 g_rdp_shareid;
@ -80,6 +83,11 @@ extern RD_BOOL g_has_reconnect_random;
extern uint8 g_client_random[SEC_RANDOM_SIZE];
static uint32 g_packetno;
/* holds the actual session size reported by server */
uint16 g_session_width;
uint16 g_session_height;
static void rdp_out_unistr(STREAM s, char *string, int len);
/* Receive an RDP packet */
@ -556,8 +564,8 @@ rdp_send_suppress_output_pdu(enum RDP_SUPPRESS_STATUS allowupdates)
case ALLOW_DISPLAY_UPDATES: /* receive data again */
out_uint16_le(s, 0); /* left */
out_uint16_le(s, 0); /* top */
out_uint16_le(s, g_width); /* right */
out_uint16_le(s, g_height); /* bottom */
out_uint16_le(s, g_session_width); /* right */
out_uint16_le(s, g_session_height); /* bottom */
break;
}
@ -659,14 +667,16 @@ rdp_out_ts_general_capabilityset(STREAM s)
static void
rdp_out_ts_bitmap_capabilityset(STREAM s)
{
logger(Protocol, Debug, "rdp_out_ts_bitmap_capabilityset(), %dx%d",
g_session_width, g_session_height);
out_uint16_le(s, RDP_CAPSET_BITMAP);
out_uint16_le(s, RDP_CAPLEN_BITMAP);
out_uint16_le(s, g_server_depth); /* preferredBitsPerPixel */
out_uint16_le(s, 1); /* receive1BitPerPixel (ignored, should be 1) */
out_uint16_le(s, 1); /* receive4BitPerPixel (ignored, should be 1) */
out_uint16_le(s, 1); /* receive8BitPerPixel (ignored, should be 1) */
out_uint16_le(s, g_width); /* desktopWidth */
out_uint16_le(s, g_height); /* desktopHeight */
out_uint16_le(s, g_session_width); /* desktopWidth */
out_uint16_le(s, g_session_height); /* desktopHeight */
out_uint16_le(s, 0); /* pad2Octets */
out_uint16_le(s, 1); /* desktopResizeFlag */
out_uint16_le(s, 1); /* bitmapCompressionFlag (must be 1) */
@ -1051,17 +1061,17 @@ rdp_process_general_caps(STREAM s)
static void
rdp_process_bitmap_caps(STREAM s)
{
uint16 width, height, depth;
uint16 depth;
in_uint16_le(s, depth);
in_uint8s(s, 6);
in_uint16_le(s, width);
in_uint16_le(s, height);
in_uint16_le(s, g_session_width);
in_uint16_le(s, g_session_height);
logger(Protocol, Debug,
"rdp_process_bitmap_caps(), setting desktop size and depth to: %dx%dx%d", width,
height, depth);
"rdp_process_bitmap_caps(), setting desktop size and depth to: %dx%dx%d",
g_session_width, g_session_height, depth);
/*
* The server may limit depth and change the size of the desktop (for
@ -1074,15 +1084,11 @@ rdp_process_bitmap_caps(STREAM s)
g_server_depth, depth);
g_server_depth = depth;
}
if (g_width != width || g_height != height)
{
logger(Protocol, Debug,
"rdp_process_bitmap_caps(), remote desktop changed from %dx%d to %dx%d.\n",
g_width, g_height, width, height);
g_width = width;
g_height = height;
ui_resize_window();
}
/* resize viewport window to new session size, this is an
no-op if there is no change in size between session size
reported from server and the actual window size */
ui_resize_window(g_session_width, g_session_height);
}
/* Process server capabilities */
@ -1790,20 +1796,23 @@ process_redirect_pdu(STREAM s, RD_BOOL enhanced_redirect /*, uint32 * ext_disc_r
"process_redirect_pdu(), unhandled LB_TARGET_CERTIFICATE");
}
return True;
return g_redirect;
}
/* Process incoming packets */
void
rdp_main_loop(RD_BOOL * deactivated, uint32 * ext_disc_reason)
{
while (rdp_loop(deactivated, ext_disc_reason))
do
{
if (g_pending_resize || g_redirect)
if (rdp_loop(deactivated, ext_disc_reason) == False)
{
return;
}
g_exit_mainloop = True;
}
} while(g_exit_mainloop == False);
/* clear the exit main loop flag */
g_exit_mainloop = False;
}
/* used in uiports and rdp_main_loop, processes the RDP packets waiting */
@ -1814,7 +1823,7 @@ rdp_loop(RD_BOOL * deactivated, uint32 * ext_disc_reason)
RD_BOOL cont = True;
STREAM s;
while (cont)
while (g_exit_mainloop == False && cont)
{
s = rdp_recv(&type);
if (s == NULL)
@ -1829,12 +1838,15 @@ rdp_loop(RD_BOOL * deactivated, uint32 * ext_disc_reason)
logger(Protocol, Debug,
"rdp_loop(), RDP_PDU_DEACTIVATE packet received");
*deactivated = True;
g_wait_for_deactivate_ts = 0;
break;
case RDP_PDU_REDIRECT:
return process_redirect_pdu(s, False);
break;
case RDP_PDU_ENHANCED_REDIRECT:
return process_redirect_pdu(s, True);
if (process_redirect_pdu(s, !(type == RDP_PDU_REDIRECT)) == True)
{
g_exit_mainloop = True;
continue;
}
break;
case RDP_PDU_DATA:
/* If we got a data PDU, we don't need to keep the password in memory

View File

@ -26,6 +26,8 @@
#define DISPLAYCONTROL_MONITOR_PRIMARY 0x1
#define RDPEDISP_CHANNEL_NAME "Microsoft::Windows::RDS::DisplayControl"
extern int g_dpi;
static void rdpedisp_send(STREAM s);
static void rdpedisp_init_packet(STREAM s, uint32 type, uint32 length);
@ -89,7 +91,9 @@ rdpedisp_send_monitor_layout_pdu(uint32 width, uint32 height)
out_uint32_le(&s, width); /* width */
out_uint32_le(&s, height); /* height */
utils_calculate_dpi_scale_factors(&physwidth, &physheight, &desktopscale, &devicescale);
utils_calculate_dpi_scale_factors(width, height, g_dpi,
&physwidth, &physheight, &desktopscale, &devicescale);
out_uint32_le(&s, physwidth); /* physicalwidth */
out_uint32_le(&s, physheight); /* physicalheight */
out_uint32_le(&s, ORIENTATION_LANDSCAPE); /* Orientation */
@ -116,7 +120,7 @@ rdpedisp_send(STREAM s)
dvc_send(RDPEDISP_CHANNEL_NAME, s);
}
static RD_BOOL
RD_BOOL
rdpedisp_is_available()
{
return dvc_channels_is_available(RDPEDISP_CHANNEL_NAME);
@ -128,6 +132,9 @@ rdpedisp_set_session_size(uint32 width, uint32 height)
if (rdpedisp_is_available() == False)
return;
/* monitor width MUST be even number */
utils_apply_session_size_limitations(&width, &height);
rdpedisp_send_monitor_layout_pdu(width, height);
}

View File

@ -23,8 +23,8 @@
#include "ssl.h"
extern char g_hostname[16];
extern int g_width;
extern int g_height;
extern uint32 g_initial_width;
extern uint32 g_initial_height;
extern int g_dpi;
extern unsigned int g_keylayout;
extern int g_keyboard_type;
@ -426,8 +426,8 @@ sec_out_mcs_connect_initial_pdu(STREAM s, uint32 selected_protocol)
out_uint16_le(s, CS_CORE); /* type */
out_uint16_le(s, 216 + (g_dpi > 0 ? 18 : 0)); /* length */
out_uint32_le(s, rdpversion); /* version */
out_uint16_le(s, g_width); /* desktopWidth */
out_uint16_le(s, g_height); /* desktopHeight */
out_uint16_le(s, g_initial_width); /* desktopWidth */
out_uint16_le(s, g_initial_height); /* desktopHeight */
out_uint16_le(s, RNS_UD_COLOR_8BPP); /* colorDepth */
out_uint16_le(s, RNS_UD_SAS_DEL); /* SASSequence */
out_uint32_le(s, g_keylayout); /* keyboardLayout */
@ -459,7 +459,8 @@ sec_out_mcs_connect_initial_pdu(STREAM s, uint32 selected_protocol)
if (g_dpi > 0)
{
/* Extended client info describing monitor geometry */
utils_calculate_dpi_scale_factors(&physwidth, &physheight,
utils_calculate_dpi_scale_factors(g_initial_width, g_initial_height, g_dpi,
&physwidth, &physheight,
&desktopscale, &devicescale);
out_uint32_le(s, physwidth); /* physicalwidth */
out_uint32_le(s, physheight); /* physicalheight */

13
tcp.c
View File

@ -66,7 +66,8 @@ static RD_BOOL g_run_ui = False;
static struct stream g_in;
static struct stream g_out[STREAM_COUNT];
int g_tcp_port_rdp = TCP_PORT_RDP;
extern RD_BOOL g_user_quit;
extern RD_BOOL g_exit_mainloop;
extern RD_BOOL g_network_error;
extern RD_BOOL g_reconnect_loop;
@ -220,13 +221,13 @@ tcp_recv(STREAM s, uint32 length)
{
if ((!g_ssl || SSL_pending(g_ssl) <= 0) && g_run_ui)
{
if (!ui_select(g_sock))
{
/* User quit */
g_user_quit = True;
ui_select(g_sock);
/* break out of recv, if request of exiting
main loop has been done */
if (g_exit_mainloop == True)
return NULL;
}
}
if (g_ssl)
{

49
tests/Makefile Normal file
View File

@ -0,0 +1,49 @@
CC=gcc
CFLAGS=-fPIC -Wall -Wextra -ggdb -gdwarf-2 -g3
CGREEN_RUNNER=cgreen-runner
TESTS=resize rdp xwin utils
RDP_MOCKS=ui_mock.o bitmap_mock.o secure_mock.o ssl_mock.o mppc_mock.o \
cache_mock.o pstcache_mock.o orders_mock.o rdesktop_mock.o \
rdp5_mock.o xkeymap_mock.o tcp_mock.o
XWIN_MOCKS=x11_mock.o cache_mock.o xclip_mock.o xkeymap_mock.o seamless_mock.o \
ctrl_mock.o rdpdr_mock.o ewmh_mock.o rdpedisp_mock.o rdp_mock.o
UTILS_MOCKS=
RESIZE_MOCKS=x11_mock.o cache_mock.o xclip_mock.o xkeymap_mock.o seamless_mock.o \
ctrl_mock.o rdpdr_mock.o ewmh_mock.o rdpedisp_mock.o bitmap_mock.o \
ssl_mock.o mppc_mock.o pstcache_mock.o orders_mock.o rdesktop_mock.o rdp5_mock.o \
tcp_mock.o licence_mock.o mcs_mock.o channels_mock.o
all: test
.PHONY: test
test: $(foreach test, $(TESTS), runtest.$(test))
.PHONY: runtest.%
runtest.%: %
$(CGREEN_RUNNER) $^
rdp: rdp_test.o $(RDP_MOCKS)
$(CC) $(CFLAGS) -shared -lcgreen -o $@ $^
xwin: xwin_test.o $(XWIN_MOCKS)
$(CC) $(CFLAGS) -shared -lcgreen -o $@ $^ -lX11 -lXcursor
utils: utils_test.o $(UTILS_MOCKS)
$(CC) $(CFLAGS) -shared -lcgreen -o $@ $^
resize: resize_test.o $(RESIZE_MOCKS)
$(CC) $(CFLAGS) -shared -lcgreen -o $@ $^ -lX11 -lXcursor
.PHONY: clean
clean:
rm -f $(TESTS) *_mock.o *_test.o

26
tests/README.md Normal file
View File

@ -0,0 +1,26 @@
# Automated test suite
An basic test suite was added while implementing the dynamic resize
feature. Improvements to this test suite are very welcome.
## Requirements
In addition to the build requirements of rdesktop itself, you also need:
* [cgreen](https://github.com/cgreen-devs/cgreen)
## Building and running the tests
cd tests
make
This will build and run each test in turn. Re-running `make` will
recompile the tests as necessary, and run them again.
## Cgreen documentation
You can find the Cgreen documentation over
at [their github site](https://cgreen-devs.github.io).

7
tests/bitmap_mock.c Normal file
View File

@ -0,0 +1,7 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
RD_BOOL bitmap_decompress(uint8 * output, int width, int height, uint8 * input, int size, int Bpp)
{
return mock(output, width, height, input, size, Bpp);
};

46
tests/cache_mock.c Normal file
View File

@ -0,0 +1,46 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
RD_HCURSOR
cache_get_cursor(uint16 cache_idx)
{
return (RD_HCURSOR)mock(cache_idx);
}
void
cache_put_cursor(uint16 cache_idx, RD_HCURSOR cursor)
{
mock(cache_idx, cursor);
}
FONTGLYPH *
cache_get_font(uint8 font, uint16 character)
{
return (FONTGLYPH *) mock(font, character);
}
DATABLOB *
cache_get_text(uint8 cache_id)
{
return (DATABLOB *)mock(cache_id);
}
void
cache_put_text(uint8 cache_id, void *data, int length)
{
mock(cache_id, data, length);
}
uint8 *
cache_get_desktop(uint32 offset, int cx, int cy, int bytes_per_pixel)
{
return (uint8 *) mock(offset, cx, cy, bytes_per_pixel);
}
void
cache_put_desktop(uint32 offset, int cx, int cy, int scanline,
int bytes_per_pixel, uint8 * data)
{
mock(offset, cx, cy, scanline, bytes_per_pixel, data);
}

7
tests/channels_mock.c Normal file
View File

@ -0,0 +1,7 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
void channel_process(STREAM s, uint16 mcs_channel)
{
mock(s, mcs_channel);
}

14
tests/ctrl_mock.c Normal file
View File

@ -0,0 +1,14 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
void
ctrl_add_fds(int *n, fd_set * rfds)
{
mock(n, rfds);
}
void
ctrl_check_fds(fd_set * rfds, fd_set * wfds)
{
mock(rfds, wfds);
}

26
tests/dvc_mock.c Normal file
View File

@ -0,0 +1,26 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
RD_BOOL
dvc_init(void)
{
return mock();
}
RD_BOOL
dvc_channels_register(const char *name, dvc_channel_process_fn handler)
{
return mock(name, handler);
}
RD_BOOL
dvc_channels_is_available(const char *name)
{
return mock(name);
}
void
dvc_send(const char *name, STREAM s)
{
mock(name, s);
}

87
tests/ewmh_mock.c Normal file
View File

@ -0,0 +1,87 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
#include <X11/Xlib.h>
int
ewmh_get_window_state(Window w)
{
return mock(w);
}
int
ewmh_change_state(Window wnd, int state)
{
return mock(wnd, state);
}
int
ewmh_move_to_desktop(Window wnd, unsigned int desktop)
{
return mock(wnd, desktop);
}
int
ewmh_get_window_desktop(Window wnd)
{
return mock(wnd);
}
void
ewmh_set_wm_name(Window wnd, const char *title)
{
mock(wnd, title);
}
void
ewmh_set_wm_pid(Window wnd, pid_t pid)
{
mock(wnd, pid);
}
int
ewmh_set_window_popup(Window wnd)
{
return mock(wnd);
}
int
ewmh_set_window_modal(Window wnd)
{
return mock(wnd);
}
void
ewmh_set_icon(Window wnd, int width, int height, const char *rgba_data)
{
mock(wnd, width, height, rgba_data);
}
void
ewmh_del_icon(Window wnd, int width, int height)
{
mock(wnd, width, height);
}
int
ewmh_set_window_above(Window wnd)
{
return mock(wnd);
}
RD_BOOL
ewmh_is_window_above(Window w)
{
return mock(w);
}
int
get_current_workarea(uint32 *x, uint32 *y, uint32 *width, uint32 *height)
{
return mock(x, y, width, height);
}
void
ewmh_init()
{
mock();
}

9
tests/licence_mock.c Normal file
View File

@ -0,0 +1,9 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
void
licence_process(STREAM s)
{
mock(s);
}

51
tests/mcs_mock.c Normal file
View File

@ -0,0 +1,51 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
STREAM
mcs_init(int length)
{
return (STREAM)mock(length);
}
void
mcs_send_to_channel(STREAM s, uint16 channel)
{
mock(s, channel);
}
void
mcs_send(STREAM s)
{
mock(s);
}
STREAM
mcs_recv(uint16 * channel, uint8 * rdpver)
{
return (STREAM)mock(channel, rdpver);
}
RD_BOOL
mcs_connect_start(char *server, char *username, char *domain, char *password,
RD_BOOL reconnect, uint32 * selected_protocol)
{
return mock(server,username,domain,password,reconnect,selected_protocol);
}
RD_BOOL
mcs_connect_finalize(STREAM s)
{
return mock(s);
}
void
mcs_disconnect(void)
{
mock();
}
void
mcs_reset_state(void)
{
mock();
}

8
tests/mppc_mock.c Normal file
View File

@ -0,0 +1,8 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
int
mppc_expand(uint8 * data, uint32 clen, uint8 ctype, uint32 * roff, uint32 * rlen)
{
return mock(data, clen, ctype, roff, rlen);
}

12
tests/orders_mock.c Normal file
View File

@ -0,0 +1,12 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
void process_orders(STREAM s, uint16 num_orders)
{
mock(s, num_orders);
}
void reset_order_state()
{
mock();
}

12
tests/pstcache_mock.c Normal file
View File

@ -0,0 +1,12 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
int pstcache_enumerate(uint8 id, HASH_KEY * keylist)
{
return mock(id, keylist);
}
RD_BOOL pstcache_init(uint8 cache_id)
{
return mock(cache_id);
}

13
tests/rdesktop_mock.c Normal file
View File

@ -0,0 +1,13 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
void rd_create_ui(void)
{
mock();
}
void generate_random(uint8 * random)
{
mock(random);
}

7
tests/rdp5_mock.c Normal file
View File

@ -0,0 +1,7 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
void process_ts_fp_updates(STREAM s)
{
mock(s);
}

15
tests/rdp_mock.c Normal file
View File

@ -0,0 +1,15 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
void
rdp_send_input(uint32 time, uint16 message_type, uint16 device_flags, uint16 param1, uint16 param2)
{
mock(time, message_type, device_flags, param1, param2);
}
void
rdp_send_suppress_output_pdu(enum RDP_SUPPRESS_STATUS allowupdates)
{
mock(allowupdates);
}

141
tests/rdp_test.c Normal file
View File

@ -0,0 +1,141 @@
#include <cgreen/cgreen.h>
#include <cgreen/mocks.h>
#include "../rdesktop.h"
#include "../proto.h"
/* Boilerplate */
Describe(RDP);
BeforeEach(RDP) {};
AfterEach(RDP) {};
/* Global Variables.. :( */
uint16 g_mcs_userid;
char *g_username;
char g_password[64];
char g_codepage[16];
RD_BOOL g_orders;
RD_BOOL g_encryption;
RD_BOOL g_desktop_save;
RD_BOOL g_polygon_ellipse_orders;
RDP_VERSION g_rdp_version;
uint16 g_server_rdp_version;
uint32 g_rdp5_performanceflags;
int g_server_depth;
uint32 g_initial_width;
uint32 g_initial_height;
RD_BOOL g_bitmap_cache;
RD_BOOL g_bitmap_cache_persist_enable;
RD_BOOL g_numlock_sync;
RD_BOOL g_pending_resize;
RD_BOOL g_network_error;
time_t g_wait_for_deactivate_ts;
RDPCOMP g_mppc_dict;
RD_BOOL g_redirect;
char *g_redirect_server;
uint32 g_redirect_server_len;
char *g_redirect_domain;
uint32 g_redirect_domain_len;
char *g_redirect_username;
uint32 g_redirect_username_len;
uint8 *g_redirect_lb_info;
uint32 g_redirect_lb_info_len;
uint8 *g_redirect_cookie;
uint32 g_redirect_cookie_len;
uint32 g_redirect_flags;
uint32 g_redirect_session_id;
uint32 g_reconnect_logonid;
char g_reconnect_random[16];
time_t g_reconnect_random_ts;
RD_BOOL g_has_reconnect_random;
uint8 g_client_random[SEC_RANDOM_SIZE];
RD_BOOL g_local_cursor;
#include "../rdp.c"
#include "../utils.c"
#include "../stream.c"
/* malloc; exit if out of memory */
void *
xmalloc(int size)
{
void *mem = malloc(size);
if (mem == NULL)
{
logger(Core, Error, "xmalloc, failed to allocate %d bytes", size);
exit(EX_UNAVAILABLE);
}
return mem;
}
/* Exit on NULL pointer. Use to verify result from XGetImage etc */
void
exit_if_null(void *ptr)
{
if (ptr == NULL)
{
logger(Core, Error, "unexpected null pointer. Out of memory?");
exit(EX_UNAVAILABLE);
}
}
/* strdup */
char *
xstrdup(const char *s)
{
char *mem = strdup(s);
if (mem == NULL)
{
logger(Core, Error, "xstrdup(), strdup() failed: %s", strerror(errno));
exit(EX_UNAVAILABLE);
}
return mem;
}
/* realloc; exit if out of memory */
void *
xrealloc(void *oldmem, size_t size)
{
void *mem;
if (size == 0)
size = 1;
mem = realloc(oldmem, size);
if (mem == NULL)
{
logger(Core, Error, "xrealloc, failed to reallocate %ld bytes", size);
exit(EX_UNAVAILABLE);
}
return mem;
}
/* free */
void
xfree(void *mem)
{
free(mem);
}
/* Test function */
Ensure(RDP, ProcessBitmapCapsCallsUiResizeWindow) {
struct stream s;
memset(&s, 0, sizeof(struct stream));
expect(ui_resize_window,
when(width, is_equal_to(1024)),
when(height, is_equal_to(768)));
s_realloc(&s, 32);
s_reset(&s);
out_uint16_le(&s, 32); /* depth */
out_uint8s(&s, 6); /* pad? dunno */
out_uint16_le(&s, 1024);
out_uint16_le(&s, 768);
s_mark_end(&s);
s_reset(&s);
rdp_process_bitmap_caps(&s);
free(s.data);
}

14
tests/rdpdr_mock.c Normal file
View File

@ -0,0 +1,14 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
void
rdpdr_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv, RD_BOOL * timeout)
{
mock(n, rfds, wfds, tv, timeout);
}
void
rdpdr_check_fds(fd_set * rfds, fd_set * wfds, RD_BOOL timed_out)
{
mock(rfds, wfds, timed_out);
}

14
tests/rdpedisp_mock.c Normal file
View File

@ -0,0 +1,14 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
RD_BOOL
rdpedisp_is_available()
{
return mock();
}
void
rdpedisp_set_session_size(uint32 width, uint32 height)
{
mock(width, height);
}

571
tests/resize_test.c Normal file
View File

@ -0,0 +1,571 @@
#include <cgreen/cgreen.h>
#include <cgreen/mocks.h>
#include <X11/Xlib.h>
#include "../rdesktop.h"
/* Boilerplate */
Describe(Resize);
BeforeEach(Resize) {};
AfterEach(Resize) {};
/* globals driven by xwin.c */
RD_BOOL g_user_quit;
RD_BOOL g_exit_mainloop;
int g_sizeopt;
uint32 g_initial_width;
uint32 g_initial_height;
uint16 g_session_width;
uint16 g_session_height;
int g_xpos;
int g_ypos;
int g_pos;
RD_BOOL g_sendmotion;
RD_BOOL g_fullscreen;
RD_BOOL g_grab_keyboard;
RD_BOOL g_hide_decorations;
RD_BOOL g_pending_resize;
char g_title[] = "MyTitle";
char g_seamless_spawn_cmd[] = "";
int g_server_depth;
int g_win_button_size;
RD_BOOL g_seamless_persistent_mode;
RD_BOOL g_seamless_rdp;
RD_BOOL g_seamless_persistent_mode;
uint32 g_embed_wnd;
Atom g_net_wm_state_atom;
Atom g_net_wm_desktop_atom;
Atom g_net_wm_ping_atom;
RD_BOOL g_ownbackstore;
RD_BOOL g_rdpsnd;
RD_BOOL g_owncolmap;
RD_BOOL g_local_cursor;
/* globals driven by utils.c */
char g_codepage[16] = "";
/* global driven by rdp.c */
uint16 g_mcs_userid;
char *g_username;
char g_password[64];
char g_codepage[16];
RD_BOOL g_orders;
RD_BOOL g_encryption;
RD_BOOL g_desktop_save;
RD_BOOL g_polygon_ellipse_orders;
RDP_VERSION g_rdp_version;
uint16 g_server_rdp_version;
uint32 g_rdp5_performanceflags;
int g_server_depth;
uint32 g_initial_width;
uint32 g_initial_height;
RD_BOOL g_bitmap_cache;
RD_BOOL g_bitmap_cache_persist_enable;
RD_BOOL g_numlock_sync;
RD_BOOL g_pending_resize;
RD_BOOL g_network_error;
time_t g_wait_for_deactivate_ts;
RDPCOMP g_mppc_dict;
RD_BOOL g_redirect;
char *g_redirect_server;
uint32 g_redirect_server_len;
char *g_redirect_domain;
uint32 g_redirect_domain_len;
char *g_redirect_username;
uint32 g_redirect_username_len;
uint8 *g_redirect_lb_info;
uint32 g_redirect_lb_info_len;
uint8 *g_redirect_cookie;
uint32 g_redirect_cookie_len;
uint32 g_redirect_flags;
uint32 g_redirect_session_id;
uint32 g_reconnect_logonid;
char g_reconnect_random[16];
time_t g_reconnect_random_ts;
RD_BOOL g_has_reconnect_random;
uint8 g_client_random[SEC_RANDOM_SIZE];
RD_BOOL g_local_cursor;
/* globals from secure.c */
char g_hostname[16];
uint32 g_initial_width;
uint32 g_initial_height;
int g_dpi;
unsigned int g_keylayout;
int g_keyboard_type;
int g_keyboard_subtype;
int g_keyboard_functionkeys;
RD_BOOL g_encryption;
RD_BOOL g_licence_issued;
RD_BOOL g_licence_error_result;
RDP_VERSION g_rdp_version;
RD_BOOL g_console_session;
uint32 g_redirect_session_id;
int g_server_depth;
VCHANNEL g_channels[1];
unsigned int g_num_channels;
uint8 g_client_random[SEC_RANDOM_SIZE];
/* Xlib macros to mock functions */
#undef DefaultRootWindow
Window DefaultRootWindow(Display *display) { return (Window) mock(display); }
#undef WidthOfScreen
int WidthOfScreen(Screen* x) { return mock(x); }
#undef HeightOfScreen
int HeightOfScreen(Screen *x) { return mock(x); }
#include "../xwin.c"
#include "../utils.c"
#include "../rdp.c"
#include "../stream.c"
#include "../secure.c"
/* malloc; exit if out of memory */
void *
xmalloc(int size)
{
void *mem = malloc(size);
if (mem == NULL)
{
logger(Core, Error, "xmalloc, failed to allocate %d bytes", size);
exit(EX_UNAVAILABLE);
}
return mem;
}
/* Exit on NULL pointer. Use to verify result from XGetImage etc */
void
exit_if_null(void *ptr)
{
if (ptr == NULL)
{
logger(Core, Error, "unexpected null pointer. Out of memory?");
exit(EX_UNAVAILABLE);
}
}
/* strdup */
char *
xstrdup(const char *s)
{
char *mem = strdup(s);
if (mem == NULL)
{
logger(Core, Error, "xstrdup(), strdup() failed: %s", strerror(errno));
exit(EX_UNAVAILABLE);
}
return mem;
}
/* realloc; exit if out of memory */
void *
xrealloc(void *oldmem, size_t size)
{
void *mem;
if (size == 0)
size = 1;
mem = realloc(oldmem, size);
if (mem == NULL)
{
logger(Core, Error, "xrealloc, failed to reallocate %ld bytes", size);
exit(EX_UNAVAILABLE);
}
return mem;
}
/* free */
void
xfree(void *mem)
{
free(mem);
}
/* X11 mocks */
Status
XGetWindowAttributes(Display *display, Window wnd, XWindowAttributes *attr)
{
return mock(display, wnd, attr);
}
int
XResizeWindow(Display *display, Window wnd, unsigned width, unsigned height)
{
return mock(display, wnd, width, height);
}
XSizeHints *
XAllocSizeHints(void)
{
return (XSizeHints *) mock();
}
int
XSetClipRectangles(Display *display, GC gc, int clip_x_origin, int clip_y_origin,
XRectangle rectangles[], int n, int ordering)
{
return mock(display, gc, clip_x_origin, clip_y_origin, rectangles, n, ordering);
}
/* Test helpers */
struct stream
bitmap_caps_packet(int width, int height)
{
struct stream s;
memset(&s, 0, sizeof(s));
s_realloc(&s, 32);
s_reset(&s);
out_uint16_le(&s, 32); /* depth */
out_uint8s(&s, 6); /* pad? dunno */
out_uint16_le(&s, width);
out_uint16_le(&s, height);
s_mark_end(&s);
s_reset(&s);
return s;
}
#define RDPEDISP True
#define RECONNECT False
#define FULLSCREEN True
#define WINDOW False
void setup_user_initiated_resize(int width, int height, RD_BOOL use_rdpedisp, RD_BOOL fullscreen)
{
/* g_resize_timer = A second ago */
struct timeval tv;
gettimeofday(&tv, NULL);
tv.tv_sec -= 1;
g_resize_timer = tv;
g_fullscreen = fullscreen;
g_window_width = width;
g_window_height = height;
expect(rdpedisp_is_available,
will_return(use_rdpedisp));
}
struct stream setup_server_resize_response(int width, int height) {
expect(XGetWindowAttributes);
expect(XSetClipRectangles);
expect(XAllocSizeHints,
will_return(0));
return bitmap_caps_packet(width, height);
}
Ensure(Resize, UsingRDPEDISP)
{
struct stream s;
int user_wanted_width = 1280;
int user_wanted_height = 1024;
/* Step 1 : Act on UI side initiated resize and tell server about it using RDPEDISP */
setup_user_initiated_resize(user_wanted_width, user_wanted_height, RDPEDISP, WINDOW);
expect(rdpedisp_set_session_size,
when(width, is_equal_to(user_wanted_width)),
when(height, is_equal_to(user_wanted_height)));
/* FIXME: Move process_pending_resize out of X11 UI implementation */
assert_that(process_pending_resize(), is_equal_to(False));
/* Step 2 : Handle a BITMAP_CAPS containing session size from server
so set window size accordingly */
s = setup_server_resize_response(user_wanted_width, user_wanted_height);
expect(XResizeWindow,
when(width, is_equal_to(user_wanted_width)),
when(height, is_equal_to(user_wanted_height)));
rdp_process_bitmap_caps(&s);
free(s.data);
}
Ensure(Resize, UsingRDPEDISPHonoursServerMaximumSessionSizeLimit)
{
/* User has changed window size, X has resized, RDP needs to tell server.
RDPEDISP is available and a Windows 2012 server will limit session size
to maximum 8192x8192. We will set window size to whatever server says.
*/
struct stream s;
int user_wanted_width = 9000;
int user_wanted_height = 9000;
/* Step 1 : Act on UI side initiated resize and tell server about it
using RDPEDISP */
setup_user_initiated_resize(user_wanted_width, user_wanted_height, RDPEDISP, WINDOW);
expect(rdpedisp_set_session_size,
when(width, is_equal_to(user_wanted_width)),
when(height, is_equal_to(user_wanted_height)));
/* FIXME: Move process_pending_resize out of X11 UI implementation */
assert_that(process_pending_resize(), is_equal_to(False));
/* Step 2 : Handle a BITMAP_CAPS containing session size from server
so set window size accordingly */
int resulting_server_width = 8192;
int resulting_server_height = 8192;
s = setup_server_resize_response(resulting_server_width, resulting_server_height);
expect(XResizeWindow,
when(width, is_equal_to(resulting_server_width)),
when(height, is_equal_to(resulting_server_height)));
/* simulate response from server */
rdp_process_bitmap_caps(&s);
free(s.data);
}
Ensure(Resize, UsingRDPEDISPHonoursServerMinimumSessionSizeLimit)
{
/* User has changed window size, X has resized, RDP needs to tell server.
RDPEDISP is available and a Windows 2012 server will limit session size
to minimum 200x200. We will set window size to whatever server says.
*/
struct stream s;
/* Step 1 : Act on UI side initiated resize and tell server about it
using RDPEDISP */
int user_wanted_width = 100;
int user_wanted_height = 100;
setup_user_initiated_resize(user_wanted_width, user_wanted_height, RDPEDISP, WINDOW);
expect(rdpedisp_set_session_size,
when(width, is_equal_to(user_wanted_width)),
when(height, is_equal_to(user_wanted_height)));
/* FIXME: Move process_pending_resize out of X11 UI implementation */
assert_that(process_pending_resize(), is_equal_to(False));
/* Step 2 : Handle a BITMAP_CAPS containing session size from server
so set window size accordingly */
int resulting_server_width = 200;
int resulting_server_height = 200;
s = setup_server_resize_response(resulting_server_width, resulting_server_height);
expect(XResizeWindow,
when(width, is_equal_to(resulting_server_width)),
when(height, is_equal_to(resulting_server_height)));
/* simulate response from server */
rdp_process_bitmap_caps(&s);
free(s.data);
}
Ensure(Resize, UsingRDPEDISPHonoursServerSessionWidthConstraintMustBeEven)
{
/* User has changed window size, X has resized, RDP needs to tell server.
RDPEDISP is available and a Windows 2012 server will limit session size
to minimum 200x200. We will set window size to whatever server says.
*/
struct stream s;
/* Step 1 : Act on UI side initiated resize and tell server about it
using RDPEDISP */
int user_wanted_width = 999;
int user_wanted_height = 900;
setup_user_initiated_resize(user_wanted_width, user_wanted_height, RDPEDISP, WINDOW);
expect(rdpedisp_set_session_size,
when(width, is_equal_to(user_wanted_width)),
when(height, is_equal_to(user_wanted_height)));
/* FIXME: Move process_pending_resize out of X11 UI implementation */
assert_that(process_pending_resize(), is_equal_to(False));
/* Step 2 : Handle a BITMAP_CAPS containing session size from server
so set window size accordingly */
/* FIXME: Does the server round up or down? */
int resulting_server_width = 998;
int resulting_server_height = 900;
s = setup_server_resize_response(resulting_server_width, resulting_server_height);
expect(XResizeWindow,
when(width, is_equal_to(resulting_server_width)),
when(height, is_equal_to(resulting_server_height)));
/* simulate response from server */
rdp_process_bitmap_caps(&s);
free(s.data);
}
/* FIXME: promote to actual function in stream.c */
STREAM s_alloc(size_t capacity)
{
STREAM s;
s = xmalloc(sizeof(struct stream));
memset(s, 0, sizeof(struct stream));
s_realloc(s, capacity);
s_reset(s);
return s;
}
void get_width_and_height_from_mcs_connect_initial(int *width, int *height)
{
STREAM s;
/* Allocate stream and write mcs_connect_initial PDU to it */
s = s_alloc(4096);
sec_out_mcs_connect_initial_pdu(s, 0);
/* Rewind and extract the requested session size */
s_reset(s);
in_skip(s, 31);
in_uint16_le(s, *width); /* desktopWidth */
in_uint16_le(s, *height); /* desktopHeight */
s_free(s);
}
Ensure(Resize, UsingReconnect)
{
struct stream s;
int user_wanted_width = 1280;
int user_wanted_height = 1024;
/* Step 1 : Act on UI side initiated resize */
setup_user_initiated_resize(user_wanted_width, user_wanted_height, RECONNECT, WINDOW);
assert_that(process_pending_resize(),
is_equal_to(True));
/* we assume that process_pending_resize returning True will exit the main loop and initiate a
reconnect */
/* Step 2 : Simulate parts of the connection sequence where we send
width & height through a MCS Connect Initial packet to the server */
int sent_width, sent_height;
get_width_and_height_from_mcs_connect_initial(&sent_width, &sent_height);
assert_that(sent_width, is_equal_to(user_wanted_width));
assert_that(sent_height, is_equal_to(user_wanted_height));
/* Step 3 : Handle a BITMAP_CAPS containing session size from server
so set window size accordingly */
s = setup_server_resize_response(user_wanted_width,
user_wanted_height);
expect(XResizeWindow,
when(width, is_equal_to(user_wanted_width)),
when(height, is_equal_to(user_wanted_height)));
rdp_process_bitmap_caps(&s);
free(s.data);
}
Ensure(Resize, UsingReconnectHonoursServerMaximumSessionSizeLimit)
{
struct stream s;
int user_wanted_width = 9000;
int user_wanted_height = 9000;
/* Step 1 : Act on UI side initiated resize */
setup_user_initiated_resize(user_wanted_width, user_wanted_height, RECONNECT, WINDOW);
/* FIXME: Move process_pending_resize out of X11 UI implementation */
assert_that(process_pending_resize(),
is_equal_to(True));
/* We assume that process_pending_resize returning True exits the main
loop and initiates a reconnect */
/* Step 2 : Simulate parts of the connection sequence where we send
width & height through a MCS Connect Initial packet to the server */
int sent_width, sent_height;
get_width_and_height_from_mcs_connect_initial(&sent_width, &sent_height);
assert_that(sent_width, is_equal_to(user_wanted_width));
assert_that(sent_height, is_equal_to(user_wanted_height));
/* Step 3 : Handle a BITMAP_CAPS containing session size from server
so set window size accordingly */
int resulting_server_width = 4096;
int resulting_server_height = 2048;
s = setup_server_resize_response(resulting_server_width,
resulting_server_height);
expect(XResizeWindow,
when(width, is_equal_to(resulting_server_width)),
when(height, is_equal_to(resulting_server_height)));
rdp_process_bitmap_caps(&s);
free(s.data);
}
int
XNextEvent(Display *display, XEvent *event)
{
return mock(display, event);
}
int
XPending(Display *display)
{
return mock(display);
}
void
setup_user_initiated_root_window_resize(int width, int height,
RD_BOOL use_rdpedisp, RD_BOOL fullscreen)
{
XEvent rootWindowResizeEvent;
memset(&rootWindowResizeEvent, 0, sizeof(XEvent));
rootWindowResizeEvent.xconfigure.type = ConfigureNotify;
rootWindowResizeEvent.xconfigure.window = DefaultRootWindow(g_display);
rootWindowResizeEvent.xconfigure.width = width;
rootWindowResizeEvent.xconfigure.height = height;
/* one event to process */
expect(XPending, will_return(1));
/* event is a ConfigureNotify event on root window */
expect(XNextEvent,
will_set_contents_of_parameter(event,
&rootWindowResizeEvent,
sizeof(XConfigureEvent *)));
/* no more events to process */
expect(XPending,
will_return(0));
expect(rdpedisp_is_available, will_return(use_rdpedisp));
g_fullscreen = fullscreen;
}

58
tests/seamless_mock.c Normal file
View File

@ -0,0 +1,58 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
RD_BOOL seamless_init()
{
return mock();
}
void seamless_reset_state()
{
mock();
}
unsigned int seamless_send_sync(void)
{
return mock();
}
unsigned int seamless_send_state(unsigned long id, unsigned int state, unsigned long flags)
{
return mock(id, state, flags);
}
unsigned int seamless_send_position(unsigned long id, int x, int y,
int width, int height, unsigned long flags)
{
return mock(id, x, y, width, height, flags);
}
void seamless_select_timeout(struct timeval *tv)
{
mock(tv);
}
unsigned int seamless_send_zchange(unsigned long id, unsigned long below, unsigned long flags)
{
return mock(id, below, flags);
}
unsigned int seamless_send_focus(unsigned long id, unsigned long flags)
{
return mock(id, flags);
}
unsigned int seamless_send_destroy(unsigned long id)
{
return mock(id);
}
unsigned int seamless_send_spawn(char *cmd)
{
return mock(cmd);
}
unsigned int seamless_send_persistent(RD_BOOL enable)
{
return mock(enable);
}

32
tests/secure_mock.c Normal file
View File

@ -0,0 +1,32 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
STREAM sec_recv(uint8 * rdpver)
{
return (STREAM)mock(rdpver);
}
void sec_disconnect()
{
mock();
}
STREAM sec_init(uint32 flags, int maxlen)
{
return (STREAM) mock(flags, maxlen);
}
RD_BOOL sec_connect(char *server, char *username, char *domain, char *password, RD_BOOL reconnect)
{
return mock(server, username, domain, password, reconnect);
}
void sec_reset_state()
{
mock();
}
void sec_send(STREAM s, uint32 flags)
{
mock(s, flags);
}

108
tests/ssl_mock.c Normal file
View File

@ -0,0 +1,108 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
#include "../ssl.h"
void rdssl_hmac_md5(const void *key, int key_len,
const unsigned char *msg, int msg_len, unsigned char *md)
{
mock(key, key_len, msg, msg_len, md);
}
void
rdssl_cert_free(RDSSL_CERT * cert)
{
mock(cert);
}
RDSSL_CERT *
rdssl_cert_read(uint8 *data, uint32 len)
{
return (RDSSL_CERT *) mock(data, len);
}
RD_BOOL
rdssl_certs_ok(RDSSL_CERT * server_cert, RDSSL_CERT * cacert)
{
return mock(server_cert, cacert);
}
RDSSL_RKEY *
rdssl_cert_to_rkey(RDSSL_CERT * cert, uint32 * key_len)
{
return (RDSSL_RKEY *) mock(cert, key_len);
}
void
rdssl_md5_init(RDSSL_MD5 * md5)
{
mock(md5);
}
void
rdssl_md5_update(RDSSL_MD5 * md5, uint8 * data, uint32 len)
{
mock(md5, data, len);
}
void
rdssl_md5_final(RDSSL_MD5 * md5, uint8 * out_data)
{
mock(md5, out_data);
}
void
rdssl_rc4_set_key(RDSSL_RC4 * rc4, uint8 * key, uint32 len)
{
mock(rc4, key, len);
}
void
rdssl_rc4_crypt(RDSSL_RC4 * rc4, uint8 * in_data, uint8 * out_data, uint32 len)
{
mock(rc4, in_data, out_data, len);
}
void
rdssl_rkey_free(RDSSL_RKEY * rkey)
{
mock(rkey);
}
int
rdssl_rkey_get_exp_mod(RDSSL_RKEY * rkey, uint8 * exponent, uint32 max_exp_len, uint8 * modulus,
uint32 max_mod_len)
{
return mock(rkey, exponent, max_exp_len, modulus, max_mod_len);
}
void
rdssl_rsa_encrypt(uint8 * out, uint8 * in, int len, uint32 modulus_size, uint8 * modulus,
uint8 * exponent)
{
mock(out, in, len, modulus_size, modulus, exponent);
}
void
rdssl_sha1_final(RDSSL_SHA1 * sha1, uint8 * out_data)
{
mock(sha1, out_data);
}
void
rdssl_sha1_init(RDSSL_SHA1 *sha1)
{
mock(sha1);
}
void
rdssl_sha1_update(RDSSL_SHA1 *sha1, uint8 *data, uint32 len)
{
mock(sha1, data, len);
}
RD_BOOL
rdssl_sig_ok(uint8 *exponent, uint32 exp_len, uint8 *modulus, uint32 mod_len,
uint8 *signature, uint32 sig_len)
{
return mock(exponent, exp_len, modulus, mod_len, signature, sig_len);
}

7
tests/tcp_mock.c Normal file
View File

@ -0,0 +1,7 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
char *tcp_get_address()
{
return (char *) mock();
}

72
tests/ui_mock.c Normal file
View File

@ -0,0 +1,72 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
time_t g_wait_for_deactivate_ts;
/* Mock implementation of F1 */
void ui_resize_window(uint32 width, uint32 height)
{
mock(width, height);
}
void ui_set_cursor(RD_HCURSOR cursor)
{
mock(cursor);
}
void ui_set_standard_cursor()
{
mock();
}
void ui_bell()
{
mock();
}
void ui_paint_bitmap(int x, int y, int cx, int cy, int width, int height, uint8 * data)
{
mock(x,y,cx,cy,width,height,data);
}
void ui_begin_update()
{
mock();
}
RD_HCOLOURMAP ui_create_colourmap(COLOURMAP * colours)
{
return (RD_HCOLOURMAP) mock(colours);
}
RD_HCURSOR ui_create_cursor(unsigned int x, unsigned int y, uint32 width, uint32 height, uint8 * andmask,
uint8 * xormask, int bpp)
{
return (RD_HCURSOR) mock(x, y, width, height, andmask, xormask, bpp);
}
void ui_end_update()
{
mock();
}
void ui_move_pointer(int x, int y)
{
mock(x, y);
}
void ui_set_colourmap(RD_HCOLOURMAP map)
{
mock(map);
}
void ui_set_null_cursor()
{
mock();
}
void ui_set_clip(int x,int y, int cx, int cy)
{
mock(x,y,cx,cy);
}

179
tests/utils_test.c Normal file
View File

@ -0,0 +1,179 @@
#include <cgreen/cgreen.h>
#include <cgreen/mocks.h>
#include "../rdesktop.h"
/* Boilerplate */
Describe(Utils);
BeforeEach(Utils) {};
AfterEach(Utils) {};
/* globals */
char g_codepage[16];
#include "../utils.c"
/* malloc; exit if out of memory */
void *
xmalloc(int size)
{
void *mem = malloc(size);
if (mem == NULL)
{
logger(Core, Error, "xmalloc, failed to allocate %d bytes", size);
exit(EX_UNAVAILABLE);
}
return mem;
}
/* Exit on NULL pointer. Use to verify result from XGetImage etc */
void
exit_if_null(void *ptr)
{
if (ptr == NULL)
{
logger(Core, Error, "unexpected null pointer. Out of memory?");
exit(EX_UNAVAILABLE);
}
}
/* strdup */
char *
xstrdup(const char *s)
{
char *mem = strdup(s);
if (mem == NULL)
{
logger(Core, Error, "xstrdup(), strdup() failed: %s", strerror(errno));
exit(EX_UNAVAILABLE);
}
return mem;
}
/* realloc; exit if out of memory */
void *
xrealloc(void *oldmem, size_t size)
{
void *mem;
if (size == 0)
size = 1;
mem = realloc(oldmem, size);
if (mem == NULL)
{
logger(Core, Error, "xrealloc, failed to reallocate %ld bytes", size);
exit(EX_UNAVAILABLE);
}
return mem;
}
/* free */
void
xfree(void *mem)
{
free(mem);
}
/* Test function */
Ensure(Utils, CalculateDpiScaleFactorsWhenDpiIsZero) {
uint32 physical_width, physical_height, desktop_scale, device_scale;
utils_calculate_dpi_scale_factors(1024, 768, 0,
&physical_width, &physical_height,
&desktop_scale, &device_scale);
assert_that(physical_width, is_equal_to(0));
assert_that(physical_height, is_equal_to(0));
assert_that(desktop_scale, is_equal_to(0));
assert_that(device_scale, is_equal_to(0));
}
Ensure(Utils, CalculateDpiScaleFactorsWhenDpiLessThan96) {
uint32 physical_width, physical_height, desktop_scale, device_scale;
utils_calculate_dpi_scale_factors(1024, 768, 95,
&physical_width, &physical_height,
&desktop_scale, &device_scale);
assert_that(physical_width, is_equal_to(273));
assert_that(physical_height, is_equal_to(205));
assert_that(desktop_scale, is_equal_to(100));
assert_that(device_scale, is_equal_to(100));
}
Ensure(Utils, CalculateDpiScaleFactorsWhenDpiLessThan134) {
uint32 physical_width, physical_height, desktop_scale, device_scale;
utils_calculate_dpi_scale_factors(1024, 768, 133,
&physical_width, &physical_height,
&desktop_scale, &device_scale);
assert_that(physical_width, is_equal_to(195));
assert_that(physical_height, is_equal_to(146));
assert_that(desktop_scale, is_equal_to(139));
assert_that(device_scale, is_equal_to(100));
}
Ensure(Utils, CalculateDpiScaleFactorsWhenDpiLessThan173) {
uint32 physical_width, physical_height, desktop_scale, device_scale;
utils_calculate_dpi_scale_factors(1024, 768, 172,
&physical_width, &physical_height,
&desktop_scale, &device_scale);
assert_that(physical_width, is_equal_to(151));
assert_that(physical_height, is_equal_to(113));
assert_that(desktop_scale, is_equal_to(179));
assert_that(device_scale, is_equal_to(140));
}
Ensure(Utils, CalculateDpiScaleFactorsWhenDpiGreaterThanOrEqualTo173) {
uint32 physical_width, physical_height, desktop_scale, device_scale;
utils_calculate_dpi_scale_factors(1024, 768, 173,
&physical_width, &physical_height,
&desktop_scale, &device_scale);
assert_that(physical_width, is_equal_to(150));
assert_that(physical_height, is_equal_to(112));
assert_that(desktop_scale, is_equal_to(180));
assert_that(device_scale, is_equal_to(180));
}
Ensure(Utils, ApplySessionSizeLimitationLimitsWidthAndHeightToMax8192)
{
uint32 width, height;
width = height = 90000;
utils_apply_session_size_limitations(&width, &height);
assert_that(width, is_equal_to(8192));
assert_that(height, is_equal_to(8192));
}
Ensure(Utils, ApplySessionSizeLimitationLimitsWidthAndHeightToMin200)
{
uint32 width, height;
width = height = 100;
utils_apply_session_size_limitations(&width, &height);
assert_that(width, is_equal_to(200));
assert_that(height, is_equal_to(200));
}
Ensure(Utils, ApplySessionSizeLimitationRoundsWidthToClosestSmallerEvenNumber)
{
uint32 width, height;
width = height = 201;
utils_apply_session_size_limitations(&width, &height);
assert_that(width, is_equal_to(200));
assert_that(height, is_equal_to(201));
}

10
tests/x11_mock.c Normal file
View File

@ -0,0 +1,10 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
XClassHint *XAllocClassHint()
{
return (XClassHint *)mock();
}

37
tests/xclip_mock.c Normal file
View File

@ -0,0 +1,37 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
#include <X11/Xlib.h>
void xclip_init()
{
mock();
}
void xclip_deinit()
{
mock();
}
void
xclip_handle_SelectionNotify(XSelectionEvent * event)
{
mock(event);
}
void
xclip_handle_SelectionRequest(XSelectionRequestEvent * xevent)
{
mock(xevent);
}
void
xclip_handle_SelectionClear(void)
{
mock();
}
void
xclip_handle_PropertyNotify(XPropertyEvent * xev)
{
mock(xev);
}

63
tests/xkeymap_mock.c Normal file
View File

@ -0,0 +1,63 @@
#include <cgreen/mocks.h>
#include "../rdesktop.h"
#include <X11/Xlib.h>
unsigned int read_keyboard_state()
{
return mock();
}
void
xkeymap_init()
{
mock();
}
void
xkeymap_send_keys(uint32 keysym, unsigned int keycode, unsigned int state, uint32 ev_time,
RD_BOOL pressed, uint8 nesting)
{
mock(keysym, keycode, state, ev_time, pressed, nesting);
}
uint16
xkeymap_translate_button(unsigned int button, uint16 * input_type)
{
return mock(button, input_type);
}
RD_BOOL
handle_special_keys(uint32 keysym, unsigned int state, uint32 ev_time, RD_BOOL pressed)
{
return mock(keysym, state, ev_time, pressed);
}
void
set_keypress_keysym(unsigned int keycode, KeySym keysym)
{
mock(keycode, keysym);
}
KeySym
reset_keypress_keysym(unsigned int keycode, KeySym keysym)
{
return mock(keycode, keysym);
}
void
reset_modifier_keys()
{
mock();
}
char *
get_ksname(uint32 keysym)
{
return (char *) mock(keysym);
}
uint16 ui_get_numlock_state(unsigned int state)
{
return mock(state);
}

216
tests/xwin_test.c Normal file
View File

@ -0,0 +1,216 @@
#include <cgreen/cgreen.h>
#include <cgreen/mocks.h>
#include "../rdesktop.h"
#include "../proto.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xproto.h>
#include <X11/Xatom.h>
/* Boilerplate */
Describe(XWIN);
BeforeEach(XWIN) {};
AfterEach(XWIN) {};
/* Global Variables.. :( */
RD_BOOL g_user_quit;
RD_BOOL g_exit_mainloop;
int g_sizeopt;
uint32 g_initial_width;
uint32 g_initial_height;
uint16 g_session_width;
uint16 g_session_height;
int g_xpos;
int g_ypos;
int g_pos;
RD_BOOL g_sendmotion;
RD_BOOL g_fullscreen;
RD_BOOL g_grab_keyboard;
RD_BOOL g_hide_decorations;
RD_BOOL g_pending_resize;
char g_title[64];
char g_seamless_spawn_cmd[512];
/* Color depth of the RDP session.
As of RDP 5.1, it may be 8, 15, 16 or 24. */
int g_server_depth;
int g_win_button_size;
RD_BOOL g_seamless_rdp;
RD_BOOL g_seamless_persistent_mode;
uint32 g_embed_wnd;
Atom g_net_wm_state_atom;
Atom g_net_wm_desktop_atom;
Atom g_net_wm_ping_atom;
RD_BOOL g_ownbackstore;
RD_BOOL g_rdpsnd;
RD_BOOL g_owncolmap;
RD_BOOL g_local_cursor;
char g_codepage[16];
#include "../xwin.c"
#include "../utils.c"
#include "../stream.c"
/* malloc; exit if out of memory */
void *
xmalloc(int size)
{
void *mem = malloc(size);
if (mem == NULL)
{
logger(Core, Error, "xmalloc, failed to allocate %d bytes", size);
exit(EX_UNAVAILABLE);
}
return mem;
}
/* Exit on NULL pointer. Use to verify result from XGetImage etc */
void
exit_if_null(void *ptr)
{
if (ptr == NULL)
{
logger(Core, Error, "unexpected null pointer. Out of memory?");
exit(EX_UNAVAILABLE);
}
}
/* strdup */
char *
xstrdup(const char *s)
{
char *mem = strdup(s);
if (mem == NULL)
{
logger(Core, Error, "xstrdup(), strdup() failed: %s", strerror(errno));
exit(EX_UNAVAILABLE);
}
return mem;
}
/* realloc; exit if out of memory */
void *
xrealloc(void *oldmem, size_t size)
{
void *mem;
if (size == 0)
size = 1;
mem = realloc(oldmem, size);
if (mem == NULL)
{
logger(Core, Error, "xrealloc, failed to reallocate %ld bytes", size);
exit(EX_UNAVAILABLE);
}
return mem;
}
/* free */
void
xfree(void *mem)
{
free(mem);
}
/* Special mocks */
int XResizeWindow(Display *display, Window wnd, unsigned int width, unsigned int height)
{
return mock(display, wnd, width, height);
}
Status
XGetWindowAttributes(Display *display, Window wnd, XWindowAttributes *attr)
{
return mock(display, wnd, attr);
}
void
XSetWMSizeHints(Display *display, Window wnd, XSizeHints *hints, Atom property)
{
mock(display, wnd, hints, property);
}
int
XSetClipRectangles(Display *display, GC gc, int clip_x_origin,
int clip_y_origin, XRectangle rectangles[],
int n, int ordering)
{
return mock(display, gc, clip_x_origin, clip_y_origin,
rectangles, n, ordering);
}
int XPending(Display *display)
{
return mock(display);
}
/* Test functions */
Ensure(XWIN, UiResizeWindowCallsXResizeWindow) {
int width = 1024;
int height = 768;
/* stubs */
expect(XGetWindowAttributes, will_return(True));
expect(XSetWMSizeHints);
expect(XSetClipRectangles);
/* expects */
expect(XResizeWindow,
when(width, is_equal_to(width)),
when(height, is_equal_to(height)));
ui_resize_window(width, height);
}
/* FIXME: This test is broken */
#if 0
Ensure(XWIN, UiSelectCallsProcessPendingResizeIfGPendingResizeIsTrue)
{
g_pending_resize = True;
expect(rdpdr_add_fds);
expect(rdpdr_check_fds);
expect(ctrl_add_fds);
expect(ctrl_check_fds);
expect(seamless_select_timeout);
expect(XPending, will_return(0));
expect(rdpedisp_is_available, will_return(False));
/* HELP! How do we mock functions within the same unit? We're
drawing blanks with plenty of "redefined function" or "multiple
definition" errors from the compiler and linker.
The two attempts below does not work as intended.
*/
/* Letting process_pending_resize return true will break the
ui_select loop. */
expect(process_pending_resize, will_return(True));
/* process_fds returning True indicates there is data on rdp socket,
assumed this will break out of ui_select loop */
//expect(process_fds, will_return(True));
ui_select(0);
}
#endif /* broken */
#if 0 /* hackety-hackety-hack /k */
Ensure(XWIN, UiSelectCallsRDPEDISPSetSessionSizeOnResize)
{
g_pending_resize = True;
g_resize_timer = (struct timeval) {0, 0};
expect(rdpedisp_is_available, will_return(True));
expect(rdpedisp_set_session_size);
ui_select(0);
}
#endif

33
utils.c
View File

@ -28,9 +28,6 @@
#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;
@ -240,28 +237,46 @@ 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,
utils_calculate_dpi_scale_factors(uint32 width, uint32 height, uint32 dpi,
uint32 *physwidth, uint32 *physheight,
uint32 *desktopscale, uint32 *devicescale)
{
*physwidth = *physheight = *desktopscale = *devicescale = 0;
if (g_dpi > 0)
if (dpi > 0)
{
*physwidth = g_width * 254 / (g_dpi * 10);
*physheight = g_height * 254 / (g_dpi * 10);
*physwidth = width * 254 / (dpi * 10);
*physheight = height * 254 / (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;
*desktopscale = dpi < 96 ? 100 : (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);
*devicescale = dpi < 134 ? 100 : (dpi < 173 ? 140 : 180);
}
}
void
utils_apply_session_size_limitations(uint32 *width, uint32 *height)
{
/* width MUST be even number */
*width -= (*width) % 2;
if (*width > 8192)
*width = 8192;
else if (*width < 200)
*width = 200;
if (*height > 8192)
*height = 8192;
else if (*height < 200)
*height = 200;
}
/*
* component logging
*

View File

@ -28,8 +28,11 @@ 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,
void utils_calculate_dpi_scale_factors(uint32 width, uint32 height, uint32 dpi,
uint32 *physwidth, uint32 *physheight,
uint32 *desktopscale, uint32 *devicescale);
void utils_apply_session_size_limitations(uint32 *width, uint32 *height);
typedef enum log_level_t
{

View File

@ -44,6 +44,8 @@ extern int g_keyboard_subtype;
extern int g_keyboard_functionkeys;
extern int g_win_button_size;
extern RD_BOOL g_enable_compose;
extern RD_BOOL g_seamless_rdp;
extern RD_BOOL g_seamless_active;
extern RDP_VERSION g_rdp_version;
extern RD_BOOL g_numlock_sync;
@ -644,7 +646,21 @@ handle_special_keys(uint32 keysym, unsigned int state, uint32 ev_time, RD_BOOL p
{
/* Ctrl-Alt-Enter: toggle full screen */
if (pressed)
{
if (!g_seamless_rdp)
{
/* only allow toggle fullscreen when not running
rdesktop in seamless mode */
xwin_toggle_fullscreen();
}
else
{
/* deactivate seamless mode for debug purpose, one can
not activate seamless mode again */
if (g_seamless_active)
ui_seamless_toggle();
}
}
return True;
}
break;

535
xwin.c
View File

@ -42,11 +42,14 @@
#define HOST_NAME_MAX MAXHOSTNAMELEN
#endif
extern RD_BOOL g_user_quit;
extern RD_BOOL g_exit_mainloop;
extern int g_sizeopt;
extern int g_width;
extern int g_height;
extern uint32 g_windowed_width;
extern uint32 g_windowed_height;
extern uint32 g_initial_width;
extern uint32 g_initial_height;
extern uint16 g_session_width;
extern uint16 g_session_height;
extern int g_xpos;
extern int g_ypos;
extern int g_pos;
@ -62,12 +65,19 @@ extern char g_seamless_spawn_cmd[];
extern int g_server_depth;
extern int g_win_button_size;
/* This is a timer used to rate limit actual resizing */
static struct timeval g_resize_timer = {0};
Display *g_display;
Time g_last_gesturetime;
static int g_x_socket;
static Screen *g_screen;
Window g_wnd;
/* These are the last known window sizes. They are updated whenever the window size is changed. */
static uint32 g_window_width;
static uint32 g_window_height;
/* SeamlessRDP support */
typedef struct _seamless_group
{
@ -101,7 +111,7 @@ typedef struct _seamless_window
static seamless_window *g_seamless_windows = NULL;
static unsigned long g_seamless_focused = 0;
static RD_BOOL g_seamless_started = False; /* Server end is up and running */
static RD_BOOL g_seamless_active = False; /* We are currently in seamless mode */
RD_BOOL g_seamless_active = False; /* We are currently in seamless mode */
static RD_BOOL g_seamless_hidden = False; /* Desktop is hidden on server */
static RD_BOOL g_seamless_broken_restack = False; /* WM does not properly restack */
extern RD_BOOL g_seamless_rdp;
@ -1953,7 +1963,7 @@ ui_init(void)
/*
Initialize connection specific data, such as session size.
Initialize connection specific data, such as initial session size.
*/
void
ui_init_connection(void)
@ -1963,8 +1973,8 @@ ui_init_connection(void)
*/
if (g_fullscreen)
{
g_width = WidthOfScreen(g_screen);
g_height = HeightOfScreen(g_screen);
g_initial_width = WidthOfScreen(g_screen);
g_initial_height = HeightOfScreen(g_screen);
g_using_full_workarea = True;
}
else if (g_sizeopt < 0)
@ -1973,14 +1983,14 @@ ui_init_connection(void)
if (-g_sizeopt >= 100)
g_using_full_workarea = True;
if (g_width > 0)
g_width = g_sizeopt;
if (g_initial_width > 0)
g_initial_width = g_sizeopt;
if (g_height > 0)
g_height = g_sizeopt;
if (g_initial_height > 0)
g_initial_height = g_sizeopt;
g_height = HeightOfScreen(g_screen) * (-g_height) / 100;
g_width = WidthOfScreen(g_screen) * (-g_width) / 100;
g_initial_height = HeightOfScreen(g_screen) * (-g_initial_height) / 100;
g_initial_width = WidthOfScreen(g_screen) * (-g_initial_width) / 100;
}
else if (g_sizeopt == 1)
{
@ -1988,21 +1998,18 @@ ui_init_connection(void)
uint32 x, y, cx, cy;
if (get_current_workarea(&x, &y, &cx, &cy) == 0)
{
g_width = cx;
g_height = cy;
g_initial_width = cx;
g_initial_height = cy;
g_using_full_workarea = True;
}
else
{
logger(GUI, Warning,
"Failed to get workarea: probably your window manager does not support extended hints\n");
g_width = WidthOfScreen(g_screen);
g_height = HeightOfScreen(g_screen);
g_initial_width = WidthOfScreen(g_screen);
g_initial_height = HeightOfScreen(g_screen);
}
}
/* make sure width is a multiple of 4 */
g_width = (g_width + 3) & ~3;
}
@ -2025,14 +2032,26 @@ ui_deinit(void)
}
static void
static unsigned long
get_window_attribs(XSetWindowAttributes * attribs)
{
unsigned long vmask = 0;
vmask = CWBackPixel | CWBorderPixel | CWBackingStore | CWOverrideRedirect | CWColormap;
attribs->background_pixel = BlackPixelOfScreen(g_screen);
attribs->border_pixel = WhitePixelOfScreen(g_screen);
attribs->backing_store = g_ownbackstore ? NotUseful : Always;
attribs->override_redirect = g_fullscreen;
attribs->colormap = g_xcolmap;
return vmask;
}
static unsigned long
get_window_attribs_seamless(XSetWindowAttributes * attribs)
{
return (get_window_attribs(attribs) & ~CWOverrideRedirect);
}
static void
@ -2051,8 +2070,38 @@ get_input_mask(long *input_mask)
*input_mask |= LeaveWindowMask;
}
static void
get_sizehints(XSizeHints *sizehints, uint32 width, uint32 height)
{
if (sizehints == NULL)
return;
/* user specified position, this is needed to override the choice of
window position by window manager when a window is mapped. */
sizehints->flags = USPosition;
/* set minimal size of rdesktop main window */
sizehints->flags |= PMinSize;
sizehints->min_width = 200;
sizehints->min_height = 200;
/* resize increment */
sizehints->flags |= PResizeInc;
sizehints->width_inc = 2; /* session width must be divisible by two */
sizehints->height_inc = 1;
if (g_seamless_rdp)
{
/* disable dynamic session resize based on window size for
rdesktop main window when seamless is enabled */
sizehints->flags |= PMaxSize;
sizehints->min_width = sizehints->max_width = width;
sizehints->min_height = sizehints->max_height = height;
}
}
RD_BOOL
ui_create_window(void)
ui_create_window(uint32 width, uint32 height)
{
uint8 null_pointer_mask[1] = { 0x80 };
uint8 null_pointer_data[24] = { 0x00 };
@ -2060,34 +2109,27 @@ ui_create_window(void)
XSetWindowAttributes attribs;
XClassHint *classhints;
XSizeHints *sizehints;
unsigned long value_mask;
long input_mask, ic_input_mask;
XEvent xevent;
if (g_fullscreen)
{
g_width = WidthOfScreen(g_screen);
g_height = HeightOfScreen(g_screen);
}
else
{
g_width = g_windowed_width;
g_height = g_windowed_height;
}
/* reset stored window sizes */
g_window_width = 0;
g_window_height = 0;
logger(GUI, Debug, "ui_create_window() width = %d, height = %d", g_width, g_height);
logger(GUI, Debug, "ui_create_window() width = %d, height = %d", width, height);
/* Handle -x-y portion of geometry string */
if (g_xpos < 0 || (g_xpos == 0 && (g_pos & 2)))
g_xpos = WidthOfScreen(g_screen) + g_xpos - g_width;
g_xpos = WidthOfScreen(g_screen) + g_xpos - width;
if (g_ypos < 0 || (g_ypos == 0 && (g_pos & 4)))
g_ypos = HeightOfScreen(g_screen) + g_ypos - g_height;
g_ypos = HeightOfScreen(g_screen) + g_ypos - height;
get_window_attribs(&attribs);
value_mask = get_window_attribs(&attribs);
g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, width,
height, 0, g_depth, InputOutput, g_visual, value_mask, &attribs);
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());
set_wm_client_machine(g_display, g_wnd);
@ -2102,11 +2144,11 @@ ui_create_window(void)
if ((g_ownbackstore) && (g_backstore == 0))
{
g_backstore = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth);
g_backstore = XCreatePixmap(g_display, g_wnd, width, height, g_depth);
/* clear to prevent rubbish being exposed at startup */
XSetForeground(g_display, g_gc, BlackPixelOfScreen(g_screen));
XFillRectangle(g_display, g_backstore, g_gc, 0, 0, g_width, g_height);
XFillRectangle(g_display, g_backstore, g_gc, 0, 0, width, height);
}
XStoreName(g_display, g_wnd, g_title);
@ -2126,11 +2168,7 @@ ui_create_window(void)
sizehints = XAllocSizeHints();
if (sizehints)
{
sizehints->flags = PMinSize | PMaxSize;
if (g_pos)
sizehints->flags |= PPosition;
sizehints->min_width = sizehints->max_width = g_width;
sizehints->min_height = sizehints->max_height = g_height;
get_sizehints(sizehints, width, height);
XSetWMNormalHints(g_display, g_wnd, sizehints);
XFree(sizehints);
}
@ -2191,43 +2229,52 @@ ui_create_window(void)
seamless_restack_test();
}
rdpedisp_set_session_size(g_width, g_height);
return True;
}
void
ui_resize_window()
ui_resize_window(uint32 width, uint32 height)
{
XWindowAttributes attr;
XSizeHints *sizehints;
Pixmap bs;
XGetWindowAttributes(g_display, g_wnd, &attr);
if ((attr.width == (int)width && attr.height == (int)height))
{
/* no-op */
return;
}
logger(GUI, Debug, "ui_resize_window(), Changing window %dx%d to match new session %dx%d size",
attr.width, attr.height, width, height);
sizehints = XAllocSizeHints();
if (sizehints)
{
sizehints->flags = PMinSize | PMaxSize;
sizehints->min_width = sizehints->max_width = g_width;
sizehints->min_height = sizehints->max_height = g_height;
get_sizehints(sizehints, width, height);
XSetWMNormalHints(g_display, g_wnd, sizehints);
XFree(sizehints);
}
if (!(g_fullscreen || g_embed_wnd))
if (!g_embed_wnd)
{
XResizeWindow(g_display, g_wnd, g_width, g_height);
XResizeWindow(g_display, g_wnd, width, height);
}
/* create new backstore pixmap */
if (g_backstore != 0)
{
bs = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth);
bs = XCreatePixmap(g_display, g_wnd, width, height, g_depth);
XSetForeground(g_display, g_gc, BlackPixelOfScreen(g_screen));
XFillRectangle(g_display, bs, g_gc, 0, 0, g_width, g_height);
XCopyArea(g_display, g_backstore, bs, g_gc, 0, 0, g_width, g_height, 0, 0);
XFillRectangle(g_display, bs, g_gc, 0, 0, width, height);
XCopyArea(g_display, g_backstore, bs, g_gc, 0, 0, width, height, 0, 0);
XFreePixmap(g_display, g_backstore);
g_backstore = bs;
}
ui_set_clip(0, 0, width, height);
}
RD_BOOL
@ -2255,28 +2302,96 @@ ui_destroy_window(void)
void
xwin_toggle_fullscreen(void)
{
uint32 x, y, width, height;
XWindowAttributes attr;
XSetWindowAttributes setattr;
unsigned long value_mask;
Pixmap contents = 0;
Window unused;
int dest_x, dest_y;
static uint32 windowed_x = 0;
static uint32 windowed_y = 0;
static uint32 windowed_height = 0;
static uint32 windowed_width = 0;
if (g_seamless_active)
/* Turn off SeamlessRDP mode */
ui_seamless_toggle();
/* When running rdesktop in seamless mode, toggling of fullscreen is not allowed */
if (g_seamless_rdp)
return;
/* get current window size and store it to be used when switching back
* from fullscreen mode.
*/
XGetWindowAttributes(g_display, g_wnd, &attr);
if (!g_fullscreen || (windowed_width == 0 || windowed_height == 0))
{
/* only stored if we toggle from windowed -> fullscreen or when
* going from fullscreen -> windowed when started in fullscreen mode.
*/
XTranslateCoordinates(g_display, g_wnd,
DefaultRootWindow(g_display),
0, 0, &dest_x, &dest_y, &unused );
windowed_x = dest_x;
windowed_y = dest_y;
windowed_width = attr.width;
windowed_height = attr.height;
}
if (!g_ownbackstore)
{
/* need to save contents of window */
contents = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth);
XCopyArea(g_display, g_wnd, contents, g_gc, 0, 0, g_width, g_height, 0, 0);
/* need to save contents of current window */
contents = XCreatePixmap(g_display, g_wnd, attr.width, attr.height, g_depth);
XCopyArea(g_display, g_wnd, contents, g_gc, 0, 0, attr.width, attr.height, 0, 0);
}
ui_destroy_window();
g_fullscreen = !g_fullscreen;
ui_create_window();
if (g_fullscreen)
{
x = 0;
y = 0,
width = WidthOfScreen(g_screen);
height = HeightOfScreen(g_screen);
}
else
{
x = windowed_x;
y = windowed_y;
width = windowed_width;
height = windowed_height;
}
/* Resize rdesktop window using new size and window attributes */
XUnmapWindow(g_display, g_wnd);
XMoveResizeWindow(g_display, g_wnd, x, y, width, height);
value_mask = get_window_attribs(&setattr);
XChangeWindowAttributes(g_display, g_wnd, value_mask, &setattr);
XMapWindow(g_display, g_wnd);
/* Change session size to match new window size */
if (rdpedisp_is_available() == False)
{
/* Change session size using disconnect / reconnect mechanism */
g_pending_resize = True;
return;
}
else
{
/* Change session size using DisplayControl extension (RDPEDISP) */
rdpedisp_set_session_size(width, height);
}
XDefineCursor(g_display, g_wnd, g_current_cursor);
if (!g_ownbackstore)
{
XCopyArea(g_display, contents, g_wnd, g_gc, 0, 0, g_width, g_height, 0, 0);
/* copy back saved contents into new window */
XCopyArea(g_display, contents, g_wnd, g_gc, 0, 0, attr.width, attr.height, 0, 0);
XFreePixmap(g_display, contents);
}
}
@ -2284,7 +2399,11 @@ xwin_toggle_fullscreen(void)
static void
handle_button_event(XEvent xevent, RD_BOOL down)
{
XWindowAttributes attr;
uint16 button, input_type, flags = 0;
XGetWindowAttributes(g_display, g_wnd, &attr);
g_last_gesturetime = xevent.xbutton.time;
/* Reverse the pointer button mapping, e.g. in the case of
"left-handed mouse mode"; the RDP session expects to
@ -2307,12 +2426,12 @@ handle_button_event(XEvent xevent, RD_BOOL down)
if (xevent.xbutton.y < g_win_button_size)
{
/* Check from right to left: */
if (xevent.xbutton.x >= g_width - g_win_button_size)
if (xevent.xbutton.x >= attr.width - g_win_button_size)
{
/* The close button, continue */
;
}
else if (xevent.xbutton.x >= g_width - g_win_button_size * 2)
else if (xevent.xbutton.x >= attr.width - g_win_button_size * 2)
{
/* The maximize/restore button. Do not send to
server. It might be a good idea to change the
@ -2321,7 +2440,7 @@ handle_button_event(XEvent xevent, RD_BOOL down)
if (xevent.type == ButtonPress)
return;
}
else if (xevent.xbutton.x >= g_width - g_win_button_size * 3)
else if (xevent.xbutton.x >= attr.width - g_win_button_size * 3)
{
/* The minimize button. Iconify window. */
if (xevent.type == ButtonRelease)
@ -2376,7 +2495,6 @@ handle_button_event(XEvent xevent, RD_BOOL down)
}
}
/* Process events in Xlib queue
Returns 0 after user quit, 1 otherwise */
static int
@ -2389,6 +2507,7 @@ xwin_process_events(void)
Status status;
int events = 0;
seamless_window *sw;
static RD_BOOL is_g_wnd_mapped = False;
while ((XPending(g_display) > 0) && events++ < 20)
{
@ -2682,28 +2801,101 @@ xwin_process_events(void)
break;
case MapNotify:
if (xevent.xconfigure.window == g_wnd)
{
XWindowAttributes attr;
XGetWindowAttributes(g_display, g_wnd, &attr);
g_window_width = attr.width;
g_window_height = attr.height;
logger(GUI, Debug, "xwin_process_events(), Window mapped with size %dx%d",
g_window_width, g_window_height);
is_g_wnd_mapped = True;
}
if (!g_seamless_active)
{
rdp_send_suppress_output_pdu(ALLOW_DISPLAY_UPDATES);
}
break;
case UnmapNotify:
if (xevent.xconfigure.window == g_wnd)
{
is_g_wnd_mapped = False;
}
if (!g_seamless_active)
{
rdp_send_suppress_output_pdu(SUPPRESS_DISPLAY_UPDATES);
}
break;
case ConfigureNotify:
#ifdef HAVE_XRANDR
if ((g_sizeopt || g_fullscreen)
&& xevent.xconfigure.window == DefaultRootWindow(g_display))
/* Resize on root window size change */
if (xevent.xconfigure.window == DefaultRootWindow(g_display))
{
/* only for fullscreen or x%-of-screen-sized windows */
if (g_sizeopt || g_fullscreen)
{
if (xevent.xconfigure.width != WidthOfScreen(g_screen)
|| xevent.xconfigure.height != HeightOfScreen(g_screen))
{
XRRUpdateConfiguration(&xevent);
XSync(g_display, False);
logger(GUI, Debug,
"xwin_process_events(), ConfigureNotify: Root window changed to %dx%d",
xevent.xconfigure.width,
xevent.xconfigure.height);
gettimeofday(&g_resize_timer, NULL);
g_pending_resize = True;
}
}
XRRUpdateConfiguration(&xevent);
XSync(g_display, False);
} else
#endif
if (xevent.xconfigure.window == g_wnd && !g_seamless_rdp && is_g_wnd_mapped)
{
/* Update window size */
g_window_width = xevent.xconfigure.width;
g_window_height = xevent.xconfigure.height;
uint32 w, h;
w = g_window_width;
h = g_window_height;
utils_apply_session_size_limitations(&w, &h);
logger(GUI, Debug, "xwin_process_events(), ConfigureNotify: session: %dx%d, new window: %dx%d (adj: %dx%d)",
g_session_width,
g_session_height,
g_window_width,
g_window_height,
w, h);
if (g_session_width != w
|| g_session_height != h)
{
logger(GUI, Debug, "xwin_process_events(), ConfigureNotify: session: %dx%d, new window: %dx%d",
g_session_width,
g_session_height,
g_window_width,
g_window_height);
/* perform a resize */
gettimeofday(&g_resize_timer, NULL);
g_pending_resize = True;
}
else
{
g_pending_resize = False;
}
}
if (!g_seamless_active)
break;
@ -2732,28 +2924,27 @@ xwin_process_events(void)
return 1;
}
/* Returns 0 after user quit or pending resize, 1 otherwise */
int
ui_select(int rdp_socket)
static inline uint32
time_difference_in_ms(struct timeval then, struct timeval now)
{
int n;
uint32 ms;
ms = 0;
ms += (now.tv_sec - then.tv_sec) * 1000;
ms += (now.tv_usec - then.tv_usec) / 1000;
return ms;
}
time_t g_wait_for_deactivate_ts = 0;
static RD_BOOL
process_fds(int rdp_socket, int ms)
{
int n, ret;
fd_set rfds, wfds;
struct timeval tv;
RD_BOOL s_timeout = False;
while (True)
{
n = (rdp_socket > g_x_socket) ? rdp_socket : g_x_socket;
/* Process any events already waiting */
if (!xwin_process_events())
/* User quit */
return 0;
if (g_pending_resize)
return 0;
if (g_seamless_active)
sw_check_timers();
FD_ZERO(&rfds);
FD_ZERO(&wfds);
@ -2761,8 +2952,8 @@ ui_select(int rdp_socket)
FD_SET(g_x_socket, &rfds);
/* default timeout */
tv.tv_sec = 60;
tv.tv_usec = 0;
tv.tv_sec = ms / 1000;
tv.tv_usec = (ms - (tv.tv_sec * 1000)) * 1000;
#ifdef WITH_RDPSND
rdpsnd_add_fds(&n, &rfds, &wfds, &tv);
@ -2777,13 +2968,14 @@ ui_select(int rdp_socket)
n++;
switch (select(n, &rfds, &wfds, NULL, &tv))
ret = select(n, &rfds, &wfds, NULL, &tv);
if (ret <= 0)
{
case -1:
logger(GUI, Error, "ui_select(), select failed: %s",
if (ret == -1)
{
logger(GUI, Error, "process_fds(), select failed: %s",
strerror(errno));
case 0:
}
#ifdef WITH_RDPSND
rdpsnd_check_fds(&rfds, &wfds);
#endif
@ -2791,7 +2983,7 @@ ui_select(int rdp_socket)
/* Abort serial read calls */
if (s_timeout)
rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) True);
continue;
return False;
}
#ifdef WITH_RDPSND
@ -2803,7 +2995,124 @@ ui_select(int rdp_socket)
ctrl_check_fds(&rfds, &wfds);
if (FD_ISSET(rdp_socket, &rfds))
return 1;
return True;
return False;
}
static RD_BOOL
process_ui()
{
if (!xwin_process_events())
{
/* User quit */
g_pending_resize = False;
return True;
}
return False;
}
static RD_BOOL
process_pending_resize ()
{
uint32 width, height;
time_t now_ts;
struct timeval now;
/* Rate limit ConfigureNotify events before performing a
resize - enough time has to pass after the last event
*/
gettimeofday(&now, NULL);
if (time_difference_in_ms(g_resize_timer, now) <= 500)
return False;
/* carry out a resize to desired size */
if (rdpedisp_is_available() == False)
{
/* resize session using disconnect reconnect
* sequence if RDPEDISP is not support by
* server.
*/
g_initial_width = g_window_width;
g_initial_height = g_window_height;
logger(GUI, Verbose, "Window resize detected, reconnecting to new size %dx%d",
g_initial_width,
g_initial_height);
return True;
}
else
{
now_ts = time(NULL);
if (now_ts - g_wait_for_deactivate_ts <= 5)
return False;
/* size of current window */
width = g_window_width;
height = g_window_height;
/* resize session using RDPEDISP */
if (g_fullscreen || g_seamless_rdp)
{
/* size of screen */
width = WidthOfScreen(g_screen);
height = HeightOfScreen(g_screen);
}
logger(GUI, Verbose, "Window resize detected, requesting matching session size %dx%d",
width, height);
rdpedisp_set_session_size(width, height);
g_pending_resize = False;
g_wait_for_deactivate_ts = now_ts;
}
return False;
}
/* Breaks out of loop if g_exit_mainloop is set or if there is data available on rdp socket for
processing. */
void
ui_select(int rdp_socket)
{
RD_BOOL rdp_socket_has_data = False;
while (g_exit_mainloop == False && rdp_socket_has_data == False)
{
/* Process any events already waiting */
/* returns True on user quit */
if (process_ui() == True)
{
g_exit_mainloop = True;
g_user_quit = True;
continue;
}
if (g_pending_resize == True)
{
/* returns True on disconnect-reconnect resize */
if (process_pending_resize() == True)
{
g_exit_mainloop = True;
continue;
}
}
if (g_seamless_active)
sw_check_timers();
/* We end up here when we are waiting for a resize timer to expire before attempting
to resize the session. We don't want to sleep in the select for up to 60 seconds
if there are no RDP packets if the resize timer is 0.5 seconds. */
if (g_pending_resize == True)
rdp_socket_has_data = process_fds(rdp_socket, 100);
else
rdp_socket_has_data = process_fds(rdp_socket, 60000);
}
}
@ -3275,11 +3584,9 @@ ui_set_clip(int x, int y, int cx, int cy)
void
ui_reset_clip(void)
{
g_clip_rectangle.x = 0;
g_clip_rectangle.y = 0;
g_clip_rectangle.width = g_width;
g_clip_rectangle.height = g_height;
XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded);
XWindowAttributes attr;
XGetWindowAttributes(g_display, g_wnd, &attr);
ui_set_clip(0, 0, attr.width, attr.height);
}
void
@ -3756,9 +4063,12 @@ ui_draw_text(uint8 font, uint8 flags, uint8 opcode, int mixmode, int x, int y,
int boxx, int boxy, int boxcx, int boxcy, BRUSH * brush,
uint32 bgcolour, uint32 fgcolour, uint8 * text, uint8 length)
{
XWindowAttributes attr;
UNUSED(opcode);
UNUSED(brush);
XGetWindowAttributes(g_display, g_wnd, &attr);
/* TODO: use brush appropriately */
FONTGLYPH *glyph;
@ -3770,8 +4080,8 @@ ui_draw_text(uint8 font, uint8 flags, uint8 opcode, int mixmode, int x, int y,
/* Sometimes, the boxcx value is something really large, like
32691. This makes XCopyArea fail with Xvnc. The code below
is a quick fix. */
if (boxx + boxcx > g_width)
boxcx = g_width - boxx;
if (boxx + boxcx > attr.width)
boxcx = attr.width - boxx;
if (boxcx > 1)
{
@ -4062,6 +4372,7 @@ ui_seamless_create_window(unsigned long id, unsigned long group, unsigned long p
XSizeHints *sizehints;
XWMHints *wmhints;
long input_mask;
unsigned long value_mask;
seamless_window *sw, *sw_parent;
if (!g_seamless_active)
@ -4072,10 +4383,10 @@ ui_seamless_create_window(unsigned long id, unsigned long group, unsigned long p
if (sw)
return;
get_window_attribs(&attribs);
value_mask = get_window_attribs_seamless(&attribs);
wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), -1, -1, 1, 1, 0, g_depth,
InputOutput, g_visual,
CWBackPixel | CWBackingStore | CWColormap | CWBorderPixel, &attribs);
InputOutput, g_visual, value_mask, &attribs);
ewmh_set_wm_pid(wnd, getpid());
set_wm_client_machine(g_display, wnd);