aboutsummaryrefslogtreecommitdiff
path: root/ui/vnc.c
diff options
context:
space:
mode:
Diffstat (limited to 'ui/vnc.c')
-rw-r--r--ui/vnc.c144
1 files changed, 120 insertions, 24 deletions
diff --git a/ui/vnc.c b/ui/vnc.c
index d0a4b75132..7330b2c889 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -25,6 +25,7 @@
*/
#include "vnc.h"
+#include "vnc-jobs.h"
#include "sysemu.h"
#include "qemu_socket.h"
#include "qemu-timer.h"
@@ -45,7 +46,6 @@
} \
}
-
static VncDisplay *vnc_display; /* needed for info vnc */
static DisplayChangeListener *dcl;
@@ -359,6 +359,7 @@ void do_info_vnc(Monitor *mon, QObject **ret_data)
*/
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);
@@ -502,19 +503,48 @@ static void vnc_desktop_resize(VncState *vs)
}
vs->client_width = ds_get_width(ds);
vs->client_height = ds_get_height(ds);
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */
vnc_framebuffer_update(vs, 0, 0, vs->client_width, vs->client_height,
VNC_ENCODING_DESKTOPRESIZE);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
+#ifdef CONFIG_VNC_THREAD
+static void vnc_abort_display_jobs(VncDisplay *vd)
+{
+ VncState *vs;
+
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_lock_output(vs);
+ vs->abort = true;
+ vnc_unlock_output(vs);
+ }
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_jobs_join(vs);
+ }
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_lock_output(vs);
+ vs->abort = false;
+ vnc_unlock_output(vs);
+ }
+}
+#else
+static void vnc_abort_display_jobs(VncDisplay *vd)
+{
+}
+#endif
+
static void vnc_dpy_resize(DisplayState *ds)
{
VncDisplay *vd = ds->opaque;
VncState *vs;
+ vnc_abort_display_jobs(vd);
+
/* server surface */
if (!vd->server)
vd->server = qemu_mallocz(sizeof(*vd->server));
@@ -642,7 +672,7 @@ int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
return 1;
}
-static int send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
+int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
{
int n = 0;
@@ -671,12 +701,14 @@ static int send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
{
/* send bitblit op to the vnc client */
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */
vnc_framebuffer_update(vs, dst_x, dst_y, w, h, VNC_ENCODING_COPYRECT);
vnc_write_u16(vs, src_x);
vnc_write_u16(vs, src_y);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
@@ -693,7 +725,7 @@ static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int
QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
vs->force_update = 1;
- vnc_update_client(vs, 1);
+ vnc_update_client_sync(vs, 1);
/* vs might be free()ed here */
}
}
@@ -813,15 +845,29 @@ static int find_and_clear_dirty_height(struct VncState *vs,
return h;
}
+#ifdef CONFIG_VNC_THREAD
+static int vnc_update_client_sync(VncState *vs, int has_dirty)
+{
+ int ret = vnc_update_client(vs, has_dirty);
+ vnc_jobs_join(vs);
+ return ret;
+}
+#else
+static int vnc_update_client_sync(VncState *vs, int has_dirty)
+{
+ return vnc_update_client(vs, has_dirty);
+}
+#endif
+
static int vnc_update_client(VncState *vs, int has_dirty)
{
if (vs->need_update && vs->csock != -1) {
VncDisplay *vd = vs->vd;
+ VncJob *job;
int y;
- int n_rectangles;
- int saved_offset;
int width, height;
- int n;
+ int n = 0;
+
if (vs->output.offset && !vs->audio_cap && !vs->force_update)
/* kernel send buffers are full -> drop frames to throttle */
@@ -836,11 +882,7 @@ static int vnc_update_client(VncState *vs, int has_dirty)
* happening in parallel don't disturb us, the next pass will
* send them to the client.
*/
- n_rectangles = 0;
- vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
- vnc_write_u8(vs, 0);
- saved_offset = vs->output.offset;
- vnc_write_u16(vs, 0);
+ job = vnc_job_new(vs);
width = MIN(vd->server->width, vs->client_width);
height = MIN(vd->server->height, vs->client_height);
@@ -857,25 +899,23 @@ static int vnc_update_client(VncState *vs, int has_dirty)
} else {
if (last_x != -1) {
int h = find_and_clear_dirty_height(vs, y, last_x, x);
- n = send_framebuffer_update(vs, last_x * 16, y,
- (x - last_x) * 16, h);
- n_rectangles += n;
+
+ n += vnc_job_add_rect(job, last_x * 16, y,
+ (x - last_x) * 16, h);
}
last_x = -1;
}
}
if (last_x != -1) {
int h = find_and_clear_dirty_height(vs, y, last_x, x);
- n = send_framebuffer_update(vs, last_x * 16, y,
- (x - last_x) * 16, h);
- n_rectangles += n;
+ n += vnc_job_add_rect(job, last_x * 16, y,
+ (x - last_x) * 16, h);
}
}
- vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
- vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
- vnc_flush(vs);
+
+ vnc_job_push(job);
vs->force_update = 0;
- return n_rectangles;
+ return n;
}
if (vs->csock == -1)
@@ -891,16 +931,20 @@ static void audio_capture_notify(void *opaque, audcnotification_e cmd)
switch (cmd) {
case AUD_CNOTIFY_DISABLE:
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_END);
+ vnc_unlock_output(vs);
vnc_flush(vs);
break;
case AUD_CNOTIFY_ENABLE:
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_BEGIN);
+ vnc_unlock_output(vs);
vnc_flush(vs);
break;
}
@@ -914,11 +958,13 @@ static void audio_capture(void *opaque, void *buf, int size)
{
VncState *vs = opaque;
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA);
vnc_write_u32(vs, size);
vnc_write(vs, buf, size);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
@@ -960,6 +1006,9 @@ static void vnc_disconnect_start(VncState *vs)
static void vnc_disconnect_finish(VncState *vs)
{
+ vnc_jobs_join(vs); /* Wait encoding jobs */
+
+ vnc_lock_output(vs);
vnc_qmp_event(vs, QEVENT_VNC_DISCONNECTED);
buffer_free(&vs->input);
@@ -988,6 +1037,11 @@ static void vnc_disconnect_finish(VncState *vs)
vnc_remove_timer(vs->vd);
if (vs->vd->lock_key_sync)
qemu_remove_led_event_handler(vs->led);
+ vnc_unlock_output(vs);
+
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_destroy(&vs->output_mutex);
+#endif
qemu_free(vs);
}
@@ -1107,7 +1161,7 @@ static long vnc_client_write_plain(VncState *vs)
* the client socket. Will delegate actual work according to whether
* SASL SSF layers are enabled (thus requiring encryption calls)
*/
-void vnc_client_write(void *opaque)
+static void vnc_client_write_locked(void *opaque)
{
VncState *vs = opaque;
@@ -1121,6 +1175,19 @@ void vnc_client_write(void *opaque)
vnc_client_write_plain(vs);
}
+void vnc_client_write(void *opaque)
+{
+ VncState *vs = opaque;
+
+ vnc_lock_output(vs);
+ if (vs->output.offset) {
+ vnc_client_write_locked(opaque);
+ } else {
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ }
+ vnc_unlock_output(vs);
+}
+
void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
{
vs->read_handler = func;
@@ -1272,8 +1339,11 @@ void vnc_write_u8(VncState *vs, uint8_t value)
void vnc_flush(VncState *vs)
{
- if (vs->csock != -1 && vs->output.offset)
- vnc_client_write(vs);
+ vnc_lock_output(vs);
+ if (vs->csock != -1 && vs->output.offset) {
+ vnc_client_write_locked(vs);
+ }
+ vnc_unlock_output(vs);
}
uint8_t read_u8(uint8_t *data, size_t offset)
@@ -1308,12 +1378,14 @@ static void check_pointer_type_change(Notifier *notifier)
int absolute = kbd_mouse_is_absolute();
if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) {
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, absolute, 0,
ds_get_width(vs->ds), ds_get_height(vs->ds),
VNC_ENCODING_POINTER_TYPE_CHANGE);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
vs->absolute = absolute;
@@ -1617,21 +1689,25 @@ static void framebuffer_update_request(VncState *vs, int incremental,
static void send_ext_key_event_ack(VncState *vs)
{
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
VNC_ENCODING_EXT_KEY_EVENT);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
static void send_ext_audio_ack(VncState *vs)
{
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
VNC_ENCODING_AUDIO);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
@@ -1794,12 +1870,14 @@ static void vnc_colordepth(VncState *vs)
{
if (vnc_has_feature(vs, VNC_FEATURE_WMVI)) {
/* Sending a WMVi message to notify the client*/
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */
vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds),
ds_get_height(vs->ds), VNC_ENCODING_WMVi);
pixel_format_message(vs);
+ vnc_unlock_output(vs);
vnc_flush(vs);
} else {
set_pixel_conversion(vs);
@@ -2227,12 +2305,21 @@ static void vnc_refresh(void *opaque)
vga_hw_update();
+ if (vnc_trylock_display(vd)) {
+ vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+ qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) +
+ vd->timer_interval);
+ return;
+ }
+
has_dirty = vnc_refresh_server_surface(vd);
+ vnc_unlock_display(vd);
QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
rects += vnc_update_client(vs, has_dirty);
/* vs might be free()ed here */
}
+
/* vd->timer could be NULL now if the last client disconnected,
* in this case don't update the timer */
if (vd->timer == NULL)
@@ -2291,6 +2378,10 @@ static void vnc_connect(VncDisplay *vd, int csock)
vs->as.fmt = AUD_FMT_S16;
vs->as.endianness = 0;
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_init(&vs->output_mutex);
+#endif
+
QTAILQ_INSERT_HEAD(&vd->clients, vs, next);
vga_hw_update();
@@ -2348,6 +2439,11 @@ void vnc_display_init(DisplayState *ds)
if (!vs->kbd_layout)
exit(1);
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_init(&vs->mutex);
+ vnc_start_worker_thread();
+#endif
+
dcl->dpy_copy = vnc_dpy_copy;
dcl->dpy_update = vnc_dpy_update;
dcl->dpy_resize = vnc_dpy_resize;