Implement dynamic session resize

This adds support for resizing the RDP session dynamically based on
the window size. Some complicated logic has been added to avoid
sending excessive amounts of resize requests to the RDP server.

When supported, this resize mechanism should use the RDPEDISP way of
signalling the server to initiate a Deactivate/Activate sequence, but
rdesktop will fall back on Disconnect/Reconnect if RDPEDISP is not
supported by the server.

ui_select has been refactored and most functionality has been broken
out into three new functions, simplifying ui_select into a loop.

Signed-off-by: Henrik Andersson <hean01@cendio.com>
Signed-off-by: Karl Mikaelsson <derfian@cendio.se>
Signed-off-by: Thomas Nilefalk <thoni56@cendio.se>
This commit is contained in:
Cendio 2017-11-14 09:39:39 +01:00
parent 4ea0e06713
commit ab50ea31cf
9 changed files with 530 additions and 212 deletions

View File

@ -259,12 +259,12 @@ RD_BOOL get_key_state(unsigned int state, uint32 keysym);
RD_BOOL ui_init(void); RD_BOOL ui_init(void);
void ui_init_connection(void); void ui_init_connection(void);
void ui_deinit(void); void ui_deinit(void);
RD_BOOL ui_create_window(void); RD_BOOL ui_create_window(uint32 width, uint32 height);
void ui_resize_window(void); void ui_resize_window(uint32 width, uint32 height);
void ui_destroy_window(void); void ui_destroy_window(void);
RD_BOOL ui_have_window(void); RD_BOOL ui_have_window(void);
void xwin_toggle_fullscreen(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); void ui_move_pointer(int x, int y);
RD_HBITMAP ui_create_bitmap(int width, int height, uint8 * data); 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); 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); RD_BOOL lspci_init(void);
/* rdpedisp.c */ /* rdpedisp.c */
void rdpedisp_init(void); void rdpedisp_init(void);
RD_BOOL rdpedisp_is_available();
void rdpedisp_set_session_size(uint32 width, uint32 height); void rdpedisp_set_session_size(uint32 width, uint32 height);
/* dvc.c */ /* dvc.c */
typedef void (*dvc_channel_process_fn) (STREAM s); 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 from _NET_WORKAREA. If negative, absolute value
specifies the percent of the whole screen. */ specifies the percent of the whole screen. */
int g_dpi = 0; /* device DPI: default not set */ int g_dpi = 0; /* device DPI: default not set */
int g_width = 1024;
int g_height = 768; /* Following variables holds the initial width and height for a
uint32 g_windowed_width = 1024; rdesktop window, this is sent upon connect and tells the server
uint32 g_windowed_height = 768; 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_xpos = 0;
int g_ypos = 0; int g_ypos = 0;
int g_pos = 0; /* 0 position unspecified, int g_pos = 0; /* 0 position unspecified,
@ -712,17 +715,17 @@ main(int argc, char *argv[])
break; break;
} }
g_width = strtol(optarg, &p, 10); g_initial_width = strtol(optarg, &p, 10);
if (g_width <= 0) if (g_initial_width <= 0)
{ {
logger(Core, Error, "invalid geometry width specified"); logger(Core, Error, "invalid geometry width specified");
return EX_USAGE; return EX_USAGE;
} }
if (*p == 'x') 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"); logger(Core, Error, "invalid geometry height specified");
return EX_USAGE; return EX_USAGE;
@ -730,16 +733,16 @@ main(int argc, char *argv[])
if (*p == '%') if (*p == '%')
{ {
g_sizeopt = -g_width; g_sizeopt = -g_initial_width;
g_width = g_sizeopt; g_initial_width = g_sizeopt;
if (*(p + 1) == 'x') if (*(p + 1) == 'x')
{ {
g_height = -strtol(p + 2, &p, 10); g_initial_height = -strtol(p + 2, &p, 10);
} }
else else
{ {
g_height = g_sizeopt; g_initial_height = g_sizeopt;
} }
p++; p++;
@ -767,9 +770,6 @@ main(int argc, char *argv[])
g_ypos = strtol(p, NULL, 10); g_ypos = strtol(p, NULL, 10);
} }
g_windowed_height = g_height;
g_windowed_width = g_width;
break; break;
case 'f': case 'f':
@ -1240,6 +1240,8 @@ main(int argc, char *argv[])
} }
ui_init_connection(); ui_init_connection();
utils_apply_session_size_limitations(&g_initial_width, &g_initial_height);
if (!rdp_connect if (!rdp_connect
(server, flags, domain, g_password, shell, directory, g_reconnect_loop)) (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 */ /* Enter a reconnect loop if we have a pending resize request */
if (g_pending_resize) 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_pending_resize = False;
g_reconnect_loop = True; g_reconnect_loop = True;
continue; continue;
@ -1839,7 +1843,7 @@ rd_create_ui()
if (!ui_have_window()) if (!ui_have_window())
{ {
/* create a window if we don't have one initialized */ /* 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); exit(EX_OSERR);
} }
else else

76
rdp.c
View File

@ -43,13 +43,16 @@ extern RDP_VERSION g_rdp_version;
extern uint16 g_server_rdp_version; extern uint16 g_server_rdp_version;
extern uint32 g_rdp5_performanceflags; extern uint32 g_rdp5_performanceflags;
extern int g_server_depth; extern int g_server_depth;
extern int g_width; extern uint32 g_initial_width;
extern int g_height; extern uint32 g_initial_height;
extern RD_BOOL g_bitmap_cache; extern RD_BOOL g_bitmap_cache;
extern RD_BOOL g_bitmap_cache_persist_enable; extern RD_BOOL g_bitmap_cache_persist_enable;
extern RD_BOOL g_numlock_sync; extern RD_BOOL g_numlock_sync;
extern RD_BOOL g_pending_resize; extern RD_BOOL g_pending_resize;
extern RD_BOOL g_network_error; extern RD_BOOL g_network_error;
extern time_t g_wait_for_deactivate_ts;
RD_BOOL g_exit_mainloop = False;
uint8 *g_next_packet; uint8 *g_next_packet;
uint32 g_rdp_shareid; uint32 g_rdp_shareid;
@ -80,6 +83,11 @@ extern RD_BOOL g_has_reconnect_random;
extern uint8 g_client_random[SEC_RANDOM_SIZE]; extern uint8 g_client_random[SEC_RANDOM_SIZE];
static uint32 g_packetno; 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); static void rdp_out_unistr(STREAM s, char *string, int len);
/* Receive an RDP packet */ /* Receive an RDP packet */
@ -553,11 +561,11 @@ rdp_send_suppress_output_pdu(enum RDP_SUPPRESS_STATUS allowupdates)
case SUPPRESS_DISPLAY_UPDATES: /* shut the server up */ case SUPPRESS_DISPLAY_UPDATES: /* shut the server up */
break; break;
case ALLOW_DISPLAY_UPDATES: /* receive data again */ case ALLOW_DISPLAY_UPDATES: /* receive data again */
out_uint16_le(s, 0); /* left */ out_uint16_le(s, 0); /* left */
out_uint16_le(s, 0); /* top */ out_uint16_le(s, 0); /* top */
out_uint16_le(s, g_width); /* right */ out_uint16_le(s, g_session_width); /* right */
out_uint16_le(s, g_height); /* bottom */ out_uint16_le(s, g_session_height); /* bottom */
break; break;
} }
@ -659,14 +667,16 @@ rdp_out_ts_general_capabilityset(STREAM s)
static void static void
rdp_out_ts_bitmap_capabilityset(STREAM s) 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_CAPSET_BITMAP);
out_uint16_le(s, RDP_CAPLEN_BITMAP); out_uint16_le(s, RDP_CAPLEN_BITMAP);
out_uint16_le(s, g_server_depth); /* preferredBitsPerPixel */ out_uint16_le(s, g_server_depth); /* preferredBitsPerPixel */
out_uint16_le(s, 1); /* receive1BitPerPixel (ignored, should be 1) */ 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); /* receive4BitPerPixel (ignored, should be 1) */
out_uint16_le(s, 1); /* receive8BitPerPixel (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_session_width); /* desktopWidth */
out_uint16_le(s, g_height); /* desktopHeight */ out_uint16_le(s, g_session_height); /* desktopHeight */
out_uint16_le(s, 0); /* pad2Octets */ out_uint16_le(s, 0); /* pad2Octets */
out_uint16_le(s, 1); /* desktopResizeFlag */ out_uint16_le(s, 1); /* desktopResizeFlag */
out_uint16_le(s, 1); /* bitmapCompressionFlag (must be 1) */ out_uint16_le(s, 1); /* bitmapCompressionFlag (must be 1) */
@ -1051,17 +1061,17 @@ rdp_process_general_caps(STREAM s)
static void static void
rdp_process_bitmap_caps(STREAM s) rdp_process_bitmap_caps(STREAM s)
{ {
uint16 width, height, depth; uint16 depth;
in_uint16_le(s, depth); in_uint16_le(s, depth);
in_uint8s(s, 6); in_uint8s(s, 6);
in_uint16_le(s, width); in_uint16_le(s, g_session_width);
in_uint16_le(s, height); in_uint16_le(s, g_session_height);
logger(Protocol, Debug, logger(Protocol, Debug,
"rdp_process_bitmap_caps(), setting desktop size and depth to: %dx%dx%d", width, "rdp_process_bitmap_caps(), setting desktop size and depth to: %dx%dx%d",
height, depth); g_session_width, g_session_height, depth);
/* /*
* The server may limit depth and change the size of the desktop (for * 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);
g_server_depth = depth; g_server_depth = depth;
} }
if (g_width != width || g_height != height)
{ /* resize viewport window to new session size, this is an
logger(Protocol, Debug, no-op if there is no change in size between session size
"rdp_process_bitmap_caps(), remote desktop changed from %dx%d to %dx%d.\n", reported from server and the actual window size */
g_width, g_height, width, height); ui_resize_window(g_session_width, g_session_height);
g_width = width;
g_height = height;
ui_resize_window();
}
} }
/* Process server capabilities */ /* 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"); "process_redirect_pdu(), unhandled LB_TARGET_CERTIFICATE");
} }
return True; return g_redirect;
} }
/* Process incoming packets */ /* Process incoming packets */
void void
rdp_main_loop(RD_BOOL * deactivated, uint32 * ext_disc_reason) 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 */ /* 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; RD_BOOL cont = True;
STREAM s; STREAM s;
while (cont) while (g_exit_mainloop == False && cont)
{ {
s = rdp_recv(&type); s = rdp_recv(&type);
if (s == NULL) if (s == NULL)
@ -1829,12 +1838,15 @@ rdp_loop(RD_BOOL * deactivated, uint32 * ext_disc_reason)
logger(Protocol, Debug, logger(Protocol, Debug,
"rdp_loop(), RDP_PDU_DEACTIVATE packet received"); "rdp_loop(), RDP_PDU_DEACTIVATE packet received");
*deactivated = True; *deactivated = True;
g_wait_for_deactivate_ts = 0;
break; break;
case RDP_PDU_REDIRECT: case RDP_PDU_REDIRECT:
return process_redirect_pdu(s, False);
break;
case RDP_PDU_ENHANCED_REDIRECT: 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; break;
case RDP_PDU_DATA: case RDP_PDU_DATA:
/* If we got a data PDU, we don't need to keep the password in memory /* 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 DISPLAYCONTROL_MONITOR_PRIMARY 0x1
#define RDPEDISP_CHANNEL_NAME "Microsoft::Windows::RDS::DisplayControl" #define RDPEDISP_CHANNEL_NAME "Microsoft::Windows::RDS::DisplayControl"
extern int g_dpi;
static void rdpedisp_send(STREAM s); static void rdpedisp_send(STREAM s);
static void rdpedisp_init_packet(STREAM s, uint32 type, uint32 length); 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, width); /* width */
out_uint32_le(&s, height); /* height */ 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, physwidth); /* physicalwidth */
out_uint32_le(&s, physheight); /* physicalheight */ out_uint32_le(&s, physheight); /* physicalheight */
out_uint32_le(&s, ORIENTATION_LANDSCAPE); /* Orientation */ out_uint32_le(&s, ORIENTATION_LANDSCAPE); /* Orientation */
@ -116,7 +120,7 @@ rdpedisp_send(STREAM s)
dvc_send(RDPEDISP_CHANNEL_NAME, s); dvc_send(RDPEDISP_CHANNEL_NAME, s);
} }
static RD_BOOL RD_BOOL
rdpedisp_is_available() rdpedisp_is_available()
{ {
return dvc_channels_is_available(RDPEDISP_CHANNEL_NAME); 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) if (rdpedisp_is_available() == False)
return; return;
/* monitor width MUST be even number */
utils_apply_session_size_limitations(&width, &height);
rdpedisp_send_monitor_layout_pdu(width, height); rdpedisp_send_monitor_layout_pdu(width, height);
} }

View File

@ -23,8 +23,8 @@
#include "ssl.h" #include "ssl.h"
extern char g_hostname[16]; extern char g_hostname[16];
extern int g_width; extern uint32 g_initial_width;
extern int g_height; extern uint32 g_initial_height;
extern int g_dpi; extern int g_dpi;
extern unsigned int g_keylayout; extern unsigned int g_keylayout;
extern int g_keyboard_type; 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, CS_CORE); /* type */
out_uint16_le(s, 216 + (g_dpi > 0 ? 18 : 0)); /* length */ out_uint16_le(s, 216 + (g_dpi > 0 ? 18 : 0)); /* length */
out_uint32_le(s, rdpversion); /* version */ out_uint32_le(s, rdpversion); /* version */
out_uint16_le(s, g_width); /* desktopWidth */ out_uint16_le(s, g_initial_width); /* desktopWidth */
out_uint16_le(s, g_height); /* desktopHeight */ out_uint16_le(s, g_initial_height); /* desktopHeight */
out_uint16_le(s, RNS_UD_COLOR_8BPP); /* colorDepth */ out_uint16_le(s, RNS_UD_COLOR_8BPP); /* colorDepth */
out_uint16_le(s, RNS_UD_SAS_DEL); /* SASSequence */ out_uint16_le(s, RNS_UD_SAS_DEL); /* SASSequence */
out_uint32_le(s, g_keylayout); /* keyboardLayout */ 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) if (g_dpi > 0)
{ {
/* Extended client info describing monitor geometry */ /* 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); &desktopscale, &devicescale);
out_uint32_le(s, physwidth); /* physicalwidth */ out_uint32_le(s, physwidth); /* physicalwidth */
out_uint32_le(s, physheight); /* physicalheight */ 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_in;
static struct stream g_out[STREAM_COUNT]; static struct stream g_out[STREAM_COUNT];
int g_tcp_port_rdp = TCP_PORT_RDP; 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_network_error;
extern RD_BOOL g_reconnect_loop; extern RD_BOOL g_reconnect_loop;
@ -220,12 +221,12 @@ tcp_recv(STREAM s, uint32 length)
{ {
if ((!g_ssl || SSL_pending(g_ssl) <= 0) && g_run_ui) if ((!g_ssl || SSL_pending(g_ssl) <= 0) && g_run_ui)
{ {
if (!ui_select(g_sock)) ui_select(g_sock);
{
/* User quit */ /* break out of recv, if request of exiting
g_user_quit = True; main loop has been done */
if (g_exit_mainloop == True)
return NULL; return NULL;
}
} }
if (g_ssl) if (g_ssl)

33
utils.c
View File

@ -28,9 +28,6 @@
#include "utils.h" #include "utils.h"
extern char g_codepage[16]; extern char g_codepage[16];
extern int g_dpi;
extern int g_width;
extern int g_height;
static RD_BOOL g_iconv_works = True; 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 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) uint32 *desktopscale, uint32 *devicescale)
{ {
*physwidth = *physheight = *desktopscale = *devicescale = 0; *physwidth = *physheight = *desktopscale = *devicescale = 0;
if (g_dpi > 0) if (dpi > 0)
{ {
*physwidth = g_width * 254 / (g_dpi * 10); *physwidth = width * 254 / (dpi * 10);
*physheight = g_height * 254 / (g_dpi * 10); *physheight = height * 254 / (dpi * 10);
/* the spec calls this out as being valid for range /* the spec calls this out as being valid for range
100-500 but I doubt the upper range is accurate */ 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 /* the only allowed values for device scale factor are
100, 140, and 180. */ 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 * 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_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_safe(const char *path, int mask);
int utils_mkdir_p(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); uint32 *desktopscale, uint32 *devicescale);
void utils_apply_session_size_limitations(uint32 *width, uint32 *height);
typedef enum log_level_t typedef enum log_level_t
{ {

550
xwin.c
View File

@ -42,11 +42,14 @@
#define HOST_NAME_MAX MAXHOSTNAMELEN #define HOST_NAME_MAX MAXHOSTNAMELEN
#endif #endif
extern RD_BOOL g_user_quit;
extern RD_BOOL g_exit_mainloop;
extern int g_sizeopt; extern int g_sizeopt;
extern int g_width; extern uint32 g_initial_width;
extern int g_height; extern uint32 g_initial_height;
extern uint32 g_windowed_width; extern uint16 g_session_width;
extern uint32 g_windowed_height; extern uint16 g_session_height;
extern int g_xpos; extern int g_xpos;
extern int g_ypos; extern int g_ypos;
extern int g_pos; extern int g_pos;
@ -62,12 +65,19 @@ extern char g_seamless_spawn_cmd[];
extern int g_server_depth; extern int g_server_depth;
extern int g_win_button_size; 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; Display *g_display;
Time g_last_gesturetime; Time g_last_gesturetime;
static int g_x_socket; static int g_x_socket;
static Screen *g_screen; static Screen *g_screen;
Window g_wnd; 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 */ /* SeamlessRDP support */
typedef struct _seamless_group typedef struct _seamless_group
{ {
@ -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 void
ui_init_connection(void) ui_init_connection(void)
@ -1963,8 +1973,8 @@ ui_init_connection(void)
*/ */
if (g_fullscreen) if (g_fullscreen)
{ {
g_width = WidthOfScreen(g_screen); g_initial_width = WidthOfScreen(g_screen);
g_height = HeightOfScreen(g_screen); g_initial_height = HeightOfScreen(g_screen);
g_using_full_workarea = True; g_using_full_workarea = True;
} }
else if (g_sizeopt < 0) else if (g_sizeopt < 0)
@ -1973,14 +1983,14 @@ ui_init_connection(void)
if (-g_sizeopt >= 100) if (-g_sizeopt >= 100)
g_using_full_workarea = True; g_using_full_workarea = True;
if (g_width > 0) if (g_initial_width > 0)
g_width = g_sizeopt; g_initial_width = g_sizeopt;
if (g_height > 0) if (g_initial_height > 0)
g_height = g_sizeopt; g_initial_height = g_sizeopt;
g_height = HeightOfScreen(g_screen) * (-g_height) / 100; g_initial_height = HeightOfScreen(g_screen) * (-g_initial_height) / 100;
g_width = WidthOfScreen(g_screen) * (-g_width) / 100; g_initial_width = WidthOfScreen(g_screen) * (-g_initial_width) / 100;
} }
else if (g_sizeopt == 1) else if (g_sizeopt == 1)
{ {
@ -1988,21 +1998,18 @@ ui_init_connection(void)
uint32 x, y, cx, cy; uint32 x, y, cx, cy;
if (get_current_workarea(&x, &y, &cx, &cy) == 0) if (get_current_workarea(&x, &y, &cx, &cy) == 0)
{ {
g_width = cx; g_initial_width = cx;
g_height = cy; g_initial_height = cy;
g_using_full_workarea = True; g_using_full_workarea = True;
} }
else else
{ {
logger(GUI, Warning, logger(GUI, Warning,
"Failed to get workarea: probably your window manager does not support extended hints\n"); "Failed to get workarea: probably your window manager does not support extended hints\n");
g_width = WidthOfScreen(g_screen); g_initial_width = WidthOfScreen(g_screen);
g_height = HeightOfScreen(g_screen); g_initial_height = HeightOfScreen(g_screen);
} }
} }
/* make sure width is a multiple of 4 */
g_width = (g_width + 3) & ~3;
} }
@ -2051,8 +2058,38 @@ get_input_mask(long *input_mask)
*input_mask |= LeaveWindowMask; *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 RD_BOOL
ui_create_window(void) ui_create_window(uint32 width, uint32 height)
{ {
uint8 null_pointer_mask[1] = { 0x80 }; uint8 null_pointer_mask[1] = { 0x80 };
uint8 null_pointer_data[24] = { 0x00 }; uint8 null_pointer_data[24] = { 0x00 };
@ -2063,29 +2100,22 @@ ui_create_window(void)
long input_mask, ic_input_mask; long input_mask, ic_input_mask;
XEvent xevent; XEvent xevent;
if (g_fullscreen) /* reset stored window sizes */
{ g_window_width = 0;
g_width = WidthOfScreen(g_screen); g_window_height = 0;
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); logger(GUI, Debug, "ui_create_window() width = %d, height = %d", width, height);
/* Handle -x-y portion of geometry string */ /* Handle -x-y portion of geometry string */
if (g_xpos < 0 || (g_xpos == 0 && (g_pos & 2))) 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))) 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); get_window_attribs(&attribs);
g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, g_width, g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, width,
g_height, 0, g_depth, InputOutput, g_visual, height, 0, g_depth, InputOutput, g_visual,
CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap | CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap |
CWBorderPixel, &attribs); CWBorderPixel, &attribs);
ewmh_set_wm_pid(g_wnd, getpid()); ewmh_set_wm_pid(g_wnd, getpid());
@ -2102,11 +2132,11 @@ ui_create_window(void)
if ((g_ownbackstore) && (g_backstore == 0)) 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 */ /* clear to prevent rubbish being exposed at startup */
XSetForeground(g_display, g_gc, BlackPixelOfScreen(g_screen)); 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); XStoreName(g_display, g_wnd, g_title);
@ -2126,11 +2156,7 @@ ui_create_window(void)
sizehints = XAllocSizeHints(); sizehints = XAllocSizeHints();
if (sizehints) if (sizehints)
{ {
sizehints->flags = PMinSize | PMaxSize; get_sizehints(sizehints, width, height);
if (g_pos)
sizehints->flags |= PPosition;
sizehints->min_width = sizehints->max_width = g_width;
sizehints->min_height = sizehints->max_height = g_height;
XSetWMNormalHints(g_display, g_wnd, sizehints); XSetWMNormalHints(g_display, g_wnd, sizehints);
XFree(sizehints); XFree(sizehints);
} }
@ -2191,43 +2217,52 @@ ui_create_window(void)
seamless_restack_test(); seamless_restack_test();
} }
rdpedisp_set_session_size(g_width, g_height);
return True; return True;
} }
void void
ui_resize_window() ui_resize_window(uint32 width, uint32 height)
{ {
XWindowAttributes attr;
XSizeHints *sizehints; XSizeHints *sizehints;
Pixmap bs; 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(); sizehints = XAllocSizeHints();
if (sizehints) if (sizehints)
{ {
sizehints->flags = PMinSize | PMaxSize; get_sizehints(sizehints, width, height);
sizehints->min_width = sizehints->max_width = g_width;
sizehints->min_height = sizehints->max_height = g_height;
XSetWMNormalHints(g_display, g_wnd, sizehints); XSetWMNormalHints(g_display, g_wnd, sizehints);
XFree(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 */ /* create new backstore pixmap */
if (g_backstore != 0) 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)); XSetForeground(g_display, g_gc, BlackPixelOfScreen(g_screen));
XFillRectangle(g_display, bs, g_gc, 0, 0, g_width, g_height); XFillRectangle(g_display, bs, g_gc, 0, 0, width, height);
XCopyArea(g_display, g_backstore, bs, g_gc, 0, 0, g_width, g_height, 0, 0); XCopyArea(g_display, g_backstore, bs, g_gc, 0, 0, width, height, 0, 0);
XFreePixmap(g_display, g_backstore); XFreePixmap(g_display, g_backstore);
g_backstore = bs; g_backstore = bs;
} }
ui_set_clip(0, 0, width, height);
} }
RD_BOOL RD_BOOL
@ -2255,28 +2290,73 @@ ui_destroy_window(void)
void void
xwin_toggle_fullscreen(void) xwin_toggle_fullscreen(void)
{ {
uint32 width, height;
XWindowAttributes attr;
Pixmap contents = 0; Pixmap contents = 0;
static uint32 windowed_height = 0;
static uint32 windowed_width = 0;
/* When running rdesktop in seamless mode, toggling of fullscreen is not allowed */ /* When running rdesktop in seamless mode, toggling of fullscreen is not allowed */
if (g_seamless_rdp) if (g_seamless_rdp)
return; return;
if (!g_ownbackstore) /* 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))
{ {
/* need to save contents of window */ /* only stored if we toggle from windowed -> fullscreen or when
contents = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth); * going from fullscreen -> windowed when started in fullscreen mode.
XCopyArea(g_display, g_wnd, contents, g_gc, 0, 0, g_width, g_height, 0, 0); */
windowed_width = attr.width;
windowed_height = attr.height;
}
if (!g_ownbackstore)
{
/* 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; g_fullscreen = !g_fullscreen;
ui_create_window();
/* recreate new rdesktop window using new window size and fullscreen flag */
ui_destroy_window();
if (g_fullscreen)
{
width = WidthOfScreen(g_screen);
height = HeightOfScreen(g_screen);
}
else
{
width = windowed_width;
height = windowed_height;
}
ui_create_window(width, height);
/* 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); XDefineCursor(g_display, g_wnd, g_current_cursor);
if (!g_ownbackstore) 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); XFreePixmap(g_display, contents);
} }
} }
@ -2284,7 +2364,11 @@ xwin_toggle_fullscreen(void)
static void static void
handle_button_event(XEvent xevent, RD_BOOL down) handle_button_event(XEvent xevent, RD_BOOL down)
{ {
XWindowAttributes attr;
uint16 button, input_type, flags = 0; uint16 button, input_type, flags = 0;
XGetWindowAttributes(g_display, g_wnd, &attr);
g_last_gesturetime = xevent.xbutton.time; g_last_gesturetime = xevent.xbutton.time;
/* Reverse the pointer button mapping, e.g. in the case of /* Reverse the pointer button mapping, e.g. in the case of
"left-handed mouse mode"; the RDP session expects to "left-handed mouse mode"; the RDP session expects to
@ -2307,12 +2391,12 @@ handle_button_event(XEvent xevent, RD_BOOL down)
if (xevent.xbutton.y < g_win_button_size) if (xevent.xbutton.y < g_win_button_size)
{ {
/* Check from right to left: */ /* 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 */ /* 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 /* The maximize/restore button. Do not send to
server. It might be a good idea to change the server. It might be a good idea to change the
@ -2321,7 +2405,7 @@ handle_button_event(XEvent xevent, RD_BOOL down)
if (xevent.type == ButtonPress) if (xevent.type == ButtonPress)
return; 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. */ /* The minimize button. Iconify window. */
if (xevent.type == ButtonRelease) if (xevent.type == ButtonRelease)
@ -2376,7 +2460,6 @@ handle_button_event(XEvent xevent, RD_BOOL down)
} }
} }
/* Process events in Xlib queue /* Process events in Xlib queue
Returns 0 after user quit, 1 otherwise */ Returns 0 after user quit, 1 otherwise */
static int static int
@ -2389,6 +2472,7 @@ xwin_process_events(void)
Status status; Status status;
int events = 0; int events = 0;
seamless_window *sw; seamless_window *sw;
static RD_BOOL is_g_wnd_mapped = False;
while ((XPending(g_display) > 0) && events++ < 20) while ((XPending(g_display) > 0) && events++ < 20)
{ {
@ -2682,28 +2766,100 @@ xwin_process_events(void)
break; break;
case MapNotify: 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) if (!g_seamless_active)
{
rdp_send_suppress_output_pdu(ALLOW_DISPLAY_UPDATES); rdp_send_suppress_output_pdu(ALLOW_DISPLAY_UPDATES);
}
break; break;
case UnmapNotify: case UnmapNotify:
if (xevent.xconfigure.window == g_wnd)
{
is_g_wnd_mapped = False;
}
if (!g_seamless_active) if (!g_seamless_active)
{
rdp_send_suppress_output_pdu(SUPPRESS_DISPLAY_UPDATES); rdp_send_suppress_output_pdu(SUPPRESS_DISPLAY_UPDATES);
}
break; break;
case ConfigureNotify: case ConfigureNotify:
#ifdef HAVE_XRANDR #ifdef HAVE_XRANDR
if ((g_sizeopt || g_fullscreen) /* Resize on root window size change */
&& xevent.xconfigure.window == DefaultRootWindow(g_display)) if (xevent.xconfigure.window == DefaultRootWindow(g_display))
{ {
if (xevent.xconfigure.width != WidthOfScreen(g_screen) /* only for fullscreen or x%-of-screen-sized windows */
|| xevent.xconfigure.height != HeightOfScreen(g_screen)) if (g_sizeopt || g_fullscreen)
{ {
XRRUpdateConfiguration(&xevent); if (xevent.xconfigure.width != WidthOfScreen(g_screen)
XSync(g_display, False); || xevent.xconfigure.height != HeightOfScreen(g_screen))
g_pending_resize = True; {
logger(GUI, Debug,
"xwin_process_events(), ConfigureNotify: Root window changed to %dx%d",
xevent.xconfigure.width,
xevent.xconfigure.height);
XRRUpdateConfiguration(&xevent);
XSync(g_display, False);
gettimeofday(&g_resize_timer, NULL);
g_pending_resize = True;
}
} }
} } else
#endif #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) if (!g_seamless_active)
break; break;
@ -2732,78 +2888,195 @@ xwin_process_events(void)
return 1; return 1;
} }
/* Returns 0 after user quit or pending resize, 1 otherwise */ static inline uint32
int time_difference_in_ms(struct timeval then, struct timeval now)
ui_select(int rdp_socket)
{ {
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; fd_set rfds, wfds;
struct timeval tv; struct timeval tv;
RD_BOOL s_timeout = False; RD_BOOL s_timeout = False;
while (True) n = (rdp_socket > g_x_socket) ? rdp_socket : g_x_socket;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_SET(rdp_socket, &rfds);
FD_SET(g_x_socket, &rfds);
/* default timeout */
tv.tv_sec = ms / 1000;
tv.tv_usec = (ms - (tv.tv_sec * 1000)) * 1000;
#ifdef WITH_RDPSND
rdpsnd_add_fds(&n, &rfds, &wfds, &tv);
#endif
/* add redirection handles */
rdpdr_add_fds(&n, &rfds, &wfds, &tv, &s_timeout);
seamless_select_timeout(&tv);
/* add ctrl slaves handles */
ctrl_add_fds(&n, &rfds);
n++;
ret = select(n, &rfds, &wfds, NULL, &tv);
if (ret <= 0)
{ {
n = (rdp_socket > g_x_socket) ? rdp_socket : g_x_socket; if (ret == -1)
/* 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);
FD_SET(rdp_socket, &rfds);
FD_SET(g_x_socket, &rfds);
/* default timeout */
tv.tv_sec = 60;
tv.tv_usec = 0;
#ifdef WITH_RDPSND
rdpsnd_add_fds(&n, &rfds, &wfds, &tv);
#endif
/* add redirection handles */
rdpdr_add_fds(&n, &rfds, &wfds, &tv, &s_timeout);
seamless_select_timeout(&tv);
/* add ctrl slaves handles */
ctrl_add_fds(&n, &rfds);
n++;
switch (select(n, &rfds, &wfds, NULL, &tv))
{ {
case -1: logger(GUI, Error, "process_fds(), select failed: %s",
logger(GUI, Error, "ui_select(), select failed: %s", strerror(errno));
strerror(errno));
case 0:
#ifdef WITH_RDPSND
rdpsnd_check_fds(&rfds, &wfds);
#endif
/* Abort serial read calls */
if (s_timeout)
rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) True);
continue;
} }
#ifdef WITH_RDPSND #ifdef WITH_RDPSND
rdpsnd_check_fds(&rfds, &wfds); rdpsnd_check_fds(&rfds, &wfds);
#endif #endif
rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) False); /* Abort serial read calls */
if (s_timeout)
rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) True);
return False;
}
ctrl_check_fds(&rfds, &wfds); #ifdef WITH_RDPSND
rdpsnd_check_fds(&rfds, &wfds);
#endif
if (FD_ISSET(rdp_socket, &rfds)) rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) False);
return 1;
ctrl_check_fds(&rfds, &wfds);
if (FD_ISSET(rdp_socket, &rfds))
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 +3548,9 @@ ui_set_clip(int x, int y, int cx, int cy)
void void
ui_reset_clip(void) ui_reset_clip(void)
{ {
g_clip_rectangle.x = 0; XWindowAttributes attr;
g_clip_rectangle.y = 0; XGetWindowAttributes(g_display, g_wnd, &attr);
g_clip_rectangle.width = g_width; ui_set_clip(0, 0, attr.width, attr.height);
g_clip_rectangle.height = g_height;
XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded);
} }
void void
@ -3756,9 +4027,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, int boxx, int boxy, int boxcx, int boxcy, BRUSH * brush,
uint32 bgcolour, uint32 fgcolour, uint8 * text, uint8 length) uint32 bgcolour, uint32 fgcolour, uint8 * text, uint8 length)
{ {
XWindowAttributes attr;
UNUSED(opcode); UNUSED(opcode);
UNUSED(brush); UNUSED(brush);
XGetWindowAttributes(g_display, g_wnd, &attr);
/* TODO: use brush appropriately */ /* TODO: use brush appropriately */
FONTGLYPH *glyph; FONTGLYPH *glyph;
@ -3770,8 +4044,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 /* Sometimes, the boxcx value is something really large, like
32691. This makes XCopyArea fail with Xvnc. The code below 32691. This makes XCopyArea fail with Xvnc. The code below
is a quick fix. */ is a quick fix. */
if (boxx + boxcx > g_width) if (boxx + boxcx > attr.width)
boxcx = g_width - boxx; boxcx = attr.width - boxx;
if (boxcx > 1) if (boxcx > 1)
{ {