*/
#include "vnc.h"
+#include "vnc-jobs.h"
#include "sysemu.h"
#include "qemu_socket.h"
#include "qemu-timer.h"
#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;
static void vnc_client_cache_auth(VncState *client)
{
+#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
QDict *qdict;
+#endif
if (!client->info) {
return;
}
+#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
qdict = qobject_to_qdict(client->info);
+#endif
#ifdef CONFIG_VNC_TLS
if (client->tls.session &&
}
}
-static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
- return (vs->features & (1 << feature));
-}
-
/* TODO
1) Get the queue working for IO.
2) there is some weirdness when using the -S option (the screen is grey
*/
static int vnc_update_client(VncState *vs, int has_dirty);
+static int vnc_update_client_sync(VncState *vs, int has_dirty);
static void vnc_disconnect_start(VncState *vs);
static void vnc_disconnect_finish(VncState *vs);
static void vnc_init_timer(VncDisplay *vd);
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;
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,
}
vs->client_width = ds_get_width(ds);
vs->client_height = ds_get_height(ds);
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */
vnc_framebuffer_update(vs, 0, 0, vs->client_width, vs->client_height,
VNC_ENCODING_DESKTOPRESIZE);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
+#ifdef CONFIG_VNC_THREAD
+static void vnc_abort_display_jobs(VncDisplay *vd)
+{
+ VncState *vs;
+
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_lock_output(vs);
+ vs->abort = true;
+ vnc_unlock_output(vs);
+ }
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_jobs_join(vs);
+ }
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_lock_output(vs);
+ vs->abort = false;
+ vnc_unlock_output(vs);
+ }
+}
+#else
+static void vnc_abort_display_jobs(VncDisplay *vd)
+{
+}
+#endif
+
static void vnc_dpy_resize(DisplayState *ds)
{
VncDisplay *vd = ds->opaque;
VncState *vs;
+ vnc_abort_display_jobs(vd);
+
/* server surface */
if (!vd->server)
vd->server = qemu_mallocz(sizeof(*vd->server));
return 1;
}
-static int send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
+int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
{
int n = 0;
case VNC_ENCODING_TIGHT:
n = vnc_tight_send_framebuffer_update(vs, x, y, w, h);
break;
+ 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);
static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
{
/* send bitblit op to the vnc client */
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */
vnc_framebuffer_update(vs, dst_x, dst_y, w, h, VNC_ENCODING_COPYRECT);
vnc_write_u16(vs, src_x);
vnc_write_u16(vs, src_y);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
vs->force_update = 1;
- vnc_update_client(vs, 1);
+ vnc_update_client_sync(vs, 1);
/* vs might be free()ed here */
}
}
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]);
}
}
}
int isize;
if (vnc_has_feature(vs, VNC_FEATURE_RICH_CURSOR)) {
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0); /* padding */
vnc_write_u16(vs, 1); /* # of rects */
isize = c->width * c->height * vs->clientds.pf.bytes_per_pixel;
vnc_write_pixels_generic(vs, &pf, c->data, isize);
vnc_write(vs, vs->vd->cursor_mask, vs->vd->cursor_msize);
+ vnc_unlock_output(vs);
return 0;
}
return -1;
}
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;
}
+#ifdef CONFIG_VNC_THREAD
+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;
+}
+#else
+static int vnc_update_client_sync(VncState *vs, int has_dirty)
+{
+ return vnc_update_client(vs, has_dirty);
+}
+#endif
+
static int vnc_update_client(VncState *vs, int has_dirty)
{
if (vs->need_update && vs->csock != -1) {
VncDisplay *vd = vs->vd;
+ VncJob *job;
int y;
- int n_rectangles;
- int saved_offset;
int width, height;
- int n;
+ int n = 0;
+
if (vs->output.offset && !vs->audio_cap && !vs->force_update)
/* kernel send buffers are full -> drop frames to throttle */
* happening in parallel don't disturb us, the next pass will
* send them to the client.
*/
- n_rectangles = 0;
- vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
- vnc_write_u8(vs, 0);
- saved_offset = vs->output.offset;
- vnc_write_u16(vs, 0);
+ job = vnc_job_new(vs);
width = MIN(vd->server->width, vs->client_width);
height = MIN(vd->server->height, vs->client_height);
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);
- n = send_framebuffer_update(vs, last_x * 16, y,
- (x - last_x) * 16, h);
- n_rectangles += n;
+ 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);
- n = send_framebuffer_update(vs, last_x * 16, y,
- (x - last_x) * 16, h);
- n_rectangles += n;
+ 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);
}
}
- vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
- vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
- vnc_flush(vs);
+
+ vnc_job_push(job);
vs->force_update = 0;
- return n_rectangles;
+ return n;
}
if (vs->csock == -1)
switch (cmd) {
case AUD_CNOTIFY_DISABLE:
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_END);
+ vnc_unlock_output(vs);
vnc_flush(vs);
break;
case AUD_CNOTIFY_ENABLE:
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_BEGIN);
+ vnc_unlock_output(vs);
vnc_flush(vs);
break;
}
{
VncState *vs = opaque;
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA);
vnc_write_u32(vs, size);
vnc_write(vs, buf, size);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
static void vnc_disconnect_finish(VncState *vs)
{
+ int i;
+
+ vnc_jobs_join(vs); /* Wait encoding jobs */
+
+ vnc_lock_output(vs);
vnc_qmp_event(vs, QEVENT_VNC_DISCONNECTED);
buffer_free(&vs->input);
vnc_zlib_clear(vs);
vnc_tight_clear(vs);
+ vnc_zrle_clear(vs);
#ifdef CONFIG_VNC_TLS
vnc_tls_client_cleanup(vs);
vnc_remove_timer(vs->vd);
if (vs->vd->lock_key_sync)
qemu_remove_led_event_handler(vs->led);
+ vnc_unlock_output(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);
}
* the client socket. Will delegate actual work according to whether
* SASL SSF layers are enabled (thus requiring encryption calls)
*/
-void vnc_client_write(void *opaque)
+static void vnc_client_write_locked(void *opaque)
{
VncState *vs = opaque;
vnc_client_write_plain(vs);
}
+void vnc_client_write(void *opaque)
+{
+ VncState *vs = opaque;
+
+ vnc_lock_output(vs);
+ if (vs->output.offset) {
+ vnc_client_write_locked(opaque);
+ } else if (vs->csock != -1) {
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ }
+ vnc_unlock_output(vs);
+}
+
void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
{
vs->read_handler = func;
}
} else
#endif /* CONFIG_VNC_TLS */
- ret = recv(vs->csock, (void *)data, datalen, 0);
+ ret = qemu_recv(vs->csock, data, datalen, 0);
VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret);
return vnc_client_io_error(vs, ret, socket_error());
}
void vnc_flush(VncState *vs)
{
- if (vs->csock != -1 && vs->output.offset)
- vnc_client_write(vs);
+ vnc_lock_output(vs);
+ if (vs->csock != -1 && vs->output.offset) {
+ vnc_client_write_locked(vs);
+ }
+ vnc_unlock_output(vs);
}
uint8_t read_u8(uint8_t *data, size_t offset)
{
}
-static void check_pointer_type_change(Notifier *notifier)
+static void check_pointer_type_change(Notifier *notifier, void *data)
{
VncState *vs = container_of(notifier, VncState, mouse_mode_notifier);
int absolute = kbd_mouse_is_absolute();
if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) {
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, absolute, 0,
ds_get_width(vs->ds), ds_get_height(vs->ds),
VNC_ENCODING_POINTER_TYPE_CHANGE);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
vs->absolute = absolute;
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
}
}
- 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
int x_position, int y_position,
int w, int h)
{
+ int i;
+ const size_t width = ds_get_width(vs->ds) / 16;
+
if (y_position > ds_get_height(vs->ds))
y_position = ds_get_height(vs->ds);
if (y_position + h >= ds_get_height(vs->ds))
h = ds_get_height(vs->ds) - y_position;
- int i;
vs->need_update = 1;
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], 0, width);
+ bitmap_clear(vs->dirty[y_position + i], width,
+ VNC_DIRTY_BITS - width);
}
}
}
static void send_ext_key_event_ack(VncState *vs)
{
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
VNC_ENCODING_EXT_KEY_EVENT);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
static void send_ext_audio_ack(VncState *vs)
{
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
VNC_ENCODING_AUDIO);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
vs->features = 0;
vs->vnc_encoding = 0;
- vs->tight_compression = 9;
- vs->tight_quality = -1; /* Lossless by default */
+ vs->tight.compression = 9;
+ vs->tight.quality = -1; /* Lossless by default */
vs->absolute = -1;
/*
vs->features |= VNC_FEATURE_TIGHT_MASK;
vs->vnc_encoding = enc;
break;
+ case VNC_ENCODING_TIGHT_PNG:
+ vs->features |= VNC_FEATURE_TIGHT_PNG_MASK;
+ vs->vnc_encoding = enc;
+ break;
case VNC_ENCODING_ZLIB:
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;
vs->features |= VNC_FEATURE_WMVI_MASK;
break;
case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9:
- vs->tight_compression = (enc & 0x0F);
+ 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);
}
}
vnc_desktop_resize(vs);
- check_pointer_type_change(&vs->mouse_mode_notifier);
+ check_pointer_type_change(&vs->mouse_mode_notifier, NULL);
}
static void set_pixel_conversion(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;
{
if (vnc_has_feature(vs, VNC_FEATURE_WMVI)) {
/* Sending a WMVi message to notify the client*/
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */
vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds),
ds_get_height(vs->ds), VNC_ENCODING_WMVi);
pixel_format_message(vs);
+ vnc_unlock_output(vs);
vnc_flush(vs);
} else {
set_pixel_conversion(vs);
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]) {
unsigned char response[VNC_AUTH_CHALLENGE_SIZE];
int i, j, pwlen;
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");
- vnc_write_u32(vs, 1); /* Reject auth */
- if (vs->minor >= 8) {
- static const char err[] = "Authentication failed";
- vnc_write_u32(vs, sizeof(err));
- vnc_write(vs, err, sizeof(err));
- }
- vnc_flush(vs);
- vnc_client_error(vs);
- return 0;
+ goto reject;
+ }
+ if (vs->vd->expires < now) {
+ VNC_DEBUG("Password is expired");
+ goto reject;
}
memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);
/* Compare expected vs actual challenge response */
if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) {
VNC_DEBUG("Client challenge reponse did not match\n");
- vnc_write_u32(vs, 1); /* Reject auth */
- if (vs->minor >= 8) {
- static const char err[] = "Authentication failed";
- vnc_write_u32(vs, sizeof(err));
- vnc_write(vs, err, sizeof(err));
- }
- vnc_flush(vs);
- vnc_client_error(vs);
+ goto reject;
} else {
VNC_DEBUG("Accepting VNC challenge response\n");
vnc_write_u32(vs, 0); /* Accept auth */
start_client_init(vs);
}
return 0;
+
+reject:
+ vnc_write_u32(vs, 1); /* Reject auth */
+ if (vs->minor >= 8) {
+ static const char err[] = "Authentication failed";
+ vnc_write_u32(vs, sizeof(err));
+ vnc_write(vs, err, sizeof(err));
+ }
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ return 0;
}
void start_auth_vnc(VncState *vs)
{
/* We only advertise 1 auth scheme at a time, so client
* must pick the one we sent. Verify this */
- if (data[0] != vs->vd->auth) { /* Reject auth */
+ if (data[0] != vs->auth) { /* Reject auth */
VNC_DEBUG("Reject auth %d because it didn't match advertized\n", (int)data[0]);
vnc_write_u32(vs, 1);
if (vs->minor >= 8) {
vnc_client_error(vs);
} else { /* Accept requested auth */
VNC_DEBUG("Client requested auth %d\n", (int)data[0]);
- switch (vs->vd->auth) {
+ switch (vs->auth) {
case VNC_AUTH_NONE:
VNC_DEBUG("Accept auth none\n");
if (vs->minor >= 8) {
#endif /* CONFIG_VNC_SASL */
default: /* Should not be possible, but just in case */
- VNC_DEBUG("Reject auth %d server code bug\n", vs->vd->auth);
+ VNC_DEBUG("Reject auth %d server code bug\n", vs->auth);
vnc_write_u8(vs, 1);
if (vs->minor >= 8) {
static const char err[] = "Authentication failed";
vs->minor = 3;
if (vs->minor == 3) {
- if (vs->vd->auth == VNC_AUTH_NONE) {
+ if (vs->auth == VNC_AUTH_NONE) {
VNC_DEBUG("Tell client auth none\n");
- vnc_write_u32(vs, vs->vd->auth);
+ vnc_write_u32(vs, vs->auth);
vnc_flush(vs);
start_client_init(vs);
- } else if (vs->vd->auth == VNC_AUTH_VNC) {
+ } else if (vs->auth == VNC_AUTH_VNC) {
VNC_DEBUG("Tell client VNC auth\n");
- vnc_write_u32(vs, vs->vd->auth);
+ vnc_write_u32(vs, vs->auth);
vnc_flush(vs);
start_auth_vnc(vs);
} else {
- VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->vd->auth);
+ VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->auth);
vnc_write_u32(vs, VNC_AUTH_INVALID);
vnc_flush(vs);
vnc_client_error(vs);
}
} else {
- VNC_DEBUG("Telling client we support auth %d\n", vs->vd->auth);
+ VNC_DEBUG("Telling client we support auth %d\n", vs->auth);
vnc_write_u8(vs, 1); /* num auth */
- vnc_write_u8(vs, vs->vd->auth);
+ vnc_write_u8(vs, vs->auth);
vnc_read_when(vs, protocol_client_auth, 1);
vnc_flush(vs);
}
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;
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++;
}
vga_hw_update();
+ if (vnc_trylock_display(vd)) {
+ vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+ qemu_mod_timer(vd->timer, qemu_get_clock_ms(rt_clock) +
+ vd->timer_interval);
+ return;
+ }
+
has_dirty = vnc_refresh_server_surface(vd);
+ vnc_unlock_display(vd);
QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
rects += vnc_update_client(vs, has_dirty);
/* vs might be free()ed here */
}
+
/* vd->timer could be NULL now if the last client disconnected,
* in this case don't update the timer */
if (vd->timer == NULL)
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);
}
}
}
}
-static void vnc_connect(VncDisplay *vd, int csock)
+static void vnc_connect(VncDisplay *vd, int csock, int skipauth)
{
VncState *vs = qemu_mallocz(sizeof(VncState));
+ int i;
+
vs->csock = csock;
+ if (skipauth) {
+ vs->auth = VNC_AUTH_NONE;
+#ifdef CONFIG_VNC_TLS
+ vs->subauth = VNC_AUTH_INVALID;
+#endif
+ } else {
+ vs->auth = vd->auth;
+#ifdef CONFIG_VNC_TLS
+ vs->subauth = vd->subauth;
+#endif
+ }
+
+ 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;
socket_set_nonblock(vs->csock);
vs->as.fmt = AUD_FMT_S16;
vs->as.endianness = 0;
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_init(&vs->output_mutex);
+#endif
+
QTAILQ_INSERT_HEAD(&vd->clients, vs, next);
vga_hw_update();
int csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
if (csock != -1) {
- vnc_connect(vs, csock);
+ vnc_connect(vs, csock, 0);
}
}
vs->ds = ds;
QTAILQ_INIT(&vs->clients);
+ vs->expires = TIME_MAX;
if (keyboard_layout)
vs->kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
if (!vs->kbd_layout)
exit(1);
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_init(&vs->mutex);
+ vnc_start_worker_thread();
+#endif
+
dcl->dpy_copy = vnc_dpy_copy;
dcl->dpy_update = vnc_dpy_update;
dcl->dpy_resize = vnc_dpy_resize;
#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;
return -1;
}
+ if (vs->password) {
+ qemu_free(vs->password);
+ }
+
+ 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;
}
- 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 = 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;
+ vs->expires = expires;
return 0;
}
int sasl = 0;
int saslErr;
#endif
+#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
int acl = 0;
+#endif
int lock_key_sync = 1;
if (!vnc_display)
return -1;
}
#endif
+#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
} else if (strncmp(options, "acl", 3) == 0) {
acl = 1;
+#endif
} else if (strncmp(options, "lossy", 5) == 0) {
vs->lossy = true;
+ } else if (strncmp(options, "non-adapative", 13) == 0) {
+ vs->non_adaptive = true;
}
}
} else {
int csock = vs->lsock;
vs->lsock = -1;
- vnc_connect(vs, csock);
+ vnc_connect(vs, csock, 0);
}
return 0;
}
return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs);
}
+
+void vnc_display_add_client(DisplayState *ds, int csock, int skipauth)
+{
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
+
+ return vnc_connect(vs, csock, skipauth);
+}