ia64/xen-unstable

changeset 11566:449dcaff2551

[HVM][VNC] Add a backoff feature to the vnc server, so that if it detects
that the display hasn't changed for a while it starts scanning more slowly.

Signed-off-by: Steven Smith <sos22@cam.ac.uk>
author Steven Smith <ssmith@xensource.com>
date Thu Sep 21 18:29:48 2006 +0100 (2006-09-21)
parents c742b2ae920c
children 8860eba3dcad ebb1982c03a1
files tools/ioemu/vl.c tools/ioemu/vl.h tools/ioemu/vnc.c
line diff
     1.1 --- a/tools/ioemu/vl.c	Thu Sep 21 17:56:14 2006 +0100
     1.2 +++ b/tools/ioemu/vl.c	Thu Sep 21 18:29:48 2006 +0100
     1.3 @@ -725,6 +725,12 @@ void qemu_del_timer(QEMUTimer *ts)
     1.4      }
     1.5  }
     1.6  
     1.7 +void qemu_advance_timer(QEMUTimer *ts, int64_t expire_time)
     1.8 +{
     1.9 +    if (ts->expire_time > expire_time)
    1.10 +	qemu_mod_timer(ts, expire_time);
    1.11 +}
    1.12 +
    1.13  /* modify the current timer so that it will be fired when current_time
    1.14     >= expire_time. The corresponding callback will be called. */
    1.15  void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time)
     2.1 --- a/tools/ioemu/vl.h	Thu Sep 21 17:56:14 2006 +0100
     2.2 +++ b/tools/ioemu/vl.h	Thu Sep 21 18:29:48 2006 +0100
     2.3 @@ -405,6 +405,7 @@ QEMUTimer *qemu_new_timer(QEMUClock *clo
     2.4  void qemu_free_timer(QEMUTimer *ts);
     2.5  void qemu_del_timer(QEMUTimer *ts);
     2.6  void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time);
     2.7 +void qemu_advance_timer(QEMUTimer *ts, int64_t expire_time);
     2.8  int qemu_timer_pending(QEMUTimer *ts);
     2.9  
    2.10  extern int64_t ticks_per_sec;
     3.1 --- a/tools/ioemu/vnc.c	Thu Sep 21 17:56:14 2006 +0100
     3.2 +++ b/tools/ioemu/vnc.c	Thu Sep 21 18:29:48 2006 +0100
     3.3 @@ -27,7 +27,19 @@
     3.4  #include "vl.h"
     3.5  #include "qemu_socket.h"
     3.6  
     3.7 -#define VNC_REFRESH_INTERVAL (1000 / 30)
     3.8 +/* The refresh interval starts at BASE.  If we scan the buffer and
     3.9 +   find no change, we increase by INC, up to MAX.  If the mouse moves
    3.10 +   or we get a keypress, the interval is set back to BASE.  If we find
    3.11 +   an update, halve the interval.
    3.12 +
    3.13 +   All times in milliseconds. */
    3.14 +#define VNC_REFRESH_INTERVAL_BASE 30
    3.15 +#define VNC_REFRESH_INTERVAL_INC  50
    3.16 +#define VNC_REFRESH_INTERVAL_MAX  2000
    3.17 +
    3.18 +/* Wait at most one second between updates, so that we can detect a
    3.19 +   minimised vncviewer reasonably quickly. */
    3.20 +#define VNC_MAX_UPDATE_INTERVAL   5000
    3.21  
    3.22  #include "vnc_keysym.h"
    3.23  #include "keymaps.c"
    3.24 @@ -64,10 +76,11 @@ typedef void VncSendHextileTile(VncState
    3.25  struct VncState
    3.26  {
    3.27      QEMUTimer *timer;
    3.28 +    int timer_interval;
    3.29 +    int64_t last_update_time;
    3.30      int lsock;
    3.31      int csock;
    3.32      DisplayState *ds;
    3.33 -    int need_update;
    3.34      int width;
    3.35      int height;
    3.36      uint64_t *dirty_row;	/* screen regions which are possibly dirty */
    3.37 @@ -98,8 +111,6 @@ struct VncState
    3.38      int visible_w;
    3.39      int visible_h;
    3.40  
    3.41 -    int slow_client;
    3.42 -
    3.43      int ctl_keys;               /* Ctrl+Alt starts calibration */
    3.44  };
    3.45  
    3.46 @@ -380,7 +391,7 @@ static void vnc_copy(DisplayState *ds, i
    3.47      int y = 0;
    3.48      int pitch = ds->linesize;
    3.49      VncState *vs = ds->opaque;
    3.50 -    int updating_client = !vs->slow_client;
    3.51 +    int updating_client = 1;
    3.52  
    3.53      if (src_x < vs->visible_x || src_y < vs->visible_y ||
    3.54  	dst_x < vs->visible_x || dst_y < vs->visible_y ||
    3.55 @@ -390,11 +401,6 @@ static void vnc_copy(DisplayState *ds, i
    3.56  	(dst_y + h) > (vs->visible_y + vs->visible_h))
    3.57  	updating_client = 0;
    3.58  
    3.59 -    if (updating_client) {
    3.60 -	vs->need_update = 1;
    3.61 -	_vnc_update_client(vs);
    3.62 -    }
    3.63 -
    3.64      if (dst_y > src_y) {
    3.65  	y = h - 1;
    3.66  	pitch = -pitch;
    3.67 @@ -445,110 +451,145 @@ static int find_update_height(VncState *
    3.68  static void _vnc_update_client(void *opaque)
    3.69  {
    3.70      VncState *vs = opaque;
    3.71 -    int64_t now = qemu_get_clock(rt_clock);
    3.72 +    int64_t now;
    3.73 +    int y;
    3.74 +    char *row;
    3.75 +    char *old_row;
    3.76 +    uint64_t width_mask;
    3.77 +    int n_rectangles;
    3.78 +    int saved_offset;
    3.79 +    int maxx, maxy;
    3.80 +    int tile_bytes = vs->depth * DP2X(vs, 1);
    3.81  
    3.82 -    if (vs->need_update && vs->csock != -1) {
    3.83 -	int y;
    3.84 -	char *row;
    3.85 -	char *old_row;
    3.86 -	uint64_t width_mask;
    3.87 -	int n_rectangles;
    3.88 -	int saved_offset;
    3.89 -	int maxx, maxy;
    3.90 -	int tile_bytes = vs->depth * DP2X(vs, 1);
    3.91 +    if (vs->csock == -1)
    3.92 +	return;
    3.93 +
    3.94 +    now = qemu_get_clock(rt_clock);
    3.95  
    3.96 -	qemu_mod_timer(vs->timer, now + VNC_REFRESH_INTERVAL);
    3.97 -
    3.98 -	if (vs->width != DP2X(vs, DIRTY_PIXEL_BITS))
    3.99 -	    width_mask = (1ULL << X2DP_UP(vs, vs->ds->width)) - 1;
   3.100 -	else
   3.101 -	    width_mask = ~(0ULL);
   3.102 +    if (vs->width != DP2X(vs, DIRTY_PIXEL_BITS))
   3.103 +	width_mask = (1ULL << X2DP_UP(vs, vs->ds->width)) - 1;
   3.104 +    else
   3.105 +	width_mask = ~(0ULL);
   3.106  
   3.107 -	/* Walk through the dirty map and eliminate tiles that
   3.108 -	   really aren't dirty */
   3.109 -	row = vs->ds->data;
   3.110 -	old_row = vs->old_data;
   3.111 +    /* Walk through the dirty map and eliminate tiles that really
   3.112 +       aren't dirty */
   3.113 +    row = vs->ds->data;
   3.114 +    old_row = vs->old_data;
   3.115  
   3.116 -	for (y = 0; y < vs->ds->height; y++) {
   3.117 -	    if (vs->dirty_row[y] & width_mask) {
   3.118 -		int x;
   3.119 -		char *ptr, *old_ptr;
   3.120 +    for (y = 0; y < vs->ds->height; y++) {
   3.121 +	if (vs->dirty_row[y] & width_mask) {
   3.122 +	    int x;
   3.123 +	    char *ptr, *old_ptr;
   3.124  
   3.125 -		ptr = row;
   3.126 -		old_ptr = old_row;
   3.127 +	    ptr = row;
   3.128 +	    old_ptr = old_row;
   3.129  
   3.130 -		for (x = 0; x < X2DP_UP(vs, vs->ds->width); x++) {
   3.131 -		    if (vs->dirty_row[y] & (1ULL << x)) {
   3.132 -			if (memcmp(old_ptr, ptr, tile_bytes)) {
   3.133 -			    vs->has_update = 1;
   3.134 -			    vs->update_row[y] |= (1ULL << x);
   3.135 -			    memcpy(old_ptr, ptr, tile_bytes);
   3.136 -			}
   3.137 -			vs->dirty_row[y] &= ~(1ULL << x);
   3.138 +	    for (x = 0; x < X2DP_UP(vs, vs->ds->width); x++) {
   3.139 +		if (vs->dirty_row[y] & (1ULL << x)) {
   3.140 +		    if (memcmp(old_ptr, ptr, tile_bytes)) {
   3.141 +			vs->has_update = 1;
   3.142 +			vs->update_row[y] |= (1ULL << x);
   3.143 +			memcpy(old_ptr, ptr, tile_bytes);
   3.144  		    }
   3.145 -
   3.146 -		    ptr += tile_bytes;
   3.147 -		    old_ptr += tile_bytes;
   3.148 +		    vs->dirty_row[y] &= ~(1ULL << x);
   3.149  		}
   3.150 -	    }
   3.151  
   3.152 -	    row += vs->ds->linesize;
   3.153 -	    old_row += vs->ds->linesize;
   3.154 +		ptr += tile_bytes;
   3.155 +		old_ptr += tile_bytes;
   3.156 +	    }
   3.157  	}
   3.158 +  
   3.159 +	row += vs->ds->linesize;
   3.160 +	old_row += vs->ds->linesize;
   3.161 +    }
   3.162  
   3.163 -	if (!vs->has_update || vs->visible_y >= vs->ds->height ||
   3.164 -	    vs->visible_x >= vs->ds->width)
   3.165 -	    return;
   3.166 +    if (!vs->has_update || vs->visible_y >= vs->ds->height ||
   3.167 +	vs->visible_x >= vs->ds->width)
   3.168 +	goto backoff;
   3.169  
   3.170 -	/* Count rectangles */
   3.171 -	n_rectangles = 0;
   3.172 -	vnc_write_u8(vs, 0);  /* msg id */
   3.173 -	vnc_write_u8(vs, 0);
   3.174 -	saved_offset = vs->output.offset;
   3.175 -	vnc_write_u16(vs, 0);
   3.176 +    /* Count rectangles */
   3.177 +    n_rectangles = 0;
   3.178 +    vnc_write_u8(vs, 0);  /* msg id */
   3.179 +    vnc_write_u8(vs, 0);
   3.180 +    saved_offset = vs->output.offset;
   3.181 +    vnc_write_u16(vs, 0);
   3.182 +    
   3.183 +    maxy = vs->visible_y + vs->visible_h;
   3.184 +    if (maxy > vs->ds->height)
   3.185 +	maxy = vs->ds->height;
   3.186 +    maxx = vs->visible_x + vs->visible_w;
   3.187 +    if (maxx > vs->ds->width)
   3.188 +	maxx = vs->ds->width;
   3.189  
   3.190 -	maxy = vs->visible_y + vs->visible_h;
   3.191 -	if (maxy > vs->ds->height)
   3.192 -	    maxy = vs->ds->height;
   3.193 -	maxx = vs->visible_x + vs->visible_w;
   3.194 -	if (maxx > vs->ds->width)
   3.195 -	    maxx = vs->ds->width;
   3.196 -
   3.197 -	for (y = vs->visible_y; y < maxy; y++) {
   3.198 -	    int x;
   3.199 -	    int last_x = -1;
   3.200 -	    for (x = X2DP_DOWN(vs, vs->visible_x);
   3.201 -		 x < X2DP_UP(vs, maxx); x++) {
   3.202 -		if (vs->update_row[y] & (1ULL << x)) {
   3.203 -		    if (last_x == -1)
   3.204 -			last_x = x;
   3.205 -		    vs->update_row[y] &= ~(1ULL << x);
   3.206 -		} else {
   3.207 -		    if (last_x != -1) {
   3.208 -			int h = find_update_height(vs, y, maxy, last_x, x);
   3.209 +    for (y = vs->visible_y; y < maxy; y++) {
   3.210 +	int x;
   3.211 +	int last_x = -1;
   3.212 +	for (x = X2DP_DOWN(vs, vs->visible_x);
   3.213 +	     x < X2DP_UP(vs, maxx); x++) {
   3.214 +	    if (vs->update_row[y] & (1ULL << x)) {
   3.215 +		if (last_x == -1)
   3.216 +		    last_x = x;
   3.217 +		vs->update_row[y] &= ~(1ULL << x);
   3.218 +	    } else {
   3.219 +		if (last_x != -1) {
   3.220 +		    int h = find_update_height(vs, y, maxy, last_x, x);
   3.221 +		    if (h != 0) {
   3.222  			send_framebuffer_update(vs, DP2X(vs, last_x), y,
   3.223  						DP2X(vs, (x - last_x)), h);
   3.224  			n_rectangles++;
   3.225  		    }
   3.226 -		    last_x = -1;
   3.227  		}
   3.228 +		last_x = -1;
   3.229  	    }
   3.230 -	    if (last_x != -1) {
   3.231 -		int h = find_update_height(vs, y, maxy, last_x, x);
   3.232 +	}
   3.233 +	if (last_x != -1) {
   3.234 +	    int h = find_update_height(vs, y, maxy, last_x, x);
   3.235 +	    if (h != 0) {
   3.236  		send_framebuffer_update(vs, DP2X(vs, last_x), y,
   3.237  					DP2X(vs, (x - last_x)), h);
   3.238  		n_rectangles++;
   3.239  	    }
   3.240  	}
   3.241 -	vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
   3.242 -	vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
   3.243 +    }
   3.244 +    vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
   3.245 +    vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
   3.246 +
   3.247 +    if (n_rectangles == 0)
   3.248 +	goto backoff;
   3.249 +
   3.250 +    vs->has_update = 0;
   3.251 +    vnc_flush(vs);
   3.252 +    vs->last_update_time = now;
   3.253 +
   3.254 +    vs->timer_interval /= 2;
   3.255 +    if (vs->timer_interval < VNC_REFRESH_INTERVAL_BASE)
   3.256 +	vs->timer_interval = VNC_REFRESH_INTERVAL_BASE;
   3.257 +
   3.258 +    return;
   3.259  
   3.260 -	vs->has_update = 0;
   3.261 -	vs->need_update = 0;
   3.262 -	vnc_flush(vs);
   3.263 -	vs->slow_client = 0;
   3.264 -    } else
   3.265 -	vs->slow_client = 1;
   3.266 + backoff:
   3.267 +    /* No update -> back off a bit */
   3.268 +    vs->timer_interval += VNC_REFRESH_INTERVAL_INC;
   3.269 +    if (vs->timer_interval > VNC_REFRESH_INTERVAL_MAX) {
   3.270 +	vs->timer_interval = VNC_REFRESH_INTERVAL_MAX;
   3.271 +	if (now - vs->last_update_time >= VNC_MAX_UPDATE_INTERVAL) {
   3.272 +	    /* Send a null update.  If the client is no longer
   3.273 +	       interested (e.g. minimised) it'll ignore this, and we
   3.274 +	       can stop scanning the buffer until it sends another
   3.275 +	       update request. */
   3.276 +	    /* Note that there are bugs in xvncviewer which prevent
   3.277 +	       this from actually working.  Leave the code in place
   3.278 +	       for correct clients. */
   3.279 +	    vnc_write_u8(vs, 0);
   3.280 +	    vnc_write_u8(vs, 0);
   3.281 +	    vnc_write_u16(vs, 0);
   3.282 +	    vnc_flush(vs);
   3.283 +	    vs->last_update_time = now;
   3.284 +	    return;
   3.285 +	}
   3.286 +    }
   3.287 +    qemu_mod_timer(vs->timer, now + vs->timer_interval);
   3.288 +    return;
   3.289  }
   3.290  
   3.291  static void vnc_update_client(void *opaque)
   3.292 @@ -561,8 +602,10 @@ static void vnc_update_client(void *opaq
   3.293  
   3.294  static void vnc_timer_init(VncState *vs)
   3.295  {
   3.296 -    if (vs->timer == NULL)
   3.297 +    if (vs->timer == NULL) {
   3.298  	vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);
   3.299 +	vs->timer_interval = VNC_REFRESH_INTERVAL_BASE;
   3.300 +    }
   3.301  }
   3.302  
   3.303  static void vnc_dpy_refresh(DisplayState *ds)
   3.304 @@ -622,7 +665,6 @@ static int vnc_client_io_error(VncState 
   3.305  	vs->csock = -1;
   3.306  	buffer_reset(&vs->input);
   3.307  	buffer_reset(&vs->output);
   3.308 -	vs->need_update = 0;
   3.309  	return 0;
   3.310      }
   3.311      return ret;
   3.312 @@ -892,7 +934,6 @@ static void framebuffer_update_request(V
   3.313  				       int x_position, int y_position,
   3.314  				       int w, int h)
   3.315  {
   3.316 -    vs->need_update = 1;
   3.317      if (!incremental)
   3.318  	framebuffer_set_updated(vs, x_position, y_position, w, h);
   3.319      vs->visible_x = x_position;
   3.320 @@ -1015,6 +1056,7 @@ static int protocol_client_msg(VncState 
   3.321  {
   3.322      int i;
   3.323      uint16_t limit;
   3.324 +    int64_t now;
   3.325  
   3.326      switch (data[0]) {
   3.327      case 0:
   3.328 @@ -1054,12 +1096,18 @@ static int protocol_client_msg(VncState 
   3.329  	if (len == 1)
   3.330  	    return 8;
   3.331  
   3.332 +	vs->timer_interval = VNC_REFRESH_INTERVAL_BASE;
   3.333 +	qemu_advance_timer(vs->timer,
   3.334 +			   qemu_get_clock(rt_clock) + vs->timer_interval);
   3.335  	key_event(vs, read_u8(data, 1), read_u32(data, 4));
   3.336  	break;
   3.337      case 5:
   3.338  	if (len == 1)
   3.339  	    return 6;
   3.340  
   3.341 +	vs->timer_interval = VNC_REFRESH_INTERVAL_BASE;
   3.342 +	qemu_advance_timer(vs->timer,
   3.343 +			   qemu_get_clock(rt_clock) + vs->timer_interval);
   3.344  	pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4));
   3.345  	break;
   3.346      case 6:
   3.347 @@ -1268,7 +1316,7 @@ int vnc_start_viewer(int port)
   3.348  	exit(1);
   3.349  
   3.350      case 0:	/* child */
   3.351 -	execlp("vncviewer", "vncviewer", s, 0);
   3.352 +	execlp("vncviewer", "vncviewer", s, NULL);
   3.353  	fprintf(stderr, "vncviewer execlp failed\n");
   3.354  	exit(1);
   3.355