Index: ioemu/vnc.c
===================================================================
--- ioemu.orig/vnc.c	2007-05-03 10:07:56.000000000 +0100
+++ ioemu/vnc.c	2007-05-03 10:07:56.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 */
 };
 
@@ -383,7 +394,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 ||
@@ -393,10 +404,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;
@@ -448,110 +457,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)
@@ -564,8 +612,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)
@@ -625,7 +675,6 @@
 	vs->csock = -1;
 	buffer_reset(&vs->input);
 	buffer_reset(&vs->output);
-	vs->need_update = 0;
 	return 0;
     }
     return ret;
@@ -897,7 +946,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;
@@ -1020,6 +1068,7 @@
 {
     int i;
     uint16_t limit;
+    int64_t now;
 
     switch (data[0]) {
     case 0:
@@ -1063,12 +1112,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	2007-05-03 10:07:56.000000000 +0100
+++ ioemu/vl.c	2007-05-03 10:07:56.000000000 +0100
@@ -725,6 +725,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	2007-05-03 10:07:56.000000000 +0100
+++ ioemu/vl.h	2007-05-03 10:07:56.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;
