ui: avoid sending framebuffer updates outside client desktop bounds

We plan framebuffer update rects based on the VNC server surface. If the
client doesn't support desktop resize, then the client bounds may differ
from the server surface bounds. VNC clients may become upset if we then
send an update message outside the bounds of the client desktop.

This takes the approach of clamping the rectangles from the worker
thread immediately before sending them. This may sometimes results in
sending a framebuffer update message with zero rectangles.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20210311182957.486939-3-berrange@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
Daniel P. Berrangé 2021-03-11 18:29:55 +00:00 committed by Gerd Hoffmann
parent adc8fce871
commit 55b400497c
2 changed files with 45 additions and 4 deletions

View file

@ -59,6 +59,11 @@ vnc_client_throttle_audio(void *state, void *ioc, size_t offset) "VNC client thr
vnc_client_unthrottle_forced(void *state, void *ioc) "VNC client unthrottle forced offset state=%p ioc=%p"
vnc_client_unthrottle_incremental(void *state, void *ioc, size_t offset) "VNC client unthrottle incremental state=%p ioc=%p offset=%zu"
vnc_client_output_limit(void *state, void *ioc, size_t offset, size_t threshold) "VNC client output limit state=%p ioc=%p offset=%zu threshold=%zu"
vnc_job_add_rect(void *state, void *job, int x, int y, int w, int h) "VNC add rect state=%p job=%p offset=%d,%d size=%dx%d"
vnc_job_discard_rect(void *state, void *job, int x, int y, int w, int h) "VNC job discard rect state=%p job=%p offset=%d,%d size=%dx%d"
vnc_job_clamp_rect(void *state, void *job, int x, int y, int w, int h) "VNC job clamp rect state=%p job=%p offset=%d,%d size=%dx%d"
vnc_job_clamped_rect(void *state, void *job, int x, int y, int w, int h) "VNC job clamp rect state=%p job=%p offset=%d,%d size=%dx%d"
vnc_job_nrects(void *state, void *job, int nrects) "VNC job state=%p job=%p nrects=%d"
vnc_auth_init(void *display, int websock, int auth, int subauth) "VNC auth init state=%p websock=%d auth=%d subauth=%d"
vnc_auth_start(void *state, int method) "VNC client auth start state=%p method=%d"
vnc_auth_pass(void *state, int method) "VNC client auth passed state=%p method=%d"

View file

@ -32,6 +32,7 @@
#include "qemu/sockets.h"
#include "qemu/main-loop.h"
#include "block/aio.h"
#include "trace.h"
/*
* Locking:
@ -94,6 +95,8 @@ int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h)
{
VncRectEntry *entry = g_new0(VncRectEntry, 1);
trace_vnc_job_add_rect(job->vs, job, x, y, w, h);
entry->rect.x = x;
entry->rect.y = y;
entry->rect.w = w;
@ -190,6 +193,8 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local)
local->zlib = orig->zlib;
local->hextile = orig->hextile;
local->zrle = orig->zrle;
local->client_width = orig->client_width;
local->client_height = orig->client_height;
}
static void vnc_async_encoding_end(VncState *orig, VncState *local)
@ -202,6 +207,34 @@ static void vnc_async_encoding_end(VncState *orig, VncState *local)
orig->lossy_rect = local->lossy_rect;
}
static bool vnc_worker_clamp_rect(VncState *vs, VncJob *job, VncRect *rect)
{
trace_vnc_job_clamp_rect(vs, job, rect->x, rect->y, rect->w, rect->h);
if (rect->x >= vs->client_width) {
goto discard;
}
rect->w = MIN(vs->client_width - rect->x, rect->w);
if (rect->w == 0) {
goto discard;
}
if (rect->y >= vs->client_height) {
goto discard;
}
rect->h = MIN(vs->client_height - rect->y, rect->h);
if (rect->h == 0) {
goto discard;
}
trace_vnc_job_clamped_rect(vs, job, rect->x, rect->y, rect->w, rect->h);
return true;
discard:
trace_vnc_job_discard_rect(vs, job, rect->x, rect->y, rect->w, rect->h);
return false;
}
static int vnc_worker_thread_loop(VncJobQueue *queue)
{
VncJob *job;
@ -260,14 +293,17 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
goto disconnected;
}
n = vnc_send_framebuffer_update(&vs, entry->rect.x, entry->rect.y,
entry->rect.w, entry->rect.h);
if (vnc_worker_clamp_rect(&vs, job, &entry->rect)) {
n = vnc_send_framebuffer_update(&vs, entry->rect.x, entry->rect.y,
entry->rect.w, entry->rect.h);
if (n >= 0) {
n_rectangles += n;
if (n >= 0) {
n_rectangles += n;
}
}
g_free(entry);
}
trace_vnc_job_nrects(&vs, job, n_rectangles);
vnc_unlock_display(job->vs->vd);
/* Put n_rectangles at the beginning of the message */