From 3b6293efad8d17b95d275893427fd290f0421298 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 3 Jun 2024 17:22:47 -0700 Subject: [PATCH] GUACAMOLE-377: Migrate VNC support to guac_display API. --- src/protocols/vnc/client.c | 5 +- src/protocols/vnc/cursor.c | 7 +-- src/protocols/vnc/display.c | 110 +++++++++++++++++------------------- src/protocols/vnc/display.h | 11 ++++ src/protocols/vnc/input.c | 5 +- src/protocols/vnc/log.c | 1 - src/protocols/vnc/log.h | 1 - src/protocols/vnc/user.c | 8 +-- src/protocols/vnc/vnc.c | 85 +++++++--------------------- src/protocols/vnc/vnc.h | 5 +- 10 files changed, 95 insertions(+), 143 deletions(-) diff --git a/src/protocols/vnc/client.c b/src/protocols/vnc/client.c index 43ead098f..4ec8c7b93 100644 --- a/src/protocols/vnc/client.c +++ b/src/protocols/vnc/client.c @@ -34,6 +34,7 @@ #endif #include +#include #include #include @@ -89,7 +90,7 @@ static int guac_vnc_join_pending_handler(guac_client* client) { /* Synchronize with current display */ if (vnc_client->display != NULL) { - guac_common_display_dup(vnc_client->display, client, broadcast_socket); + guac_display_dup(vnc_client->display, broadcast_socket); guac_socket_flush(broadcast_socket); } @@ -192,7 +193,7 @@ int guac_vnc_client_free_handler(guac_client* client) { /* Free display */ if (vnc_client->display != NULL) - guac_common_display_free(vnc_client->display); + guac_display_free(vnc_client->display); #ifdef ENABLE_PULSE /* If audio enabled, stop streaming */ diff --git a/src/protocols/vnc/cursor.c b/src/protocols/vnc/cursor.c index 449fea382..b177776c5 100644 --- a/src/protocols/vnc/cursor.c +++ b/src/protocols/vnc/cursor.c @@ -20,9 +20,6 @@ #include "config.h" #include "client.h" -#include "common/cursor.h" -#include "common/display.h" -#include "common/surface.h" #include "vnc.h" #include @@ -46,7 +43,8 @@ #include void guac_vnc_cursor(rfbClient* client, int x, int y, int w, int h, int bpp) { - + /* TODO */ +#if 0 guac_client* gc = rfbClientGetClientData(client, GUAC_VNC_CLIENT_KEY); guac_vnc_client* vnc_client = (guac_vnc_client*) gc->data; @@ -128,5 +126,6 @@ void guac_vnc_cursor(rfbClient* client, int x, int y, int w, int h, int bpp) { free(client->rcMask); client->rcMask = NULL; } +#endif } diff --git a/src/protocols/vnc/display.c b/src/protocols/vnc/display.c index 8848e8933..9c383f4cf 100644 --- a/src/protocols/vnc/display.c +++ b/src/protocols/vnc/display.c @@ -21,7 +21,6 @@ #include "client.h" #include "common/iconv.h" -#include "common/surface.h" #include "vnc.h" #include @@ -49,98 +48,93 @@ void guac_vnc_update(rfbClient* client, int x, int y, int w, int h) { guac_client* gc = rfbClientGetClientData(client, GUAC_VNC_CLIENT_KEY); guac_vnc_client* vnc_client = (guac_vnc_client*) gc->data; - int dx, dy; - - /* Cairo image buffer */ - int stride; - unsigned char* buffer; - unsigned char* buffer_row_current; - cairo_surface_t* surface; - - /* VNC framebuffer */ - unsigned int bpp; - unsigned int fb_stride; - unsigned char* fb_row_current; - /* Ignore extra update if already handled by copyrect */ if (vnc_client->copy_rect_used) { vnc_client->copy_rect_used = 0; return; } - /* Init Cairo buffer */ - stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w); - buffer = guac_mem_alloc(h, stride); - buffer_row_current = buffer; + /* Begin drawing operation directly to default layer */ + guac_display_layer* default_layer = guac_display_default_layer(vnc_client->display); + guac_display_layer_raw_context* context = guac_display_layer_open_raw(default_layer); - bpp = client->format.bitsPerPixel/8; - fb_stride = bpp * client->width; - fb_row_current = client->frameBuffer + (y * fb_stride) + (x * bpp); + /* Convert operation coordinates to guac_rect for easier manipulation */ + guac_rect op_bounds; + guac_rect_init(&op_bounds, x, y, w, h); - /* Copy image data from VNC client to PNG */ - for (dy = y; dybounds); - unsigned int* buffer_current; - unsigned char* fb_current; - - /* Get current buffer row, advance to next */ - buffer_current = (unsigned int*) buffer_row_current; - buffer_row_current += stride; + /* VNC framebuffer */ + unsigned int vnc_bpp = client->format.bitsPerPixel / 8; + size_t vnc_stride = guac_mem_ckd_mul_or_die(vnc_bpp, client->width); + const unsigned char* vnc_current_row = GUAC_RECT_CONST_BUFFER(op_bounds, client->frameBuffer, vnc_stride, vnc_bpp); + + unsigned char* layer_current_row = context->buffer + (op_bounds.top * context->stride) + (op_bounds.left * 4); + for (int dy = op_bounds.top; dy < op_bounds.bottom; dy++) { - /* Get current framebuffer row, advance to next */ - fb_current = fb_row_current; - fb_row_current += fb_stride; + /* Get current Guacamole buffer row, advance to next */ + uint32_t* layer_current_pixel = (uint32_t*) layer_current_row; + layer_current_row += context->stride; - for (dx = x; dx> client->format.redShift) * 0x100 / (client->format.redMax + 1); - green = (v >> client->format.greenShift) * 0x100 / (client->format.greenMax+ 1); - blue = (v >> client->format.blueShift) * 0x100 / (client->format.blueMax + 1); + /* Translate value to 32-bit RGB */ + uint8_t red = (v >> client->format.redShift) * 0x100 / (client->format.redMax + 1); + uint8_t green = (v >> client->format.greenShift) * 0x100 / (client->format.greenMax + 1); + uint8_t blue = (v >> client->format.blueShift) * 0x100 / (client->format.blueMax + 1); /* Output RGB */ if (vnc_client->settings->swap_red_blue) - *(buffer_current++) = (blue << 16) | (green << 8) | red; + *(layer_current_pixel++) = 0xFF000000 | (blue << 16) | (green << 8) | red; else - *(buffer_current++) = (red << 16) | (green << 8) | blue; + *(layer_current_pixel++) = 0xFF000000 | (red << 16) | (green << 8) | blue; - fb_current += bpp; + /* Advance to next pixel in VNC framebuffer */ + vnc_current_pixel += vnc_bpp; } } - /* Create surface from decoded buffer */ - surface = cairo_image_surface_create_for_data(buffer, CAIRO_FORMAT_RGB24, - w, h, stride); + /* Mark modified region as dirty */ + guac_rect_extend(&context->dirty, &op_bounds); - /* Draw directly to default layer */ - guac_common_surface_draw(vnc_client->display->default_surface, - x, y, surface); + /* Draw operation is now complete */ + guac_display_layer_close_raw(default_layer, context); + +} - /* Free surface */ - cairo_surface_destroy(surface); - guac_mem_free(buffer); +void guac_vnc_update_finished(rfbClient* client) { + + guac_client* gc = rfbClientGetClientData(client, GUAC_VNC_CLIENT_KEY); + guac_vnc_client* vnc_client = (guac_vnc_client*) gc->data; + + guac_display_end_multiple_frames(vnc_client->display, 1); } void guac_vnc_copyrect(rfbClient* client, int src_x, int src_y, int w, int h, int dest_x, int dest_y) { - + /* TODO */ +#if 0 guac_client* gc = rfbClientGetClientData(client, GUAC_VNC_CLIENT_KEY); guac_vnc_client* vnc_client = (guac_vnc_client*) gc->data; @@ -150,7 +144,7 @@ void guac_vnc_copyrect(rfbClient* client, int src_x, int src_y, int w, int h, in vnc_client->display->default_surface, dest_x, dest_y); vnc_client->copy_rect_used = 1; - +#endif } void guac_vnc_set_pixel_format(rfbClient* client, int color_depth) { @@ -199,7 +193,7 @@ rfbBool guac_vnc_malloc_framebuffer(rfbClient* rfb_client) { /* Resize surface */ if (vnc_client->display != NULL) - guac_common_surface_resize(vnc_client->display->default_surface, + guac_display_layer_resize(guac_display_default_layer(vnc_client->display), rfb_client->width, rfb_client->height); /* Use original, wrapped proc */ diff --git a/src/protocols/vnc/display.h b/src/protocols/vnc/display.h index 831f012f5..1ef550bb9 100644 --- a/src/protocols/vnc/display.h +++ b/src/protocols/vnc/display.h @@ -50,6 +50,17 @@ */ void guac_vnc_update(rfbClient* client, int x, int y, int w, int h); +/** + * Callback invoked by libVNCServer when all binary image data for the current + * frame has been received from the VNC server. The image data that frame will + * have been exposed via previous calls to guac_vnc_update(). + * + * @param client + * The VNC client associated with the VNC session in which the new image + * was received. + */ +void guac_vnc_update_finished(rfbClient* client); + /** * Callback invoked by libVNCServer when it receives a CopyRect message. * CopyRect specified a rectangle of source data within the display and a diff --git a/src/protocols/vnc/input.c b/src/protocols/vnc/input.c index 584819183..166bb6ca4 100644 --- a/src/protocols/vnc/input.c +++ b/src/protocols/vnc/input.c @@ -19,10 +19,9 @@ #include "config.h" -#include "common/cursor.h" -#include "common/display.h" #include "vnc.h" +#include #include #include #include @@ -34,7 +33,7 @@ int guac_vnc_user_mouse_handler(guac_user* user, int x, int y, int mask) { rfbClient* rfb_client = vnc_client->rfb_client; /* Store current mouse location/state */ - guac_common_cursor_update(vnc_client->display->cursor, user, x, y, mask); + guac_display_notify_user_moved_mouse(vnc_client->display, user, x, y, mask); /* Report mouse position within recording */ if (vnc_client->recording != NULL) diff --git a/src/protocols/vnc/log.c b/src/protocols/vnc/log.c index ba593e81d..afb93d0e7 100644 --- a/src/protocols/vnc/log.c +++ b/src/protocols/vnc/log.c @@ -21,7 +21,6 @@ #include "client.h" #include "common/iconv.h" -#include "common/surface.h" #include #include diff --git a/src/protocols/vnc/log.h b/src/protocols/vnc/log.h index 65f3dc2d4..3f348605c 100644 --- a/src/protocols/vnc/log.h +++ b/src/protocols/vnc/log.h @@ -24,7 +24,6 @@ #include "client.h" #include "common/iconv.h" -#include "common/surface.h" #include #include diff --git a/src/protocols/vnc/user.c b/src/protocols/vnc/user.c index 42bae34aa..61616e196 100644 --- a/src/protocols/vnc/user.c +++ b/src/protocols/vnc/user.c @@ -21,7 +21,6 @@ #include "clipboard.h" #include "input.h" -#include "common/display.h" #include "common/dot_cursor.h" #include "common/pointer_cursor.h" #include "user.h" @@ -35,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -121,10 +121,8 @@ int guac_vnc_user_leave_handler(guac_user* user) { guac_vnc_client* vnc_client = (guac_vnc_client*) user->client->data; - if (vnc_client->display) { - /* Update shared cursor state */ - guac_common_cursor_remove_user(vnc_client->display->cursor, user); - } + if (vnc_client->display) + guac_display_notify_user_left(vnc_client->display, user); /* Free settings if not owner (owner settings will be freed with client) */ if (!user->owner) { diff --git a/src/protocols/vnc/vnc.c b/src/protocols/vnc/vnc.c index ec2c505d4..9d90fc39b 100644 --- a/src/protocols/vnc/vnc.c +++ b/src/protocols/vnc/vnc.c @@ -23,8 +23,6 @@ #include "client.h" #include "clipboard.h" #include "common/clipboard.h" -#include "common/cursor.h" -#include "common/display.h" #include "cursor.h" #include "display.h" #include "log.h" @@ -42,6 +40,7 @@ #endif #include +#include #include #include #include @@ -140,6 +139,7 @@ rfbClient* guac_vnc_get_client(guac_client* client) { /* Framebuffer update handler */ rfb_client->GotFrameBufferUpdate = guac_vnc_update; + rfb_client->FinishedFrameBufferUpdate = guac_vnc_update_finished; rfb_client->GotCopyRect = guac_vnc_copyrect; #ifdef ENABLE_VNC_TLS_LOCKING @@ -480,12 +480,13 @@ void* guac_vnc_client_thread(void* data) { } /* Create display */ - vnc_client->display = guac_common_display_alloc(client, - rfb_client->width, rfb_client->height); + vnc_client->display = guac_display_alloc(client); + guac_display_layer_resize(guac_display_default_layer(vnc_client->display), 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); + guac_display_layer_set_lossless(guac_display_default_layer(vnc_client->display), + settings->lossless); /* If compression and display quality have been configured, set those. */ if (settings->compress_level >= 0 && settings->compress_level <= 9) @@ -497,81 +498,33 @@ void* guac_vnc_client_thread(void* data) { /* If not read-only, set an appropriate cursor */ if (settings->read_only == 0) { if (settings->remote_cursor) - guac_common_cursor_set_dot(vnc_client->display->cursor); + guac_display_set_cursor(vnc_client->display, GUAC_DISPLAY_CURSOR_DOT); else - guac_common_cursor_set_pointer(vnc_client->display->cursor); - + guac_display_set_cursor(vnc_client->display, GUAC_DISPLAY_CURSOR_POINTER); } - guac_socket_flush(client->socket); - - guac_timestamp last_frame_end = guac_timestamp_current(); + guac_display_end_frame(vnc_client->display); /* Handle messages from VNC server while client is running */ while (client->state == GUAC_CLIENT_RUNNING) { /* Wait for start of frame */ - int wait_result = guac_vnc_wait_for_messages(rfb_client, - GUAC_VNC_FRAME_START_TIMEOUT); - if (wait_result > 0) { - - int processing_lag = guac_client_get_processing_lag(client); - guac_timestamp frame_start = guac_timestamp_current(); - - /* Read server messages until frame is built */ - do { - - guac_timestamp frame_end; - int frame_remaining; - - /* Handle any message received */ - if (!HandleRFBServerMessage(rfb_client)) { - guac_client_abort(client, - GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, - "Error handling message from VNC server."); - break; - } - - /* Calculate time remaining in frame */ - frame_end = guac_timestamp_current(); - frame_remaining = frame_start + GUAC_VNC_FRAME_DURATION - - frame_end; - - /* Calculate time that client needs to catch up */ - int time_elapsed = frame_end - last_frame_end; - int required_wait = processing_lag - time_elapsed; - - /* Increase the duration of this frame if client is lagging */ - if (required_wait > GUAC_VNC_FRAME_TIMEOUT) - wait_result = guac_vnc_wait_for_messages(rfb_client, - required_wait*1000); - - /* Wait again if frame remaining */ - else if (frame_remaining > 0) - wait_result = guac_vnc_wait_for_messages(rfb_client, - GUAC_VNC_FRAME_TIMEOUT*1000); - else - break; - - } while (wait_result > 0); - - /* Record end of frame, excluding server-side rendering time (we - * assume server-side rendering time will be consistent between any - * two subsequent frames, and that this time should thus be - * excluded from the required wait period of the next frame). */ - last_frame_end = frame_start; - + int wait_result = guac_vnc_wait_for_messages(rfb_client, GUAC_VNC_FRAME_START_TIMEOUT); + if (wait_result == 0) + continue; + + /* Handle any message received */ + if (!HandleRFBServerMessage(rfb_client)) { + guac_client_abort(client, + GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, + "Error handling message from VNC server."); + break; } /* If an error occurs, log it and fail */ if (wait_result < 0) guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Connection closed."); - /* Flush frame */ - guac_common_surface_flush(vnc_client->display->default_surface); - guac_client_end_frame(client); - guac_socket_flush(client->socket); - } /* Kill client and finish connection */ diff --git a/src/protocols/vnc/vnc.h b/src/protocols/vnc/vnc.h index bdc62e6de..a35cddec3 100644 --- a/src/protocols/vnc/vnc.h +++ b/src/protocols/vnc/vnc.h @@ -23,12 +23,11 @@ #include "config.h" #include "common/clipboard.h" -#include "common/display.h" #include "common/iconv.h" -#include "common/surface.h" #include "settings.h" #include +#include #include #include @@ -88,7 +87,7 @@ typedef struct guac_vnc_client { /** * The current display state. */ - guac_common_display* display; + guac_display* display; /** * Internal clipboard.