GUACAMOLE-1196: Correct VNC resize support to conform to the RFB standard.

This commit is contained in:
Virtually Nick 2024-12-20 11:37:10 -05:00
parent af7ed38d8e
commit 32067616c9
6 changed files with 77 additions and 72 deletions

View File

@ -653,59 +653,40 @@ then
fi
#
# libVNCserver support for the rfbSetDesktopSizeMsg message, which allows the
# VNC client to send its dimensions to the server, requesting that the server
# resize itself to match. If libvnc lacks this message, remote resize will
# be completely disabled.
# libVNCserver support for the rfbSetDesktopSizeMsg message and the screen
# data structure, both of which are required in order to properly request that
# a remote server resize its screen to match the dimensions that the client
# sends. If libvnc lacks either this message or the screen data structure
# remote resize will be completely disabled.
#
if test "x${have_libvncserver}" = "xyes"
then
have_vnc_size_msg=yes
have_vnc_resize_support=yes
AC_CHECK_TYPE([rfbSetDesktopSizeMsg],
[], [have_vnc_size_msg=no],
[], [have_vnc_resize_support=no],
[[#include <rfb/rfbproto.h>]])
if test "x${have_vnc_size_msg}" = "xno"
AC_CHECK_MEMBERS([rfbClient.screen],
[], [have_vnc_resize_support=no],
[[#include <rfb/rfbclient.h>]])
if test "x${have_vnc_resize_support}" = "xno"
then
AC_MSG_WARN([
--------------------------------------------------------
No support for rfbSetDesktopSizeMsg in libvncclient.
VNC support for remote display resize will be disabled.
The libvncclient library lacks support for either the
rfbSetDesktopSizeMsg message or for extended screen
support. Resizing of remote displays will be disabled.
--------------------------------------------------------])
else
AC_DEFINE([LIBVNC_HAS_SIZE_MSG],,
AC_DEFINE([LIBVNC_HAS_RESIZE_SUPPORT],,
[Whether VNC client will support sending desktop size messages.])
fi
fi
#
# libVNCserver support for the screen structure, which allows for tracking
# multiple screens that are part of a larger frame buffer display. If this
# feature is missing, the VNC client may still attempt to send the display
# resize messages, but will assume that there is a one-to-one relationship
# between the screen size and the frame buffer size (which is currently safe
# for Guacamole, as it lacks multi-monitor/screen support).
#
if test "x${have_libvncserver}" = "xyes"
then
have_vnc_screen=yes
AC_CHECK_MEMBERS([rfbClient.screen],
[], [have_vnc_screen=no],
[[#include <rfb/rfbclient.h>]])
if test "x${have_vnc_screen}" = "xyes"
then
AC_DEFINE([LIBVNC_CLIENT_HAS_SCREEN],,
[Whether rfbClient contains the screen data structure.])
fi
fi
#
# libVNCserver support for the requestedResize member, which enables the
# client to pause frame buffer updates during a resize operation. If support

View File

@ -146,7 +146,7 @@ void guac_vnc_copyrect(rfbClient* client, int src_x, int src_y, int w, int h, in
}
#ifdef LIBVNC_HAS_SIZE_MSG
#ifdef LIBVNC_HAS_RESIZE_SUPPORT
/**
* This function does the actual work of sending the message to the RFB/VNC
* server to request the resize, and then makes sure that the client frame
@ -173,7 +173,11 @@ static rfbBool guac_vnc_send_desktop_size(rfbClient* client, int width, int heig
/* Get the Guacamole client data */
guac_client* gc = rfbClientGetClientData(client, GUAC_VNC_CLIENT_KEY);
#ifdef LIBVNC_CLIENT_HAS_SCREEN
if (client->screen.width == 0 || client->screen.height == 0) {
guac_client_log(gc, GUAC_LOG_WARNING, "Screen data has not been initialized, yet.");
return FALSE;
}
guac_client_log(gc, GUAC_LOG_TRACE,
"Current screen size is %ix%i; setting new size %ix%i\n",
rfbClientSwap16IfLE(client->screen.width),
@ -185,19 +189,6 @@ static rfbBool guac_vnc_send_desktop_size(rfbClient* client, int width, int heig
guac_client_log(gc, GUAC_LOG_WARNING, "Screen size has not changed, not sending update.");
return FALSE;
}
#else
/* Don't send an update if the screen appears to be uninitialized. */
if (client->width == 0 || client->height == 0) {
guac_client_log(gc, GUAC_LOG_WARNING, "Framebuffer has not been initialized, cannot send resize.");
return FALSE;
}
/* Don't send an update if the requested dimensions are identical to current dimensions. */
if (client->width == rfbClientSwap16IfLE(width) && client->height == rfbClientSwap16IfLE(height)) {
guac_client_log(gc, GUAC_LOG_WARNING, "Screen size has not changed, not sending update.");
return FALSE;
}
#endif
/**
* Note: The RFB protocol requires two message types to be sent during a
@ -219,19 +210,11 @@ static rfbBool guac_vnc_send_desktop_size(rfbClient* client, int width, int heig
size_msg.height = rfbClientSwap16IfLE(height);
size_msg.numberOfScreens = 1;
#ifdef LIBVNC_CLIENT_HAS_SCREEN
/* Configure the screen update message. */
new_screen.id = GUAC_VNC_SCREEN_ID;
new_screen.x = client->screen.x;
new_screen.y = client->screen.y;
new_screen.flags = client->screen.flags;
#else
/* Assume screen starts at the origin. */
new_screen.id = GUAC_VNC_SCREEN_ID;
new_screen.x = 0;
new_screen.y = 0;
new_screen.flags = 0;
#endif // LIBVNC_CLIENT_HAS_SCREEN
new_screen.width = rfbClientSwap16IfLE(width);
new_screen.height = rfbClientSwap16IfLE(height);
@ -246,11 +229,9 @@ static rfbBool guac_vnc_send_desktop_size(rfbClient* client, int width, int heig
}
#ifdef LIBVNC_CLIENT_HAS_SCREEN
/* Update the client frame buffer with the requested size. */
client->screen.width = rfbClientSwap16IfLE(width);
client->screen.height = rfbClientSwap16IfLE(height);
#endif // LIBVNC_CLIENT_HAS_SCREEN
#ifdef LIBVNC_CLIENT_HAS_REQUESTED_RESIZE
client->requestedResize = FALSE;
@ -266,6 +247,7 @@ static rfbBool guac_vnc_send_desktop_size(rfbClient* client, int width, int heig
/* Update should be successful. */
return TRUE;
}
void* guac_vnc_display_set_owner_size(guac_user* owner, void* data) {
@ -323,7 +305,7 @@ void guac_vnc_display_set_size(rfbClient* client, int requested_width, int reque
pthread_mutex_unlock(&(vnc_client->message_lock));
}
#endif // LIBVNC_HAS_SIZE_MSG
#endif // LIBVNC_HAS_RESIZE_SUPPORT
void guac_vnc_set_pixel_format(rfbClient* client, int color_depth) {
client->format.trueColour = 1;

View File

@ -64,7 +64,7 @@ int guac_vnc_user_key_handler(guac_user* user, int keysym, int pressed) {
return 0;
}
#ifdef LIBVNC_HAS_SIZE_MSG
#ifdef LIBVNC_HAS_RESIZE_SUPPORT
int guac_vnc_user_size_handler(guac_user* user, int width, int height) {
guac_user_log(user, GUAC_LOG_TRACE, "Running user size handler.");
@ -78,4 +78,4 @@ int guac_vnc_user_size_handler(guac_user* user, int width, int height) {
return 0;
}
#endif //LIBVNC_HAS_SIZE_MSG
#endif // LIBVNC_HAS_RESIZE_SUPPORT

View File

@ -89,11 +89,14 @@ int guac_vnc_user_join_handler(guac_user* user, int argc, char** argv) {
user->file_handler = guac_vnc_sftp_file_handler;
#endif
#ifdef LIBVNC_HAS_SIZE_MSG
#ifdef LIBVNC_HAS_RESIZE_SUPPORT
/* If user is owner, set size handler. */
if (user->owner && !settings->disable_display_resize)
user->size_handler = guac_vnc_user_size_handler;
#endif // LIBVNC_HAS_SIZE_MSG
#else
guac_user_log(user, GUAC_LOG_WARNING,
"The libvncclient library does not support remote resize.");
#endif // LIBVNC_HAS_RESIZE_SUPPORT
}

View File

@ -281,8 +281,9 @@ static int guac_vnc_wait_for_messages(rfbClient* rfb_client, int msec_timeout) {
* True (non-zero) if messages were handled successfully, false (zero)
* otherwise.
*/
static rfbBool guac_vnc_handle_messages(guac_vnc_client* vnc_client) {
static rfbBool guac_vnc_handle_messages(guac_client* client) {
guac_vnc_client* vnc_client = (guac_vnc_client*) client->data;
rfbClient* rfb_client = vnc_client->rfb_client;
guac_display_layer* default_layer = guac_display_default_layer(vnc_client->display);
@ -311,6 +312,31 @@ static rfbBool guac_vnc_handle_messages(guac_vnc_client* vnc_client) {
guac_display_layer_close_raw(default_layer, context);
vnc_client->current_context = NULL;
#ifdef LIBVNC_HAS_RESIZE_SUPPORT
// If screen was not previously initialized, check for it and set it.
if (!vnc_client->rfb_screen_initialized
&& rfb_client->screen.width > 0
&& rfb_client->screen.height > 0) {
vnc_client->rfb_screen_initialized = true;
guac_client_log(client, GUAC_LOG_DEBUG, "Screen is now initialized.");
}
/*
* If the screen is now or has been initialized, check to see if the initial
* dimensions have already been sent. If not, and resize is not disabled,
* send the initial size.
*/
if (vnc_client->rfb_screen_initialized) {
guac_vnc_settings* settings = vnc_client->settings;
if (!vnc_client->rfb_initial_resize && !settings->disable_display_resize) {
guac_client_log(client, GUAC_LOG_DEBUG,
"Sending initial screen size to VNC server.");
guac_client_for_owner(client, guac_vnc_display_set_owner_size, rfb_client);
vnc_client->rfb_initial_resize = true;
}
}
#endif // LIBVNC_HAS_RESIZE_SUPPORT
/* Resize the surface if VNC screen size has changed (this call
* automatically deals with invalid dimensions and is a no-op
* if the size has not changed) */
@ -588,11 +614,11 @@ void* guac_vnc_client_thread(void* data) {
guac_display_set_cursor(vnc_client->display, GUAC_DISPLAY_CURSOR_POINTER);
}
#ifdef LIBVNC_HAS_SIZE_MSG
/* Update the display with the owner's screen size. */
if (!settings->disable_display_resize)
guac_client_for_owner(client, guac_vnc_display_set_owner_size, rfb_client);
#endif // LIBVNC_HAS_SIZE_MSG
#ifdef LIBVNC_HAS_RESIZE_SUPPORT
/* Set initial state of the screen and resize flags. */
vnc_client->rfb_screen_initialized = false;
vnc_client->rfb_initial_resize = false;
#endif // LIBVNC_HAS_RESIZE_SUPPORT
guac_display_end_frame(vnc_client->display);
@ -606,7 +632,7 @@ void* guac_vnc_client_thread(void* data) {
if (wait_result > 0) {
/* Handle any message received */
if (!guac_vnc_handle_messages(vnc_client)) {
if (!guac_vnc_handle_messages(client)) {
guac_client_abort(client,
GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
"Error handling message from VNC server.");

View File

@ -164,6 +164,19 @@ typedef struct guac_vnc_client {
*/
guac_iconv_write* clipboard_writer;
#ifdef LIBVNC_HAS_RESIZE_SUPPORT
/**
* Whether or not the server has sent the required message to initialize
* the screen data in the client.
*/
bool rfb_screen_initialized;
/**
* Whether or not the client has sent it's starting size to the server.
*/
bool rfb_initial_resize;
#endif
} guac_vnc_client;
/**