X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=ui%2Fvnc.c;h=0b5aad2952ffbaf43b8bb1d4288a7877717226e2;hb=821601ea5b02a68ada479731a4d3d07a9876632a;hp=495d6d6ef1ea43e839ccb49d4b8a2cd30d2d11cf;hpb=6107ff12922e5508a44b376b40a3041810cb00d2;p=qemu.git diff --git a/ui/vnc.c b/ui/vnc.c index 495d6d6ef..0b5aad295 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -35,17 +35,12 @@ #define VNC_REFRESH_INTERVAL_BASE 30 #define VNC_REFRESH_INTERVAL_INC 50 #define VNC_REFRESH_INTERVAL_MAX 2000 +static const struct timeval VNC_REFRESH_STATS = { 0, 500000 }; +static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 }; #include "vnc_keysym.h" #include "d3des.h" -#define count_bits(c, v) { \ - for (c = 0; v; v >>= 1) \ - { \ - c += v & 1; \ - } \ -} - static VncDisplay *vnc_display; /* needed for info vnc */ static DisplayChangeListener *dcl; @@ -376,47 +371,6 @@ static void framebuffer_update_request(VncState *vs, int incremental, 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) -{ - d[k >> 5] |= 1 << (k & 0x1f); -} - -static inline void vnc_clear_bit(uint32_t *d, int k) -{ - d[k >> 5] &= ~(1 << (k & 0x1f)); -} - -static inline void vnc_set_bits(uint32_t *d, int n, int nb_words) -{ - int j; - - j = 0; - while (n >= 32) { - d[j++] = -1; - n -= 32; - } - if (n > 0) - d[j++] = (1 << n) - 1; - while (j < nb_words) - d[j++] = 0; -} - -static inline int vnc_get_bit(const uint32_t *d, int k) -{ - return (d[k >> 5] >> (k & 0x1f)) & 1; -} - -static inline int vnc_and_bits(const uint32_t *d1, const uint32_t *d2, - int nb_words) -{ - int i; - for(i = 0; i < nb_words; i++) { - if ((d1[i] & d2[i]) != 0) - return 1; - } - return 0; -} - static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h) { int i; @@ -439,7 +393,7 @@ static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h) for (; y < h; y++) for (i = 0; i < w; i += 16) - vnc_set_bit(s->dirty[y], (x + i) / 16); + set_bit((x + i) / 16, s->dirty[y]); } void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, @@ -694,6 +648,12 @@ int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) case VNC_ENCODING_TIGHT_PNG: n = vnc_tight_png_send_framebuffer_update(vs, x, y, w, h); break; + case VNC_ENCODING_ZRLE: + n = vnc_zrle_send_framebuffer_update(vs, x, y, w, h); + break; + case VNC_ENCODING_ZYWRLE: + n = vnc_zywrle_send_framebuffer_update(vs, x, y, w, h); + break; default: vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW); n = vnc_raw_send_framebuffer_update(vs, x, y, w, h); @@ -772,7 +732,7 @@ static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int memmove(dst_row, src_row, cmp_bytes); QTAILQ_FOREACH(vs, &vd->clients, next) { if (!vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) { - vnc_set_bit(vs->dirty[y], ((x + dst_x) / 16)); + set_bit(((x + dst_x) / 16), vs->dirty[y]); } } } @@ -835,17 +795,18 @@ static void vnc_dpy_cursor_define(QEMUCursor *c) } 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 height) { int h; - VncDisplay *vd = vs->vd; - for (h = 1; h < (vd->server->height - y); h++) { + for (h = 1; h < (height - y); h++) { int tmp_x; - if (!vnc_get_bit(vs->dirty[y + h], last_x)) + if (!test_bit(last_x, vs->dirty[y + h])) { break; - for (tmp_x = last_x; tmp_x < x; tmp_x++) - vnc_clear_bit(vs->dirty[y + h], tmp_x); + } + for (tmp_x = last_x; tmp_x < x; tmp_x++) { + clear_bit(tmp_x, vs->dirty[y + h]); + } } return h; @@ -897,14 +858,14 @@ static int vnc_update_client(VncState *vs, int has_dirty) int x; int last_x = -1; for (x = 0; x < width / 16; x++) { - if (vnc_get_bit(vs->dirty[y], x)) { + if (test_and_clear_bit(x, vs->dirty[y])) { if (last_x == -1) { last_x = x; } - vnc_clear_bit(vs->dirty[y], x); } else { if (last_x != -1) { - int h = find_and_clear_dirty_height(vs, y, last_x, x); + 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); @@ -913,7 +874,7 @@ static int vnc_update_client(VncState *vs, int has_dirty) } } if (last_x != -1) { - int h = find_and_clear_dirty_height(vs, y, last_x, x); + 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); } @@ -1012,6 +973,8 @@ static void vnc_disconnect_start(VncState *vs) static void vnc_disconnect_finish(VncState *vs) { + int i; + vnc_jobs_join(vs); /* Wait encoding jobs */ vnc_lock_output(vs); @@ -1024,6 +987,7 @@ static void vnc_disconnect_finish(VncState *vs) vnc_zlib_clear(vs); vnc_tight_clear(vs); + vnc_zrle_clear(vs); #ifdef CONFIG_VNC_TLS vnc_tls_client_cleanup(vs); @@ -1048,6 +1012,10 @@ static void vnc_disconnect_finish(VncState *vs) #ifdef CONFIG_VNC_THREAD qemu_mutex_destroy(&vs->output_mutex); #endif + for (i = 0; i < VNC_STAT_ROWS; ++i) { + qemu_free(vs->lossy_rect[i]); + } + qemu_free(vs->lossy_rect); qemu_free(vs); } @@ -1504,7 +1472,7 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) break; } - if (vs->vd->lock_key_sync && + if (down && vs->vd->lock_key_sync && keycode_is_keypad(vs->vd->kbd_layout, keycode)) { /* If the numlock state needs to change then simulate an additional keypress before sending this one. This will happen if the user @@ -1523,7 +1491,7 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) } } - if (vs->vd->lock_key_sync && + if (down && vs->vd->lock_key_sync && ((sym >= 'A' && sym <= 'Z') || (sym >= 'a' && sym <= 'z'))) { /* If the capslock state needs to change then simulate an additional keypress before sending this one. This will happen if the user @@ -1687,8 +1655,7 @@ static void framebuffer_update_request(VncState *vs, int incremental, if (!incremental) { vs->force_update = 1; for (i = 0; i < h; i++) { - vnc_set_bits(vs->dirty[y_position + i], - (ds_get_width(vs->ds) / 16), VNC_DIRTY_WORDS); + bitmap_set(vs->dirty[y_position + i], x_position / 16, w / 16); } } } @@ -1758,6 +1725,14 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) vs->features |= VNC_FEATURE_ZLIB_MASK; vs->vnc_encoding = enc; break; + case VNC_ENCODING_ZRLE: + vs->features |= VNC_FEATURE_ZRLE_MASK; + vs->vnc_encoding = enc; + break; + case VNC_ENCODING_ZYWRLE: + vs->features |= VNC_FEATURE_ZYWRLE_MASK; + vs->vnc_encoding = enc; + break; case VNC_ENCODING_DESKTOPRESIZE: vs->features |= VNC_FEATURE_RESIZE_MASK; break; @@ -1780,7 +1755,9 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) vs->tight.compression = (enc & 0x0F); break; case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9: - vs->tight.quality = (enc & 0x0F); + if (vs->vd->lossy) { + vs->tight.quality = (enc & 0x0F); + } break; default: VNC_DEBUG("Unknown encoding: %d (0x%.8x): %d\n", i, enc, enc); @@ -1817,15 +1794,15 @@ static void set_pixel_format(VncState *vs, vs->clientds = *(vs->vd->guest.ds); vs->clientds.pf.rmax = red_max; - count_bits(vs->clientds.pf.rbits, red_max); + vs->clientds.pf.rbits = hweight_long(red_max); vs->clientds.pf.rshift = red_shift; vs->clientds.pf.rmask = red_max << red_shift; vs->clientds.pf.gmax = green_max; - count_bits(vs->clientds.pf.gbits, green_max); + vs->clientds.pf.gbits = hweight_long(green_max); vs->clientds.pf.gshift = green_shift; vs->clientds.pf.gmask = green_max << green_shift; vs->clientds.pf.bmax = blue_max; - count_bits(vs->clientds.pf.bbits, blue_max); + vs->clientds.pf.bbits = hweight_long(blue_max); vs->clientds.pf.bshift = blue_shift; vs->clientds.pf.bmask = blue_max << blue_shift; vs->clientds.pf.bits_per_pixel = bits_per_pixel; @@ -1898,8 +1875,8 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (data[0] > 3) { vd->timer_interval = VNC_REFRESH_INTERVAL_BASE; - if (!qemu_timer_expired(vd->timer, qemu_get_clock(rt_clock) + vd->timer_interval)) - qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) + vd->timer_interval); + if (!qemu_timer_expired(vd->timer, qemu_get_clock_ms(rt_clock) + vd->timer_interval)) + qemu_mod_timer(vd->timer, qemu_get_clock_ms(rt_clock) + vd->timer_interval); } switch (data[0]) { @@ -2084,7 +2061,7 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len) unsigned char key[8]; time_t now = time(NULL); - if (!vs->vd->password || !vs->vd->password[0]) { + if (!vs->vd->password) { VNC_DEBUG("No password configured on server"); goto reject; } @@ -2256,27 +2233,176 @@ static int protocol_version(VncState *vs, uint8_t *version, size_t len) return 0; } +static VncRectStat *vnc_stat_rect(VncDisplay *vd, int x, int y) +{ + struct VncSurface *vs = &vd->guest; + + return &vs->stats[y / VNC_STAT_RECT][x / VNC_STAT_RECT]; +} + +void vnc_sent_lossy_rect(VncState *vs, int x, int y, int w, int h) +{ + int i, j; + + w = (x + w) / VNC_STAT_RECT; + h = (y + h) / VNC_STAT_RECT; + x /= VNC_STAT_RECT; + y /= VNC_STAT_RECT; + + for (j = y; j <= h; j++) { + for (i = x; i <= w; i++) { + vs->lossy_rect[j][i] = 1; + } + } +} + +static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y) +{ + VncState *vs; + int sty = y / VNC_STAT_RECT; + int stx = x / VNC_STAT_RECT; + int has_dirty = 0; + + y = y / VNC_STAT_RECT * VNC_STAT_RECT; + x = x / VNC_STAT_RECT * VNC_STAT_RECT; + + QTAILQ_FOREACH(vs, &vd->clients, next) { + int j; + + /* kernel send buffers are full -> refresh later */ + if (vs->output.offset) { + continue; + } + + if (!vs->lossy_rect[sty][stx]) { + continue; + } + + 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); + } + has_dirty++; + } + + return has_dirty; +} + +static int vnc_update_stats(VncDisplay *vd, struct timeval * tv) +{ + int x, y; + struct timeval res; + int has_dirty = 0; + + for (y = 0; y < vd->guest.ds->height; y += VNC_STAT_RECT) { + for (x = 0; x < vd->guest.ds->width; x += VNC_STAT_RECT) { + VncRectStat *rect = vnc_stat_rect(vd, x, y); + + rect->updated = false; + } + } + + qemu_timersub(tv, &VNC_REFRESH_STATS, &res); + + if (timercmp(&vd->guest.last_freq_check, &res, >)) { + return has_dirty; + } + vd->guest.last_freq_check = *tv; + + for (y = 0; y < vd->guest.ds->height; y += VNC_STAT_RECT) { + for (x = 0; x < vd->guest.ds->width; x += VNC_STAT_RECT) { + VncRectStat *rect= vnc_stat_rect(vd, x, y); + int count = ARRAY_SIZE(rect->times); + struct timeval min, max; + + if (!timerisset(&rect->times[count - 1])) { + continue ; + } + + max = rect->times[(rect->idx + count - 1) % count]; + qemu_timersub(tv, &max, &res); + + if (timercmp(&res, &VNC_REFRESH_LOSSY, >)) { + rect->freq = 0; + has_dirty += vnc_refresh_lossy_rect(vd, x, y); + memset(rect->times, 0, sizeof (rect->times)); + continue ; + } + + min = rect->times[rect->idx]; + max = rect->times[(rect->idx + count - 1) % count]; + qemu_timersub(&max, &min, &res); + + rect->freq = res.tv_sec + res.tv_usec / 1000000.; + rect->freq /= count; + rect->freq = 1. / rect->freq; + } + } + return has_dirty; +} + +double vnc_update_freq(VncState *vs, int x, int y, int w, int h) +{ + int i, j; + double total = 0; + int num = 0; + + x = (x / VNC_STAT_RECT) * VNC_STAT_RECT; + y = (y / VNC_STAT_RECT) * VNC_STAT_RECT; + + for (j = y; j <= y + h; j += VNC_STAT_RECT) { + for (i = x; i <= x + w; i += VNC_STAT_RECT) { + total += vnc_stat_rect(vs->vd, i, j)->freq; + num++; + } + } + + if (num) { + return total / num; + } else { + return 0; + } +} + +static void vnc_rect_updated(VncDisplay *vd, int x, int y, struct timeval * tv) +{ + VncRectStat *rect; + + rect = vnc_stat_rect(vd, x, y); + if (rect->updated) { + return ; + } + rect->times[rect->idx] = *tv; + rect->idx = (rect->idx + 1) % ARRAY_SIZE(rect->times); + rect->updated = true; +} + 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; int has_dirty = 0; + struct timeval tv = { 0, 0 }; + + if (!vd->non_adaptive) { + gettimeofday(&tv, NULL); + has_dirty = vnc_update_stats(vd, &tv); + } + /* * 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)) { + if (!bitmap_empty(vd->guest.dirty[y], VNC_DIRTY_BITS)) { int x; uint8_t *guest_ptr; uint8_t *server_ptr; @@ -2286,14 +2412,15 @@ static int vnc_refresh_server_surface(VncDisplay *vd) 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))) + if (!test_and_clear_bit((x / 16), vd->guest.dirty[y])) 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); + if (!vd->non_adaptive) + vnc_rect_updated(vd, x, y, &tv); QTAILQ_FOREACH(vs, &vd->clients, next) { - vnc_set_bit(vs->dirty[y], (x / 16)); + set_bit((x / 16), vs->dirty[y]); } has_dirty++; } @@ -2314,7 +2441,7 @@ static void vnc_refresh(void *opaque) if (vnc_trylock_display(vd)) { vd->timer_interval = VNC_REFRESH_INTERVAL_BASE; - qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) + + qemu_mod_timer(vd->timer, qemu_get_clock_ms(rt_clock) + vd->timer_interval); return; } @@ -2341,14 +2468,15 @@ static void vnc_refresh(void *opaque) if (vd->timer_interval > VNC_REFRESH_INTERVAL_MAX) vd->timer_interval = VNC_REFRESH_INTERVAL_MAX; } - qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) + vd->timer_interval); + qemu_mod_timer(vd->timer, qemu_get_clock_ms(rt_clock) + vd->timer_interval); } static void vnc_init_timer(VncDisplay *vd) { vd->timer_interval = VNC_REFRESH_INTERVAL_BASE; if (vd->timer == NULL && !QTAILQ_EMPTY(&vd->clients)) { - vd->timer = qemu_new_timer(rt_clock, vnc_refresh, vd); + vd->timer = qemu_new_timer_ms(rt_clock, vnc_refresh, vd); + vnc_dpy_resize(vd->ds); vnc_refresh(vd); } } @@ -2365,7 +2493,13 @@ static void vnc_remove_timer(VncDisplay *vd) static void vnc_connect(VncDisplay *vd, int csock) { VncState *vs = qemu_mallocz(sizeof(VncState)); + int i; + vs->csock = csock; + vs->lossy_rect = qemu_mallocz(VNC_STAT_ROWS * sizeof (*vs->lossy_rect)); + for (i = 0; i < VNC_STAT_ROWS; ++i) { + vs->lossy_rect[i] = qemu_mallocz(VNC_STAT_COLS * sizeof (uint8_t)); + } VNC_DEBUG("New client on socket %d\n", csock); dcl->idle = 0; @@ -2484,7 +2618,7 @@ void vnc_display_close(DisplayState *ds) #endif } -int vnc_display_password(DisplayState *ds, const char *password) +int vnc_display_disable_login(DisplayState *ds) { VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; @@ -2494,21 +2628,44 @@ int vnc_display_password(DisplayState *ds, const char *password) if (vs->password) { qemu_free(vs->password); - vs->password = NULL; - } - if (password && password[0]) { - if (!(vs->password = qemu_strdup(password))) - return -1; - if (vs->auth == VNC_AUTH_NONE) { - vs->auth = VNC_AUTH_VNC; - } - } else { - vs->auth = VNC_AUTH_NONE; } + vs->password = NULL; + vs->auth = VNC_AUTH_VNC; + return 0; } +int vnc_display_password(DisplayState *ds, const char *password) +{ + int ret = 0; + VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + + if (!vs) { + ret = -EINVAL; + goto out; + } + + if (!password) { + /* This is not the intention of this interface but err on the side + of being safe */ + ret = vnc_display_disable_login(ds); + goto out; + } + + if (vs->password) { + qemu_free(vs->password); + vs->password = NULL; + } + vs->password = qemu_strdup(password); + vs->auth = VNC_AUTH_VNC; +out: + if (ret != 0) { + qerror_report(QERR_SET_PASSWD_FAILED); + } + return ret; +} + int vnc_display_pw_expire(DisplayState *ds, time_t expires) { VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; @@ -2603,6 +2760,8 @@ int vnc_display_open(DisplayState *ds, const char *display) #endif } else if (strncmp(options, "lossy", 5) == 0) { vs->lossy = true; + } else if (strncmp(options, "non-adapative", 13) == 0) { + vs->non_adaptive = true; } }