X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=ui%2Fvnc.c;h=dfc74591f4b731e25dddbcd56bc22bf941ceb87b;hb=1483adcf6ac978656718d4383d909c96dce395a6;hp=61f120e315710aba9cfb31cf7ad776fe6a5b45a0;hpb=ef84755ebb38b4f5629b24440bb00f1ef5287707;p=qemu.git diff --git a/ui/vnc.c b/ui/vnc.c index 61f120e31..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); @@ -420,24 +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 = ds_get_width(ds); - int height = ds_get_height(ds); + int width = surface_width(vd->ds); + int height = surface_height(vd->ds); h += y; @@ -486,7 +483,7 @@ static int buffer_empty(Buffer *buffer) return buffer->offset == 0; } -static uint8_t *buffer_end(Buffer *buffer) +uint8_t *buffer_end(Buffer *buffer) { return buffer->buffer + buffer->offset; } @@ -510,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); @@ -567,18 +571,20 @@ void *vnc_server_fb_ptr(VncDisplay *vd, int x, int y) return ptr; } -static void vnc_dpy_resize(DisplayState *ds) +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 */ qemu_pixman_image_unref(vd->server); + vd->ds = surface; vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT, - ds_get_width(ds), - ds_get_height(ds), + surface_width(vd->ds), + surface_height(vd->ds), NULL, 0); /* guest surface */ @@ -587,8 +593,8 @@ static void vnc_dpy_resize(DisplayState *ds) console_color_init(ds); #endif qemu_pixman_image_unref(vd->guest.fb); - vd->guest.fb = pixman_image_ref(ds->surface->image); - vd->guest.format = ds->surface->format; + 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) { @@ -729,9 +735,11 @@ 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; @@ -800,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(DisplayState *ds, 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 ??? */ } @@ -826,7 +835,8 @@ static int vnc_cursor_define(VncState *vs) return -1; } -static void vnc_dpy_cursor_define(DisplayState *ds, QEMUCursor *c) +static void vnc_dpy_cursor_define(DisplayChangeListener *dcl, + QEMUCursor *c) { VncDisplay *vd = vnc_display; VncState *vs; @@ -1016,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; @@ -1027,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); @@ -1043,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) { @@ -1098,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 @@ -1119,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()); } @@ -1166,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); @@ -1193,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) @@ -1201,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); @@ -1215,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 @@ -1236,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()); } @@ -1295,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); @@ -1313,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; } @@ -1367,7 +1444,11 @@ 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); @@ -1410,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); @@ -1422,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; @@ -1435,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; @@ -1479,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; @@ -1493,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) @@ -1525,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 @@ -1545,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 @@ -1566,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) @@ -1685,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++) { @@ -1705,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'; } @@ -1728,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) { @@ -1752,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); @@ -1764,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); @@ -1839,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; @@ -1854,6 +1997,7 @@ 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) @@ -1899,8 +2043,8 @@ static void set_pixel_format(VncState *vs, 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) { @@ -1929,16 +2073,6 @@ static void pixel_format_message (VncState *vs) { vs->write_pixels = vnc_write_pixels_copy; } -static void vnc_dpy_setdata(DisplayState *ds) -{ - VncDisplay *vd = ds->opaque; - - qemu_pixman_image_unref(vd->guest.fb); - vd->guest.fb = pixman_image_ref(ds->surface->image); - vd->guest.format = ds->surface->format; - vnc_dpy_update(ds, 0, 0, ds_get_width(ds), ds_get_height(ds)); -} - static void vnc_colordepth(VncState *vs) { if (vnc_has_feature(vs, VNC_FEATURE_WMVI)) { @@ -1947,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); @@ -1964,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]) { @@ -2164,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); @@ -2569,7 +2703,7 @@ static int vnc_refresh_server_surface(VncDisplay *vd) uint8_t *server_ptr; if (vd->guest.format != VNC_SERVER_FB_FORMAT) { - qemu_pixman_linebuf_fill(tmpbuf, vd->guest.fb, width, y); + 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; @@ -2598,18 +2732,16 @@ static int vnc_refresh_server_surface(VncDisplay *vd) 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; } @@ -2621,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; @@ -2682,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; @@ -2705,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); @@ -2717,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; @@ -2764,19 +2934,14 @@ void vnc_display_init(DisplayState *ds) qemu_mutex_init(&vs->mutex); vnc_start_worker_thread(); - dcl->dpy_gfx_copy = vnc_dpy_copy; - dcl->dpy_gfx_update = vnc_dpy_update; - dcl->dpy_gfx_resize = vnc_dpy_resize; - dcl->dpy_gfx_setdata = vnc_dpy_setdata; - dcl->dpy_mouse_set = vnc_mouse_set; - dcl->dpy_cursor_define = vnc_dpy_cursor_define; - register_displaychangelistener(ds, dcl); + vs->dcl.ops = &dcl_ops; + register_displaychangelistener(&vs->dcl); } static void vnc_display_close(DisplayState *ds) { - VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + VncDisplay *vs = vnc_display; if (!vs) return; @@ -2789,6 +2954,15 @@ static 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; @@ -2798,7 +2972,7 @@ static void vnc_display_close(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; @@ -2818,7 +2992,7 @@ static 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; @@ -2844,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; @@ -2856,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); } 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; @@ -2910,6 +3084,36 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp) } 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 */ @@ -2945,7 +3149,7 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp) #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) { @@ -3068,6 +3272,9 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp) /* connect to viewer */ 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 { @@ -3076,7 +3283,7 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp) if (csock < 0) { goto fail; } - vnc_connect(vs, csock, 0); + vnc_connect(vs, csock, 0, 0); } else { /* listen for connects */ char *dpy; @@ -3087,25 +3294,56 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp) } else { vs->lsock = inet_listen(display, dpy, 256, SOCK_STREAM, 5900, errp); - } - if (vs->lsock < 0) { - g_free(dpy); - goto fail; + 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 */ } g_free(vs->display); vs->display = dpy; - qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs); + 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; 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); }