GUACAMOLE-1302: Merge support for forcing lossless compression in VNC and RDP connections.

This commit is contained in:
James Muehlner 2021-03-03 19:31:25 -08:00 committed by GitHub
commit e2a136f41e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 148 additions and 1 deletions

View File

@ -99,6 +99,13 @@ typedef struct guac_common_display {
*/ */
guac_common_display_layer* buffers; guac_common_display_layer* buffers;
/**
* Non-zero if all graphical updates for this display should use lossless
* compression, 0 otherwise. By default, newly-created displays will use
* lossy compression when heuristics determine it is appropriate.
*/
int lossless;
/** /**
* Mutex which is locked internally when access to the display must be * Mutex which is locked internally when access to the display must be
* synchronized. All public functions of guac_common_display should be * synchronized. All public functions of guac_common_display should be
@ -228,5 +235,27 @@ void guac_common_display_free_layer(guac_common_display* display,
void guac_common_display_free_buffer(guac_common_display* display, void guac_common_display_free_buffer(guac_common_display* display,
guac_common_display_layer* display_buffer); guac_common_display_layer* display_buffer);
/**
* Sets the overall lossless compression policy of the given display to the
* given value, affecting all current and future layers/buffers maintained by
* the display. By default, newly-created displays will use lossy compression
* for graphical updates when heuristics determine that doing so is
* appropriate. Specifying a non-zero value here will force all graphical
* updates to always use lossless compression, whereas specifying zero will
* restore the default policy.
*
* Note that this can also be adjusted on a per-layer / per-buffer basis with
* guac_common_surface_set_lossless().
*
* @param display
* The display to modify.
*
* @param lossless
* Non-zero if all graphical updates for this display should use lossless
* compression, 0 otherwise.
*/
void guac_common_display_set_lossless(guac_common_display* display,
int lossless);
#endif #endif

View File

@ -126,6 +126,13 @@ typedef struct guac_common_surface {
*/ */
int touches; int touches;
/**
* Non-zero if all graphical updates for this surface should use lossless
* compression, 0 otherwise. By default, newly-created surfaces will use
* lossy compression when heuristics determine it is appropriate.
*/
int lossless;
/** /**
* The X coordinate of the upper-left corner of this layer, in pixels, * The X coordinate of the upper-left corner of this layer, in pixels,
* relative to its parent layer. This is only applicable to visible * relative to its parent layer. This is only applicable to visible
@ -510,5 +517,23 @@ void guac_common_surface_dup(guac_common_surface* surface, guac_user* user,
void guac_common_surface_set_multitouch(guac_common_surface* surface, void guac_common_surface_set_multitouch(guac_common_surface* surface,
int touches); int touches);
/**
* Sets the lossless compression policy of the given surface to the given
* value. By default, newly-created surfaces will use lossy compression for
* graphical updates when heuristics determine that doing so is appropriate.
* Specifying a non-zero value here will force all graphical updates to always
* use lossless compression, whereas specifying zero will restore the default
* policy.
*
* @param surface
* The surface to modify.
*
* @param lossless
* Non-zero if all graphical updates for this surface should use lossless
* compression, 0 otherwise.
*/
void guac_common_surface_set_lossless(guac_common_surface* surface,
int lossless);
#endif #endif

View File

@ -182,6 +182,30 @@ void guac_common_display_dup(guac_common_display* display, guac_user* user,
} }
void guac_common_display_set_lossless(guac_common_display* display,
int lossless) {
pthread_mutex_lock(&display->_lock);
/* Update lossless setting to be applied to all newly-allocated
* layers/buffers */
display->lossless = lossless;
/* Update losslessness of all allocated layers/buffers */
guac_common_display_layer* current = display->layers;
while (current != NULL) {
guac_common_surface_set_lossless(current->surface, lossless);
current = current->next;
}
/* Update losslessness of default display layer (not included within layers
* list) */
guac_common_surface_set_lossless(display->default_surface, lossless);
pthread_mutex_unlock(&display->_lock);
}
void guac_common_display_flush(guac_common_display* display) { void guac_common_display_flush(guac_common_display* display) {
pthread_mutex_lock(&display->_lock); pthread_mutex_lock(&display->_lock);
@ -287,6 +311,9 @@ guac_common_display_layer* guac_common_display_alloc_layer(
guac_common_surface* surface = guac_common_surface_alloc(display->client, guac_common_surface* surface = guac_common_surface_alloc(display->client,
display->client->socket, layer, width, height); display->client->socket, layer, width, height);
/* Apply current display losslessness */
guac_common_surface_set_lossless(surface, display->lossless);
/* Add layer and surface to list */ /* Add layer and surface to list */
guac_common_display_layer* display_layer = guac_common_display_layer* display_layer =
guac_common_display_add_layer(&display->layers, layer, surface); guac_common_display_add_layer(&display->layers, layer, surface);
@ -308,6 +335,9 @@ guac_common_display_layer* guac_common_display_alloc_buffer(
guac_common_surface* surface = guac_common_surface_alloc(display->client, guac_common_surface* surface = guac_common_surface_alloc(display->client,
display->client->socket, buffer, width, height); display->client->socket, buffer, width, height);
/* Apply current display losslessness */
guac_common_surface_set_lossless(surface, display->lossless);
/* Add buffer and surface to list */ /* Add buffer and surface to list */
guac_common_display_layer* display_layer = guac_common_display_layer* display_layer =
guac_common_display_add_layer(&display->buffers, buffer, surface); guac_common_display_add_layer(&display->buffers, buffer, surface);

View File

@ -116,6 +116,15 @@ void guac_common_surface_set_multitouch(guac_common_surface* surface,
} }
void guac_common_surface_set_lossless(guac_common_surface* surface,
int lossless) {
pthread_mutex_lock(&surface->_lock);
surface->lossless = lossless;
pthread_mutex_unlock(&surface->_lock);
}
void guac_common_surface_move(guac_common_surface* surface, int x, int y) { void guac_common_surface_move(guac_common_surface* surface, int x, int y) {
pthread_mutex_lock(&surface->_lock); pthread_mutex_lock(&surface->_lock);
@ -534,6 +543,10 @@ static int __guac_common_surface_png_optimality(guac_common_surface* surface,
static int __guac_common_surface_should_use_jpeg(guac_common_surface* surface, static int __guac_common_surface_should_use_jpeg(guac_common_surface* surface,
const guac_common_rect* rect) { const guac_common_rect* rect) {
/* Do not use JPEG if lossless quality is required */
if (surface->lossless)
return 0;
/* Calculate the average framerate for the given rect */ /* Calculate the average framerate for the given rect */
int framerate = __guac_common_surface_calculate_framerate(surface, rect); int framerate = __guac_common_surface_calculate_framerate(surface, rect);
@ -1806,7 +1819,8 @@ static void __guac_common_surface_flush_to_webp(guac_common_surface* surface,
/* Send WebP for rect */ /* Send WebP for rect */
guac_client_stream_webp(surface->client, socket, GUAC_COMP_OVER, layer, guac_client_stream_webp(surface->client, socket, GUAC_COMP_OVER, layer,
surface->dirty_rect.x, surface->dirty_rect.y, rect, surface->dirty_rect.x, surface->dirty_rect.y, rect,
guac_common_surface_suggest_quality(surface->client), 0); guac_common_surface_suggest_quality(surface->client),
surface->lossless ? 1 : 0);
cairo_surface_destroy(rect); cairo_surface_destroy(rect);
surface->realized = 1; surface->realized = 1;

View File

@ -435,6 +435,10 @@ static int guac_rdp_handle_connection(guac_client* client) {
rdp_client->settings->width, rdp_client->settings->width,
rdp_client->settings->height); rdp_client->settings->height);
/* Use lossless compression only if requested (otherwise, use default
* heuristics) */
guac_common_display_set_lossless(rdp_client->display, settings->lossless);
rdp_client->current_surface = rdp_client->display->default_surface; rdp_client->current_surface = rdp_client->display->default_surface;
rdp_client->available_svc = guac_common_list_alloc(); rdp_client->available_svc = guac_common_list_alloc();

View File

@ -128,6 +128,8 @@ const char* GUAC_RDP_CLIENT_ARGS[] = {
"wol-broadcast-addr", "wol-broadcast-addr",
"wol-udp-port", "wol-udp-port",
"wol-wait-time", "wol-wait-time",
"force-lossless",
NULL NULL
}; };
@ -639,6 +641,12 @@ enum RDP_ARGS_IDX {
*/ */
IDX_WOL_WAIT_TIME, IDX_WOL_WAIT_TIME,
/**
* "true" if all graphical updates for this connection should use lossless
* compresion only, "false" or blank otherwise.
*/
IDX_FORCE_LOSSLESS,
RDP_ARGS_COUNT RDP_ARGS_COUNT
}; };
@ -779,6 +787,11 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
settings->height, settings->height,
settings->resolution); settings->resolution);
/* Lossless compression */
settings->lossless =
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_FORCE_LOSSLESS, 0);
/* Domain */ /* Domain */
settings->domain = settings->domain =
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv, guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,

View File

@ -192,6 +192,12 @@ typedef struct guac_rdp_settings {
*/ */
int resolution; int resolution;
/**
* Whether all graphical updates for this connection should use lossless
* compression only.
*/
int lossless;
/** /**
* Whether audio is enabled. * Whether audio is enabled.
*/ */

View File

@ -91,6 +91,8 @@ const char* GUAC_VNC_CLIENT_ARGS[] = {
"wol-broadcast-addr", "wol-broadcast-addr",
"wol-udp-port", "wol-udp-port",
"wol-wait-time", "wol-wait-time",
"force-lossless",
NULL NULL
}; };
@ -373,6 +375,12 @@ enum VNC_ARGS_IDX {
*/ */
IDX_WOL_WAIT_TIME, IDX_WOL_WAIT_TIME,
/**
* "true" if all graphical updates for this connection should use lossless
* compresion only, "false" or blank otherwise.
*/
IDX_FORCE_LOSSLESS,
VNC_ARGS_COUNT VNC_ARGS_COUNT
}; };
@ -432,6 +440,11 @@ guac_vnc_settings* guac_vnc_parse_args(guac_user* user,
guac_user_parse_args_int(user, GUAC_VNC_CLIENT_ARGS, argv, guac_user_parse_args_int(user, GUAC_VNC_CLIENT_ARGS, argv,
IDX_COLOR_DEPTH, 0); IDX_COLOR_DEPTH, 0);
/* Lossless compression */
settings->lossless =
guac_user_parse_args_boolean(user, GUAC_VNC_CLIENT_ARGS, argv,
IDX_FORCE_LOSSLESS, false);
#ifdef ENABLE_VNC_REPEATER #ifdef ENABLE_VNC_REPEATER
/* Set repeater parameters if specified */ /* Set repeater parameters if specified */
settings->dest_host = settings->dest_host =

View File

@ -77,6 +77,12 @@ typedef struct guac_vnc_settings {
*/ */
bool read_only; bool read_only;
/**
* Whether all graphical updates for this connection should use lossless
* compression only.
*/
bool lossless;
#ifdef ENABLE_VNC_REPEATER #ifdef ENABLE_VNC_REPEATER
/** /**
* The VNC host to connect to, if using a repeater. * The VNC host to connect to, if using a repeater.

View File

@ -435,6 +435,10 @@ void* guac_vnc_client_thread(void* data) {
vnc_client->display = guac_common_display_alloc(client, vnc_client->display = guac_common_display_alloc(client,
rfb_client->width, rfb_client->height); rfb_client->width, rfb_client->height);
/* Use lossless compression only if requested (otherwise, use default
* heuristics) */
guac_common_display_set_lossless(vnc_client->display, settings->lossless);
/* If not read-only, set an appropriate cursor */ /* If not read-only, set an appropriate cursor */
if (settings->read_only == 0) { if (settings->read_only == 0) {
if (settings->remote_cursor) if (settings->remote_cursor)

View File

@ -221,6 +221,9 @@ guac_terminal_display* guac_terminal_display_alloc(guac_client* client,
display->display_surface = guac_common_surface_alloc(client, display->display_surface = guac_common_surface_alloc(client,
client->socket, display->display_layer, 0, 0); client->socket, display->display_layer, 0, 0);
/* Never use lossy compression for terminal contents */
guac_common_surface_set_lossless(display->display_surface, 1);
/* Select layer is a child of the display layer */ /* Select layer is a child of the display layer */
guac_protocol_send_move(client->socket, display->select_layer, guac_protocol_send_move(client->socket, display->select_layer,
display->display_layer, 0, 0, 0); display->display_layer, 0, 0, 0);