]> git.proxmox.com Git - qemu.git/blobdiff - vnc.c
Don't leak VLANClientState on PCI hot remove
[qemu.git] / vnc.c
diff --git a/vnc.c b/vnc.c
index e88f38f973dfe07335a0c223784fd94b913bbe05..1d8ebe7b656e7a11a7f949722743ecd3b1554c79 100644 (file)
--- a/vnc.c
+++ b/vnc.c
@@ -29,6 +29,7 @@
 #include "qemu_socket.h"
 #include "qemu-timer.h"
 #include "audio/audio.h"
+#include <zlib.h>
 
 #define VNC_REFRESH_INTERVAL (1000 / 30)
 
@@ -47,7 +48,7 @@
 #ifdef _VNC_DEBUG
 #define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
 
-#if CONFIG_VNC_TLS && _VNC_DEBUG >= 2
+#if defined(CONFIG_VNC_TLS) && _VNC_DEBUG >= 2
 /* Very verbose, so only enabled for _VNC_DEBUG >= 2 */
 static void vnc_debug_gnutls_log(int level, const char* str) {
     VNC_DEBUG("%d %s", level, str);
@@ -89,25 +90,14 @@ typedef void VncSendHextileTile(VncState *vs,
 
 #define VNC_AUTH_CHALLENGE_SIZE 16
 
-struct VncState
+typedef struct VncDisplay VncDisplay;
+
+struct VncDisplay
 {
-    QEMUTimer *timer;
     int lsock;
-    int csock;
     DisplayState *ds;
-    int need_update;
-    uint32_t dirty_row[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS];
-    char *old_data;
-    int has_resize;
-    int has_hextile;
-    int has_pointer_type_change;
-    int has_WMVi;
-    int absolute;
-    int last_x;
-    int last_y;
-
-    int major;
-    int minor;
+    VncState *clients;
+    kbd_layout_t *kbd_layout;
 
     char *display;
     char *password;
@@ -121,6 +111,29 @@ struct VncState
     char *x509cert;
     char *x509key;
 #endif
+};
+
+struct VncState
+{
+    QEMUTimer *timer;
+    int csock;
+    DisplayState *ds;
+    VncDisplay *vd;
+    int need_update;
+    uint32_t dirty_row[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS];
+    char *old_data;
+    uint32_t features;
+    int absolute;
+    int last_x;
+    int last_y;
+
+    uint32_t vnc_encoding;
+    uint8_t tight_quality;
+    uint8_t tight_compression;
+
+    int major;
+    int minor;
+
     char challenge[VNC_AUTH_CHALLENGE_SIZE];
 
 #ifdef CONFIG_VNC_TLS
@@ -130,7 +143,6 @@ struct VncState
 
     Buffer output;
     Buffer input;
-    kbd_layout_t *kbd_layout;
     /* current output mode information */
     VncWritePixels *write_pixels;
     VncSendHextileTile *send_hextile_tile;
@@ -143,27 +155,37 @@ struct VncState
     size_t read_handler_expect;
     /* input */
     uint8_t modifiers_state[256];
+
+    Buffer zlib;
+    Buffer zlib_tmp;
+    z_stream zlib_stream[4];
+
+    VncState *next;
 };
 
-static VncState *vnc_state; /* needed for info vnc */
+static VncDisplay *vnc_display; /* needed for info vnc */
 static DisplayChangeListener *dcl;
 
 void do_info_vnc(void)
 {
-    if (vnc_state == NULL || vnc_state->display == NULL)
+    if (vnc_display == NULL || vnc_display->display == NULL)
        term_printf("VNC server disabled\n");
     else {
        term_printf("VNC server active on: ");
-       term_print_filename(vnc_state->display);
+       term_print_filename(vnc_display->display);
        term_printf("\n");
 
-       if (vnc_state->csock == -1)
+       if (vnc_display->clients == NULL)
            term_printf("No client connected\n");
        else
            term_printf("Client connected\n");
     }
 }
 
+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
@@ -180,7 +202,7 @@ static void vnc_flush(VncState *vs);
 static void vnc_update_client(void *opaque);
 static void vnc_client_read(void *opaque);
 
-static void vnc_colordepth(DisplayState *ds);
+static void vnc_colordepth(VncState *vs);
 
 static inline void vnc_set_bit(uint32_t *d, int k)
 {
@@ -223,9 +245,8 @@ static inline int vnc_and_bits(const uint32_t *d1, const uint32_t *d2,
     return 0;
 }
 
-static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h)
+static void vnc_update(VncState *vs, int x, int y, int w, int h)
 {
-    VncState *vs = ds->opaque;
     int i;
 
     h += y;
@@ -247,6 +268,16 @@ static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h)
            vnc_set_bit(vs->dirty_row[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,
                                   int32_t encoding)
 {
@@ -258,10 +289,44 @@ static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
     vnc_write_s32(vs, encoding);
 }
 
-static void vnc_dpy_resize(DisplayState *ds)
+static void buffer_reserve(Buffer *buffer, size_t len)
 {
+    if ((buffer->capacity - buffer->offset) < len) {
+       buffer->capacity += (len + 1024);
+       buffer->buffer = qemu_realloc(buffer->buffer, buffer->capacity);
+       if (buffer->buffer == NULL) {
+           fprintf(stderr, "vnc: out of memory\n");
+           exit(1);
+       }
+    }
+}
+
+static int buffer_empty(Buffer *buffer)
+{
+    return buffer->offset == 0;
+}
+
+static uint8_t *buffer_end(Buffer *buffer)
+{
+    return buffer->buffer + buffer->offset;
+}
+
+static void buffer_reset(Buffer *buffer)
+{
+       buffer->offset = 0;
+}
+
+static void buffer_append(Buffer *buffer, const void *data, size_t len)
+{
+    memcpy(buffer->buffer + buffer->offset, data, len);
+    buffer->offset += len;
+}
+
+static void vnc_resize(VncState *vs)
+{
+    DisplayState *ds = vs->ds;
+
     int size_changed;
-    VncState *vs = ds->opaque;
 
     vs->old_data = qemu_realloc(vs->old_data, ds_get_linesize(ds) * ds_get_height(ds));
 
@@ -272,16 +337,17 @@ static void vnc_dpy_resize(DisplayState *ds)
 
     if (ds_get_bytes_per_pixel(ds) != vs->serverds.pf.bytes_per_pixel)
         console_color_init(ds);
-    vnc_colordepth(ds);
+    vnc_colordepth(vs);
     size_changed = ds_get_width(ds) != vs->serverds.width ||
                    ds_get_height(ds) != vs->serverds.height;
     vs->serverds = *(ds->surface);
     if (size_changed) {
-        if (vs->csock != -1 && vs->has_resize) {
+        if (vs->csock != -1 && vnc_has_feature(vs, VNC_FEATURE_RESIZE)) {
             vnc_write_u8(vs, 0);  /* msg id */
             vnc_write_u8(vs, 0);
             vnc_write_u16(vs, 1); /* number of rects */
-            vnc_framebuffer_update(vs, 0, 0, ds_get_width(ds), ds_get_height(ds), -223);
+            vnc_framebuffer_update(vs, 0, 0, ds_get_width(ds), ds_get_height(ds),
+                                   VNC_ENCODING_DESKTOPRESIZE);
             vnc_flush(vs);
         }
     }
@@ -290,6 +356,16 @@ static void vnc_dpy_resize(DisplayState *ds)
     memset(vs->old_data, 42, ds_get_linesize(vs->ds) * ds_get_height(vs->ds));
 }
 
+static void vnc_dpy_resize(DisplayState *ds)
+{
+    VncDisplay *vd = ds->opaque;
+    VncState *vs = vd->clients;
+    while (vs != NULL) {
+        vnc_resize(vs);
+        vs = vs->next;
+    }
+}
+
 /* fastest code */
 static void vnc_write_pixels_copy(VncState *vs, void *pixels, int size)
 {
@@ -378,8 +454,6 @@ static void send_framebuffer_update_raw(VncState *vs, int x, int y, int w, int h
     int i;
     uint8_t *row;
 
-    vnc_framebuffer_update(vs, x, y, w, h, 0);
-
     row = ds_get_data(vs->ds) + 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));
@@ -429,10 +503,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;
 
-    vnc_framebuffer_update(vs, x, y, w, h, 5);
-
-    last_fg = (uint8_t *) malloc(vs->serverds.pf.bytes_per_pixel);
-    last_bg = (uint8_t *) malloc(vs->serverds.pf.bytes_per_pixel);
+    last_fg = (uint8_t *) qemu_malloc(vs->serverds.pf.bytes_per_pixel);
+    last_bg = (uint8_t *) qemu_malloc(vs->serverds.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) {
@@ -446,29 +518,145 @@ static void send_framebuffer_update_hextile(VncState *vs, int x, int y, int w, i
 
 }
 
+static void vnc_zlib_init(VncState *vs)
+{
+    int i;
+    for (i=0; i<(sizeof(vs->zlib_stream) / sizeof(z_stream)); i++)
+        vs->zlib_stream[i].opaque = NULL;
+}
+
+static void vnc_zlib_start(VncState *vs)
+{
+    buffer_reset(&vs->zlib);
+
+    // make the output buffer be the zlib buffer, so we can compress it later
+    vs->zlib_tmp = vs->output;
+    vs->output = vs->zlib;
+}
+
+static int vnc_zlib_stop(VncState *vs, int stream_id)
+{
+    z_streamp zstream = &vs->zlib_stream[stream_id];
+    int previous_out;
+
+    // switch back to normal output/zlib buffers
+    vs->zlib = vs->output;
+    vs->output = vs->zlib_tmp;
+
+    // compress the zlib buffer
+
+    // initialize the stream
+    // XXX need one stream per session
+    if (zstream->opaque != vs) {
+        int err;
+
+        VNC_DEBUG("VNC: initializing zlib stream %d\n", stream_id);
+        VNC_DEBUG("VNC: opaque = %p | vs = %p\n", zstream->opaque, vs);
+        zstream->zalloc = Z_NULL;
+        zstream->zfree = Z_NULL;
+
+        err = deflateInit2(zstream, vs->tight_compression, Z_DEFLATED, MAX_WBITS,
+                           MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+
+        if (err != Z_OK) {
+            fprintf(stderr, "VNC: error initializing zlib\n");
+            return -1;
+        }
+
+        zstream->opaque = vs;
+    }
+
+    // XXX what to do if tight_compression changed in between?
+
+    // reserve memory in output buffer
+    buffer_reserve(&vs->output, vs->zlib.offset + 64);
+
+    // set pointers
+    zstream->next_in = vs->zlib.buffer;
+    zstream->avail_in = vs->zlib.offset;
+    zstream->next_out = vs->output.buffer + vs->output.offset;
+    zstream->avail_out = vs->output.capacity - vs->output.offset;
+    zstream->data_type = Z_BINARY;
+    previous_out = zstream->total_out;
+
+    // start encoding
+    if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) {
+        fprintf(stderr, "VNC: error during zlib compression\n");
+        return -1;
+    }
+
+    vs->output.offset = vs->output.capacity - zstream->avail_out;
+    return zstream->total_out - previous_out;
+}
+
+static void send_framebuffer_update_zlib(VncState *vs, int x, int y, int w, int h)
+{
+    int old_offset, new_offset, bytes_written;
+
+    vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_ZLIB);
+
+    // remember where we put in the follow-up size
+    old_offset = vs->output.offset;
+    vnc_write_s32(vs, 0);
+
+    // compress the stream
+    vnc_zlib_start(vs);
+    send_framebuffer_update_raw(vs, x, y, w, h);
+    bytes_written = vnc_zlib_stop(vs, 0);
+
+    if (bytes_written == -1)
+        return;
+
+    // hack in the size
+    new_offset = vs->output.offset;
+    vs->output.offset = old_offset;
+    vnc_write_u32(vs, bytes_written);
+    vs->output.offset = new_offset;
+}
+
 static void send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
 {
-       if (vs->has_hextile)
+    switch(vs->vnc_encoding) {
+       case VNC_ENCODING_ZLIB:
+           send_framebuffer_update_zlib(vs, x, y, w, h);
+           break;
+       case VNC_ENCODING_HEXTILE:
+           vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_HEXTILE);
            send_framebuffer_update_hextile(vs, x, y, w, h);
-       else
+           break;
+       default:
+           vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW);
            send_framebuffer_update_raw(vs, x, y, w, h);
+           break;
+    }
 }
 
-static void vnc_copy(DisplayState *ds, 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)
 {
-    VncState *vs = ds->opaque;
-
     vnc_update_client(vs);
 
     vnc_write_u8(vs, 0);  /* msg id */
     vnc_write_u8(vs, 0);
     vnc_write_u16(vs, 1); /* number of rects */
-    vnc_framebuffer_update(vs, dst_x, dst_y, w, h, 1);
+    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_flush(vs);
 }
 
+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;
+    VncState *vs = vd->clients;
+    while (vs != NULL) {
+        if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT))
+            vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h);
+        else /* TODO */
+            vnc_update(vs, dst_x, dst_y, w, h);
+        vs = vs->next;
+    }
+}
+
 static int find_dirty_height(VncState *vs, int y, int last_x, int x)
 {
     int h;
@@ -487,7 +675,6 @@ static int find_dirty_height(VncState *vs, int y, int last_x, int x)
 static void vnc_update_client(void *opaque)
 {
     VncState *vs = opaque;
-
     if (vs->need_update && vs->csock != -1) {
        int y;
        uint8_t *row;
@@ -580,47 +767,6 @@ static void vnc_update_client(void *opaque)
 
 }
 
-static int vnc_listen_poll(void *opaque)
-{
-    VncState *vs = opaque;
-    if (vs->csock == -1)
-       return 1;
-    return 0;
-}
-
-static void buffer_reserve(Buffer *buffer, size_t len)
-{
-    if ((buffer->capacity - buffer->offset) < len) {
-       buffer->capacity += (len + 1024);
-       buffer->buffer = qemu_realloc(buffer->buffer, buffer->capacity);
-       if (buffer->buffer == NULL) {
-           fprintf(stderr, "vnc: out of memory\n");
-           exit(1);
-       }
-    }
-}
-
-static int buffer_empty(Buffer *buffer)
-{
-    return buffer->offset == 0;
-}
-
-static uint8_t *buffer_end(Buffer *buffer)
-{
-    return buffer->buffer + buffer->offset;
-}
-
-static void buffer_reset(Buffer *buffer)
-{
-       buffer->offset = 0;
-}
-
-static void buffer_append(Buffer *buffer, const void *data, size_t len)
-{
-    memcpy(buffer->buffer + buffer->offset, data, len);
-    buffer->offset += len;
-}
-
 /* audio */
 static void audio_capture_notify(void *opaque, audcnotification_e cmd)
 {
@@ -705,19 +851,35 @@ static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
        VNC_DEBUG("Closing down client sock %d %d\n", ret, ret < 0 ? last_errno : 0);
        qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
        closesocket(vs->csock);
-       vs->csock = -1;
-       dcl->idle = 1;
-       buffer_reset(&vs->input);
-       buffer_reset(&vs->output);
-       vs->need_update = 0;
+        qemu_del_timer(vs->timer);
+        qemu_free_timer(vs->timer);
+        if (vs->input.buffer) qemu_free(vs->input.buffer);
+        if (vs->output.buffer) qemu_free(vs->output.buffer);
 #ifdef CONFIG_VNC_TLS
        if (vs->tls_session) {
            gnutls_deinit(vs->tls_session);
            vs->tls_session = NULL;
        }
-       vs->wiremode = VNC_WIREMODE_CLEAR;
 #endif /* CONFIG_VNC_TLS */
         audio_del(vs);
+
+        VncState *p, *parent = NULL;
+        for (p = vs->vd->clients; p != NULL; p = p->next) {
+            if (p == vs) {
+                if (parent)
+                    parent->next = p->next;
+                else
+                    vs->vd->clients = p->next;
+                break;
+            }
+            parent = p;
+        }
+        if (!vs->vd->clients)
+            dcl->idle = 1;
+
+        qemu_free(vs->old_data);
+        qemu_free(vs);
+  
        return 0;
     }
     return ret;
@@ -919,12 +1081,13 @@ static void client_cut_text(VncState *vs, size_t len, uint8_t *text)
 
 static void check_pointer_type_change(VncState *vs, int absolute)
 {
-    if (vs->has_pointer_type_change && vs->absolute != absolute) {
+    if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) {
        vnc_write_u8(vs, 0);
        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), -257);
+                              ds_get_width(vs->ds), ds_get_height(vs->ds),
+                               VNC_ENCODING_POINTER_TYPE_CHANGE);
        vnc_flush(vs);
     }
     vs->absolute = absolute;
@@ -950,7 +1113,7 @@ static void pointer_event(VncState *vs, int button_mask, int x, int y)
        kbd_mouse_event(x * 0x7FFF / (ds_get_width(vs->ds) - 1),
                        y * 0x7FFF / (ds_get_height(vs->ds) - 1),
                        dz, buttons);
-    } else if (vs->has_pointer_type_change) {
+    } else if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE)) {
        x -= 0x7FFF;
        y -= 0x7FFF;
 
@@ -982,8 +1145,8 @@ static void reset_keys(VncState *vs)
 
 static void press_key(VncState *vs, int keysym)
 {
-    kbd_put_keycode(keysym2scancode(vs->kbd_layout, keysym) & 0x7f);
-    kbd_put_keycode(keysym2scancode(vs->kbd_layout, keysym) | 0x80);
+    kbd_put_keycode(keysym2scancode(vs->vd->kbd_layout, keysym) & 0x7f);
+    kbd_put_keycode(keysym2scancode(vs->vd->kbd_layout, keysym) | 0x80);
 }
 
 static void do_key_event(VncState *vs, int down, int keycode, int sym)
@@ -1016,12 +1179,12 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
         break;
     }
 
-    if (keycode_is_keypad(vs->kbd_layout, keycode)) {
+    if (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
            toggles numlock away from the VNC window.
         */
-        if (keysym_is_numlock(vs->kbd_layout, sym & 0xFFFF)) {
+        if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) {
             if (!vs->modifiers_state[0x45]) {
                 vs->modifiers_state[0x45] = 1;
                 press_key(vs, 0xff7f);
@@ -1053,30 +1216,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:
@@ -1094,7 +1266,7 @@ static void key_event(VncState *vs, int down, uint32_t sym)
     if (sym >= 'A' && sym <= 'Z' && is_graphic_console())
        sym = sym - 'A' + 'a';
 
-    keycode = keysym2scancode(vs->kbd_layout, sym & 0xFFFF);
+    keycode = keysym2scancode(vs->vd->kbd_layout, sym & 0xFFFF);
     do_key_event(vs, down, keycode, sym);
 }
 
@@ -1140,7 +1312,8 @@ static void send_ext_key_event_ack(VncState *vs)
     vnc_write_u8(vs, 0);
     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), -258);
+    vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
+                           VNC_ENCODING_EXT_KEY_EVENT);
     vnc_flush(vs);
 }
 
@@ -1149,50 +1322,65 @@ static void send_ext_audio_ack(VncState *vs)
     vnc_write_u8(vs, 0);
     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), -259);
+    vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
+                           VNC_ENCODING_AUDIO);
     vnc_flush(vs);
 }
 
 static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
 {
     int i;
+    unsigned int enc = 0;
 
-    vs->has_hextile = 0;
-    vs->has_resize = 0;
-    vs->has_pointer_type_change = 0;
-    vs->has_WMVi = 0;
+    vnc_zlib_init(vs);
+    vs->features = 0;
+    vs->vnc_encoding = 0;
+    vs->tight_compression = 9;
+    vs->tight_quality = 9;
     vs->absolute = -1;
-    dcl->dpy_copy = NULL;
 
     for (i = n_encodings - 1; i >= 0; i--) {
-       switch (encodings[i]) {
-       case 0: /* Raw */
-           vs->has_hextile = 0;
-           break;
-       case 1: /* CopyRect */
-           dcl->dpy_copy = vnc_copy;
-           break;
-       case 5: /* Hextile */
-           vs->has_hextile = 1;
-           break;
-       case -223: /* DesktopResize */
-           vs->has_resize = 1;
-           break;
-       case -257:
-           vs->has_pointer_type_change = 1;
-           break;
-        case -258:
+        enc = encodings[i];
+        switch (enc) {
+        case VNC_ENCODING_RAW:
+            vs->vnc_encoding = enc;
+            break;
+        case VNC_ENCODING_COPYRECT:
+            vs->features |= VNC_FEATURE_COPYRECT_MASK;
+            break;
+        case VNC_ENCODING_HEXTILE:
+            vs->features |= VNC_FEATURE_HEXTILE_MASK;
+            vs->vnc_encoding = enc;
+            break;
+        case VNC_ENCODING_ZLIB:
+            vs->features |= VNC_FEATURE_ZLIB_MASK;
+            vs->vnc_encoding = enc;
+            break;
+        case VNC_ENCODING_DESKTOPRESIZE:
+            vs->features |= VNC_FEATURE_RESIZE_MASK;
+            break;
+        case VNC_ENCODING_POINTER_TYPE_CHANGE:
+            vs->features |= VNC_FEATURE_POINTER_TYPE_CHANGE_MASK;
+            break;
+        case VNC_ENCODING_EXT_KEY_EVENT:
             send_ext_key_event_ack(vs);
             break;
-        case -259:
+        case VNC_ENCODING_AUDIO:
             send_ext_audio_ack(vs);
             break;
-        case 0x574D5669:
-            vs->has_WMVi = 1;
+        case VNC_ENCODING_WMVi:
+            vs->features |= VNC_FEATURE_WMVI_MASK;
             break;
-       default:
-           break;
-       }
+        case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9:
+            vs->tight_compression = (enc & 0x0F);
+            break;
+        case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9:
+            vs->tight_quality = (enc & 0x0F);
+            break;
+        default:
+            VNC_DEBUG("Unknown encoding: %d (0x%.8x): %d\n", i, enc, enc);
+            break;
+        }
     }
 
     check_pointer_type_change(vs, kbd_mouse_is_absolute());
@@ -1302,16 +1490,15 @@ static void vnc_dpy_setdata(DisplayState *ds)
     /* We don't have to do anything */
 }
 
-static void vnc_colordepth(DisplayState *ds)
+static void vnc_colordepth(VncState *vs)
 {
-    struct VncState *vs = ds->opaque;
-
-    if (vs->csock != -1 && vs->has_WMVi) {
+    if (vnc_has_feature(vs, VNC_FEATURE_WMVI)) {
         /* Sending a WMVi message to notify the client*/
         vnc_write_u8(vs, 0);  /* msg id */
         vnc_write_u8(vs, 0);
         vnc_write_u16(vs, 1); /* number of rects */
-        vnc_framebuffer_update(vs, 0, 0, ds_get_width(ds), ds_get_height(ds), 0x574D5669);
+        vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), 
+                               ds_get_height(vs->ds), VNC_ENCODING_WMVi);
         pixel_format_message(vs);
         vnc_flush(vs);
     } else {
@@ -1495,7 +1682,7 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
     int i, j, pwlen;
     unsigned char key[8];
 
-    if (!vs->password || !vs->password[0]) {
+    if (!vs->vd->password || !vs->vd->password[0]) {
        VNC_DEBUG("No password configured on server");
        vnc_write_u32(vs, 1); /* Reject auth */
        if (vs->minor >= 8) {
@@ -1511,9 +1698,9 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
     memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);
 
     /* Calculate the expected challenge response */
-    pwlen = strlen(vs->password);
+    pwlen = strlen(vs->vd->password);
     for (i=0; i<sizeof(key); i++)
-        key[i] = i<pwlen ? vs->password[i] : 0;
+        key[i] = i<pwlen ? vs->vd->password[i] : 0;
     deskey(key, EN0);
     for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8)
         des(response+j, response+j);
@@ -1602,15 +1789,15 @@ static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncState *v
     gnutls_certificate_credentials_t x509_cred;
     int ret;
 
-    if (!vs->x509cacert) {
+    if (!vs->vd->x509cacert) {
        VNC_DEBUG("No CA x509 certificate specified\n");
        return NULL;
     }
-    if (!vs->x509cert) {
+    if (!vs->vd->x509cert) {
        VNC_DEBUG("No server x509 certificate specified\n");
        return NULL;
     }
-    if (!vs->x509key) {
+    if (!vs->vd->x509key) {
        VNC_DEBUG("No server private key specified\n");
        return NULL;
     }
@@ -1620,7 +1807,7 @@ static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncState *v
        return NULL;
     }
     if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
-                                                     vs->x509cacert,
+                                                     vs->vd->x509cacert,
                                                      GNUTLS_X509_FMT_PEM)) < 0) {
        VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret));
        gnutls_certificate_free_credentials(x509_cred);
@@ -1628,17 +1815,17 @@ static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncState *v
     }
 
     if ((ret = gnutls_certificate_set_x509_key_file (x509_cred,
-                                                    vs->x509cert,
-                                                    vs->x509key,
+                                                    vs->vd->x509cert,
+                                                    vs->vd->x509key,
                                                     GNUTLS_X509_FMT_PEM)) < 0) {
        VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret));
        gnutls_certificate_free_credentials(x509_cred);
        return NULL;
     }
 
-    if (vs->x509cacrl) {
+    if (vs->vd->x509cacrl) {
        if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
-                                                       vs->x509cacrl,
+                                                       vs->vd->x509cacrl,
                                                        GNUTLS_X509_FMT_PEM)) < 0) {
            VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));
            gnutls_certificate_free_credentials(x509_cred);
@@ -1732,7 +1919,7 @@ static int vnc_validate_certificate(struct VncState *vs)
 
 static int start_auth_vencrypt_subauth(VncState *vs)
 {
-    switch (vs->subauth) {
+    switch (vs->vd->subauth) {
     case VNC_AUTH_VENCRYPT_TLSNONE:
     case VNC_AUTH_VENCRYPT_X509NONE:
        VNC_DEBUG("Accept TLS auth none\n");
@@ -1746,7 +1933,7 @@ static int start_auth_vencrypt_subauth(VncState *vs)
        return start_auth_vnc(vs);
 
     default: /* Should not be possible, but just in case */
-       VNC_DEBUG("Reject auth %d\n", vs->auth);
+       VNC_DEBUG("Reject auth %d\n", vs->vd->auth);
        vnc_write_u8(vs, 1);
        if (vs->minor >= 8) {
            static const char err[] = "Unsupported authentication type";
@@ -1778,7 +1965,7 @@ static int vnc_continue_handshake(struct VncState *vs) {
        return -1;
     }
 
-    if (vs->x509verify) {
+    if (vs->vd->x509verify) {
        if (vnc_validate_certificate(vs) < 0) {
            VNC_DEBUG("Client verification failed\n");
            vnc_client_error(vs);
@@ -1803,9 +1990,9 @@ static void vnc_handshake_io(void *opaque) {
 }
 
 #define NEED_X509_AUTH(vs)                           \
-    ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE ||   \
-     (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC ||    \
-     (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN)
+    ((vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509NONE ||   \
+     (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509VNC ||    \
+     (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN)
 
 
 static int vnc_start_tls(struct VncState *vs) {
@@ -1869,7 +2056,7 @@ static int vnc_start_tls(struct VncState *vs) {
                vnc_client_error(vs);
                return -1;
            }
-           if (vs->x509verify) {
+           if (vs->vd->x509verify) {
                VNC_DEBUG("Requesting a client certificate\n");
                gnutls_certificate_server_set_request (vs->tls_session, GNUTLS_CERT_REQUEST);
            }
@@ -1904,7 +2091,7 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len
 {
     int auth = read_u32(data, 0);
 
-    if (auth != vs->subauth) {
+    if (auth != vs->vd->subauth) {
        VNC_DEBUG("Rejecting auth %d\n", auth);
        vnc_write_u8(vs, 0); /* Reject auth */
        vnc_flush(vs);
@@ -1939,10 +2126,10 @@ static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len
        vnc_flush(vs);
        vnc_client_error(vs);
     } else {
-       VNC_DEBUG("Sending allowed auth %d\n", vs->subauth);
+       VNC_DEBUG("Sending allowed auth %d\n", vs->vd->subauth);
        vnc_write_u8(vs, 0); /* Accept version */
        vnc_write_u8(vs, 1); /* Number of sub-auths */
-       vnc_write_u32(vs, vs->subauth); /* The supported auth */
+       vnc_write_u32(vs, vs->vd->subauth); /* The supported auth */
        vnc_flush(vs);
        vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
     }
@@ -1964,7 +2151,7 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
 {
     /* We only advertise 1 auth scheme at a time, so client
      * must pick the one we sent. Verify this */
-    if (data[0] != vs->auth) { /* Reject auth */
+    if (data[0] != vs->vd->auth) { /* Reject auth */
        VNC_DEBUG("Reject auth %d\n", (int)data[0]);
        vnc_write_u32(vs, 1);
        if (vs->minor >= 8) {
@@ -1975,7 +2162,7 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
        vnc_client_error(vs);
     } else { /* Accept requested auth */
        VNC_DEBUG("Client requested auth %d\n", (int)data[0]);
-       switch (vs->auth) {
+       switch (vs->vd->auth) {
        case VNC_AUTH_NONE:
            VNC_DEBUG("Accept auth none\n");
            if (vs->minor >= 8) {
@@ -1996,7 +2183,7 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
 #endif /* CONFIG_VNC_TLS */
 
        default: /* Should not be possible, but just in case */
-           VNC_DEBUG("Reject auth %d\n", vs->auth);
+           VNC_DEBUG("Reject auth %d\n", vs->vd->auth);
            vnc_write_u8(vs, 1);
            if (vs->minor >= 8) {
                static const char err[] = "Authentication failed";
@@ -2041,26 +2228,26 @@ static int protocol_version(VncState *vs, uint8_t *version, size_t len)
        vs->minor = 3;
 
     if (vs->minor == 3) {
-       if (vs->auth == VNC_AUTH_NONE) {
+       if (vs->vd->auth == VNC_AUTH_NONE) {
             VNC_DEBUG("Tell client auth none\n");
-            vnc_write_u32(vs, vs->auth);
+            vnc_write_u32(vs, vs->vd->auth);
             vnc_flush(vs);
             vnc_read_when(vs, protocol_client_init, 1);
-       } else if (vs->auth == VNC_AUTH_VNC) {
+       } else if (vs->vd->auth == VNC_AUTH_VNC) {
             VNC_DEBUG("Tell client VNC auth\n");
-            vnc_write_u32(vs, vs->auth);
+            vnc_write_u32(vs, vs->vd->auth);
             vnc_flush(vs);
             start_auth_vnc(vs);
        } else {
-            VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->auth);
+            VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->vd->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->auth);
+       VNC_DEBUG("Telling client we support auth %d\n", vs->vd->auth);
        vnc_write_u8(vs, 1); /* num auth */
-       vnc_write_u8(vs, vs->auth);
+       vnc_write_u8(vs, vs->vd->auth);
        vnc_read_when(vs, protocol_client_auth, 1);
        vnc_flush(vs);
     }
@@ -2068,59 +2255,66 @@ static int protocol_version(VncState *vs, uint8_t *version, size_t len)
     return 0;
 }
 
-static void vnc_connect(VncState *vs)
+static void vnc_connect(VncDisplay *vd, int csock)
 {
-    VNC_DEBUG("New client on socket %d\n", vs->csock);
+    VncState *vs = qemu_mallocz(sizeof(VncState));
+    vs->csock = csock;
+
+    VNC_DEBUG("New client on socket %d\n", csock);
     dcl->idle = 0;
     socket_set_nonblock(vs->csock);
     qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+
+    vs->vd = vd;
+    vs->ds = vd->ds;
+    vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);
+    vs->last_x = -1;
+    vs->last_y = -1;
+
+    vs->as.freq = 44100;
+    vs->as.nchannels = 2;
+    vs->as.fmt = AUD_FMT_S16;
+    vs->as.endianness = 0;
+
+    vnc_resize(vs);
     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));
-    vs->has_resize = 0;
-    vs->has_hextile = 0;
-    vs->has_WMVi = 0;
-    dcl->dpy_copy = NULL;
     vnc_update_client(vs);
     reset_keys(vs);
+
+    vs->next = vd->clients;
+    vd->clients = vs;
 }
 
 static void vnc_listen_read(void *opaque)
 {
-    VncState *vs = opaque;
+    VncDisplay *vs = opaque;
     struct sockaddr_in addr;
     socklen_t addrlen = sizeof(addr);
 
     /* Catch-up */
     vga_hw_update();
 
-    vs->csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
-    if (vs->csock != -1) {
-        vnc_connect(vs);
+    int csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
+    if (csock != -1) {
+        vnc_connect(vs, csock);
     }
 }
 
 void vnc_display_init(DisplayState *ds)
 {
-    VncState *vs;
+    VncDisplay *vs = qemu_mallocz(sizeof(*vs));
 
-    vs = qemu_mallocz(sizeof(VncState));
     dcl = qemu_mallocz(sizeof(DisplayChangeListener));
-    if (!vs || !dcl)
-       exit(1);
 
     ds->opaque = vs;
     dcl->idle = 1;
-    vnc_state = vs;
-    vs->display = NULL;
-    vs->password = NULL;
+    vnc_display = vs;
 
     vs->lsock = -1;
-    vs->csock = -1;
-    vs->last_x = -1;
-    vs->last_y = -1;
 
     vs->ds = ds;
 
@@ -2132,22 +2326,15 @@ void vnc_display_init(DisplayState *ds)
     if (!vs->kbd_layout)
        exit(1);
 
-    vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);
-
+    dcl->dpy_copy = vnc_dpy_copy;
     dcl->dpy_update = vnc_dpy_update;
     dcl->dpy_resize = vnc_dpy_resize;
     dcl->dpy_setdata = vnc_dpy_setdata;
-    dcl->dpy_refresh = NULL;
     register_displaychangelistener(ds, dcl);
-
-    vs->as.freq = 44100;
-    vs->as.nchannels = 2;
-    vs->as.fmt = AUD_FMT_S16;
-    vs->as.endianness = 0;
 }
 
 #ifdef CONFIG_VNC_TLS
-static int vnc_set_x509_credential(VncState *vs,
+static int vnc_set_x509_credential(VncDisplay *vs,
                                   const char *certdir,
                                   const char *filename,
                                   char **cred,
@@ -2160,8 +2347,7 @@ static int vnc_set_x509_credential(VncState *vs,
        *cred = NULL;
     }
 
-    if (!(*cred = qemu_malloc(strlen(certdir) + strlen(filename) + 2)))
-       return -1;
+    *cred = qemu_malloc(strlen(certdir) + strlen(filename) + 2);
 
     strcpy(*cred, certdir);
     strcat(*cred, "/");
@@ -2179,7 +2365,7 @@ static int vnc_set_x509_credential(VncState *vs,
     return 0;
 }
 
-static int vnc_set_x509_credential_dir(VncState *vs,
+static int vnc_set_x509_credential_dir(VncDisplay *vs,
                                       const char *certdir)
 {
     if (vnc_set_x509_credential(vs, certdir, X509_CA_CERT_FILE, &vs->x509cacert, 0) < 0)
@@ -2205,8 +2391,10 @@ static int vnc_set_x509_credential_dir(VncState *vs,
 
 void vnc_display_close(DisplayState *ds)
 {
-    VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
+    VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
 
+    if (!vs)
+        return;
     if (vs->display) {
        qemu_free(vs->display);
        vs->display = NULL;
@@ -2216,32 +2404,16 @@ void vnc_display_close(DisplayState *ds)
        close(vs->lsock);
        vs->lsock = -1;
     }
-    if (vs->csock != -1) {
-       qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
-       closesocket(vs->csock);
-       vs->csock = -1;
-       buffer_reset(&vs->input);
-       buffer_reset(&vs->output);
-       vs->need_update = 0;
-#ifdef CONFIG_VNC_TLS
-       if (vs->tls_session) {
-           gnutls_deinit(vs->tls_session);
-           vs->tls_session = NULL;
-       }
-       vs->wiremode = VNC_WIREMODE_CLEAR;
-#endif /* CONFIG_VNC_TLS */
-    }
     vs->auth = VNC_AUTH_INVALID;
 #ifdef CONFIG_VNC_TLS
     vs->subauth = VNC_AUTH_INVALID;
     vs->x509verify = 0;
 #endif
-    audio_del(vs);
 }
 
 int vnc_display_password(DisplayState *ds, const char *password)
 {
-    VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
+    VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
 
     if (vs->password) {
        qemu_free(vs->password);
@@ -2257,7 +2429,7 @@ int vnc_display_password(DisplayState *ds, const char *password)
 
 int vnc_display_open(DisplayState *ds, const char *display)
 {
-    VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
+    VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
     const char *options;
     int password = 0;
     int reverse = 0;
@@ -2266,6 +2438,8 @@ int vnc_display_open(DisplayState *ds, const char *display)
     int tls = 0, x509 = 0;
 #endif
 
+    if (!vnc_display)
+        return -1;
     vnc_display_close(ds);
     if (strcmp(display, "none") == 0)
        return 0;
@@ -2369,9 +2543,9 @@ int vnc_display_open(DisplayState *ds, const char *display)
             vs->display = NULL;
             return -1;
         } else {
-            vs->csock = vs->lsock;
+            int csock = vs->lsock;
             vs->lsock = -1;
-            vnc_connect(vs);
+            vnc_connect(vs, csock);
         }
         return 0;
 
@@ -2393,6 +2567,5 @@ int vnc_display_open(DisplayState *ds, const char *display)
             vs->display = dpy;
         }
     }
-
-    return qemu_set_fd_handler2(vs->lsock, vnc_listen_poll, vnc_listen_read, NULL, vs);
+    return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs);
 }