diff --git a/src/protocols/rdp/client.c b/src/protocols/rdp/client.c index 940635f5..4efb6956 100644 --- a/src/protocols/rdp/client.c +++ b/src/protocols/rdp/client.c @@ -24,6 +24,7 @@ #include "client.h" #include "rdp.h" +#include "rdp_disp.h" #include "rdp_keymap.h" #include "user.h" @@ -33,10 +34,6 @@ #include #endif -#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT -#include "rdp_disp.h" -#endif - #include #include #include @@ -66,10 +63,11 @@ int guac_client_init(guac_client* client, int argc, char** argv) { guac_rdp_client* rdp_client = calloc(1, sizeof(guac_rdp_client)); client->data = rdp_client; - /* Init clipboard and shared mouse */ + /* Init clipboard */ rdp_client->clipboard = guac_common_clipboard_alloc(GUAC_RDP_CLIPBOARD_MAX_LENGTH); - rdp_client->requested_clipboard_format = CB_FORMAT_TEXT; - rdp_client->available_svc = guac_common_list_alloc(); + + /* Init display update module */ + rdp_client->disp = guac_rdp_disp_alloc(); /* Recursive attribute for locks */ pthread_mutexattr_init(&(rdp_client->attributes)); @@ -98,54 +96,15 @@ int guac_rdp_client_free_handler(guac_client* client) { /* Wait for client thread */ pthread_join(rdp_client->client_thread, NULL); - freerdp* rdp_inst = rdp_client->rdp_inst; - if (rdp_inst != NULL) { - rdpChannels* channels = rdp_inst->context->channels; - - /* Clean up RDP client */ - freerdp_channels_close(channels, rdp_inst); - freerdp_channels_free(channels); - freerdp_disconnect(rdp_inst); - freerdp_clrconv_free(((rdp_freerdp_context*) rdp_inst->context)->clrconv); - cache_free(rdp_inst->context->cache); - freerdp_free(rdp_inst); - } - - /* Clean up filesystem, if allocated */ - if (rdp_client->filesystem != NULL) - guac_rdp_fs_free(rdp_client->filesystem); - -#ifdef ENABLE_COMMON_SSH - /* Free SFTP filesystem, if loaded */ - if (rdp_client->sftp_filesystem) - guac_common_ssh_destroy_sftp_filesystem(rdp_client->sftp_filesystem); - - /* Free SFTP session */ - if (rdp_client->sftp_session) - guac_common_ssh_destroy_session(rdp_client->sftp_session); - - /* Free SFTP user */ - if (rdp_client->sftp_user) - guac_common_ssh_destroy_user(rdp_client->sftp_user); - - guac_common_ssh_uninit(); -#endif - -#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT - /* Free display update module */ - guac_rdp_disp_free(rdp_client->disp); -#endif - - /* Free SVC list */ - guac_common_list_free(rdp_client->available_svc); - /* Free parsed settings */ if (rdp_client->settings != NULL) guac_rdp_settings_free(rdp_client->settings); + /* Free display update module */ + guac_rdp_disp_free(rdp_client->disp); + /* Free client data */ guac_common_clipboard_free(rdp_client->clipboard); - guac_common_display_free(rdp_client->display); free(rdp_client); return 0; diff --git a/src/protocols/rdp/input.c b/src/protocols/rdp/input.c index e87440a3..8a8cbaf6 100644 --- a/src/protocols/rdp/input.c +++ b/src/protocols/rdp/input.c @@ -25,27 +25,19 @@ #include "client.h" #include "input.h" #include "rdp.h" +#include "rdp_disp.h" #include "rdp_keymap.h" #include #include #include -#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT -#include "rdp_disp.h" -#endif - #include #include int guac_rdp_send_keysym(guac_client* client, int keysym, int pressed) { guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; - freerdp* rdp_inst = rdp_client->rdp_inst; - - /* Skip if not yet connected */ - if (rdp_inst == NULL) - return 0; /* If keysym can be in lookup table */ if (GUAC_RDP_KEYSYM_STORABLE(keysym)) { @@ -75,6 +67,13 @@ int guac_rdp_send_keysym(guac_client* client, int keysym, int pressed) { else pressed_flags = KBD_FLAGS_RELEASE; + /* Skip if not yet connected */ + freerdp* rdp_inst = rdp_client->rdp_inst; + if (rdp_inst == NULL) { + pthread_mutex_unlock(&(rdp_client->rdp_lock)); + return 0; + } + /* Send actual key */ rdp_inst->input->KeyboardEvent(rdp_inst->input, keysym_desc->flags | pressed_flags, keysym_desc->scancode); @@ -118,6 +117,13 @@ int guac_rdp_send_keysym(guac_client* client, int keysym, int pressed) { pthread_mutex_lock(&(rdp_client->rdp_lock)); + /* Skip if not yet connected */ + freerdp* rdp_inst = rdp_client->rdp_inst; + if (rdp_inst == NULL) { + pthread_mutex_unlock(&(rdp_client->rdp_lock)); + return 0; + } + /* Send Unicode event */ rdp_inst->input->UnicodeKeyboardEvent( rdp_inst->input, @@ -156,16 +162,18 @@ int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) { guac_client* client = user->client; guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; - freerdp* rdp_inst = rdp_client->rdp_inst; + + pthread_mutex_lock(&(rdp_client->rdp_lock)); /* Store current mouse location */ guac_common_cursor_move(rdp_client->display->cursor, user, x, y); /* Skip if not yet connected */ - if (rdp_inst == NULL) + freerdp* rdp_inst = rdp_client->rdp_inst; + if (rdp_inst == NULL) { + pthread_mutex_unlock(&(rdp_client->rdp_lock)); return 0; - - pthread_mutex_lock(&(rdp_client->rdp_lock)); + } /* If button mask unchanged, just send move event */ if (mask == rdp_client->mouse_button_mask) @@ -251,28 +259,19 @@ int guac_rdp_user_key_handler(guac_user* user, int keysym, int pressed) { int guac_rdp_user_size_handler(guac_user* user, int width, int height) { -#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT guac_client* client = user->client; guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; - + guac_rdp_settings* settings = rdp_client->settings; freerdp* rdp_inst = rdp_client->rdp_inst; - /* Skip if not yet connected */ - if (rdp_inst == NULL) - return 0; - /* Convert client pixels to remote pixels */ - width = width * rdp_client->settings->resolution - / user->info.optimal_resolution; - - height = height * rdp_client->settings->resolution - / user->info.optimal_resolution; + width = width * settings->resolution / user->info.optimal_resolution; + height = height * settings->resolution / user->info.optimal_resolution; /* Send display update */ pthread_mutex_lock(&(rdp_client->rdp_lock)); - guac_rdp_disp_set_size(rdp_client->disp, rdp_inst->context, width, height); + guac_rdp_disp_set_size(rdp_client->disp, settings, rdp_inst, width, height); pthread_mutex_unlock(&(rdp_client->rdp_lock)); -#endif return 0; diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c index 9dea54b5..a19e55ba 100644 --- a/src/protocols/rdp/rdp.c +++ b/src/protocols/rdp/rdp.c @@ -29,6 +29,7 @@ #include "rdp.h" #include "rdp_bitmap.h" #include "rdp_cliprdr.h" +#include "rdp_disp.h" #include "rdp_gdi.h" #include "rdp_glyph.h" #include "rdp_keymap.h" @@ -43,10 +44,6 @@ #include #endif -#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT -#include "rdp_disp.h" -#endif - #include #include #include @@ -183,8 +180,8 @@ static void guac_rdp_channel_connected(rdpContext* context, guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; /* Init module with current display size */ - guac_rdp_disp_set_size(rdp_client->disp, context, - guac_rdp_get_width(context->instance), + guac_rdp_disp_set_size(rdp_client->disp, rdp_client->settings, + context->instance, guac_rdp_get_width(context->instance), guac_rdp_get_height(context->instance)); /* Store connected channel */ @@ -223,17 +220,14 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) { (pChannelConnectedEventHandler) guac_rdp_channel_connected); #endif -#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT /* Load virtual channel management plugin */ if (freerdp_channels_load_plugin(channels, instance->settings, "drdynvc", instance->settings)) guac_client_log(client, GUAC_LOG_WARNING, "Failed to load drdynvc plugin."); - /* Init display update plugin */ - rdp_client->disp = guac_rdp_disp_alloc(); + /* Init display update plugin (if available) */ guac_rdp_disp_load_plugin(instance->context); -#endif /* Load clipboard plugin */ if (freerdp_channels_load_plugin(channels, instance->settings, @@ -691,9 +685,8 @@ static int rdp_guac_client_wait_for_messages(guac_client* client, } -void* guac_rdp_client_thread(void* data) { +static int guac_rdp_handle_connection(guac_client* client) { - guac_client* client = (guac_client*) data; guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; guac_rdp_settings* settings = rdp_client->settings; @@ -715,6 +708,9 @@ void* guac_rdp_client_thread(void* data) { rdp_client->current_surface = rdp_client->display->default_surface; + rdp_client->requested_clipboard_format = CB_FORMAT_TEXT; + rdp_client->available_svc = guac_common_list_alloc(); + #ifdef HAVE_FREERDP_CHANNELS_GLOBAL_INIT freerdp_channels_global_init(); #endif @@ -750,7 +746,7 @@ void* guac_rdp_client_thread(void* data) { /* Abort if username is missing */ if (settings->sftp_username == NULL) - return NULL; + return 1; guac_client_log(client, GUAC_LOG_DEBUG, "Connecting via SSH for SFTP filesystem access."); @@ -769,7 +765,7 @@ void* guac_rdp_client_thread(void* data) { settings->sftp_private_key, settings->sftp_passphrase)) { guac_common_ssh_destroy_user(rdp_client->sftp_user); - return NULL; + return 1; } } @@ -794,7 +790,7 @@ void* guac_rdp_client_thread(void* data) { if (rdp_client->sftp_session == NULL) { /* Already aborted within guac_common_ssh_create_session() */ guac_common_ssh_destroy_user(rdp_client->sftp_user); - return NULL; + return 1; } /* Load and expose filesystem */ @@ -811,7 +807,7 @@ void* guac_rdp_client_thread(void* data) { if (rdp_client->sftp_filesystem == NULL) { guac_common_ssh_destroy_session(rdp_client->sftp_session); guac_common_ssh_destroy_user(rdp_client->sftp_user); - return NULL; + return 1; } guac_client_log(client, GUAC_LOG_DEBUG, @@ -833,7 +829,7 @@ void* guac_rdp_client_thread(void* data) { if (!freerdp_connect(rdp_inst)) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Error connecting to RDP server"); - return NULL; + return 1; } /* Connection complete */ @@ -842,15 +838,17 @@ void* guac_rdp_client_thread(void* data) { guac_timestamp last_frame_end = guac_timestamp_current(); - /* Handle messages from RDP server while client is running */ - while (client->state == GUAC_CLIENT_RUNNING) { + /* Signal that reconnect has been completed */ + guac_rdp_disp_reconnect_complete(rdp_client->disp); + + /* Handle messages from RDP server while client is running */ + while (client->state == GUAC_CLIENT_RUNNING + && !guac_rdp_disp_reconnect_needed(rdp_client->disp)) { -#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT /* Update remote display size */ pthread_mutex_lock(&(rdp_client->rdp_lock)); - guac_rdp_disp_update_size(rdp_client->disp, rdp_inst->context); + guac_rdp_disp_update_size(rdp_client->disp, settings, rdp_inst); pthread_mutex_unlock(&(rdp_client->rdp_lock)); -#endif /* Wait for data and construct a reasonable frame */ int wait_result = rdp_guac_client_wait_for_messages(client, @@ -873,7 +871,7 @@ void* guac_rdp_client_thread(void* data) { guac_client_log(client, GUAC_LOG_DEBUG, "Error handling RDP file descriptors"); pthread_mutex_unlock(&(rdp_client->rdp_lock)); - return NULL; + return 1; } /* Check channel fds */ @@ -881,7 +879,7 @@ void* guac_rdp_client_thread(void* data) { guac_client_log(client, GUAC_LOG_DEBUG, "Error handling RDP channel file descriptors"); pthread_mutex_unlock(&(rdp_client->rdp_lock)); - return NULL; + return 1; } /* Check for channel events */ @@ -910,7 +908,7 @@ void* guac_rdp_client_thread(void* data) { guac_client_log(client, GUAC_LOG_INFO, "RDP server closed connection"); pthread_mutex_unlock(&(rdp_client->rdp_lock)); - return NULL; + return 1; } pthread_mutex_unlock(&(rdp_client->rdp_lock)); @@ -955,6 +953,62 @@ void* guac_rdp_client_thread(void* data) { } guac_client_log(client, GUAC_LOG_INFO, "Internal RDP client disconnected"); + + pthread_mutex_lock(&(rdp_client->rdp_lock)); + + /* Clean up RDP client */ + freerdp_channels_close(channels, rdp_inst); + freerdp_channels_free(channels); + freerdp_disconnect(rdp_inst); + freerdp_clrconv_free(((rdp_freerdp_context*) rdp_inst->context)->clrconv); + cache_free(rdp_inst->context->cache); + freerdp_free(rdp_inst); + + /* Clean up filesystem, if allocated */ + if (rdp_client->filesystem != NULL) + guac_rdp_fs_free(rdp_client->filesystem); + +#ifdef ENABLE_COMMON_SSH + /* Free SFTP filesystem, if loaded */ + if (rdp_client->sftp_filesystem) + guac_common_ssh_destroy_sftp_filesystem(rdp_client->sftp_filesystem); + + /* Free SFTP session */ + if (rdp_client->sftp_session) + guac_common_ssh_destroy_session(rdp_client->sftp_session); + + /* Free SFTP user */ + if (rdp_client->sftp_user) + guac_common_ssh_destroy_user(rdp_client->sftp_user); + + guac_common_ssh_uninit(); +#endif + + /* Free SVC list */ + guac_common_list_free(rdp_client->available_svc); + + /* Free display */ + guac_common_display_free(rdp_client->display); + + /* Mark FreeRDP instance as freed */ + rdp_client->rdp_inst = NULL; + + pthread_mutex_unlock(&(rdp_client->rdp_lock)); + return 0; + +} + +void* guac_rdp_client_thread(void* data) { + + guac_client* client = (guac_client*) data; + + while (client->state == GUAC_CLIENT_RUNNING) { + + if (guac_rdp_handle_connection(client)) + return NULL; + + } + return NULL; } diff --git a/src/protocols/rdp/rdp.h b/src/protocols/rdp/rdp.h index b648216c..fac0435d 100644 --- a/src/protocols/rdp/rdp.h +++ b/src/protocols/rdp/rdp.h @@ -29,6 +29,7 @@ #include "guac_display.h" #include "guac_surface.h" #include "guac_list.h" +#include "rdp_disp.h" #include "rdp_fs.h" #include "rdp_keymap.h" #include "rdp_settings.h" @@ -44,10 +45,6 @@ #include "guac_ssh_user.h" #endif -#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT -#include "rdp_disp.h" -#endif - #include #include @@ -148,12 +145,10 @@ typedef struct guac_rdp_client { guac_common_ssh_sftp_filesystem* sftp_filesystem; #endif -#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT /** * Display size update module. */ guac_rdp_disp* disp; -#endif /** * List of all available static virtual channels. @@ -161,7 +156,9 @@ typedef struct guac_rdp_client { guac_common_list* available_svc; /** - * Lock which is locked and unlocked for each RDP message. + * Lock which is locked and unlocked for each RDP message, and for each + * part of the RDP client instance which may be dynamically freed and + * reallocated during reconnection. */ pthread_mutex_t rdp_lock; diff --git a/src/protocols/rdp/rdp_disp.c b/src/protocols/rdp/rdp_disp.c index a94d5949..ea65289e 100644 --- a/src/protocols/rdp/rdp_disp.c +++ b/src/protocols/rdp/rdp_disp.c @@ -24,6 +24,7 @@ #include "client.h" #include "rdp.h" #include "rdp_disp.h" +#include "rdp_settings.h" #include #include @@ -41,6 +42,7 @@ guac_rdp_disp* guac_rdp_disp_alloc() { disp->last_request = 0; disp->requested_width = 0; disp->requested_height = 0; + disp->reconnect_needed = 0; return disp; @@ -52,6 +54,7 @@ void guac_rdp_disp_free(guac_rdp_disp* disp) { void guac_rdp_disp_load_plugin(rdpContext* context) { +#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT #ifdef HAVE_RDPSETTINGS_SUPPORTDISPLAYCONTROL context->settings->SupportDisplayControl = TRUE; #endif @@ -62,6 +65,7 @@ void guac_rdp_disp_load_plugin(rdpContext* context) { args->argv = malloc(sizeof(char**) * 1); args->argv[0] = strdup("disp"); freerdp_dynamic_channel_collection_add(context->settings, args); +#endif } @@ -111,8 +115,8 @@ static void guac_rdp_disp_fit(int* a, int* b) { } -void guac_rdp_disp_set_size(guac_rdp_disp* disp, rdpContext* context, - int width, int height) { +void guac_rdp_disp_set_size(guac_rdp_disp* disp, guac_rdp_settings* settings, + freerdp* rdp_inst, int width, int height) { /* Fit width within bounds, adjusting height to maintain aspect ratio */ guac_rdp_disp_fit(&width, &height); @@ -129,21 +133,47 @@ void guac_rdp_disp_set_size(guac_rdp_disp* disp, rdpContext* context, disp->requested_height = height; /* Send display update notification if possible */ - guac_rdp_disp_update_size(disp, context); + guac_rdp_disp_update_size(disp, settings, rdp_inst); } -void guac_rdp_disp_update_size(guac_rdp_disp* disp, rdpContext* context) { - - guac_client* client = ((rdp_freerdp_context*) context)->client; - - /* Send display update notification if display channel is connected */ - if (disp->disp == NULL) - return; +void guac_rdp_disp_update_size(guac_rdp_disp* disp, + guac_rdp_settings* settings, freerdp* rdp_inst) { int width = disp->requested_width; int height = disp->requested_height; + /* Do not update size if no requests have been received */ + if (width == 0 || height == 0) + return; + + guac_timestamp now = guac_timestamp_current(); + + /* Limit display update frequency */ + if (disp->last_request != 0 + && now - disp->last_request <= GUAC_RDP_DISP_UPDATE_INTERVAL) + return; + + /* Do NOT send requests unless the size will change */ + if (rdp_inst != NULL + && width == guac_rdp_get_width(rdp_inst) + && height == guac_rdp_get_height(rdp_inst)) + return; + + disp->last_request = now; + + if (1) { + /* Update settings with new dimensions */ + settings->width = width; + settings->height = height; + + /* Signal reconnect */ + disp->reconnect_needed = 1; + disp->disp = NULL; + return; + } + +#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT DISPLAY_CONTROL_MONITOR_LAYOUT monitors[1] = {{ .Flags = 0x1, /* DISPLAYCONTROL_MONITOR_PRIMARY */ .Left = 0, @@ -157,24 +187,18 @@ void guac_rdp_disp_update_size(guac_rdp_disp* disp, rdpContext* context) { .DeviceScaleFactor = 0 }}; - guac_timestamp now = guac_timestamp_current(); - - /* Limit display update frequency */ - if (disp->last_request != 0 - && now - disp->last_request <= GUAC_RDP_DISP_UPDATE_INTERVAL) - return; - - /* Do NOT send requests unless the size will change */ - if (width == guac_rdp_get_width(context->instance) - && height == guac_rdp_get_height(context->instance)) - return; - - guac_client_log(client, GUAC_LOG_DEBUG, - "Resizing remote display to %ix%i", - width, height); - - disp->last_request = now; - disp->disp->SendMonitorLayout(disp->disp, 1, monitors); + /* Send display update notification if display channel is connected */ + if (disp->disp != NULL) + disp->disp->SendMonitorLayout(disp->disp, 1, monitors); +#endif } +int guac_rdp_disp_reconnect_needed(guac_rdp_disp* disp) { + return disp->reconnect_needed; +} + +void guac_rdp_disp_reconnect_complete(guac_rdp_disp* disp) { + disp->reconnect_needed = 0; +} + diff --git a/src/protocols/rdp/rdp_disp.h b/src/protocols/rdp/rdp_disp.h index 375f476f..eb330479 100644 --- a/src/protocols/rdp/rdp_disp.h +++ b/src/protocols/rdp/rdp_disp.h @@ -23,6 +23,8 @@ #ifndef GUAC_RDP_DISP_H #define GUAC_RDP_DISP_H +#include "rdp_settings.h" + #include #include @@ -68,6 +70,12 @@ typedef struct guac_rdp_disp { */ int requested_height; + /** + * Whether the size has changed and the RDP connection must be closed and + * reestablished. + */ + int reconnect_needed; + } guac_rdp_disp; /** @@ -113,30 +121,72 @@ void guac_rdp_disp_connect(guac_rdp_disp* guac_disp, DispClientContext* disp); * be automatically altered to comply with the restrictions imposed by the * display update channel. * - * @param disp The display update module which should maintain the requested - * size, sending the corresponding display update request when - * appropriate. - * @param context The rdpContext associated with the active RDP session. - * @param width The desired display width, in pixels. Due to the restrictions - * of the RDP display update channel, this will be contrained to - * the range of 200 through 8192 inclusive, and rounded down to - * the nearest even number. - * @param height The desired display height, in pixels. Due to the restrictions - * of the RDP display update channel, this will be contrained to - * the range of 200 through 8192 inclusive. + * @param disp + * The display update module which should maintain the requested size, + * sending the corresponding display update request when appropriate. + * + * @param settings + * The RDP client settings associated with the current or pending RDP + * session. These settings will be automatically adjusted to match the new + * screen size. + * + * @param rdp_inst + * The FreeRDP instance associated with the current or pending RDP session, + * if any. If no RDP session is active, this should be NULL. + * + * @param width + * The desired display width, in pixels. Due to the restrictions of the RDP + * display update channel, this will be contrained to the range of 200 + * through 8192 inclusive, and rounded down to the nearest even number. + * + * @param height + * The desired display height, in pixels. Due to the restrictions of the + * RDP display update channel, this will be contrained to the range of 200 + * through 8192 inclusive. */ -void guac_rdp_disp_set_size(guac_rdp_disp* disp, rdpContext* context, - int width, int height); +void guac_rdp_disp_set_size(guac_rdp_disp* disp, guac_rdp_settings* settings, + freerdp* rdp_inst, int width, int height); /** * Sends an actual display update request to the RDP server based on previous * calls to guac_rdp_disp_set_size(). If an update was recently sent, the - * update may be delayed until a future call to this function. + * update may be delayed until a future call to this function. If the RDP + * session has not yet been established, the request will be delayed until the + * session exists. * - * @param disp The display update module which should track the update request. - * @param context The rdpContext associated with the active RDP session. + * @param disp + * The display update module which should track the update request. + * + * @param settings + * The RDP client settings associated with the current or pending RDP + * session. These settings will be automatically adjusted to match the new + * screen size. + * + * @param rdp_inst + * The FreeRDP instance associated with the current or pending RDP session, + * if any. If no RDP session is active, this should be NULL. */ -void guac_rdp_disp_update_size(guac_rdp_disp* disp, rdpContext* context); +void guac_rdp_disp_update_size(guac_rdp_disp* disp, + guac_rdp_settings* settings, freerdp* rdp_inst); + +/** + * Signals the given display update module that the requested reconnect has + * been performed. + * + * @param disp + * The display update module that should be signaled regarding the state + * of reconnection. + */ +void guac_rdp_disp_reconnect_complete(guac_rdp_disp* disp); + +/** + * Returns whether a full RDP reconnect is required for display update changes + * to take effect. + * + * @return + * Non-zero if a reconnect is needed, zero otherwise. + */ +int guac_rdp_disp_reconnect_needed(guac_rdp_disp* disp); #endif