X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=ui%2Fvnc.c;h=dfc74591f4b731e25dddbcd56bc22bf941ceb87b;hb=1483adcf6ac978656718d4383d909c96dce395a6;hp=312ad7fe362d581235b19cd27833db1d36363575;hpb=b34bd5e5c8f356ec206e5a306ee3a9b6f42c4315;p=qemu.git diff --git a/ui/vnc.c b/ui/vnc.c index 312ad7fe3..dfc74591f 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -26,17 +26,17 @@ #include "vnc.h" #include "vnc-jobs.h" -#include "sysemu.h" -#include "qemu_socket.h" -#include "qemu-timer.h" -#include "acl.h" -#include "qemu-objects.h" +#include "sysemu/sysemu.h" +#include "qemu/sockets.h" +#include "qemu/timer.h" +#include "qemu/acl.h" +#include "qapi/qmp/types.h" #include "qmp-commands.h" -#include "osdep.h" +#include "qemu/osdep.h" -#define VNC_REFRESH_INTERVAL_BASE 30 +#define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT #define VNC_REFRESH_INTERVAL_INC 50 -#define VNC_REFRESH_INTERVAL_MAX 2000 +#define VNC_REFRESH_INTERVAL_MAX GUI_REFRESH_INTERVAL_IDLE static const struct timeval VNC_REFRESH_STATS = { 0, 500000 }; static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 }; @@ -44,7 +44,6 @@ static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 }; #include "d3des.h" static VncDisplay *vnc_display; /* needed for info vnc */ -static DisplayChangeListener *dcl; static int vnc_cursor_define(VncState *vs); static void vnc_release_modifiers(VncState *vs); @@ -372,6 +371,10 @@ VncInfo *qmp_query_vnc(Error **errp) } } + if (vnc_display->lsock == -1) { + return info; + } + if (getsockname(vnc_display->lsock, (struct sockaddr *)&sa, &salen) == -1) { error_set(errp, QERR_UNDEFINED_ERROR); @@ -416,22 +419,22 @@ out_error: 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_remove_timer(VncDisplay *vd); static void vnc_colordepth(VncState *vs); static void framebuffer_update_request(VncState *vs, int incremental, int x_position, int y_position, int w, int h); -static void vnc_refresh(void *opaque); +static void vnc_refresh(DisplayChangeListener *dcl); static int vnc_refresh_server_surface(VncDisplay *vd); -static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h) +static void vnc_dpy_update(DisplayChangeListener *dcl, + int x, int y, int w, int h) { int i; - VncDisplay *vd = ds->opaque; + VncDisplay *vd = container_of(dcl, VncDisplay, dcl); struct VncSurface *s = &vd->guest; + int width = surface_width(vd->ds); + int height = surface_height(vd->ds); h += y; @@ -442,10 +445,10 @@ static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h) w += (x % 16); x -= (x % 16); - 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); + x = MIN(x, width); + y = MIN(y, height); + w = MIN(x + w, width) - x; + h = MIN(h, height); for (; y < h; y++) for (i = 0; i < w; i += 16) @@ -475,7 +478,7 @@ void buffer_reserve(Buffer *buffer, size_t len) } } -int buffer_empty(Buffer *buffer) +static int buffer_empty(Buffer *buffer) { return buffer->offset == 0; } @@ -504,19 +507,26 @@ void buffer_append(Buffer *buffer, const void *data, size_t len) buffer->offset += len; } +void buffer_advance(Buffer *buf, size_t len) +{ + memmove(buf->buffer, buf->buffer + len, + (buf->offset - len)); + buf->offset -= len; +} + static void vnc_desktop_resize(VncState *vs) { - DisplayState *ds = vs->ds; + DisplaySurface *ds = vs->vd->ds; if (vs->csock == -1 || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) { return; } - if (vs->client_width == ds_get_width(ds) && - vs->client_height == ds_get_height(ds)) { + if (vs->client_width == surface_width(ds) && + vs->client_height == surface_height(ds)) { return; } - vs->client_width = ds_get_width(ds); - vs->client_height = ds_get_height(ds); + vs->client_width = surface_width(ds); + vs->client_height = surface_height(ds); vnc_lock_output(vs); vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); vnc_write_u8(vs, 0); @@ -546,28 +556,45 @@ static void vnc_abort_display_jobs(VncDisplay *vd) } } -static void vnc_dpy_resize(DisplayState *ds) +int vnc_server_fb_stride(VncDisplay *vd) +{ + return pixman_image_get_stride(vd->server); +} + +void *vnc_server_fb_ptr(VncDisplay *vd, int x, int y) +{ + uint8_t *ptr; + + ptr = (uint8_t *)pixman_image_get_data(vd->server); + ptr += y * vnc_server_fb_stride(vd); + ptr += x * VNC_SERVER_FB_BYTES; + return ptr; +} + +static void vnc_dpy_switch(DisplayChangeListener *dcl, + DisplaySurface *surface) { - VncDisplay *vd = ds->opaque; + VncDisplay *vd = container_of(dcl, VncDisplay, dcl); VncState *vs; vnc_abort_display_jobs(vd); /* server surface */ - if (!vd->server) - vd->server = g_malloc0(sizeof(*vd->server)); - if (vd->server->data) - g_free(vd->server->data); - *(vd->server) = *(ds->surface); - vd->server->data = g_malloc0(vd->server->linesize * - vd->server->height); + qemu_pixman_image_unref(vd->server); + vd->ds = surface; + vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT, + surface_width(vd->ds), + surface_height(vd->ds), + NULL, 0); /* guest surface */ - if (!vd->guest.ds) - vd->guest.ds = g_malloc0(sizeof(*vd->guest.ds)); +#if 0 /* FIXME */ if (ds_get_bytes_per_pixel(ds) != vd->guest.ds->pf.bytes_per_pixel) console_color_init(ds); - *(vd->guest.ds) = *(ds->surface); +#endif + qemu_pixman_image_unref(vd->guest.fb); + vd->guest.fb = pixman_image_ref(surface->image); + vd->guest.format = surface->format; memset(vd->guest.dirty, 0xFF, sizeof(vd->guest.dirty)); QTAILQ_FOREACH(vs, &vd->clients, next) { @@ -581,7 +608,7 @@ static void vnc_dpy_resize(DisplayState *ds) } /* fastest code */ -static void vnc_write_pixels_copy(VncState *vs, struct PixelFormat *pf, +static void vnc_write_pixels_copy(VncState *vs, void *pixels, int size) { vnc_write(vs, pixels, size); @@ -591,23 +618,23 @@ static void vnc_write_pixels_copy(VncState *vs, struct PixelFormat *pf, void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v) { uint8_t r, g, b; - VncDisplay *vd = vs->vd; - r = ((((v & vd->server->pf.rmask) >> vd->server->pf.rshift) << vs->clientds.pf.rbits) >> - vd->server->pf.rbits); - g = ((((v & vd->server->pf.gmask) >> vd->server->pf.gshift) << vs->clientds.pf.gbits) >> - vd->server->pf.gbits); - b = ((((v & vd->server->pf.bmask) >> vd->server->pf.bshift) << vs->clientds.pf.bbits) >> - vd->server->pf.bbits); - v = (r << vs->clientds.pf.rshift) | - (g << vs->clientds.pf.gshift) | - (b << vs->clientds.pf.bshift); - switch(vs->clientds.pf.bytes_per_pixel) { +#if VNC_SERVER_FB_FORMAT == PIXMAN_FORMAT(32, PIXMAN_TYPE_ARGB, 0, 8, 8, 8) + r = (((v & 0x00ff0000) >> 16) << vs->client_pf.rbits) >> 8; + g = (((v & 0x0000ff00) >> 8) << vs->client_pf.gbits) >> 8; + b = (((v & 0x000000ff) >> 0) << vs->client_pf.bbits) >> 8; +#else +# error need some bits here if you change VNC_SERVER_FB_FORMAT +#endif + v = (r << vs->client_pf.rshift) | + (g << vs->client_pf.gshift) | + (b << vs->client_pf.bshift); + switch (vs->client_pf.bytes_per_pixel) { case 1: buf[0] = v; break; case 2: - if (vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) { + if (vs->client_be) { buf[0] = v >> 8; buf[1] = v; } else { @@ -617,7 +644,7 @@ void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v) break; default: case 4: - if (vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) { + if (vs->client_be) { buf[0] = v >> 24; buf[1] = v >> 16; buf[2] = v >> 8; @@ -632,37 +659,19 @@ void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v) } } -static void vnc_write_pixels_generic(VncState *vs, struct PixelFormat *pf, +static void vnc_write_pixels_generic(VncState *vs, void *pixels1, int size) { uint8_t buf[4]; - if (pf->bytes_per_pixel == 4) { + if (VNC_SERVER_FB_BYTES == 4) { uint32_t *pixels = pixels1; int n, i; n = size >> 2; - for(i = 0; i < n; i++) { + for (i = 0; i < n; i++) { vnc_convert_pixel(vs, buf, pixels[i]); - vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel); + vnc_write(vs, buf, vs->client_pf.bytes_per_pixel); } - } else if (pf->bytes_per_pixel == 2) { - uint16_t *pixels = pixels1; - int n, i; - n = size >> 1; - for(i = 0; i < n; i++) { - vnc_convert_pixel(vs, buf, pixels[i]); - vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel); - } - } else if (pf->bytes_per_pixel == 1) { - uint8_t *pixels = pixels1; - int n, i; - n = size; - for(i = 0; i < n; i++) { - vnc_convert_pixel(vs, buf, pixels[i]); - vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel); - } - } else { - fprintf(stderr, "vnc_write_pixels_generic: VncState color depth not supported\n"); } } @@ -672,10 +681,10 @@ int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) uint8_t *row; VncDisplay *vd = vs->vd; - row = vd->server->data + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds); + row = vnc_server_fb_ptr(vd, x, y); for (i = 0; i < h; i++) { - vs->write_pixels(vs, &vd->server->pf, row, w * ds_get_bytes_per_pixel(vs->ds)); - row += ds_get_linesize(vs->ds); + vs->write_pixels(vs, row, w * VNC_SERVER_FB_BYTES); + row += vnc_server_fb_stride(vd); } return 1; } @@ -726,13 +735,15 @@ static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, i 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) +static void vnc_dpy_copy(DisplayChangeListener *dcl, + int src_x, int src_y, + int dst_x, int dst_y, int w, int h) { - VncDisplay *vd = ds->opaque; + VncDisplay *vd = container_of(dcl, VncDisplay, dcl); VncState *vs, *vn; uint8_t *src_row; uint8_t *dst_row; - int i,x,y,pitch,depth,inc,w_lim,s; + int i, x, y, pitch, inc, w_lim, s; int cmp_bytes; vnc_refresh_server_surface(vd); @@ -745,10 +756,9 @@ static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int } /* do bitblit op on the local surface too */ - pitch = ds_get_linesize(vd->ds); - depth = ds_get_bytes_per_pixel(vd->ds); - src_row = vd->server->data + pitch * src_y + depth * src_x; - dst_row = vd->server->data + pitch * dst_y + depth * dst_x; + pitch = vnc_server_fb_stride(vd); + src_row = vnc_server_fb_ptr(vd, src_x, src_y); + dst_row = vnc_server_fb_ptr(vd, dst_x, dst_y); y = dst_y; inc = 1; if (dst_y > src_y) { @@ -776,7 +786,7 @@ static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int } else { s = 16; } - cmp_bytes = s * depth; + cmp_bytes = s * VNC_SERVER_FB_BYTES; if (memcmp(src_row, dst_row, cmp_bytes) == 0) continue; memmove(dst_row, src_row, cmp_bytes); @@ -786,8 +796,8 @@ static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int } } } - src_row += pitch - w * depth; - dst_row += pitch - w * depth; + src_row += pitch - w * VNC_SERVER_FB_BYTES; + dst_row += pitch - w * VNC_SERVER_FB_BYTES; y += inc; } @@ -798,7 +808,8 @@ static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int } } -static void vnc_mouse_set(int x, int y, int visible) +static void vnc_mouse_set(DisplayChangeListener *dcl, + int x, int y, int visible) { /* can we ask the client(s) to move the pointer ??? */ } @@ -806,7 +817,6 @@ static void vnc_mouse_set(int x, int y, int visible) static int vnc_cursor_define(VncState *vs) { QEMUCursor *c = vs->vd->cursor; - PixelFormat pf = qemu_default_pixelformat(32); int isize; if (vnc_has_feature(vs, VNC_FEATURE_RICH_CURSOR)) { @@ -816,8 +826,8 @@ static int vnc_cursor_define(VncState *vs) vnc_write_u16(vs, 1); /* # of rects */ vnc_framebuffer_update(vs, c->hot_x, c->hot_y, c->width, c->height, VNC_ENCODING_RICH_CURSOR); - isize = c->width * c->height * vs->clientds.pf.bytes_per_pixel; - vnc_write_pixels_generic(vs, &pf, c->data, isize); + isize = c->width * c->height * vs->client_pf.bytes_per_pixel; + vnc_write_pixels_generic(vs, c->data, isize); vnc_write(vs, vs->vd->cursor_mask, vs->vd->cursor_msize); vnc_unlock_output(vs); return 0; @@ -825,7 +835,8 @@ static int vnc_cursor_define(VncState *vs) return -1; } -static void vnc_dpy_cursor_define(QEMUCursor *c) +static void vnc_dpy_cursor_define(DisplayChangeListener *dcl, + QEMUCursor *c) { VncDisplay *vd = vnc_display; VncState *vs; @@ -894,8 +905,8 @@ static int vnc_update_client(VncState *vs, int has_dirty) */ job = vnc_job_new(vs); - width = MIN(vd->server->width, vs->client_width); - height = MIN(vd->server->height, vs->client_height); + width = MIN(pixman_image_get_width(vd->server), vs->client_width); + height = MIN(pixman_image_get_height(vd->server), vs->client_height); for (y = 0; y < height; y++) { int x; @@ -1015,7 +1026,7 @@ static void vnc_disconnect_start(VncState *vs) vs->csock = -1; } -static void vnc_disconnect_finish(VncState *vs) +void vnc_disconnect_finish(VncState *vs) { int i; @@ -1026,6 +1037,10 @@ static void vnc_disconnect_finish(VncState *vs) buffer_free(&vs->input); buffer_free(&vs->output); +#ifdef CONFIG_VNC_WS + buffer_free(&vs->ws_input); + buffer_free(&vs->ws_output); +#endif /* CONFIG_VNC_WS */ qobject_decref(vs->info); @@ -1042,20 +1057,19 @@ static void vnc_disconnect_finish(VncState *vs) audio_del(vs); vnc_release_modifiers(vs); - QTAILQ_REMOVE(&vs->vd->clients, vs, next); - - if (QTAILQ_EMPTY(&vs->vd->clients)) { - dcl->idle = 1; + if (vs->initialized) { + QTAILQ_REMOVE(&vs->vd->clients, vs, next); + qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier); } - qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier); - vnc_remove_timer(vs->vd); if (vs->vd->lock_key_sync) qemu_remove_led_event_handler(vs->led); vnc_unlock_output(vs); qemu_mutex_destroy(&vs->output_mutex); - qemu_bh_delete(vs->bh); + if (vs->bh != NULL) { + qemu_bh_delete(vs->bh); + } buffer_free(&vs->jobs_buffer); for (i = 0; i < VNC_STAT_ROWS; ++i) { @@ -1097,6 +1111,23 @@ void vnc_client_error(VncState *vs) vnc_disconnect_start(vs); } +#ifdef CONFIG_VNC_TLS +static long vnc_client_write_tls(gnutls_session_t *session, + const uint8_t *data, + size_t datalen) +{ + long ret = gnutls_write(*session, data, datalen); + if (ret < 0) { + if (ret == GNUTLS_E_AGAIN) { + errno = EAGAIN; + } else { + errno = EIO; + } + ret = -1; + } + return ret; +} +#endif /* CONFIG_VNC_TLS */ /* * Called to write a chunk of data to the client socket. The data may @@ -1118,17 +1149,20 @@ long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen) long ret; #ifdef CONFIG_VNC_TLS if (vs->tls.session) { - ret = gnutls_write(vs->tls.session, data, datalen); - if (ret < 0) { - if (ret == GNUTLS_E_AGAIN) - errno = EAGAIN; - else - errno = EIO; - ret = -1; + ret = vnc_client_write_tls(&vs->tls.session, data, datalen); + } else { +#ifdef CONFIG_VNC_WS + if (vs->ws_tls.session) { + ret = vnc_client_write_tls(&vs->ws_tls.session, data, datalen); + } else +#endif /* CONFIG_VNC_WS */ +#endif /* CONFIG_VNC_TLS */ + { + ret = send(vs->csock, (const void *)data, datalen, 0); } - } else +#ifdef CONFIG_VNC_TLS + } #endif /* CONFIG_VNC_TLS */ - ret = send(vs->csock, (const void *)data, datalen, 0); VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret); return vnc_client_io_error(vs, ret, socket_error()); } @@ -1165,8 +1199,7 @@ static long vnc_client_write_plain(VncState *vs) if (!ret) return 0; - memmove(vs->output.buffer, vs->output.buffer + ret, (vs->output.offset - ret)); - vs->output.offset -= ret; + buffer_advance(&vs->output, ret); if (vs->output.offset == 0) { qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); @@ -1192,7 +1225,16 @@ static void vnc_client_write_locked(void *opaque) vnc_client_write_sasl(vs); } else #endif /* CONFIG_VNC_SASL */ - vnc_client_write_plain(vs); + { +#ifdef CONFIG_VNC_WS + if (vs->encode_ws) { + vnc_client_write_ws(vs); + } else +#endif /* CONFIG_VNC_WS */ + { + vnc_client_write_plain(vs); + } + } } void vnc_client_write(void *opaque) @@ -1200,7 +1242,11 @@ void vnc_client_write(void *opaque) VncState *vs = opaque; vnc_lock_output(vs); - if (vs->output.offset) { + if (vs->output.offset +#ifdef CONFIG_VNC_WS + || vs->ws_output.offset +#endif + ) { vnc_client_write_locked(opaque); } else if (vs->csock != -1) { qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); @@ -1214,6 +1260,22 @@ void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting) vs->read_handler_expect = expecting; } +#ifdef CONFIG_VNC_TLS +static long vnc_client_read_tls(gnutls_session_t *session, uint8_t *data, + size_t datalen) +{ + long ret = gnutls_read(*session, data, datalen); + if (ret < 0) { + if (ret == GNUTLS_E_AGAIN) { + errno = EAGAIN; + } else { + errno = EIO; + } + ret = -1; + } + return ret; +} +#endif /* CONFIG_VNC_TLS */ /* * Called to read a chunk of data from the client socket. The data may @@ -1235,17 +1297,20 @@ long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen) long ret; #ifdef CONFIG_VNC_TLS if (vs->tls.session) { - ret = gnutls_read(vs->tls.session, data, datalen); - if (ret < 0) { - if (ret == GNUTLS_E_AGAIN) - errno = EAGAIN; - else - errno = EIO; - ret = -1; + ret = vnc_client_read_tls(&vs->tls.session, data, datalen); + } else { +#ifdef CONFIG_VNC_WS + if (vs->ws_tls.session) { + ret = vnc_client_read_tls(&vs->ws_tls.session, data, datalen); + } else +#endif /* CONFIG_VNC_WS */ +#endif /* CONFIG_VNC_TLS */ + { + ret = qemu_recv(vs->csock, data, datalen, 0); } - } else +#ifdef CONFIG_VNC_TLS + } #endif /* CONFIG_VNC_TLS */ - 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()); } @@ -1294,7 +1359,21 @@ void vnc_client_read(void *opaque) ret = vnc_client_read_sasl(vs); else #endif /* CONFIG_VNC_SASL */ +#ifdef CONFIG_VNC_WS + if (vs->encode_ws) { + ret = vnc_client_read_ws(vs); + if (ret == -1) { + vnc_disconnect_start(vs); + return; + } else if (ret == -2) { + vnc_client_error(vs); + return; + } + } else +#endif /* CONFIG_VNC_WS */ + { ret = vnc_client_read_plain(vs); + } if (!ret) { if (vs->csock == -1) vnc_disconnect_finish(vs); @@ -1312,8 +1391,7 @@ void vnc_client_read(void *opaque) } if (!ret) { - memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset - len)); - vs->input.offset -= len; + buffer_advance(&vs->input, len); } else { vs->read_handler_expect = ret; } @@ -1366,23 +1444,27 @@ void vnc_write_u8(VncState *vs, uint8_t value) void vnc_flush(VncState *vs) { vnc_lock_output(vs); - if (vs->csock != -1 && vs->output.offset) { + if (vs->csock != -1 && (vs->output.offset +#ifdef CONFIG_VNC_WS + || vs->ws_output.offset +#endif + )) { vnc_client_write_locked(vs); } vnc_unlock_output(vs); } -uint8_t read_u8(uint8_t *data, size_t offset) +static uint8_t read_u8(uint8_t *data, size_t offset) { return data[offset]; } -uint16_t read_u16(uint8_t *data, size_t offset) +static uint16_t read_u16(uint8_t *data, size_t offset) { return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF); } -int32_t read_s32(uint8_t *data, size_t offset) +static int32_t read_s32(uint8_t *data, size_t offset) { return (int32_t)((data[offset] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | data[offset + 3]); @@ -1409,7 +1491,8 @@ static void check_pointer_type_change(Notifier *notifier, void *data) 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), + surface_width(vs->vd->ds), + surface_height(vs->vd->ds), VNC_ENCODING_POINTER_TYPE_CHANGE); vnc_unlock_output(vs); vnc_flush(vs); @@ -1421,6 +1504,8 @@ static void pointer_event(VncState *vs, int button_mask, int x, int y) { int buttons = 0; int dz = 0; + int width = surface_width(vs->vd->ds); + int height = surface_height(vs->vd->ds); if (button_mask & 0x01) buttons |= MOUSE_EVENT_LBUTTON; @@ -1434,10 +1519,8 @@ static void pointer_event(VncState *vs, int button_mask, int x, int y) dz = 1; if (vs->absolute) { - kbd_mouse_event(ds_get_width(vs->ds) > 1 ? - x * 0x7FFF / (ds_get_width(vs->ds) - 1) : 0x4000, - ds_get_height(vs->ds) > 1 ? - y * 0x7FFF / (ds_get_height(vs->ds) - 1) : 0x4000, + kbd_mouse_event(width > 1 ? x * 0x7FFF / (width - 1) : 0x4000, + height > 1 ? y * 0x7FFF / (height - 1) : 0x4000, dz, buttons); } else if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE)) { x -= 0x7FFF; @@ -1478,13 +1561,51 @@ static void press_key(VncState *vs, int keysym) kbd_put_keycode(keycode | SCANCODE_UP); } +static int current_led_state(VncState *vs) +{ + int ledstate = 0; + + if (vs->modifiers_state[0x46]) { + ledstate |= QEMU_SCROLL_LOCK_LED; + } + if (vs->modifiers_state[0x45]) { + ledstate |= QEMU_NUM_LOCK_LED; + } + if (vs->modifiers_state[0x3a]) { + ledstate |= QEMU_CAPS_LOCK_LED; + } + + return ledstate; +} + +static void vnc_led_state_change(VncState *vs) +{ + int ledstate = 0; + + if (!vnc_has_feature(vs, VNC_FEATURE_LED_STATE)) { + return; + } + + ledstate = current_led_state(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, 1, 1, VNC_ENCODING_LED_STATE); + vnc_write_u8(vs, ledstate); + vnc_unlock_output(vs); + vnc_flush(vs); +} + static void kbd_leds(void *opaque, int ledstate) { VncState *vs = opaque; - int caps, num; + int caps, num, scr; + bool has_changed = (ledstate != current_led_state(vs)); caps = ledstate & QEMU_CAPS_LOCK_LED ? 1 : 0; num = ledstate & QEMU_NUM_LOCK_LED ? 1 : 0; + scr = ledstate & QEMU_SCROLL_LOCK_LED ? 1 : 0; if (vs->modifiers_state[0x3a] != caps) { vs->modifiers_state[0x3a] = caps; @@ -1492,6 +1613,14 @@ static void kbd_leds(void *opaque, int ledstate) if (vs->modifiers_state[0x45] != num) { vs->modifiers_state[0x45] = num; } + if (vs->modifiers_state[0x46] != scr) { + vs->modifiers_state[0x46] = scr; + } + + /* Sending the current led state message to the client */ + if (has_changed) { + vnc_led_state_change(vs); + } } static void do_key_event(VncState *vs, int down, int keycode, int sym) @@ -1524,7 +1653,11 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) break; } + /* Turn off the lock state sync logic if the client support the led + state extension. + */ if (down && vs->vd->lock_key_sync && + !vnc_has_feature(vs, VNC_FEATURE_LED_STATE) && 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 @@ -1544,6 +1677,7 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) } if (down && vs->vd->lock_key_sync && + !vnc_has_feature(vs, VNC_FEATURE_LED_STATE) && ((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 @@ -1565,7 +1699,7 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) } } - if (is_graphic_console()) { + if (qemu_console_is_graphic(NULL)) { if (keycode & SCANCODE_GREY) kbd_put_keycode(SCANCODE_EMUL0); if (down) @@ -1684,7 +1818,7 @@ static void vnc_release_modifiers(VncState *vs) }; int i, keycode; - if (!is_graphic_console()) { + if (!qemu_console_is_graphic(NULL)) { return; } for (i = 0; i < ARRAY_SIZE(keycodes); i++) { @@ -1704,7 +1838,7 @@ static void key_event(VncState *vs, int down, uint32_t sym) int keycode; int lsym = sym; - if (lsym >= 'A' && lsym <= 'Z' && is_graphic_console()) { + if (lsym >= 'A' && lsym <= 'Z' && qemu_console_is_graphic(NULL)) { lsym = lsym - 'A' + 'a'; } @@ -1727,12 +1861,15 @@ static void framebuffer_update_request(VncState *vs, int incremental, int w, int h) { int i; - const size_t width = ds_get_width(vs->ds) / 16; + const size_t width = surface_width(vs->vd->ds) / 16; + const size_t height = surface_height(vs->vd->ds); - 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; + if (y_position > height) { + y_position = height; + } + if (y_position + h >= height) { + h = height - y_position; + } vs->need_update = 1; if (!incremental) { @@ -1751,7 +1888,9 @@ static void send_ext_key_event_ack(VncState *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_framebuffer_update(vs, 0, 0, + surface_width(vs->vd->ds), + surface_height(vs->vd->ds), VNC_ENCODING_EXT_KEY_EVENT); vnc_unlock_output(vs); vnc_flush(vs); @@ -1763,7 +1902,9 @@ static void send_ext_audio_ack(VncState *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_framebuffer_update(vs, 0, 0, + surface_width(vs->vd->ds), + surface_height(vs->vd->ds), VNC_ENCODING_AUDIO); vnc_unlock_output(vs); vnc_flush(vs); @@ -1802,10 +1943,12 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) vs->features |= VNC_FEATURE_TIGHT_MASK; vs->vnc_encoding = enc; break; +#ifdef CONFIG_VNC_PNG case VNC_ENCODING_TIGHT_PNG: vs->features |= VNC_FEATURE_TIGHT_PNG_MASK; vs->vnc_encoding = enc; break; +#endif case VNC_ENCODING_ZLIB: vs->features |= VNC_FEATURE_ZLIB_MASK; vs->vnc_encoding = enc; @@ -1836,6 +1979,9 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) case VNC_ENCODING_WMVi: vs->features |= VNC_FEATURE_WMVI_MASK; break; + case VNC_ENCODING_LED_STATE: + vs->features |= VNC_FEATURE_LED_STATE_MASK; + break; case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9: vs->tight.compression = (enc & 0x0F); break; @@ -1851,13 +1997,14 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) } vnc_desktop_resize(vs); check_pointer_type_change(&vs->mouse_mode_notifier, NULL); + vnc_led_state_change(vs); } static void set_pixel_conversion(VncState *vs) { - if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) == - (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG) && - !memcmp(&(vs->clientds.pf), &(vs->ds->surface->pf), sizeof(PixelFormat))) { + pixman_format_code_t fmt = qemu_pixman_get_format(&vs->client_pf); + + if (fmt == VNC_SERVER_FB_FORMAT) { vs->write_pixels = vnc_write_pixels_copy; vnc_hextile_set_pixel_conversion(vs, 0); } else { @@ -1877,35 +2024,36 @@ static void set_pixel_format(VncState *vs, return; } - vs->clientds = *(vs->vd->guest.ds); - vs->clientds.pf.rmax = 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; - 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; - 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; - vs->clientds.pf.bytes_per_pixel = bits_per_pixel / 8; - vs->clientds.pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel; - vs->clientds.flags = big_endian_flag ? QEMU_BIG_ENDIAN_FLAG : 0x00; + vs->client_pf.rmax = red_max; + vs->client_pf.rbits = hweight_long(red_max); + vs->client_pf.rshift = red_shift; + vs->client_pf.rmask = red_max << red_shift; + vs->client_pf.gmax = green_max; + vs->client_pf.gbits = hweight_long(green_max); + vs->client_pf.gshift = green_shift; + vs->client_pf.gmask = green_max << green_shift; + vs->client_pf.bmax = blue_max; + vs->client_pf.bbits = hweight_long(blue_max); + vs->client_pf.bshift = blue_shift; + vs->client_pf.bmask = blue_max << blue_shift; + vs->client_pf.bits_per_pixel = bits_per_pixel; + vs->client_pf.bytes_per_pixel = bits_per_pixel / 8; + vs->client_pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel; + vs->client_be = big_endian_flag; set_pixel_conversion(vs); - vga_hw_invalidate(); - vga_hw_update(); + graphic_hw_invalidate(NULL); + graphic_hw_update(NULL); } static void pixel_format_message (VncState *vs) { char pad[3] = { 0, 0, 0 }; - vnc_write_u8(vs, vs->ds->surface->pf.bits_per_pixel); /* bits-per-pixel */ - vnc_write_u8(vs, vs->ds->surface->pf.depth); /* depth */ + vs->client_pf = qemu_default_pixelformat(32); + + vnc_write_u8(vs, vs->client_pf.bits_per_pixel); /* bits-per-pixel */ + vnc_write_u8(vs, vs->client_pf.depth); /* depth */ #ifdef HOST_WORDS_BIGENDIAN vnc_write_u8(vs, 1); /* big-endian-flag */ @@ -1913,28 +2061,16 @@ static void pixel_format_message (VncState *vs) { vnc_write_u8(vs, 0); /* big-endian-flag */ #endif vnc_write_u8(vs, 1); /* true-color-flag */ - vnc_write_u16(vs, vs->ds->surface->pf.rmax); /* red-max */ - vnc_write_u16(vs, vs->ds->surface->pf.gmax); /* green-max */ - vnc_write_u16(vs, vs->ds->surface->pf.bmax); /* blue-max */ - vnc_write_u8(vs, vs->ds->surface->pf.rshift); /* red-shift */ - vnc_write_u8(vs, vs->ds->surface->pf.gshift); /* green-shift */ - vnc_write_u8(vs, vs->ds->surface->pf.bshift); /* blue-shift */ + vnc_write_u16(vs, vs->client_pf.rmax); /* red-max */ + vnc_write_u16(vs, vs->client_pf.gmax); /* green-max */ + vnc_write_u16(vs, vs->client_pf.bmax); /* blue-max */ + vnc_write_u8(vs, vs->client_pf.rshift); /* red-shift */ + vnc_write_u8(vs, vs->client_pf.gshift); /* green-shift */ + vnc_write_u8(vs, vs->client_pf.bshift); /* blue-shift */ + vnc_write(vs, pad, 3); /* padding */ vnc_hextile_set_pixel_conversion(vs, 0); - - vs->clientds = *(vs->ds->surface); - vs->clientds.flags &= ~QEMU_ALLOCATED_FLAG; vs->write_pixels = vnc_write_pixels_copy; - - vnc_write(vs, pad, 3); /* padding */ -} - -static void vnc_dpy_setdata(DisplayState *ds) -{ - VncDisplay *vd = ds->opaque; - - *(vd->guest.ds) = *(ds->surface); - vnc_dpy_update(ds, 0, 0, ds_get_width(ds), ds_get_height(ds)); } static void vnc_colordepth(VncState *vs) @@ -1945,8 +2081,10 @@ static void vnc_colordepth(VncState *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); + vnc_framebuffer_update(vs, 0, 0, + surface_width(vs->vd->ds), + surface_height(vs->vd->ds), + VNC_ENCODING_WMVi); pixel_format_message(vs); vnc_unlock_output(vs); vnc_flush(vs); @@ -1962,9 +2100,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) VncDisplay *vd = vs->vd; if (data[0] > 3) { - vd->timer_interval = VNC_REFRESH_INTERVAL_BASE; - 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); + update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE); } switch (data[0]) { @@ -2162,8 +2298,8 @@ static int protocol_client_init(VncState *vs, uint8_t *data, size_t len) } vnc_set_share_mode(vs, mode); - vs->client_width = ds_get_width(vs->ds); - vs->client_height = ds_get_height(vs->ds); + vs->client_width = surface_width(vs->vd->ds); + vs->client_height = surface_height(vs->vd->ds); vnc_write_u16(vs, vs->client_width); vnc_write_u16(vs, vs->client_height); @@ -2437,12 +2573,14 @@ static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y) static int vnc_update_stats(VncDisplay *vd, struct timeval * tv) { + int width = pixman_image_get_width(vd->guest.fb); + int height = pixman_image_get_height(vd->guest.fb); 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) { + for (y = 0; y < height; y += VNC_STAT_RECT) { + for (x = 0; x < width; x += VNC_STAT_RECT) { VncRectStat *rect = vnc_stat_rect(vd, x, y); rect->updated = false; @@ -2456,8 +2594,8 @@ static int vnc_update_stats(VncDisplay *vd, struct timeval * tv) } 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) { + for (y = 0; y < height; y += VNC_STAT_RECT) { + for (x = 0; x < width; x += VNC_STAT_RECT) { VncRectStat *rect= vnc_stat_rect(vd, x, y); int count = ARRAY_SIZE(rect->times); struct timeval min, max; @@ -2526,12 +2664,15 @@ static void vnc_rect_updated(VncDisplay *vd, int x, int y, struct timeval * tv) static int vnc_refresh_server_surface(VncDisplay *vd) { + int width = pixman_image_get_width(vd->guest.fb); + int height = pixman_image_get_height(vd->guest.fb); int y; uint8_t *guest_row; uint8_t *server_row; int cmp_bytes; VncState *vs; int has_dirty = 0; + pixman_image_t *tmpbuf = NULL; struct timeval tv = { 0, 0 }; @@ -2545,22 +2686,31 @@ static int vnc_refresh_server_surface(VncDisplay *vd) * Check and copy modified bits from guest to server surface. * Update server dirty map. */ - cmp_bytes = 16 * ds_get_bytes_per_pixel(vd->ds); - if (cmp_bytes > vd->ds->surface->linesize) { - cmp_bytes = vd->ds->surface->linesize; + cmp_bytes = 64; + if (cmp_bytes > vnc_server_fb_stride(vd)) { + cmp_bytes = vnc_server_fb_stride(vd); } - guest_row = vd->guest.ds->data; - server_row = vd->server->data; - for (y = 0; y < vd->guest.ds->height; y++) { + if (vd->guest.format != VNC_SERVER_FB_FORMAT) { + int width = pixman_image_get_width(vd->server); + tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, width); + } + guest_row = (uint8_t *)pixman_image_get_data(vd->guest.fb); + server_row = (uint8_t *)pixman_image_get_data(vd->server); + for (y = 0; y < height; y++) { if (!bitmap_empty(vd->guest.dirty[y], VNC_DIRTY_BITS)) { int x; uint8_t *guest_ptr; uint8_t *server_ptr; - guest_ptr = guest_row; + if (vd->guest.format != VNC_SERVER_FB_FORMAT) { + qemu_pixman_linebuf_fill(tmpbuf, vd->guest.fb, width, 0, y); + guest_ptr = (uint8_t *)pixman_image_get_data(tmpbuf); + } else { + guest_ptr = guest_row; + } server_ptr = server_row; - for (x = 0; x + 15 < vd->guest.ds->width; + for (x = 0; x + 15 < width; x += 16, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) { if (!test_and_clear_bit((x / 16), vd->guest.dirty[y])) continue; @@ -2575,24 +2725,23 @@ static int vnc_refresh_server_surface(VncDisplay *vd) has_dirty++; } } - guest_row += ds_get_linesize(vd->ds); - server_row += ds_get_linesize(vd->ds); + guest_row += pixman_image_get_stride(vd->guest.fb); + server_row += pixman_image_get_stride(vd->server); } + qemu_pixman_image_unref(tmpbuf); return has_dirty; } -static void vnc_refresh(void *opaque) +static void vnc_refresh(DisplayChangeListener *dcl) { - VncDisplay *vd = opaque; + VncDisplay *vd = container_of(dcl, VncDisplay, dcl); VncState *vs, *vn; int has_dirty, rects = 0; - vga_hw_update(); + graphic_hw_update(NULL); 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); + update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE); return; } @@ -2604,43 +2753,25 @@ static void vnc_refresh(void *opaque) /* 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 (QTAILQ_EMPTY(&vd->clients)) { + update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_MAX); return; + } if (has_dirty && rects) { - vd->timer_interval /= 2; - if (vd->timer_interval < VNC_REFRESH_INTERVAL_BASE) - vd->timer_interval = VNC_REFRESH_INTERVAL_BASE; + vd->dcl.update_interval /= 2; + if (vd->dcl.update_interval < VNC_REFRESH_INTERVAL_BASE) { + vd->dcl.update_interval = VNC_REFRESH_INTERVAL_BASE; + } } else { - vd->timer_interval += VNC_REFRESH_INTERVAL_INC; - if (vd->timer_interval > VNC_REFRESH_INTERVAL_MAX) - vd->timer_interval = VNC_REFRESH_INTERVAL_MAX; - } - 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_ms(rt_clock, vnc_refresh, vd); - vnc_dpy_resize(vd->ds); - vnc_refresh(vd); - } -} - -static void vnc_remove_timer(VncDisplay *vd) -{ - if (vd->timer != NULL && QTAILQ_EMPTY(&vd->clients)) { - qemu_del_timer(vd->timer); - qemu_free_timer(vd->timer); - vd->timer = NULL; + vd->dcl.update_interval += VNC_REFRESH_INTERVAL_INC; + if (vd->dcl.update_interval > VNC_REFRESH_INTERVAL_MAX) { + vd->dcl.update_interval = VNC_REFRESH_INTERVAL_MAX; + } } } -static void vnc_connect(VncDisplay *vd, int csock, int skipauth) +static void vnc_connect(VncDisplay *vd, int csock, int skipauth, bool websocket) { VncState *vs = g_malloc0(sizeof(VncState)); int i; @@ -2665,16 +2796,46 @@ static void vnc_connect(VncDisplay *vd, int csock, int skipauth) } 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); + update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE); + qemu_set_nonblock(vs->csock); +#ifdef CONFIG_VNC_WS + if (websocket) { + vs->websocket = 1; +#ifdef CONFIG_VNC_TLS + if (vd->tls.x509cert) { + qemu_set_fd_handler2(vs->csock, NULL, vncws_tls_handshake_peek, + NULL, vs); + } else +#endif /* CONFIG_VNC_TLS */ + { + qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, + NULL, vs); + } + } else +#endif /* CONFIG_VNC_WS */ + { + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + } vnc_client_cache_addr(vs); vnc_qmp_event(vs, QEVENT_VNC_CONNECTED); vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING); vs->vd = vd; - vs->ds = vd->ds; + +#ifdef CONFIG_VNC_WS + if (!vs->websocket) +#endif + { + vnc_init_state(vs); + } +} + +void vnc_init_state(VncState *vs) +{ + vs->initialized = true; + VncDisplay *vd = vs->vd; + vs->last_x = -1; vs->last_y = -1; @@ -2688,7 +2849,7 @@ static void vnc_connect(VncDisplay *vd, int csock, int skipauth) QTAILQ_INSERT_HEAD(&vd->clients, vs, next); - vga_hw_update(); + graphic_hw_update(NULL); vnc_write(vs, "RFB 003.008\n", 12); vnc_flush(vs); @@ -2700,39 +2861,65 @@ static void vnc_connect(VncDisplay *vd, int csock, int skipauth) vs->mouse_mode_notifier.notify = check_pointer_type_change; qemu_add_mouse_mode_change_notifier(&vs->mouse_mode_notifier); - vnc_init_timer(vd); - /* vs might be free()ed here */ } -static void vnc_listen_read(void *opaque) +static void vnc_listen_read(void *opaque, bool websocket) { VncDisplay *vs = opaque; struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); + int csock; /* Catch-up */ - vga_hw_update(); + graphic_hw_update(NULL); +#ifdef CONFIG_VNC_WS + if (websocket) { + csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen); + } else +#endif /* CONFIG_VNC_WS */ + { + csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); + } - int csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); if (csock != -1) { - vnc_connect(vs, csock, 0); + vnc_connect(vs, csock, 0, websocket); } } +static void vnc_listen_regular_read(void *opaque) +{ + vnc_listen_read(opaque, 0); +} + +#ifdef CONFIG_VNC_WS +static void vnc_listen_websocket_read(void *opaque) +{ + vnc_listen_read(opaque, 1); +} +#endif /* CONFIG_VNC_WS */ + +static const DisplayChangeListenerOps dcl_ops = { + .dpy_name = "vnc", + .dpy_refresh = vnc_refresh, + .dpy_gfx_copy = vnc_dpy_copy, + .dpy_gfx_update = vnc_dpy_update, + .dpy_gfx_switch = vnc_dpy_switch, + .dpy_mouse_set = vnc_mouse_set, + .dpy_cursor_define = vnc_dpy_cursor_define, +}; + void vnc_display_init(DisplayState *ds) { VncDisplay *vs = g_malloc0(sizeof(*vs)); - dcl = g_malloc0(sizeof(DisplayChangeListener)); - - ds->opaque = vs; - dcl->idle = 1; vnc_display = vs; vs->lsock = -1; +#ifdef CONFIG_VNC_WS + vs->lwebsock = -1; +#endif - vs->ds = ds; QTAILQ_INIT(&vs->clients); vs->expires = TIME_MAX; @@ -2747,19 +2934,14 @@ void vnc_display_init(DisplayState *ds) qemu_mutex_init(&vs->mutex); vnc_start_worker_thread(); - dcl->dpy_copy = vnc_dpy_copy; - dcl->dpy_update = vnc_dpy_update; - dcl->dpy_resize = vnc_dpy_resize; - dcl->dpy_setdata = vnc_dpy_setdata; - register_displaychangelistener(ds, dcl); - ds->mouse_set = vnc_mouse_set; - ds->cursor_define = vnc_dpy_cursor_define; + vs->dcl.ops = &dcl_ops; + register_displaychangelistener(&vs->dcl); } -void vnc_display_close(DisplayState *ds) +static void vnc_display_close(DisplayState *ds) { - VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + VncDisplay *vs = vnc_display; if (!vs) return; @@ -2772,6 +2954,15 @@ void vnc_display_close(DisplayState *ds) close(vs->lsock); vs->lsock = -1; } +#ifdef CONFIG_VNC_WS + g_free(vs->ws_display); + vs->ws_display = NULL; + if (vs->lwebsock != -1) { + qemu_set_fd_handler2(vs->lwebsock, NULL, NULL, NULL, NULL); + close(vs->lwebsock); + vs->lwebsock = -1; + } +#endif /* CONFIG_VNC_WS */ vs->auth = VNC_AUTH_INVALID; #ifdef CONFIG_VNC_TLS vs->subauth = VNC_AUTH_INVALID; @@ -2779,9 +2970,9 @@ void vnc_display_close(DisplayState *ds) #endif } -int vnc_display_disable_login(DisplayState *ds) +static int vnc_display_disable_login(DisplayState *ds) { - VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + VncDisplay *vs = vnc_display; if (!vs) { return -1; @@ -2801,7 +2992,7 @@ int vnc_display_disable_login(DisplayState *ds) int vnc_display_password(DisplayState *ds, const char *password) { - VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + VncDisplay *vs = vnc_display; if (!vs) { return -EINVAL; @@ -2827,7 +3018,7 @@ int vnc_display_password(DisplayState *ds, const char *password) int vnc_display_pw_expire(DisplayState *ds, time_t expires) { - VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + VncDisplay *vs = vnc_display; if (!vs) { return -EINVAL; @@ -2839,14 +3030,14 @@ int vnc_display_pw_expire(DisplayState *ds, time_t expires) char *vnc_display_local_addr(DisplayState *ds) { - VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + VncDisplay *vs = vnc_display; return vnc_socket_local_addr("%s:%s", vs->lsock); } -int vnc_display_open(DisplayState *ds, const char *display) +void vnc_display_open(DisplayState *ds, const char *display, Error **errp) { - VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + VncDisplay *vs = vnc_display; const char *options; int password = 0; int reverse = 0; @@ -2862,14 +3053,15 @@ int vnc_display_open(DisplayState *ds, const char *display) #endif int lock_key_sync = 1; - if (!vnc_display) - return -1; + if (!vnc_display) { + error_setg(errp, "VNC display not active"); + return; + } vnc_display_close(ds); if (strcmp(display, "none") == 0) - return 0; + return; - if (!(vs->display = strdup(display))) - return -1; + vs->display = g_strdup(display); vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE; options = display; @@ -2877,13 +3069,11 @@ int vnc_display_open(DisplayState *ds, const char *display) options++; if (strncmp(options, "password", 8) == 0) { if (fips_get_state()) { - fprintf(stderr, - "VNC password auth disabled due to FIPS mode, " - "consider using the VeNCrypt or SASL authentication " - "methods as an alternative\n"); - g_free(vs->display); - vs->display = NULL; - return -1; + error_setg(errp, + "VNC password auth disabled due to FIPS mode, " + "consider using the VeNCrypt or SASL authentication " + "methods as an alternative"); + goto fail; } password = 1; /* Require password auth */ } else if (strncmp(options, "reverse", 7) == 0) { @@ -2894,6 +3084,36 @@ int vnc_display_open(DisplayState *ds, const char *display) } else if (strncmp(options, "sasl", 4) == 0) { sasl = 1; /* Require SASL auth */ #endif +#ifdef CONFIG_VNC_WS + } else if (strncmp(options, "websocket", 9) == 0) { + char *start, *end; + vs->websocket = 1; + + /* Check for 'websocket=' */ + start = strchr(options, '='); + end = strchr(options, ','); + if (start && (!end || (start < end))) { + int len = end ? end-(start+1) : strlen(start+1); + if (len < 6) { + /* extract the host specification from display */ + char *host = NULL, *port = NULL, *host_end = NULL; + port = g_strndup(start + 1, len); + + /* ipv6 hosts have colons */ + end = strchr(display, ','); + host_end = g_strrstr_len(display, end - display, ":"); + + if (host_end) { + host = g_strndup(display, host_end - display + 1); + } else { + host = g_strndup(":", 1); + } + vs->ws_display = g_strconcat(host, port, NULL); + g_free(host); + g_free(port); + } + } +#endif /* CONFIG_VNC_WS */ #ifdef CONFIG_VNC_TLS } else if (strncmp(options, "tls", 3) == 0) { tls = 1; /* Require TLS */ @@ -2913,18 +3133,14 @@ int vnc_display_open(DisplayState *ds, const char *display) VNC_DEBUG("Trying certificate path '%s'\n", path); if (vnc_tls_set_x509_creds_dir(vs, path) < 0) { - fprintf(stderr, "Failed to find x509 certificates/keys in %s\n", path); + error_setg(errp, "Failed to find x509 certificates/keys in %s", path); g_free(path); - g_free(vs->display); - vs->display = NULL; - return -1; + goto fail; } g_free(path); } else { - fprintf(stderr, "No certificate path provided\n"); - g_free(vs->display); - vs->display = NULL; - return -1; + error_setg(errp, "No certificate path provided"); + goto fail; } #endif #if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) @@ -2933,7 +3149,7 @@ 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) { + } else if (strncmp(options, "non-adaptive", 12) == 0) { vs->non_adaptive = true; } else if (strncmp(options, "share=", 6) == 0) { if (strncmp(options+6, "ignore", 6) == 0) { @@ -2943,10 +3159,8 @@ int vnc_display_open(DisplayState *ds, const char *display) } else if (strncmp(options+6, "force-shared", 12) == 0) { vs->share_policy = VNC_SHARE_POLICY_FORCE_SHARED; } else { - fprintf(stderr, "unknown vnc share= option\n"); - g_free(vs->display); - vs->display = NULL; - return -1; + error_setg(errp, "unknown vnc share= option"); + goto fail; } } } @@ -3047,57 +3261,89 @@ int vnc_display_open(DisplayState *ds, const char *display) #ifdef CONFIG_VNC_SASL if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) { - fprintf(stderr, "Failed to initialize SASL auth %s", - sasl_errstring(saslErr, NULL, NULL)); - g_free(vs->display); - vs->display = NULL; - return -1; + error_setg(errp, "Failed to initialize SASL auth: %s", + sasl_errstring(saslErr, NULL, NULL)); + goto fail; } #endif vs->lock_key_sync = lock_key_sync; if (reverse) { /* connect to viewer */ - if (strncmp(display, "unix:", 5) == 0) - vs->lsock = unix_connect(display+5); - else - vs->lsock = inet_connect(display, true, NULL); - if (-1 == vs->lsock) { - g_free(vs->display); - vs->display = NULL; - return -1; + int csock; + vs->lsock = -1; +#ifdef CONFIG_VNC_WS + vs->lwebsock = -1; +#endif + if (strncmp(display, "unix:", 5) == 0) { + csock = unix_connect(display+5, errp); } else { - int csock = vs->lsock; - vs->lsock = -1; - vnc_connect(vs, csock, 0); + csock = inet_connect(display, errp); } - return 0; - + if (csock < 0) { + goto fail; + } + vnc_connect(vs, csock, 0, 0); } else { /* listen for connects */ char *dpy; dpy = g_malloc(256); if (strncmp(display, "unix:", 5) == 0) { pstrcpy(dpy, 256, "unix:"); - vs->lsock = unix_listen(display+5, dpy+5, 256-5); + vs->lsock = unix_listen(display+5, dpy+5, 256-5, errp); } else { vs->lsock = inet_listen(display, dpy, 256, - SOCK_STREAM, 5900, NULL); + SOCK_STREAM, 5900, errp); + if (vs->lsock < 0) { + g_free(dpy); + goto fail; + } +#ifdef CONFIG_VNC_WS + if (vs->websocket) { + if (vs->ws_display) { + vs->lwebsock = inet_listen(vs->ws_display, NULL, 256, + SOCK_STREAM, 0, errp); + } else { + vs->lwebsock = inet_listen(vs->display, NULL, 256, + SOCK_STREAM, 5700, errp); + } + + if (vs->lwebsock < 0) { + if (vs->lsock) { + close(vs->lsock); + vs->lsock = -1; + } + g_free(dpy); + goto fail; + } + } +#endif /* CONFIG_VNC_WS */ } - if (-1 == vs->lsock) { - g_free(dpy); - return -1; - } else { - g_free(vs->display); - vs->display = dpy; + g_free(vs->display); + vs->display = dpy; + qemu_set_fd_handler2(vs->lsock, NULL, + vnc_listen_regular_read, NULL, vs); +#ifdef CONFIG_VNC_WS + if (vs->websocket) { + qemu_set_fd_handler2(vs->lwebsock, NULL, + vnc_listen_websocket_read, NULL, vs); } +#endif /* CONFIG_VNC_WS */ } - return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs); + return; + +fail: + g_free(vs->display); + vs->display = NULL; +#ifdef CONFIG_VNC_WS + g_free(vs->ws_display); + vs->ws_display = NULL; +#endif /* CONFIG_VNC_WS */ } void vnc_display_add_client(DisplayState *ds, int csock, int skipauth) { - VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + VncDisplay *vs = vnc_display; - vnc_connect(vs, csock, skipauth); + vnc_connect(vs, csock, skipauth, 0); }