char host[NI_MAXHOST];
char serv[NI_MAXSERV];
int err;
+ size_t addrlen;
if ((err = getnameinfo((struct sockaddr *)sa, salen,
host, sizeof(host),
return NULL;
}
- if (asprintf(&addr, format, host, serv) < 0)
- return NULL;
+ /* Enough for the existing format + the 2 vars we're
+ * subsituting in. */
+ addrlen = strlen(format) + strlen(host) + strlen(serv);
+ addr = qemu_malloc(addrlen + 1);
+ snprintf(addr, addrlen, format, host, serv);
+ addr[addrlen] = '\0';
return addr;
}
return addr_to_string(format, &sa, salen);
}
-
char *vnc_socket_remote_addr(const char *format, int fd) {
struct sockaddr_storage sa;
socklen_t salen;
return "unknown";
}
-#define VNC_SOCKET_FORMAT_PRETTY "local %s:%s"
-
static void do_info_vnc_client(Monitor *mon, VncState *client)
{
char *clientAddr =
static void vnc_update(VncState *vs, int x, int y, int w, int h)
{
+ struct VncSurface *s = &vs->guest;
int i;
h += y;
w += (x % 16);
x -= (x % 16);
- x = MIN(x, vs->serverds.width);
- y = MIN(y, vs->serverds.height);
- w = MIN(x + w, vs->serverds.width) - x;
- h = MIN(h, vs->serverds.height);
+ x = MIN(x, s->ds->width);
+ y = MIN(y, s->ds->height);
+ w = MIN(x + w, s->ds->width) - x;
+ h = MIN(h, s->ds->height);
for (; y < h; y++)
for (i = 0; i < w; i += 16)
- vnc_set_bit(vs->dirty_row[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)
static void vnc_resize(VncState *vs)
{
DisplayState *ds = vs->ds;
-
int size_changed;
- vs->old_data = qemu_realloc(vs->old_data, ds_get_linesize(ds) * ds_get_height(ds));
-
- if (vs->old_data == NULL) {
- fprintf(stderr, "vnc: memory allocation failed\n");
- exit(1);
- }
-
- if (ds_get_bytes_per_pixel(ds) != vs->serverds.pf.bytes_per_pixel)
+ /* guest surface */
+ if (!vs->guest.ds)
+ vs->guest.ds = qemu_mallocz(sizeof(*vs->guest.ds));
+ if (ds_get_bytes_per_pixel(ds) != vs->guest.ds->pf.bytes_per_pixel)
console_color_init(ds);
vnc_colordepth(vs);
- size_changed = ds_get_width(ds) != vs->serverds.width ||
- ds_get_height(ds) != vs->serverds.height;
- vs->serverds = *(ds->surface);
+ 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 (vs->csock != -1 && vnc_has_feature(vs, VNC_FEATURE_RESIZE)) {
vnc_write_u8(vs, 0); /* msg id */
vnc_flush(vs);
}
}
+ memset(vs->guest.dirty, 0xFF, sizeof(vs->guest.dirty));
- memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
- memset(vs->old_data, 42, ds_get_linesize(vs->ds) * ds_get_height(vs->ds));
+ /* 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)
{
uint8_t r, g, b;
- r = ((((v & vs->serverds.pf.rmask) >> vs->serverds.pf.rshift) << vs->clientds.pf.rbits) >>
- vs->serverds.pf.rbits);
- g = ((((v & vs->serverds.pf.gmask) >> vs->serverds.pf.gshift) << vs->clientds.pf.gbits) >>
- vs->serverds.pf.gbits);
- b = ((((v & vs->serverds.pf.bmask) >> vs->serverds.pf.bshift) << vs->clientds.pf.bbits) >>
- vs->serverds.pf.bbits);
+ r = ((((v & vs->server.ds->pf.rmask) >> vs->server.ds->pf.rshift) << vs->clientds.pf.rbits) >>
+ vs->server.ds->pf.rbits);
+ g = ((((v & vs->server.ds->pf.gmask) >> vs->server.ds->pf.gshift) << vs->clientds.pf.gbits) >>
+ vs->server.ds->pf.gbits);
+ b = ((((v & vs->server.ds->pf.bmask) >> vs->server.ds->pf.bshift) << vs->clientds.pf.bbits) >>
+ vs->server.ds->pf.bbits);
v = (r << vs->clientds.pf.rshift) |
(g << vs->clientds.pf.gshift) |
(b << vs->clientds.pf.bshift);
{
uint8_t buf[4];
- if (vs->serverds.pf.bytes_per_pixel == 4) {
+ if (vs->server.ds->pf.bytes_per_pixel == 4) {
uint32_t *pixels = pixels1;
int n, i;
n = size >> 2;
vnc_convert_pixel(vs, buf, pixels[i]);
vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
}
- } else if (vs->serverds.pf.bytes_per_pixel == 2) {
+ } else if (vs->server.ds->pf.bytes_per_pixel == 2) {
uint16_t *pixels = pixels1;
int n, i;
n = size >> 1;
vnc_convert_pixel(vs, buf, pixels[i]);
vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
}
- } else if (vs->serverds.pf.bytes_per_pixel == 1) {
+ } else if (vs->server.ds->pf.bytes_per_pixel == 1) {
uint8_t *pixels = pixels1;
int n, i;
n = size;
int i;
uint8_t *row;
- row = ds_get_data(vs->ds) + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds);
+ row = vs->server.ds->data + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds);
for (i = 0; i < h; i++) {
vs->write_pixels(vs, row, w * ds_get_bytes_per_pixel(vs->ds));
row += ds_get_linesize(vs->ds);
int has_fg, has_bg;
uint8_t *last_fg, *last_bg;
- last_fg = (uint8_t *) qemu_malloc(vs->serverds.pf.bytes_per_pixel);
- last_bg = (uint8_t *) qemu_malloc(vs->serverds.pf.bytes_per_pixel);
+ last_fg = (uint8_t *) qemu_malloc(vs->server.ds->pf.bytes_per_pixel);
+ last_bg = (uint8_t *) qemu_malloc(vs->server.ds->pf.bytes_per_pixel);
has_fg = has_bg = 0;
for (j = y; j < (y + h); j += 16) {
for (i = x; i < (x + w); i += 16) {
static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
{
+ vs->force_update = 1;
vnc_update_client(vs);
vnc_write_u8(vs, 0); /* msg id */
}
}
-static int find_dirty_height(VncState *vs, int y, int last_x, int x)
+static int find_and_clear_dirty_height(struct VncSurface *s,
+ int y, int last_x, int x)
{
int h;
- for (h = 1; h < (vs->serverds.height - y); h++) {
+ for (h = 1; h < (s->ds->height - y); h++) {
int tmp_x;
- if (!vnc_get_bit(vs->dirty_row[y + h], last_x))
+ if (!vnc_get_bit(s->dirty[y + h], last_x))
break;
for (tmp_x = last_x; tmp_x < x; tmp_x++)
- vnc_clear_bit(vs->dirty_row[y + h], tmp_x);
+ vnc_clear_bit(s->dirty[y + h], tmp_x);
}
return h;
VncState *vs = opaque;
if (vs->need_update && vs->csock != -1) {
int y;
- uint8_t *row;
- char *old_row;
+ uint8_t *guest_row;
+ uint8_t *server_row;
+ int cmp_bytes;
uint32_t width_mask[VNC_DIRTY_WORDS];
int n_rectangles;
int saved_offset;
int has_dirty = 0;
+ if (vs->output.offset && !vs->audio_cap && !vs->force_update) {
+ /* kernel send buffers are full -> drop frames to throttle */
+ qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);
+ return;
+ }
+
vga_hw_update();
+ /*
+ * 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);
-
- /* Walk through the dirty map and eliminate tiles that
- really aren't dirty */
- row = ds_get_data(vs->ds);
- old_row = vs->old_data;
-
- for (y = 0; y < ds_get_height(vs->ds); y++) {
- if (vnc_and_bits(vs->dirty_row[y], width_mask, 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 *ptr;
- char *old_ptr;
-
- ptr = row;
- old_ptr = (char*)old_row;
-
- for (x = 0; x < ds_get_width(vs->ds); x += 16) {
- if (memcmp(old_ptr, ptr, 16 * ds_get_bytes_per_pixel(vs->ds)) == 0) {
- vnc_clear_bit(vs->dirty_row[y], (x / 16));
- } else {
- has_dirty = 1;
- memcpy(old_ptr, ptr, 16 * ds_get_bytes_per_pixel(vs->ds));
- }
-
- ptr += 16 * ds_get_bytes_per_pixel(vs->ds);
- old_ptr += 16 * ds_get_bytes_per_pixel(vs->ds);
+ 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++;
}
}
-
- row += ds_get_linesize(vs->ds);
- old_row += ds_get_linesize(vs->ds);
+ guest_row += ds_get_linesize(vs->ds);
+ server_row += ds_get_linesize(vs->ds);
}
- if (!has_dirty && !vs->audio_cap) {
+ if (!has_dirty && !vs->audio_cap && !vs->force_update) {
qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);
return;
}
- /* Count rectangles */
+ /*
+ * Send screen updates to the vnc client using the server
+ * surface and server dirty map. guest surface updates
+ * happening in parallel don't disturb us, the next pass will
+ * send them to the client.
+ */
n_rectangles = 0;
vnc_write_u8(vs, 0); /* msg id */
vnc_write_u8(vs, 0);
saved_offset = vs->output.offset;
vnc_write_u16(vs, 0);
- for (y = 0; y < vs->serverds.height; y++) {
+ for (y = 0; y < vs->server.ds->height; y++) {
int x;
int last_x = -1;
- for (x = 0; x < vs->serverds.width / 16; x++) {
- if (vnc_get_bit(vs->dirty_row[y], x)) {
+ for (x = 0; x < vs->server.ds->width / 16; x++) {
+ if (vnc_get_bit(vs->server.dirty[y], x)) {
if (last_x == -1) {
last_x = x;
}
- vnc_clear_bit(vs->dirty_row[y], x);
+ vnc_clear_bit(vs->server.dirty[y], x);
} else {
if (last_x != -1) {
- int h = find_dirty_height(vs, y, last_x, x);
+ int h = find_and_clear_dirty_height(&vs->server, y, last_x, x);
send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h);
n_rectangles++;
}
}
}
if (last_x != -1) {
- int h = find_dirty_height(vs, y, last_x, x);
+ int h = find_and_clear_dirty_height(&vs->server, y, last_x, x);
send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h);
n_rectangles++;
}
vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
vnc_flush(vs);
+ vs->force_update = 0;
}
ops.destroy = audio_capture_destroy;
ops.capture = audio_capture;
- vs->audio_cap = AUD_add_capture(NULL, &vs->as, &ops, vs);
+ vs->audio_cap = AUD_add_capture(&vs->as, &ops, vs);
if (!vs->audio_cap) {
monitor_printf(mon, "Failed to add audio capture\n");
}
if (!vs->vd->clients)
dcl->idle = 1;
- qemu_free(vs->old_data);
+ qemu_free(vs->server.ds->data);
+ qemu_free(vs->server.ds);
+ qemu_free(vs->guest.ds);
qemu_free(vs);
-
+
return 0;
}
return ret;
} else
#endif /* CONFIG_VNC_TLS */
ret = send(vs->csock, data, datalen, 0);
- VNC_DEBUG("Wrote wire %p %d -> %ld\n", data, datalen, ret);
+ VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret);
return vnc_client_io_error(vs, ret, socket_error());
}
long ret;
#ifdef CONFIG_VNC_SASL
- VNC_DEBUG("Write Plain: Pending output %p size %d offset %d. Wait SSF %d\n",
+ VNC_DEBUG("Write Plain: Pending output %p size %zd offset %zd. Wait SSF %d\n",
vs->output.buffer, vs->output.capacity, vs->output.offset,
vs->sasl.waitWriteSSF);
} else
#endif /* CONFIG_VNC_TLS */
ret = recv(vs->csock, data, datalen, 0);
- VNC_DEBUG("Read wire %p %d -> %ld\n", data, datalen, ret);
+ VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret);
return vnc_client_io_error(vs, ret, socket_error());
}
static long vnc_client_read_plain(VncState *vs)
{
int ret;
- VNC_DEBUG("Read plain %p size %d offset %d\n",
+ VNC_DEBUG("Read plain %p size %zd offset %zd\n",
vs->input.buffer, vs->input.capacity, vs->input.offset);
buffer_reserve(&vs->input, 4096);
ret = vnc_client_read_buf(vs, buffer_end(&vs->input), 4096);
case 0xb8: /* Right ALT */
break;
case 0xc8:
+ case 0x48:
kbd_put_keysym(QEMU_KEY_UP);
break;
case 0xd0:
+ case 0x50:
kbd_put_keysym(QEMU_KEY_DOWN);
break;
case 0xcb:
+ case 0x4b:
kbd_put_keysym(QEMU_KEY_LEFT);
break;
case 0xcd:
+ case 0x4d:
kbd_put_keysym(QEMU_KEY_RIGHT);
break;
case 0xd3:
+ case 0x53:
kbd_put_keysym(QEMU_KEY_DELETE);
break;
case 0xc7:
+ case 0x47:
kbd_put_keysym(QEMU_KEY_HOME);
break;
case 0xcf:
+ case 0x4f:
kbd_put_keysym(QEMU_KEY_END);
break;
case 0xc9:
+ case 0x49:
kbd_put_keysym(QEMU_KEY_PAGEUP);
break;
case 0xd1:
+ case 0x51:
kbd_put_keysym(QEMU_KEY_PAGEDOWN);
break;
default:
int i;
vs->need_update = 1;
if (!incremental) {
- char *old_row = vs->old_data + y_position * ds_get_linesize(vs->ds);
-
+ vs->force_update = 1;
for (i = 0; i < h; i++) {
- vnc_set_bits(vs->dirty_row[y_position + i],
+ vnc_set_bits(vs->guest.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);
- memset(old_row, 42, ds_get_width(vs->ds) * ds_get_bytes_per_pixel(vs->ds));
- old_row += ds_get_linesize(vs->ds);
}
}
}
return;
}
- vs->clientds = vs->serverds;
+ vs->clientds = *(vs->guest.ds);
vs->clientds.pf.rmax = red_max;
count_bits(vs->clientds.pf.rbits, red_max);
vs->clientds.pf.rshift = red_shift;
else if (vs->ds->surface->pf.bits_per_pixel == 8)
vs->send_hextile_tile = send_hextile_tile_8;
vs->clientds = *(vs->ds->surface);
- vs->clientds.flags |= ~QEMU_ALLOCATED_FLAG;
+ vs->clientds.flags &= ~QEMU_ALLOCATED_FLAG;
vs->write_pixels = vnc_write_pixels_copy;
vnc_write(vs, pad, 3); /* padding */
vnc_write(vs, "RFB 003.008\n", 12);
vnc_flush(vs);
vnc_read_when(vs, protocol_version, 12);
- memset(vs->old_data, 0, ds_get_linesize(vs->ds) * ds_get_height(vs->ds));
- memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
vnc_update_client(vs);
reset_keys(vs);
return 0;
}
+char *vnc_display_local_addr(DisplayState *ds)
+{
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
+
+ return vnc_socket_local_addr("%s:%s", vs->lsock);
+}
+
int vnc_display_open(DisplayState *ds, const char *display)
{
VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;