single vnc server surface
This patch removes the server surface from VncState and adds a single server surface to VncDisplay for all the possible clients connected. Each client maintains a different dirty bitmap in VncState. The guest surface is moved to VncDisplay as well because we don't need to track guest updates in more than one place. This patch has been updated to handle CopyRect correctly and efficiently. Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> Message-Id:
This commit is contained in:
parent
703bc68ff5
commit
1fc624122f
320
vnc.c
320
vnc.c
|
@ -215,13 +215,18 @@ static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
|
||||||
3) resolutions > 1024
|
3) resolutions > 1024
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void vnc_update_client(VncState *vs);
|
static void vnc_update_client(VncState *vs, int has_dirty);
|
||||||
static void vnc_disconnect_start(VncState *vs);
|
static void vnc_disconnect_start(VncState *vs);
|
||||||
static void vnc_disconnect_finish(VncState *vs);
|
static void vnc_disconnect_finish(VncState *vs);
|
||||||
static void vnc_init_timer(VncDisplay *vd);
|
static void vnc_init_timer(VncDisplay *vd);
|
||||||
static void vnc_remove_timer(VncDisplay *vd);
|
static void vnc_remove_timer(VncDisplay *vd);
|
||||||
|
|
||||||
static void vnc_colordepth(VncState *vs);
|
static void vnc_colordepth(VncState *vs);
|
||||||
|
static void framebuffer_update_request(VncState *vs, int incremental,
|
||||||
|
int x_position, int y_position,
|
||||||
|
int w, int h);
|
||||||
|
static void vnc_refresh(void *opaque);
|
||||||
|
static int vnc_refresh_server_surface(VncDisplay *vd);
|
||||||
|
|
||||||
static inline void vnc_set_bit(uint32_t *d, int k)
|
static inline void vnc_set_bit(uint32_t *d, int k)
|
||||||
{
|
{
|
||||||
|
@ -264,10 +269,11 @@ static inline int vnc_and_bits(const uint32_t *d1, const uint32_t *d2,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vnc_update(VncState *vs, int x, int y, int w, int h)
|
static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h)
|
||||||
{
|
{
|
||||||
struct VncSurface *s = &vs->guest;
|
|
||||||
int i;
|
int i;
|
||||||
|
VncDisplay *vd = ds->opaque;
|
||||||
|
struct VncSurface *s = &vd->guest;
|
||||||
|
|
||||||
h += y;
|
h += y;
|
||||||
|
|
||||||
|
@ -288,16 +294,6 @@ static void vnc_update(VncState *vs, int x, int y, int w, int h)
|
||||||
vnc_set_bit(s->dirty[y], (x + i) / 16);
|
vnc_set_bit(s->dirty[y], (x + i) / 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h)
|
|
||||||
{
|
|
||||||
VncDisplay *vd = ds->opaque;
|
|
||||||
VncState *vs = vd->clients;
|
|
||||||
while (vs != NULL) {
|
|
||||||
vnc_update(vs, x, y, w, h);
|
|
||||||
vs = vs->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
|
static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
|
||||||
int32_t encoding)
|
int32_t encoding)
|
||||||
{
|
{
|
||||||
|
@ -342,20 +338,33 @@ void buffer_append(Buffer *buffer, const void *data, size_t len)
|
||||||
buffer->offset += len;
|
buffer->offset += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vnc_resize(VncState *vs)
|
static void vnc_dpy_resize(DisplayState *ds)
|
||||||
{
|
{
|
||||||
DisplayState *ds = vs->ds;
|
|
||||||
int size_changed;
|
int size_changed;
|
||||||
|
VncDisplay *vd = ds->opaque;
|
||||||
|
VncState *vs = vd->clients;
|
||||||
|
|
||||||
|
/* server surface */
|
||||||
|
if (!vd->server)
|
||||||
|
vd->server = qemu_mallocz(sizeof(*vd->server));
|
||||||
|
if (vd->server->data)
|
||||||
|
qemu_free(vd->server->data);
|
||||||
|
*(vd->server) = *(ds->surface);
|
||||||
|
vd->server->data = qemu_mallocz(vd->server->linesize *
|
||||||
|
vd->server->height);
|
||||||
|
|
||||||
/* guest surface */
|
/* guest surface */
|
||||||
if (!vs->guest.ds)
|
if (!vd->guest.ds)
|
||||||
vs->guest.ds = qemu_mallocz(sizeof(*vs->guest.ds));
|
vd->guest.ds = qemu_mallocz(sizeof(*vd->guest.ds));
|
||||||
if (ds_get_bytes_per_pixel(ds) != vs->guest.ds->pf.bytes_per_pixel)
|
if (ds_get_bytes_per_pixel(ds) != vd->guest.ds->pf.bytes_per_pixel)
|
||||||
console_color_init(ds);
|
console_color_init(ds);
|
||||||
|
size_changed = ds_get_width(ds) != vd->guest.ds->width ||
|
||||||
|
ds_get_height(ds) != vd->guest.ds->height;
|
||||||
|
*(vd->guest.ds) = *(ds->surface);
|
||||||
|
memset(vd->guest.dirty, 0xFF, sizeof(vd->guest.dirty));
|
||||||
|
|
||||||
|
while (vs != NULL) {
|
||||||
vnc_colordepth(vs);
|
vnc_colordepth(vs);
|
||||||
size_changed = ds_get_width(ds) != vs->guest.ds->width ||
|
|
||||||
ds_get_height(ds) != vs->guest.ds->height;
|
|
||||||
*(vs->guest.ds) = *(ds->surface);
|
|
||||||
if (size_changed) {
|
if (size_changed) {
|
||||||
if (vs->csock != -1 && vnc_has_feature(vs, VNC_FEATURE_RESIZE)) {
|
if (vs->csock != -1 && vnc_has_feature(vs, VNC_FEATURE_RESIZE)) {
|
||||||
vnc_write_u8(vs, 0); /* msg id */
|
vnc_write_u8(vs, 0); /* msg id */
|
||||||
|
@ -366,25 +375,7 @@ static void vnc_resize(VncState *vs)
|
||||||
vnc_flush(vs);
|
vnc_flush(vs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
memset(vs->guest.dirty, 0xFF, sizeof(vs->guest.dirty));
|
memset(vs->dirty, 0xFF, sizeof(vs->dirty));
|
||||||
|
|
||||||
/* server surface */
|
|
||||||
if (!vs->server.ds)
|
|
||||||
vs->server.ds = qemu_mallocz(sizeof(*vs->server.ds));
|
|
||||||
if (vs->server.ds->data)
|
|
||||||
qemu_free(vs->server.ds->data);
|
|
||||||
*(vs->server.ds) = *(ds->surface);
|
|
||||||
vs->server.ds->data = qemu_mallocz(vs->server.ds->linesize *
|
|
||||||
vs->server.ds->height);
|
|
||||||
memset(vs->server.dirty, 0xFF, sizeof(vs->guest.dirty));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vnc_dpy_resize(DisplayState *ds)
|
|
||||||
{
|
|
||||||
VncDisplay *vd = ds->opaque;
|
|
||||||
VncState *vs = vd->clients;
|
|
||||||
while (vs != NULL) {
|
|
||||||
vnc_resize(vs);
|
|
||||||
vs = vs->next;
|
vs = vs->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -399,13 +390,14 @@ static void vnc_write_pixels_copy(VncState *vs, void *pixels, int size)
|
||||||
static void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v)
|
static void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v)
|
||||||
{
|
{
|
||||||
uint8_t r, g, b;
|
uint8_t r, g, b;
|
||||||
|
VncDisplay *vd = vs->vd;
|
||||||
|
|
||||||
r = ((((v & vs->server.ds->pf.rmask) >> vs->server.ds->pf.rshift) << vs->clientds.pf.rbits) >>
|
r = ((((v & vd->server->pf.rmask) >> vd->server->pf.rshift) << vs->clientds.pf.rbits) >>
|
||||||
vs->server.ds->pf.rbits);
|
vd->server->pf.rbits);
|
||||||
g = ((((v & vs->server.ds->pf.gmask) >> vs->server.ds->pf.gshift) << vs->clientds.pf.gbits) >>
|
g = ((((v & vd->server->pf.gmask) >> vd->server->pf.gshift) << vs->clientds.pf.gbits) >>
|
||||||
vs->server.ds->pf.gbits);
|
vd->server->pf.gbits);
|
||||||
b = ((((v & vs->server.ds->pf.bmask) >> vs->server.ds->pf.bshift) << vs->clientds.pf.bbits) >>
|
b = ((((v & vd->server->pf.bmask) >> vd->server->pf.bshift) << vs->clientds.pf.bbits) >>
|
||||||
vs->server.ds->pf.bbits);
|
vd->server->pf.bbits);
|
||||||
v = (r << vs->clientds.pf.rshift) |
|
v = (r << vs->clientds.pf.rshift) |
|
||||||
(g << vs->clientds.pf.gshift) |
|
(g << vs->clientds.pf.gshift) |
|
||||||
(b << vs->clientds.pf.bshift);
|
(b << vs->clientds.pf.bshift);
|
||||||
|
@ -442,8 +434,9 @@ static void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v)
|
||||||
static void vnc_write_pixels_generic(VncState *vs, void *pixels1, int size)
|
static void vnc_write_pixels_generic(VncState *vs, void *pixels1, int size)
|
||||||
{
|
{
|
||||||
uint8_t buf[4];
|
uint8_t buf[4];
|
||||||
|
VncDisplay *vd = vs->vd;
|
||||||
|
|
||||||
if (vs->server.ds->pf.bytes_per_pixel == 4) {
|
if (vd->server->pf.bytes_per_pixel == 4) {
|
||||||
uint32_t *pixels = pixels1;
|
uint32_t *pixels = pixels1;
|
||||||
int n, i;
|
int n, i;
|
||||||
n = size >> 2;
|
n = size >> 2;
|
||||||
|
@ -451,7 +444,7 @@ static void vnc_write_pixels_generic(VncState *vs, void *pixels1, int size)
|
||||||
vnc_convert_pixel(vs, buf, pixels[i]);
|
vnc_convert_pixel(vs, buf, pixels[i]);
|
||||||
vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
|
vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
|
||||||
}
|
}
|
||||||
} else if (vs->server.ds->pf.bytes_per_pixel == 2) {
|
} else if (vd->server->pf.bytes_per_pixel == 2) {
|
||||||
uint16_t *pixels = pixels1;
|
uint16_t *pixels = pixels1;
|
||||||
int n, i;
|
int n, i;
|
||||||
n = size >> 1;
|
n = size >> 1;
|
||||||
|
@ -459,7 +452,7 @@ static void vnc_write_pixels_generic(VncState *vs, void *pixels1, int size)
|
||||||
vnc_convert_pixel(vs, buf, pixels[i]);
|
vnc_convert_pixel(vs, buf, pixels[i]);
|
||||||
vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
|
vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
|
||||||
}
|
}
|
||||||
} else if (vs->server.ds->pf.bytes_per_pixel == 1) {
|
} else if (vd->server->pf.bytes_per_pixel == 1) {
|
||||||
uint8_t *pixels = pixels1;
|
uint8_t *pixels = pixels1;
|
||||||
int n, i;
|
int n, i;
|
||||||
n = size;
|
n = size;
|
||||||
|
@ -476,8 +469,9 @@ static void send_framebuffer_update_raw(VncState *vs, int x, int y, int w, int h
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
uint8_t *row;
|
uint8_t *row;
|
||||||
|
VncDisplay *vd = vs->vd;
|
||||||
|
|
||||||
row = vs->server.ds->data + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds);
|
row = vd->server->data + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds);
|
||||||
for (i = 0; i < h; i++) {
|
for (i = 0; i < h; i++) {
|
||||||
vs->write_pixels(vs, row, w * ds_get_bytes_per_pixel(vs->ds));
|
vs->write_pixels(vs, row, w * ds_get_bytes_per_pixel(vs->ds));
|
||||||
row += ds_get_linesize(vs->ds);
|
row += ds_get_linesize(vs->ds);
|
||||||
|
@ -525,9 +519,10 @@ static void send_framebuffer_update_hextile(VncState *vs, int x, int y, int w, i
|
||||||
int i, j;
|
int i, j;
|
||||||
int has_fg, has_bg;
|
int has_fg, has_bg;
|
||||||
uint8_t *last_fg, *last_bg;
|
uint8_t *last_fg, *last_bg;
|
||||||
|
VncDisplay *vd = vs->vd;
|
||||||
|
|
||||||
last_fg = (uint8_t *) qemu_malloc(vs->server.ds->pf.bytes_per_pixel);
|
last_fg = (uint8_t *) qemu_malloc(vd->server->pf.bytes_per_pixel);
|
||||||
last_bg = (uint8_t *) qemu_malloc(vs->server.ds->pf.bytes_per_pixel);
|
last_bg = (uint8_t *) qemu_malloc(vd->server->pf.bytes_per_pixel);
|
||||||
has_fg = has_bg = 0;
|
has_fg = has_bg = 0;
|
||||||
for (j = y; j < (y + h); j += 16) {
|
for (j = y; j < (y + h); j += 16) {
|
||||||
for (i = x; i < (x + w); i += 16) {
|
for (i = x; i < (x + w); i += 16) {
|
||||||
|
@ -656,10 +651,6 @@ static void send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
|
||||||
|
|
||||||
static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
|
static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
|
||||||
{
|
{
|
||||||
uint8_t *src_row;
|
|
||||||
uint8_t *dst_row;
|
|
||||||
int y,pitch,depth;
|
|
||||||
|
|
||||||
/* send bitblit op to the vnc client */
|
/* send bitblit op to the vnc client */
|
||||||
vnc_write_u8(vs, 0); /* msg id */
|
vnc_write_u8(vs, 0); /* msg id */
|
||||||
vnc_write_u8(vs, 0);
|
vnc_write_u8(vs, 0);
|
||||||
|
@ -668,113 +659,110 @@ static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, i
|
||||||
vnc_write_u16(vs, src_x);
|
vnc_write_u16(vs, src_x);
|
||||||
vnc_write_u16(vs, src_y);
|
vnc_write_u16(vs, src_y);
|
||||||
vnc_flush(vs);
|
vnc_flush(vs);
|
||||||
|
|
||||||
/* do bitblit op on the local surface too */
|
|
||||||
pitch = ds_get_linesize(vs->ds);
|
|
||||||
depth = ds_get_bytes_per_pixel(vs->ds);
|
|
||||||
src_row = vs->server.ds->data + pitch * src_y + depth * src_x;
|
|
||||||
dst_row = vs->server.ds->data + pitch * dst_y + depth * dst_x;
|
|
||||||
if (dst_y > src_y) {
|
|
||||||
/* copy backwards */
|
|
||||||
src_row += pitch * (h-1);
|
|
||||||
dst_row += pitch * (h-1);
|
|
||||||
pitch = -pitch;
|
|
||||||
}
|
|
||||||
for (y = 0; y < h; y++) {
|
|
||||||
memmove(dst_row, src_row, w * depth);
|
|
||||||
src_row += pitch;
|
|
||||||
dst_row += pitch;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
|
static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
|
||||||
{
|
{
|
||||||
VncDisplay *vd = ds->opaque;
|
VncDisplay *vd = ds->opaque;
|
||||||
VncState *vs, *vn;
|
VncState *vs, *vn;
|
||||||
|
uint8_t *src_row;
|
||||||
|
uint8_t *dst_row;
|
||||||
|
int i,x,y,pitch,depth,inc,w_lim,s;
|
||||||
|
int cmp_bytes;
|
||||||
|
|
||||||
|
vnc_refresh_server_surface(vd);
|
||||||
for (vs = vd->clients; vs != NULL; vs = vn) {
|
for (vs = vd->clients; vs != NULL; vs = vn) {
|
||||||
vn = vs->next;
|
vn = vs->next;
|
||||||
if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
|
if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
|
||||||
vs->force_update = 1;
|
vs->force_update = 1;
|
||||||
vnc_update_client(vs);
|
vnc_update_client(vs, 1);
|
||||||
/* vs might be free()ed here */
|
/* vs might be free()ed here */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* do bitblit op on the local surface too */
|
||||||
|
pitch = ds_get_linesize(vd->ds);
|
||||||
|
depth = ds_get_bytes_per_pixel(vd->ds);
|
||||||
|
src_row = vd->server->data + pitch * src_y + depth * src_x;
|
||||||
|
dst_row = vd->server->data + pitch * dst_y + depth * dst_x;
|
||||||
|
y = dst_y;
|
||||||
|
inc = 1;
|
||||||
|
if (dst_y > src_y) {
|
||||||
|
/* copy backwards */
|
||||||
|
src_row += pitch * (h-1);
|
||||||
|
dst_row += pitch * (h-1);
|
||||||
|
pitch = -pitch;
|
||||||
|
y = dst_y + h - 1;
|
||||||
|
inc = -1;
|
||||||
|
}
|
||||||
|
w_lim = w - (16 - (dst_x % 16));
|
||||||
|
if (w_lim < 0)
|
||||||
|
w_lim = w;
|
||||||
|
else
|
||||||
|
w_lim = w - (w_lim % 16);
|
||||||
|
for (i = 0; i < h; i++) {
|
||||||
|
for (x = 0; x <= w_lim;
|
||||||
|
x += s, src_row += cmp_bytes, dst_row += cmp_bytes) {
|
||||||
|
if (x == w_lim) {
|
||||||
|
if ((s = w - w_lim) == 0)
|
||||||
|
break;
|
||||||
|
} else if (!x) {
|
||||||
|
s = (16 - (dst_x % 16));
|
||||||
|
s = MIN(s, w_lim);
|
||||||
|
} else {
|
||||||
|
s = 16;
|
||||||
|
}
|
||||||
|
cmp_bytes = s * depth;
|
||||||
|
if (memcmp(src_row, dst_row, cmp_bytes) == 0)
|
||||||
|
continue;
|
||||||
|
memmove(dst_row, src_row, cmp_bytes);
|
||||||
|
vs = vd->clients;
|
||||||
|
while (vs != NULL) {
|
||||||
|
if (!vnc_has_feature(vs, VNC_FEATURE_COPYRECT))
|
||||||
|
vnc_set_bit(vs->dirty[y], ((x + dst_x) / 16));
|
||||||
|
vs = vs->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
src_row += pitch - w * depth;
|
||||||
|
dst_row += pitch - w * depth;
|
||||||
|
y += inc;
|
||||||
|
}
|
||||||
|
|
||||||
for (vs = vd->clients; vs != NULL; vs = vs->next) {
|
for (vs = vd->clients; vs != NULL; vs = vs->next) {
|
||||||
if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT))
|
if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT))
|
||||||
vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h);
|
vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h);
|
||||||
else /* TODO */
|
|
||||||
vnc_update(vs, dst_x, dst_y, w, h);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int find_and_clear_dirty_height(struct VncSurface *s,
|
static int find_and_clear_dirty_height(struct VncState *vs,
|
||||||
int y, int last_x, int x)
|
int y, int last_x, int x)
|
||||||
{
|
{
|
||||||
int h;
|
int h;
|
||||||
|
VncDisplay *vd = vs->vd;
|
||||||
|
|
||||||
for (h = 1; h < (s->ds->height - y); h++) {
|
for (h = 1; h < (vd->server->height - y); h++) {
|
||||||
int tmp_x;
|
int tmp_x;
|
||||||
if (!vnc_get_bit(s->dirty[y + h], last_x))
|
if (!vnc_get_bit(vs->dirty[y + h], last_x))
|
||||||
break;
|
break;
|
||||||
for (tmp_x = last_x; tmp_x < x; tmp_x++)
|
for (tmp_x = last_x; tmp_x < x; tmp_x++)
|
||||||
vnc_clear_bit(s->dirty[y + h], tmp_x);
|
vnc_clear_bit(vs->dirty[y + h], tmp_x);
|
||||||
}
|
}
|
||||||
|
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vnc_update_client(VncState *vs)
|
static void vnc_update_client(VncState *vs, int has_dirty)
|
||||||
{
|
{
|
||||||
if (vs->need_update && vs->csock != -1) {
|
if (vs->need_update && vs->csock != -1) {
|
||||||
|
VncDisplay *vd = vs->vd;
|
||||||
int y;
|
int y;
|
||||||
uint8_t *guest_row;
|
|
||||||
uint8_t *server_row;
|
|
||||||
int cmp_bytes;
|
|
||||||
uint32_t width_mask[VNC_DIRTY_WORDS];
|
|
||||||
int n_rectangles;
|
int n_rectangles;
|
||||||
int saved_offset;
|
int saved_offset;
|
||||||
int has_dirty = 0;
|
|
||||||
|
|
||||||
if (vs->output.offset && !vs->audio_cap && !vs->force_update)
|
if (vs->output.offset && !vs->audio_cap && !vs->force_update)
|
||||||
/* kernel send buffers are full -> drop frames to throttle */
|
/* kernel send buffers are full -> drop frames to throttle */
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
|
||||||
* Walk through the guest dirty map.
|
|
||||||
* Check and copy modified bits from guest to server surface.
|
|
||||||
* Update server dirty map.
|
|
||||||
*/
|
|
||||||
vnc_set_bits(width_mask, (ds_get_width(vs->ds) / 16), VNC_DIRTY_WORDS);
|
|
||||||
cmp_bytes = 16 * ds_get_bytes_per_pixel(vs->ds);
|
|
||||||
guest_row = vs->guest.ds->data;
|
|
||||||
server_row = vs->server.ds->data;
|
|
||||||
for (y = 0; y < vs->guest.ds->height; y++) {
|
|
||||||
if (vnc_and_bits(vs->guest.dirty[y], width_mask, VNC_DIRTY_WORDS)) {
|
|
||||||
int x;
|
|
||||||
uint8_t *guest_ptr;
|
|
||||||
uint8_t *server_ptr;
|
|
||||||
|
|
||||||
guest_ptr = guest_row;
|
|
||||||
server_ptr = server_row;
|
|
||||||
|
|
||||||
for (x = 0; x < vs->guest.ds->width;
|
|
||||||
x += 16, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) {
|
|
||||||
if (!vnc_get_bit(vs->guest.dirty[y], (x / 16)))
|
|
||||||
continue;
|
|
||||||
vnc_clear_bit(vs->guest.dirty[y], (x / 16));
|
|
||||||
if (memcmp(server_ptr, guest_ptr, cmp_bytes) == 0)
|
|
||||||
continue;
|
|
||||||
memcpy(server_ptr, guest_ptr, cmp_bytes);
|
|
||||||
vnc_set_bit(vs->server.dirty[y], (x / 16));
|
|
||||||
has_dirty++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
guest_row += ds_get_linesize(vs->ds);
|
|
||||||
server_row += ds_get_linesize(vs->ds);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!has_dirty && !vs->audio_cap && !vs->force_update)
|
if (!has_dirty && !vs->audio_cap && !vs->force_update)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -790,18 +778,18 @@ static void vnc_update_client(VncState *vs)
|
||||||
saved_offset = vs->output.offset;
|
saved_offset = vs->output.offset;
|
||||||
vnc_write_u16(vs, 0);
|
vnc_write_u16(vs, 0);
|
||||||
|
|
||||||
for (y = 0; y < vs->server.ds->height; y++) {
|
for (y = 0; y < vd->server->height; y++) {
|
||||||
int x;
|
int x;
|
||||||
int last_x = -1;
|
int last_x = -1;
|
||||||
for (x = 0; x < vs->server.ds->width / 16; x++) {
|
for (x = 0; x < vd->server->width / 16; x++) {
|
||||||
if (vnc_get_bit(vs->server.dirty[y], x)) {
|
if (vnc_get_bit(vs->dirty[y], x)) {
|
||||||
if (last_x == -1) {
|
if (last_x == -1) {
|
||||||
last_x = x;
|
last_x = x;
|
||||||
}
|
}
|
||||||
vnc_clear_bit(vs->server.dirty[y], x);
|
vnc_clear_bit(vs->dirty[y], x);
|
||||||
} else {
|
} else {
|
||||||
if (last_x != -1) {
|
if (last_x != -1) {
|
||||||
int h = find_and_clear_dirty_height(&vs->server, y, last_x, x);
|
int h = find_and_clear_dirty_height(vs, y, last_x, x);
|
||||||
send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h);
|
send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h);
|
||||||
n_rectangles++;
|
n_rectangles++;
|
||||||
}
|
}
|
||||||
|
@ -809,7 +797,7 @@ static void vnc_update_client(VncState *vs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (last_x != -1) {
|
if (last_x != -1) {
|
||||||
int h = find_and_clear_dirty_height(&vs->server, y, last_x, x);
|
int h = find_and_clear_dirty_height(vs, y, last_x, x);
|
||||||
send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h);
|
send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h);
|
||||||
n_rectangles++;
|
n_rectangles++;
|
||||||
}
|
}
|
||||||
|
@ -926,9 +914,6 @@ static void vnc_disconnect_finish(VncState *vs)
|
||||||
if (!vs->vd->clients)
|
if (!vs->vd->clients)
|
||||||
dcl->idle = 1;
|
dcl->idle = 1;
|
||||||
|
|
||||||
qemu_free(vs->server.ds->data);
|
|
||||||
qemu_free(vs->server.ds);
|
|
||||||
qemu_free(vs->guest.ds);
|
|
||||||
qemu_free(vs);
|
qemu_free(vs);
|
||||||
vnc_remove_timer(vs->vd);
|
vnc_remove_timer(vs->vd);
|
||||||
}
|
}
|
||||||
|
@ -1507,9 +1492,7 @@ static void framebuffer_update_request(VncState *vs, int incremental,
|
||||||
if (!incremental) {
|
if (!incremental) {
|
||||||
vs->force_update = 1;
|
vs->force_update = 1;
|
||||||
for (i = 0; i < h; i++) {
|
for (i = 0; i < h; i++) {
|
||||||
vnc_set_bits(vs->guest.dirty[y_position + i],
|
vnc_set_bits(vs->dirty[y_position + i],
|
||||||
(ds_get_width(vs->ds) / 16), VNC_DIRTY_WORDS);
|
|
||||||
vnc_set_bits(vs->server.dirty[y_position + i],
|
|
||||||
(ds_get_width(vs->ds) / 16), VNC_DIRTY_WORDS);
|
(ds_get_width(vs->ds) / 16), VNC_DIRTY_WORDS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1638,7 +1621,7 @@ static void set_pixel_format(VncState *vs,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
vs->clientds = *(vs->guest.ds);
|
vs->clientds = *(vs->vd->guest.ds);
|
||||||
vs->clientds.pf.rmax = red_max;
|
vs->clientds.pf.rmax = red_max;
|
||||||
count_bits(vs->clientds.pf.rbits, red_max);
|
count_bits(vs->clientds.pf.rbits, red_max);
|
||||||
vs->clientds.pf.rshift = red_shift;
|
vs->clientds.pf.rshift = red_shift;
|
||||||
|
@ -2067,15 +2050,69 @@ static int protocol_version(VncState *vs, uint8_t *version, size_t len)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int vnc_refresh_server_surface(VncDisplay *vd)
|
||||||
|
{
|
||||||
|
int y;
|
||||||
|
uint8_t *guest_row;
|
||||||
|
uint8_t *server_row;
|
||||||
|
int cmp_bytes;
|
||||||
|
uint32_t width_mask[VNC_DIRTY_WORDS];
|
||||||
|
VncState *vs = NULL;
|
||||||
|
int has_dirty = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Walk through the guest dirty map.
|
||||||
|
* Check and copy modified bits from guest to server surface.
|
||||||
|
* Update server dirty map.
|
||||||
|
*/
|
||||||
|
vnc_set_bits(width_mask, (ds_get_width(vd->ds) / 16), VNC_DIRTY_WORDS);
|
||||||
|
cmp_bytes = 16 * ds_get_bytes_per_pixel(vd->ds);
|
||||||
|
guest_row = vd->guest.ds->data;
|
||||||
|
server_row = vd->server->data;
|
||||||
|
for (y = 0; y < vd->guest.ds->height; y++) {
|
||||||
|
if (vnc_and_bits(vd->guest.dirty[y], width_mask, VNC_DIRTY_WORDS)) {
|
||||||
|
int x;
|
||||||
|
uint8_t *guest_ptr;
|
||||||
|
uint8_t *server_ptr;
|
||||||
|
|
||||||
|
guest_ptr = guest_row;
|
||||||
|
server_ptr = server_row;
|
||||||
|
|
||||||
|
for (x = 0; x < vd->guest.ds->width;
|
||||||
|
x += 16, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) {
|
||||||
|
if (!vnc_get_bit(vd->guest.dirty[y], (x / 16)))
|
||||||
|
continue;
|
||||||
|
vnc_clear_bit(vd->guest.dirty[y], (x / 16));
|
||||||
|
if (memcmp(server_ptr, guest_ptr, cmp_bytes) == 0)
|
||||||
|
continue;
|
||||||
|
memcpy(server_ptr, guest_ptr, cmp_bytes);
|
||||||
|
vs = vd->clients;
|
||||||
|
while (vs != NULL) {
|
||||||
|
vnc_set_bit(vs->dirty[y], (x / 16));
|
||||||
|
vs = vs->next;
|
||||||
|
}
|
||||||
|
has_dirty++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
guest_row += ds_get_linesize(vd->ds);
|
||||||
|
server_row += ds_get_linesize(vd->ds);
|
||||||
|
}
|
||||||
|
return has_dirty;
|
||||||
|
}
|
||||||
|
|
||||||
static void vnc_refresh(void *opaque)
|
static void vnc_refresh(void *opaque)
|
||||||
{
|
{
|
||||||
VncDisplay *vd = opaque;
|
VncDisplay *vd = opaque;
|
||||||
VncState *vs = vd->clients;
|
VncState *vs = NULL;
|
||||||
|
int has_dirty = 0;
|
||||||
|
|
||||||
vga_hw_update();
|
vga_hw_update();
|
||||||
|
|
||||||
|
has_dirty = vnc_refresh_server_surface(vd);
|
||||||
|
|
||||||
|
vs = vd->clients;
|
||||||
while (vs != NULL) {
|
while (vs != NULL) {
|
||||||
vnc_update_client(vs);
|
vnc_update_client(vs, has_dirty);
|
||||||
vs = vs->next;
|
vs = vs->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2086,7 +2123,7 @@ static void vnc_init_timer(VncDisplay *vd)
|
||||||
{
|
{
|
||||||
if (vd->timer == NULL && vd->clients != NULL) {
|
if (vd->timer == NULL && vd->clients != NULL) {
|
||||||
vd->timer = qemu_new_timer(rt_clock, vnc_refresh, vd);
|
vd->timer = qemu_new_timer(rt_clock, vnc_refresh, vd);
|
||||||
qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);
|
vnc_refresh(vd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2119,17 +2156,18 @@ static void vnc_connect(VncDisplay *vd, int csock)
|
||||||
vs->as.fmt = AUD_FMT_S16;
|
vs->as.fmt = AUD_FMT_S16;
|
||||||
vs->as.endianness = 0;
|
vs->as.endianness = 0;
|
||||||
|
|
||||||
vnc_resize(vs);
|
vs->next = vd->clients;
|
||||||
|
vd->clients = vs;
|
||||||
|
|
||||||
|
vga_hw_update();
|
||||||
|
|
||||||
vnc_write(vs, "RFB 003.008\n", 12);
|
vnc_write(vs, "RFB 003.008\n", 12);
|
||||||
vnc_flush(vs);
|
vnc_flush(vs);
|
||||||
vnc_read_when(vs, protocol_version, 12);
|
vnc_read_when(vs, protocol_version, 12);
|
||||||
reset_keys(vs);
|
reset_keys(vs);
|
||||||
|
|
||||||
vs->next = vd->clients;
|
|
||||||
vd->clients = vs;
|
|
||||||
|
|
||||||
vnc_init_timer(vd);
|
vnc_init_timer(vd);
|
||||||
vnc_update_client(vs);
|
|
||||||
/* vs might be free()ed here */
|
/* vs might be free()ed here */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
17
vnc.h
17
vnc.h
|
@ -84,6 +84,11 @@ typedef struct VncDisplay VncDisplay;
|
||||||
#include "vnc-auth-sasl.h"
|
#include "vnc-auth-sasl.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct VncSurface
|
||||||
|
{
|
||||||
|
uint32_t dirty[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS];
|
||||||
|
DisplaySurface *ds;
|
||||||
|
};
|
||||||
|
|
||||||
struct VncDisplay
|
struct VncDisplay
|
||||||
{
|
{
|
||||||
|
@ -93,6 +98,9 @@ struct VncDisplay
|
||||||
VncState *clients;
|
VncState *clients;
|
||||||
kbd_layout_t *kbd_layout;
|
kbd_layout_t *kbd_layout;
|
||||||
|
|
||||||
|
struct VncSurface guest; /* guest visible surface (aka ds->surface) */
|
||||||
|
DisplaySurface *server; /* vnc server surface */
|
||||||
|
|
||||||
char *display;
|
char *display;
|
||||||
char *password;
|
char *password;
|
||||||
int auth;
|
int auth;
|
||||||
|
@ -105,19 +113,12 @@ struct VncDisplay
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VncSurface
|
|
||||||
{
|
|
||||||
uint32_t dirty[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS];
|
|
||||||
DisplaySurface *ds;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VncState
|
struct VncState
|
||||||
{
|
{
|
||||||
int csock;
|
int csock;
|
||||||
|
|
||||||
DisplayState *ds;
|
DisplayState *ds;
|
||||||
struct VncSurface guest; /* guest visible surface (aka ds->surface) */
|
uint32_t dirty[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS];
|
||||||
struct VncSurface server; /* vnc server surface */
|
|
||||||
|
|
||||||
VncDisplay *vd;
|
VncDisplay *vd;
|
||||||
int need_update;
|
int need_update;
|
||||||
|
|
|
@ -13,7 +13,8 @@ static void CONCAT(send_hextile_tile_, NAME)(VncState *vs,
|
||||||
void *last_fg_,
|
void *last_fg_,
|
||||||
int *has_bg, int *has_fg)
|
int *has_bg, int *has_fg)
|
||||||
{
|
{
|
||||||
uint8_t *row = vs->server.ds->data + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds);
|
VncDisplay *vd = vs->vd;
|
||||||
|
uint8_t *row = vd->server->data + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds);
|
||||||
pixel_t *irow = (pixel_t *)row;
|
pixel_t *irow = (pixel_t *)row;
|
||||||
int j, i;
|
int j, i;
|
||||||
pixel_t *last_bg = (pixel_t *)last_bg_;
|
pixel_t *last_bg = (pixel_t *)last_bg_;
|
||||||
|
|
Loading…
Reference in a new issue