|
@@ -417,8 +417,7 @@ out_error:
|
|
|
3) resolutions > 1024
|
|
|
*/
|
|
|
|
|
|
-static int vnc_update_client(VncState *vs, int has_dirty);
|
|
|
-static int vnc_update_client_sync(VncState *vs, int has_dirty);
|
|
|
+static int vnc_update_client(VncState *vs, int has_dirty, bool sync);
|
|
|
static void vnc_disconnect_start(VncState *vs);
|
|
|
|
|
|
static void vnc_colordepth(VncState *vs);
|
|
@@ -431,29 +430,25 @@ static int vnc_refresh_server_surface(VncDisplay *vd);
|
|
|
static void vnc_dpy_update(DisplayChangeListener *dcl,
|
|
|
int x, int y, int w, int h)
|
|
|
{
|
|
|
- int i;
|
|
|
VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
|
|
|
struct VncSurface *s = &vd->guest;
|
|
|
int width = surface_width(vd->ds);
|
|
|
int height = surface_height(vd->ds);
|
|
|
|
|
|
- h += y;
|
|
|
-
|
|
|
- /* round x down to ensure the loop only spans one 16-pixel block per,
|
|
|
- iteration. otherwise, if (x % 16) != 0, the last iteration may span
|
|
|
- two 16-pixel blocks but we only mark the first as dirty
|
|
|
- */
|
|
|
- w += (x % 16);
|
|
|
- x -= (x % 16);
|
|
|
+ /* this is needed this to ensure we updated all affected
|
|
|
+ * blocks if x % VNC_DIRTY_PIXELS_PER_BIT != 0 */
|
|
|
+ w += (x % VNC_DIRTY_PIXELS_PER_BIT);
|
|
|
+ x -= (x % VNC_DIRTY_PIXELS_PER_BIT);
|
|
|
|
|
|
x = MIN(x, width);
|
|
|
y = MIN(y, height);
|
|
|
w = MIN(x + w, width) - x;
|
|
|
- h = MIN(h, height);
|
|
|
+ h = MIN(y + h, height);
|
|
|
|
|
|
- for (; y < h; y++)
|
|
|
- for (i = 0; i < w; i += 16)
|
|
|
- set_bit((x + i) / 16, s->dirty[y]);
|
|
|
+ for (; y < h; y++) {
|
|
|
+ bitmap_set(s->dirty[y], x / VNC_DIRTY_PIXELS_PER_BIT,
|
|
|
+ DIV_ROUND_UP(w, VNC_DIRTY_PIXELS_PER_BIT));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
|
|
@@ -571,6 +566,15 @@ void *vnc_server_fb_ptr(VncDisplay *vd, int x, int y)
|
|
|
ptr += x * VNC_SERVER_FB_BYTES;
|
|
|
return ptr;
|
|
|
}
|
|
|
+/* this sets only the visible pixels of a dirty bitmap */
|
|
|
+#define VNC_SET_VISIBLE_PIXELS_DIRTY(bitmap, w, h) {\
|
|
|
+ int y;\
|
|
|
+ memset(bitmap, 0x00, sizeof(bitmap));\
|
|
|
+ for (y = 0; y < h; y++) {\
|
|
|
+ bitmap_set(bitmap[y], 0,\
|
|
|
+ DIV_ROUND_UP(w, VNC_DIRTY_PIXELS_PER_BIT));\
|
|
|
+ } \
|
|
|
+ }
|
|
|
|
|
|
static void vnc_dpy_switch(DisplayChangeListener *dcl,
|
|
|
DisplaySurface *surface)
|
|
@@ -596,7 +600,9 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl,
|
|
|
qemu_pixman_image_unref(vd->guest.fb);
|
|
|
vd->guest.fb = pixman_image_ref(surface->image);
|
|
|
vd->guest.format = surface->format;
|
|
|
- memset(vd->guest.dirty, 0xFF, sizeof(vd->guest.dirty));
|
|
|
+ VNC_SET_VISIBLE_PIXELS_DIRTY(vd->guest.dirty,
|
|
|
+ surface_width(vd->ds),
|
|
|
+ surface_height(vd->ds));
|
|
|
|
|
|
QTAILQ_FOREACH(vs, &vd->clients, next) {
|
|
|
vnc_colordepth(vs);
|
|
@@ -604,7 +610,9 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl,
|
|
|
if (vs->vd->cursor) {
|
|
|
vnc_cursor_define(vs);
|
|
|
}
|
|
|
- memset(vs->dirty, 0xFF, sizeof(vs->dirty));
|
|
|
+ VNC_SET_VISIBLE_PIXELS_DIRTY(vs->dirty,
|
|
|
+ surface_width(vd->ds),
|
|
|
+ surface_height(vd->ds));
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -751,7 +759,7 @@ static void vnc_dpy_copy(DisplayChangeListener *dcl,
|
|
|
QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
|
|
|
if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
|
|
|
vs->force_update = 1;
|
|
|
- vnc_update_client_sync(vs, 1);
|
|
|
+ vnc_update_client(vs, 1, true);
|
|
|
/* vs might be free()ed here */
|
|
|
}
|
|
|
}
|
|
@@ -770,11 +778,12 @@ static void vnc_dpy_copy(DisplayChangeListener *dcl,
|
|
|
y = dst_y + h - 1;
|
|
|
inc = -1;
|
|
|
}
|
|
|
- w_lim = w - (16 - (dst_x % 16));
|
|
|
- if (w_lim < 0)
|
|
|
+ w_lim = w - (VNC_DIRTY_PIXELS_PER_BIT - (dst_x % VNC_DIRTY_PIXELS_PER_BIT));
|
|
|
+ if (w_lim < 0) {
|
|
|
w_lim = w;
|
|
|
- else
|
|
|
- w_lim = w - (w_lim % 16);
|
|
|
+ } else {
|
|
|
+ w_lim = w - (w_lim % VNC_DIRTY_PIXELS_PER_BIT);
|
|
|
+ }
|
|
|
for (i = 0; i < h; i++) {
|
|
|
for (x = 0; x <= w_lim;
|
|
|
x += s, src_row += cmp_bytes, dst_row += cmp_bytes) {
|
|
@@ -782,10 +791,11 @@ static void vnc_dpy_copy(DisplayChangeListener *dcl,
|
|
|
if ((s = w - w_lim) == 0)
|
|
|
break;
|
|
|
} else if (!x) {
|
|
|
- s = (16 - (dst_x % 16));
|
|
|
+ s = (VNC_DIRTY_PIXELS_PER_BIT -
|
|
|
+ (dst_x % VNC_DIRTY_PIXELS_PER_BIT));
|
|
|
s = MIN(s, w_lim);
|
|
|
} else {
|
|
|
- s = 16;
|
|
|
+ s = VNC_DIRTY_PIXELS_PER_BIT;
|
|
|
}
|
|
|
cmp_bytes = s * VNC_SERVER_FB_BYTES;
|
|
|
if (memcmp(src_row, dst_row, cmp_bytes) == 0)
|
|
@@ -793,7 +803,8 @@ static void vnc_dpy_copy(DisplayChangeListener *dcl,
|
|
|
memmove(dst_row, src_row, cmp_bytes);
|
|
|
QTAILQ_FOREACH(vs, &vd->clients, next) {
|
|
|
if (!vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
|
|
|
- set_bit(((x + dst_x) / 16), vs->dirty[y]);
|
|
|
+ set_bit(((x + dst_x) / VNC_DIRTY_PIXELS_PER_BIT),
|
|
|
+ vs->dirty[y]);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -862,35 +873,24 @@ static int find_and_clear_dirty_height(struct VncState *vs,
|
|
|
int h;
|
|
|
|
|
|
for (h = 1; h < (height - y); h++) {
|
|
|
- int tmp_x;
|
|
|
if (!test_bit(last_x, vs->dirty[y + h])) {
|
|
|
break;
|
|
|
}
|
|
|
- for (tmp_x = last_x; tmp_x < x; tmp_x++) {
|
|
|
- clear_bit(tmp_x, vs->dirty[y + h]);
|
|
|
- }
|
|
|
+ bitmap_clear(vs->dirty[y + h], last_x, x - last_x);
|
|
|
}
|
|
|
|
|
|
return h;
|
|
|
}
|
|
|
|
|
|
-static int vnc_update_client_sync(VncState *vs, int has_dirty)
|
|
|
-{
|
|
|
- int ret = vnc_update_client(vs, has_dirty);
|
|
|
- vnc_jobs_join(vs);
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-static int vnc_update_client(VncState *vs, int has_dirty)
|
|
|
+static int vnc_update_client(VncState *vs, int has_dirty, bool sync)
|
|
|
{
|
|
|
if (vs->need_update && vs->csock != -1) {
|
|
|
VncDisplay *vd = vs->vd;
|
|
|
VncJob *job;
|
|
|
int y;
|
|
|
- int width, height;
|
|
|
+ int height;
|
|
|
int n = 0;
|
|
|
|
|
|
-
|
|
|
if (vs->output.offset && !vs->audio_cap && !vs->force_update)
|
|
|
/* kernel send buffers are full -> drop frames to throttle */
|
|
|
return 0;
|
|
@@ -906,33 +906,27 @@ static int vnc_update_client(VncState *vs, int has_dirty)
|
|
|
*/
|
|
|
job = vnc_job_new(vs);
|
|
|
|
|
|
- width = MIN(pixman_image_get_width(vd->server), vs->client_width);
|
|
|
height = MIN(pixman_image_get_height(vd->server), vs->client_height);
|
|
|
|
|
|
- for (y = 0; y < height; y++) {
|
|
|
- int x;
|
|
|
- int last_x = -1;
|
|
|
- for (x = 0; x < width / 16; x++) {
|
|
|
- if (test_and_clear_bit(x, vs->dirty[y])) {
|
|
|
- if (last_x == -1) {
|
|
|
- last_x = x;
|
|
|
- }
|
|
|
- } else {
|
|
|
- if (last_x != -1) {
|
|
|
- int h = find_and_clear_dirty_height(vs, y, last_x, x,
|
|
|
- height);
|
|
|
-
|
|
|
- n += vnc_job_add_rect(job, last_x * 16, y,
|
|
|
- (x - last_x) * 16, h);
|
|
|
- }
|
|
|
- last_x = -1;
|
|
|
- }
|
|
|
- }
|
|
|
- if (last_x != -1) {
|
|
|
- int h = find_and_clear_dirty_height(vs, y, last_x, x, height);
|
|
|
- n += vnc_job_add_rect(job, last_x * 16, y,
|
|
|
- (x - last_x) * 16, h);
|
|
|
+ y = 0;
|
|
|
+ for (;;) {
|
|
|
+ int x, h;
|
|
|
+ unsigned long x2;
|
|
|
+ unsigned long offset = find_next_bit((unsigned long *) &vs->dirty,
|
|
|
+ height * VNC_DIRTY_BPL(vs),
|
|
|
+ y * VNC_DIRTY_BPL(vs));
|
|
|
+ if (offset == height * VNC_DIRTY_BPL(vs)) {
|
|
|
+ /* no more dirty bits */
|
|
|
+ break;
|
|
|
}
|
|
|
+ y = offset / VNC_DIRTY_BPL(vs);
|
|
|
+ x = offset % VNC_DIRTY_BPL(vs);
|
|
|
+ x2 = find_next_zero_bit((unsigned long *) &vs->dirty[y],
|
|
|
+ VNC_DIRTY_BPL(vs), x);
|
|
|
+ bitmap_clear(vs->dirty[y], x, x2 - x);
|
|
|
+ h = find_and_clear_dirty_height(vs, y, x, x2, height);
|
|
|
+ n += vnc_job_add_rect(job, x * VNC_DIRTY_PIXELS_PER_BIT, y,
|
|
|
+ (x2 - x) * VNC_DIRTY_PIXELS_PER_BIT, h);
|
|
|
}
|
|
|
|
|
|
vnc_job_push(job);
|
|
@@ -940,8 +934,11 @@ static int vnc_update_client(VncState *vs, int has_dirty)
|
|
|
return n;
|
|
|
}
|
|
|
|
|
|
- if (vs->csock == -1)
|
|
|
+ if (vs->csock == -1) {
|
|
|
vnc_disconnect_finish(vs);
|
|
|
+ } else if (sync) {
|
|
|
+ vnc_jobs_join(vs);
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -1846,7 +1843,7 @@ static void framebuffer_update_request(VncState *vs, int incremental,
|
|
|
int w, int h)
|
|
|
{
|
|
|
int i;
|
|
|
- const size_t width = surface_width(vs->vd->ds) / 16;
|
|
|
+ const size_t width = surface_width(vs->vd->ds) / VNC_DIRTY_PIXELS_PER_BIT;
|
|
|
const size_t height = surface_height(vs->vd->ds);
|
|
|
|
|
|
if (y_position > height) {
|
|
@@ -2548,7 +2545,9 @@ static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y)
|
|
|
|
|
|
vs->lossy_rect[sty][stx] = 0;
|
|
|
for (j = 0; j < VNC_STAT_RECT; ++j) {
|
|
|
- bitmap_set(vs->dirty[y + j], x / 16, VNC_STAT_RECT / 16);
|
|
|
+ bitmap_set(vs->dirty[y + j],
|
|
|
+ x / VNC_DIRTY_PIXELS_PER_BIT,
|
|
|
+ VNC_STAT_RECT / VNC_DIRTY_PIXELS_PER_BIT);
|
|
|
}
|
|
|
has_dirty++;
|
|
|
}
|
|
@@ -2652,8 +2651,8 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
|
|
|
int width = pixman_image_get_width(vd->guest.fb);
|
|
|
int height = pixman_image_get_height(vd->guest.fb);
|
|
|
int y;
|
|
|
- uint8_t *guest_row;
|
|
|
- uint8_t *server_row;
|
|
|
+ uint8_t *guest_row0 = NULL, *server_row0;
|
|
|
+ int guest_stride = 0, server_stride;
|
|
|
int cmp_bytes;
|
|
|
VncState *vs;
|
|
|
int has_dirty = 0;
|
|
@@ -2671,47 +2670,64 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
|
|
|
* Check and copy modified bits from guest to server surface.
|
|
|
* Update server dirty map.
|
|
|
*/
|
|
|
- cmp_bytes = 64;
|
|
|
+ cmp_bytes = VNC_DIRTY_PIXELS_PER_BIT * VNC_SERVER_FB_BYTES;
|
|
|
if (cmp_bytes > vnc_server_fb_stride(vd)) {
|
|
|
cmp_bytes = vnc_server_fb_stride(vd);
|
|
|
}
|
|
|
if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
|
|
|
int width = pixman_image_get_width(vd->server);
|
|
|
tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, width);
|
|
|
- }
|
|
|
- guest_row = (uint8_t *)pixman_image_get_data(vd->guest.fb);
|
|
|
- server_row = (uint8_t *)pixman_image_get_data(vd->server);
|
|
|
- for (y = 0; y < height; y++) {
|
|
|
- if (!bitmap_empty(vd->guest.dirty[y], VNC_DIRTY_BITS)) {
|
|
|
- int x;
|
|
|
- uint8_t *guest_ptr;
|
|
|
- uint8_t *server_ptr;
|
|
|
-
|
|
|
- if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
|
|
|
- qemu_pixman_linebuf_fill(tmpbuf, vd->guest.fb, width, 0, y);
|
|
|
- guest_ptr = (uint8_t *)pixman_image_get_data(tmpbuf);
|
|
|
- } else {
|
|
|
- guest_ptr = guest_row;
|
|
|
- }
|
|
|
- server_ptr = server_row;
|
|
|
+ } else {
|
|
|
+ guest_row0 = (uint8_t *)pixman_image_get_data(vd->guest.fb);
|
|
|
+ guest_stride = pixman_image_get_stride(vd->guest.fb);
|
|
|
+ }
|
|
|
+ server_row0 = (uint8_t *)pixman_image_get_data(vd->server);
|
|
|
+ server_stride = pixman_image_get_stride(vd->server);
|
|
|
+
|
|
|
+ y = 0;
|
|
|
+ for (;;) {
|
|
|
+ int x;
|
|
|
+ uint8_t *guest_ptr, *server_ptr;
|
|
|
+ unsigned long offset = find_next_bit((unsigned long *) &vd->guest.dirty,
|
|
|
+ height * VNC_DIRTY_BPL(&vd->guest),
|
|
|
+ y * VNC_DIRTY_BPL(&vd->guest));
|
|
|
+ if (offset == height * VNC_DIRTY_BPL(&vd->guest)) {
|
|
|
+ /* no more dirty bits */
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ y = offset / VNC_DIRTY_BPL(&vd->guest);
|
|
|
+ x = offset % VNC_DIRTY_BPL(&vd->guest);
|
|
|
|
|
|
- for (x = 0; x + 15 < width;
|
|
|
- x += 16, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) {
|
|
|
- if (!test_and_clear_bit((x / 16), vd->guest.dirty[y]))
|
|
|
- continue;
|
|
|
- if (memcmp(server_ptr, guest_ptr, cmp_bytes) == 0)
|
|
|
- continue;
|
|
|
- memcpy(server_ptr, guest_ptr, cmp_bytes);
|
|
|
- if (!vd->non_adaptive)
|
|
|
- vnc_rect_updated(vd, x, y, &tv);
|
|
|
- QTAILQ_FOREACH(vs, &vd->clients, next) {
|
|
|
- set_bit((x / 16), vs->dirty[y]);
|
|
|
- }
|
|
|
- has_dirty++;
|
|
|
+ server_ptr = server_row0 + y * server_stride + x * cmp_bytes;
|
|
|
+
|
|
|
+ if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
|
|
|
+ qemu_pixman_linebuf_fill(tmpbuf, vd->guest.fb, width, 0, y);
|
|
|
+ guest_ptr = (uint8_t *)pixman_image_get_data(tmpbuf);
|
|
|
+ } else {
|
|
|
+ guest_ptr = guest_row0 + y * guest_stride;
|
|
|
+ }
|
|
|
+ guest_ptr += x * cmp_bytes;
|
|
|
+
|
|
|
+ for (; x < DIV_ROUND_UP(width, VNC_DIRTY_PIXELS_PER_BIT);
|
|
|
+ x++, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) {
|
|
|
+ if (!test_and_clear_bit(x, vd->guest.dirty[y])) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (memcmp(server_ptr, guest_ptr, cmp_bytes) == 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ memcpy(server_ptr, guest_ptr, cmp_bytes);
|
|
|
+ if (!vd->non_adaptive) {
|
|
|
+ vnc_rect_updated(vd, x * VNC_DIRTY_PIXELS_PER_BIT,
|
|
|
+ y, &tv);
|
|
|
}
|
|
|
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
|
|
|
+ set_bit(x, vs->dirty[y]);
|
|
|
+ }
|
|
|
+ has_dirty++;
|
|
|
}
|
|
|
- guest_row += pixman_image_get_stride(vd->guest.fb);
|
|
|
- server_row += pixman_image_get_stride(vd->server);
|
|
|
+
|
|
|
+ y++;
|
|
|
}
|
|
|
qemu_pixman_image_unref(tmpbuf);
|
|
|
return has_dirty;
|
|
@@ -2734,7 +2750,7 @@ static void vnc_refresh(DisplayChangeListener *dcl)
|
|
|
vnc_unlock_display(vd);
|
|
|
|
|
|
QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
|
|
|
- rects += vnc_update_client(vs, has_dirty);
|
|
|
+ rects += vnc_update_client(vs, has_dirty, false);
|
|
|
/* vs might be free()ed here */
|
|
|
}
|
|
|
|
|
@@ -3134,7 +3150,9 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp)
|
|
|
acl = 1;
|
|
|
#endif
|
|
|
} else if (strncmp(options, "lossy", 5) == 0) {
|
|
|
+#ifdef CONFIG_VNC_JPEG
|
|
|
vs->lossy = true;
|
|
|
+#endif
|
|
|
} else if (strncmp(options, "non-adaptive", 12) == 0) {
|
|
|
vs->non_adaptive = true;
|
|
|
} else if (strncmp(options, "share=", 6) == 0) {
|
|
@@ -3151,6 +3169,13 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /* adaptive updates are only used with tight encoding and
|
|
|
+ * if lossy updates are enabled so we can disable all the
|
|
|
+ * calculations otherwise */
|
|
|
+ if (!vs->lossy) {
|
|
|
+ vs->non_adaptive = true;
|
|
|
+ }
|
|
|
+
|
|
|
#ifdef CONFIG_VNC_TLS
|
|
|
if (acl && x509 && vs->tls.x509verify) {
|
|
|
if (!(vs->tls.acl = qemu_acl_init("vnc.x509dname"))) {
|