X-Git-Url: http://vcs.maemo.org/git/?a=blobdiff_plain;f=vnc.c;h=3f5d622ab451dc714fb2c91f28620efa38e32cdf;hb=62518b8b1dccb67969688228f14fb379f1918ede;hp=a67d23f8b1bda9c0ea29ee689404bc34ef29f168;hpb=28a76be8f4536619ab15ce452308df78cfc65e39;p=qemu diff --git a/vnc.c b/vnc.c index a67d23f..3f5d622 100644 --- a/vnc.c +++ b/vnc.c @@ -53,6 +53,7 @@ static char *addr_to_string(const char *format, char host[NI_MAXHOST]; char serv[NI_MAXSERV]; int err; + size_t addrlen; if ((err = getnameinfo((struct sockaddr *)sa, salen, host, sizeof(host), @@ -63,8 +64,12 @@ static char *addr_to_string(const char *format, 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; } @@ -81,7 +86,6 @@ char *vnc_socket_local_addr(const char *format, int fd) { return addr_to_string(format, &sa, salen); } - char *vnc_socket_remote_addr(const char *format, int fd) { struct sockaddr_storage sa; socklen_t salen; @@ -144,8 +148,6 @@ static const char *vnc_auth_name(VncDisplay *vd) { return "unknown"; } -#define VNC_SOCKET_FORMAT_PRETTY "local %s:%s" - static void do_info_vnc_client(Monitor *mon, VncState *client) { char *clientAddr = @@ -260,6 +262,7 @@ static inline int vnc_and_bits(const uint32_t *d1, const uint32_t *d2, static void vnc_update(VncState *vs, int x, int y, int w, int h) { + struct VncSurface *s = &vs->guest; int i; h += y; @@ -271,14 +274,14 @@ static void vnc_update(VncState *vs, int x, int y, int w, int h) 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) @@ -338,22 +341,17 @@ void buffer_append(Buffer *buffer, const void *data, size_t len) 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 */ @@ -364,9 +362,17 @@ static void vnc_resize(VncState *vs) 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) @@ -390,12 +396,12 @@ static void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v) { 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); @@ -433,7 +439,7 @@ static void vnc_write_pixels_generic(VncState *vs, void *pixels1, int size) { 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; @@ -441,7 +447,7 @@ static void vnc_write_pixels_generic(VncState *vs, void *pixels1, int size) 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; @@ -449,7 +455,7 @@ static void vnc_write_pixels_generic(VncState *vs, void *pixels1, int size) 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; @@ -467,7 +473,7 @@ static void send_framebuffer_update_raw(VncState *vs, int x, int y, int w, int h 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); @@ -516,8 +522,8 @@ static void send_framebuffer_update_hextile(VncState *vs, int x, int y, int w, i 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) { @@ -646,6 +652,7 @@ 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) { + vs->force_update = 1; vnc_update_client(vs); vnc_write_u8(vs, 0); /* msg id */ @@ -670,16 +677,17 @@ static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int } } -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; @@ -690,72 +698,85 @@ static void vnc_update_client(void *opaque) 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++; } @@ -763,7 +784,7 @@ static void vnc_update_client(void *opaque) } } 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++; } @@ -771,6 +792,7 @@ static void vnc_update_client(void *opaque) 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; } @@ -832,7 +854,7 @@ static void audio_add(VncState *vs) 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"); } @@ -892,9 +914,11 @@ int vnc_client_io_error(VncState *vs, int ret, int last_errno) 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; @@ -938,7 +962,7 @@ long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen) } 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()); } @@ -958,7 +982,7 @@ static long vnc_client_write_plain(VncState *vs) 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); @@ -1043,7 +1067,7 @@ long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen) } 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()); } @@ -1059,7 +1083,7 @@ long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen) 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); @@ -1318,30 +1342,39 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) 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: @@ -1389,13 +1422,12 @@ static void framebuffer_update_request(VncState *vs, int incremental, 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); } } } @@ -1523,7 +1555,7 @@ static void set_pixel_format(VncState *vs, 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; @@ -1572,7 +1604,7 @@ static void pixel_format_message (VncState *vs) { 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 */ @@ -1977,8 +2009,6 @@ static void vnc_connect(VncDisplay *vd, int csock) 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); @@ -2070,6 +2100,13 @@ int vnc_display_password(DisplayState *ds, const char *password) 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;