aboutsummaryrefslogtreecommitdiffstats
path: root/tools/ioemu/patches/vnc-backoff-screen-scan
diff options
context:
space:
mode:
Diffstat (limited to 'tools/ioemu/patches/vnc-backoff-screen-scan')
-rw-r--r--tools/ioemu/patches/vnc-backoff-screen-scan385
1 files changed, 385 insertions, 0 deletions
diff --git a/tools/ioemu/patches/vnc-backoff-screen-scan b/tools/ioemu/patches/vnc-backoff-screen-scan
new file mode 100644
index 0000000000..813a63ae48
--- /dev/null
+++ b/tools/ioemu/patches/vnc-backoff-screen-scan
@@ -0,0 +1,385 @@
+Index: ioemu/vnc.c
+===================================================================
+--- ioemu.orig/vnc.c 2006-10-24 14:33:17.000000000 +0100
++++ ioemu/vnc.c 2006-10-24 14:33:24.000000000 +0100
+@@ -28,7 +28,19 @@
+ #include "qemu_socket.h"
+ #include <assert.h>
+
+-#define VNC_REFRESH_INTERVAL (1000 / 30)
++/* The refresh interval starts at BASE. If we scan the buffer and
++ find no change, we increase by INC, up to MAX. If the mouse moves
++ or we get a keypress, the interval is set back to BASE. If we find
++ an update, halve the interval.
++
++ All times in milliseconds. */
++#define VNC_REFRESH_INTERVAL_BASE 30
++#define VNC_REFRESH_INTERVAL_INC 50
++#define VNC_REFRESH_INTERVAL_MAX 2000
++
++/* Wait at most one second between updates, so that we can detect a
++ minimised vncviewer reasonably quickly. */
++#define VNC_MAX_UPDATE_INTERVAL 5000
+
+ #include "vnc_keysym.h"
+ #include "keymaps.c"
+@@ -65,10 +77,11 @@
+ struct VncState
+ {
+ QEMUTimer *timer;
++ int timer_interval;
++ int64_t last_update_time;
+ int lsock;
+ int csock;
+ DisplayState *ds;
+- int need_update;
+ int width;
+ int height;
+ uint64_t *dirty_row; /* screen regions which are possibly dirty */
+@@ -99,8 +112,6 @@
+ int visible_w;
+ int visible_h;
+
+- int slow_client;
+-
+ int ctl_keys; /* Ctrl+Alt starts calibration */
+ };
+
+@@ -381,7 +392,7 @@
+ int y = 0;
+ int pitch = ds->linesize;
+ VncState *vs = ds->opaque;
+- int updating_client = !vs->slow_client;
++ int updating_client = 1;
+
+ if (src_x < vs->visible_x || src_y < vs->visible_y ||
+ dst_x < vs->visible_x || dst_y < vs->visible_y ||
+@@ -391,10 +402,8 @@
+ (dst_y + h) > (vs->visible_y + vs->visible_h))
+ updating_client = 0;
+
+- if (updating_client) {
+- vs->need_update = 1;
++ if (updating_client)
+ _vnc_update_client(vs);
+- }
+
+ if (dst_y > src_y) {
+ y = h - 1;
+@@ -446,110 +455,149 @@
+ static void _vnc_update_client(void *opaque)
+ {
+ VncState *vs = opaque;
+- int64_t now = qemu_get_clock(rt_clock);
+-
+- if (vs->need_update && vs->csock != -1) {
+- int y;
+- char *row;
+- char *old_row;
+- uint64_t width_mask;
+- int n_rectangles;
+- int saved_offset;
+- int maxx, maxy;
+- int tile_bytes = vs->depth * DP2X(vs, 1);
++ int64_t now;
++ int y;
++ char *row;
++ char *old_row;
++ uint64_t width_mask;
++ int n_rectangles;
++ int saved_offset;
++ int maxx, maxy;
++ int tile_bytes = vs->depth * DP2X(vs, 1);
+
+- qemu_mod_timer(vs->timer, now + VNC_REFRESH_INTERVAL);
++ if (vs->csock == -1)
++ return;
+
+- if (vs->width != DP2X(vs, DIRTY_PIXEL_BITS))
+- width_mask = (1ULL << X2DP_UP(vs, vs->ds->width)) - 1;
+- else
+- width_mask = ~(0ULL);
++ now = qemu_get_clock(rt_clock);
+
+- /* Walk through the dirty map and eliminate tiles that
+- really aren't dirty */
+- row = vs->ds->data;
+- old_row = vs->old_data;
+-
+- for (y = 0; y < vs->ds->height; y++) {
+- if (vs->dirty_row[y] & width_mask) {
+- int x;
+- char *ptr, *old_ptr;
+-
+- ptr = row;
+- old_ptr = old_row;
+-
+- for (x = 0; x < X2DP_UP(vs, vs->ds->width); x++) {
+- if (vs->dirty_row[y] & (1ULL << x)) {
+- if (memcmp(old_ptr, ptr, tile_bytes)) {
+- vs->has_update = 1;
+- vs->update_row[y] |= (1ULL << x);
+- memcpy(old_ptr, ptr, tile_bytes);
+- }
+- vs->dirty_row[y] &= ~(1ULL << x);
+- }
++ if (vs->width != DP2X(vs, DIRTY_PIXEL_BITS))
++ width_mask = (1ULL << X2DP_UP(vs, vs->ds->width)) - 1;
++ else
++ width_mask = ~(0ULL);
+
+- ptr += tile_bytes;
+- old_ptr += tile_bytes;
+- }
+- }
++ /* Walk through the dirty map and eliminate tiles that really
++ aren't dirty */
++ row = vs->ds->data;
++ old_row = vs->old_data;
+
+- row += vs->ds->linesize;
+- old_row += vs->ds->linesize;
+- }
++ for (y = 0; y < vs->ds->height; y++) {
++ if (vs->dirty_row[y] & width_mask) {
++ int x;
++ char *ptr, *old_ptr;
+
+- if (!vs->has_update || vs->visible_y >= vs->ds->height ||
+- vs->visible_x >= vs->ds->width)
+- return;
++ ptr = row;
++ old_ptr = old_row;
+
+- /* Count rectangles */
+- n_rectangles = 0;
+- vnc_write_u8(vs, 0); /* msg id */
+- vnc_write_u8(vs, 0);
+- saved_offset = vs->output.offset;
+- vnc_write_u16(vs, 0);
++ for (x = 0; x < X2DP_UP(vs, vs->ds->width); x++) {
++ if (vs->dirty_row[y] & (1ULL << x)) {
++ if (memcmp(old_ptr, ptr, tile_bytes)) {
++ vs->has_update = 1;
++ vs->update_row[y] |= (1ULL << x);
++ memcpy(old_ptr, ptr, tile_bytes);
++ }
++ vs->dirty_row[y] &= ~(1ULL << x);
++ }
+
+- maxy = vs->visible_y + vs->visible_h;
+- if (maxy > vs->ds->height)
+- maxy = vs->ds->height;
+- maxx = vs->visible_x + vs->visible_w;
+- if (maxx > vs->ds->width)
+- maxx = vs->ds->width;
++ ptr += tile_bytes;
++ old_ptr += tile_bytes;
++ }
++ }
++
++ row += vs->ds->linesize;
++ old_row += vs->ds->linesize;
++ }
+
+- for (y = vs->visible_y; y < maxy; y++) {
+- int x;
+- int last_x = -1;
+- for (x = X2DP_DOWN(vs, vs->visible_x);
+- x < X2DP_UP(vs, maxx); x++) {
+- if (vs->update_row[y] & (1ULL << x)) {
+- if (last_x == -1)
+- last_x = x;
+- vs->update_row[y] &= ~(1ULL << x);
+- } else {
+- if (last_x != -1) {
+- int h = find_update_height(vs, y, maxy, last_x, x);
++ if (!vs->has_update || vs->visible_y >= vs->ds->height ||
++ vs->visible_x >= vs->ds->width)
++ goto backoff;
++
++ /* Count rectangles */
++ n_rectangles = 0;
++ vnc_write_u8(vs, 0); /* msg id */
++ vnc_write_u8(vs, 0);
++ saved_offset = vs->output.offset;
++ vnc_write_u16(vs, 0);
++
++ maxy = vs->visible_y + vs->visible_h;
++ if (maxy > vs->ds->height)
++ maxy = vs->ds->height;
++ maxx = vs->visible_x + vs->visible_w;
++ if (maxx > vs->ds->width)
++ maxx = vs->ds->width;
++
++ for (y = vs->visible_y; y < maxy; y++) {
++ int x;
++ int last_x = -1;
++ for (x = X2DP_DOWN(vs, vs->visible_x);
++ x < X2DP_UP(vs, maxx); x++) {
++ if (vs->update_row[y] & (1ULL << x)) {
++ if (last_x == -1)
++ last_x = x;
++ vs->update_row[y] &= ~(1ULL << x);
++ } else {
++ if (last_x != -1) {
++ int h = find_update_height(vs, y, maxy, last_x, x);
++ if (h != 0) {
+ send_framebuffer_update(vs, DP2X(vs, last_x), y,
+ DP2X(vs, (x - last_x)), h);
+ n_rectangles++;
+ }
+- last_x = -1;
+ }
++ last_x = -1;
+ }
+- if (last_x != -1) {
+- int h = find_update_height(vs, y, maxy, last_x, x);
++ }
++ if (last_x != -1) {
++ int h = find_update_height(vs, y, maxy, last_x, x);
++ if (h != 0) {
+ send_framebuffer_update(vs, DP2X(vs, last_x), y,
+ DP2X(vs, (x - last_x)), h);
+ n_rectangles++;
+ }
+ }
+- vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
+- vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
++ }
++ vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
++ vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
+
+- vs->has_update = 0;
+- vs->need_update = 0;
+- vnc_flush(vs);
+- vs->slow_client = 0;
+- } else
+- vs->slow_client = 1;
++ if (n_rectangles == 0)
++ goto backoff;
++
++ vs->has_update = 0;
++ vnc_flush(vs);
++ vs->last_update_time = now;
++
++ vs->timer_interval /= 2;
++ if (vs->timer_interval < VNC_REFRESH_INTERVAL_BASE)
++ vs->timer_interval = VNC_REFRESH_INTERVAL_BASE;
++
++ return;
++
++ backoff:
++ /* No update -> back off a bit */
++ vs->timer_interval += VNC_REFRESH_INTERVAL_INC;
++ if (vs->timer_interval > VNC_REFRESH_INTERVAL_MAX) {
++ vs->timer_interval = VNC_REFRESH_INTERVAL_MAX;
++ if (now - vs->last_update_time >= VNC_MAX_UPDATE_INTERVAL) {
++ /* Send a null update. If the client is no longer
++ interested (e.g. minimised) it'll ignore this, and we
++ can stop scanning the buffer until it sends another
++ update request. */
++ /* It turns out that there's a bug in realvncviewer 4.1.2
++ which means that if you send a proper null update (with
++ no update rectangles), it gets a bit out of sync and
++ never sends any further requests, regardless of whether
++ it needs one or not. Fix this by sending a single 1x1
++ update rectangle instead. */
++ vnc_write_u8(vs, 0);
++ vnc_write_u8(vs, 0);
++ vnc_write_u16(vs, 1);
++ send_framebuffer_update(vs, 0, 0, 1, 1);
++ vnc_flush(vs);
++ vs->last_update_time = now;
++ return;
++ }
++ }
++ qemu_mod_timer(vs->timer, now + vs->timer_interval);
++ return;
+ }
+
+ static void vnc_update_client(void *opaque)
+@@ -562,8 +610,10 @@
+
+ static void vnc_timer_init(VncState *vs)
+ {
+- if (vs->timer == NULL)
++ if (vs->timer == NULL) {
+ vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);
++ vs->timer_interval = VNC_REFRESH_INTERVAL_BASE;
++ }
+ }
+
+ static void vnc_dpy_refresh(DisplayState *ds)
+@@ -623,7 +673,6 @@
+ vs->csock = -1;
+ buffer_reset(&vs->input);
+ buffer_reset(&vs->output);
+- vs->need_update = 0;
+ return 0;
+ }
+ return ret;
+@@ -895,7 +944,6 @@
+ int x_position, int y_position,
+ int w, int h)
+ {
+- vs->need_update = 1;
+ if (!incremental)
+ framebuffer_set_updated(vs, x_position, y_position, w, h);
+ vs->visible_x = x_position;
+@@ -1018,6 +1066,7 @@
+ {
+ int i;
+ uint16_t limit;
++ int64_t now;
+
+ switch (data[0]) {
+ case 0:
+@@ -1061,12 +1110,18 @@
+ if (len == 1)
+ return 8;
+
++ vs->timer_interval = VNC_REFRESH_INTERVAL_BASE;
++ qemu_advance_timer(vs->timer,
++ qemu_get_clock(rt_clock) + vs->timer_interval);
+ key_event(vs, read_u8(data, 1), read_u32(data, 4));
+ break;
+ case 5:
+ if (len == 1)
+ return 6;
+
++ vs->timer_interval = VNC_REFRESH_INTERVAL_BASE;
++ qemu_advance_timer(vs->timer,
++ qemu_get_clock(rt_clock) + vs->timer_interval);
+ pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4));
+ break;
+ case 6:
+Index: ioemu/vl.c
+===================================================================
+--- ioemu.orig/vl.c 2006-10-24 14:33:17.000000000 +0100
++++ ioemu/vl.c 2006-10-24 14:33:24.000000000 +0100
+@@ -726,6 +726,12 @@
+ }
+ }
+
++void qemu_advance_timer(QEMUTimer *ts, int64_t expire_time)
++{
++ if (ts->expire_time > expire_time || !qemu_timer_pending(ts))
++ qemu_mod_timer(ts, expire_time);
++}
++
+ /* modify the current timer so that it will be fired when current_time
+ >= expire_time. The corresponding callback will be called. */
+ void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time)
+Index: ioemu/vl.h
+===================================================================
+--- ioemu.orig/vl.h 2006-10-24 14:33:17.000000000 +0100
++++ ioemu/vl.h 2006-10-24 14:33:24.000000000 +0100
+@@ -407,6 +407,7 @@
+ void qemu_free_timer(QEMUTimer *ts);
+ void qemu_del_timer(QEMUTimer *ts);
+ void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time);
++void qemu_advance_timer(QEMUTimer *ts, int64_t expire_time);
+ int qemu_timer_pending(QEMUTimer *ts);
+
+ extern int64_t ticks_per_sec;