diff options
Diffstat (limited to 'tools/ioemu/patches/vnc-backoff-screen-scan')
-rw-r--r-- | tools/ioemu/patches/vnc-backoff-screen-scan | 385 |
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; |