summaryrefslogtreecommitdiff
path: root/ui/vnc.c
diff options
context:
space:
mode:
Diffstat (limited to 'ui/vnc.c')
-rw-r--r--ui/vnc.c591
1 files changed, 415 insertions, 176 deletions
diff --git a/ui/vnc.c b/ui/vnc.c
index ba303626a..5601cc34e 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,26 @@ 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,
+ bool skipauth, bool websocket)
{
VncState *vs = g_malloc0(sizeof(VncState));
int i;
@@ -2682,16 +2797,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 +2850,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 +2862,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, false, websocket);
}
}
+static void vnc_listen_regular_read(void *opaque)
+{
+ vnc_listen_read(opaque, false);
+}
+
+#ifdef CONFIG_VNC_WS
+static void vnc_listen_websocket_read(void *opaque)
+{
+ vnc_listen_read(opaque, true);
+}
+#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 +2935,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 +2955,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 +2973,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 +2993,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 +3019,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 +3031,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 +3085,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=<port>' */
+ 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 */
@@ -3068,6 +3273,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 +3284,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, false, false);
} else {
/* listen for connects */
char *dpy;
@@ -3087,25 +3295,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)
+void vnc_display_add_client(DisplayState *ds, int csock, bool skipauth)
{
- VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
+ VncDisplay *vs = vnc_display;
- vnc_connect(vs, csock, skipauth);
+ vnc_connect(vs, csock, skipauth, false);
}