| 1 | Index: ioemu/vnc.c |
|---|
| 2 | =================================================================== |
|---|
| 3 | --- ioemu.orig/vnc.c 2007-05-03 10:07:56.000000000 +0100 |
|---|
| 4 | +++ ioemu/vnc.c 2007-05-03 10:07:56.000000000 +0100 |
|---|
| 5 | @@ -28,7 +28,19 @@ |
|---|
| 6 | #include "qemu_socket.h" |
|---|
| 7 | #include <assert.h> |
|---|
| 8 | |
|---|
| 9 | -#define VNC_REFRESH_INTERVAL (1000 / 30) |
|---|
| 10 | +/* The refresh interval starts at BASE. If we scan the buffer and |
|---|
| 11 | + find no change, we increase by INC, up to MAX. If the mouse moves |
|---|
| 12 | + or we get a keypress, the interval is set back to BASE. If we find |
|---|
| 13 | + an update, halve the interval. |
|---|
| 14 | + |
|---|
| 15 | + All times in milliseconds. */ |
|---|
| 16 | +#define VNC_REFRESH_INTERVAL_BASE 30 |
|---|
| 17 | +#define VNC_REFRESH_INTERVAL_INC 50 |
|---|
| 18 | +#define VNC_REFRESH_INTERVAL_MAX 2000 |
|---|
| 19 | + |
|---|
| 20 | +/* Wait at most one second between updates, so that we can detect a |
|---|
| 21 | + minimised vncviewer reasonably quickly. */ |
|---|
| 22 | +#define VNC_MAX_UPDATE_INTERVAL 5000 |
|---|
| 23 | |
|---|
| 24 | #include "vnc_keysym.h" |
|---|
| 25 | #include "keymaps.c" |
|---|
| 26 | @@ -65,10 +77,11 @@ |
|---|
| 27 | struct VncState |
|---|
| 28 | { |
|---|
| 29 | QEMUTimer *timer; |
|---|
| 30 | + int timer_interval; |
|---|
| 31 | + int64_t last_update_time; |
|---|
| 32 | int lsock; |
|---|
| 33 | int csock; |
|---|
| 34 | DisplayState *ds; |
|---|
| 35 | - int need_update; |
|---|
| 36 | int width; |
|---|
| 37 | int height; |
|---|
| 38 | uint64_t *dirty_row; /* screen regions which are possibly dirty */ |
|---|
| 39 | @@ -99,8 +112,6 @@ |
|---|
| 40 | int visible_w; |
|---|
| 41 | int visible_h; |
|---|
| 42 | |
|---|
| 43 | - int slow_client; |
|---|
| 44 | - |
|---|
| 45 | int ctl_keys; /* Ctrl+Alt starts calibration */ |
|---|
| 46 | }; |
|---|
| 47 | |
|---|
| 48 | @@ -383,7 +394,7 @@ |
|---|
| 49 | int y = 0; |
|---|
| 50 | int pitch = ds->linesize; |
|---|
| 51 | VncState *vs = ds->opaque; |
|---|
| 52 | - int updating_client = !vs->slow_client; |
|---|
| 53 | + int updating_client = 1; |
|---|
| 54 | |
|---|
| 55 | if (src_x < vs->visible_x || src_y < vs->visible_y || |
|---|
| 56 | dst_x < vs->visible_x || dst_y < vs->visible_y || |
|---|
| 57 | @@ -393,10 +404,8 @@ |
|---|
| 58 | (dst_y + h) > (vs->visible_y + vs->visible_h)) |
|---|
| 59 | updating_client = 0; |
|---|
| 60 | |
|---|
| 61 | - if (updating_client) { |
|---|
| 62 | - vs->need_update = 1; |
|---|
| 63 | + if (updating_client) |
|---|
| 64 | _vnc_update_client(vs); |
|---|
| 65 | - } |
|---|
| 66 | |
|---|
| 67 | if (dst_y > src_y) { |
|---|
| 68 | y = h - 1; |
|---|
| 69 | @@ -448,110 +457,149 @@ |
|---|
| 70 | static void _vnc_update_client(void *opaque) |
|---|
| 71 | { |
|---|
| 72 | VncState *vs = opaque; |
|---|
| 73 | - int64_t now = qemu_get_clock(rt_clock); |
|---|
| 74 | - |
|---|
| 75 | - if (vs->need_update && vs->csock != -1) { |
|---|
| 76 | - int y; |
|---|
| 77 | - char *row; |
|---|
| 78 | - char *old_row; |
|---|
| 79 | - uint64_t width_mask; |
|---|
| 80 | - int n_rectangles; |
|---|
| 81 | - int saved_offset; |
|---|
| 82 | - int maxx, maxy; |
|---|
| 83 | - int tile_bytes = vs->depth * DP2X(vs, 1); |
|---|
| 84 | + int64_t now; |
|---|
| 85 | + int y; |
|---|
| 86 | + char *row; |
|---|
| 87 | + char *old_row; |
|---|
| 88 | + uint64_t width_mask; |
|---|
| 89 | + int n_rectangles; |
|---|
| 90 | + int saved_offset; |
|---|
| 91 | + int maxx, maxy; |
|---|
| 92 | + int tile_bytes = vs->depth * DP2X(vs, 1); |
|---|
| 93 | |
|---|
| 94 | - qemu_mod_timer(vs->timer, now + VNC_REFRESH_INTERVAL); |
|---|
| 95 | + if (vs->csock == -1) |
|---|
| 96 | + return; |
|---|
| 97 | |
|---|
| 98 | - if (vs->width != DP2X(vs, DIRTY_PIXEL_BITS)) |
|---|
| 99 | - width_mask = (1ULL << X2DP_UP(vs, vs->ds->width)) - 1; |
|---|
| 100 | - else |
|---|
| 101 | - width_mask = ~(0ULL); |
|---|
| 102 | + now = qemu_get_clock(rt_clock); |
|---|
| 103 | |
|---|
| 104 | - /* Walk through the dirty map and eliminate tiles that |
|---|
| 105 | - really aren't dirty */ |
|---|
| 106 | - row = vs->ds->data; |
|---|
| 107 | - old_row = vs->old_data; |
|---|
| 108 | - |
|---|
| 109 | - for (y = 0; y < vs->ds->height; y++) { |
|---|
| 110 | - if (vs->dirty_row[y] & width_mask) { |
|---|
| 111 | - int x; |
|---|
| 112 | - char *ptr, *old_ptr; |
|---|
| 113 | - |
|---|
| 114 | - ptr = row; |
|---|
| 115 | - old_ptr = old_row; |
|---|
| 116 | - |
|---|
| 117 | - for (x = 0; x < X2DP_UP(vs, vs->ds->width); x++) { |
|---|
| 118 | - if (vs->dirty_row[y] & (1ULL << x)) { |
|---|
| 119 | - if (memcmp(old_ptr, ptr, tile_bytes)) { |
|---|
| 120 | - vs->has_update = 1; |
|---|
| 121 | - vs->update_row[y] |= (1ULL << x); |
|---|
| 122 | - memcpy(old_ptr, ptr, tile_bytes); |
|---|
| 123 | - } |
|---|
| 124 | - vs->dirty_row[y] &= ~(1ULL << x); |
|---|
| 125 | - } |
|---|
| 126 | + if (vs->width != DP2X(vs, DIRTY_PIXEL_BITS)) |
|---|
| 127 | + width_mask = (1ULL << X2DP_UP(vs, vs->ds->width)) - 1; |
|---|
| 128 | + else |
|---|
| 129 | + width_mask = ~(0ULL); |
|---|
| 130 | |
|---|
| 131 | - ptr += tile_bytes; |
|---|
| 132 | - old_ptr += tile_bytes; |
|---|
| 133 | - } |
|---|
| 134 | - } |
|---|
| 135 | + /* Walk through the dirty map and eliminate tiles that really |
|---|
| 136 | + aren't dirty */ |
|---|
| 137 | + row = vs->ds->data; |
|---|
| 138 | + old_row = vs->old_data; |
|---|
| 139 | |
|---|
| 140 | - row += vs->ds->linesize; |
|---|
| 141 | - old_row += vs->ds->linesize; |
|---|
| 142 | - } |
|---|
| 143 | + for (y = 0; y < vs->ds->height; y++) { |
|---|
| 144 | + if (vs->dirty_row[y] & width_mask) { |
|---|
| 145 | + int x; |
|---|
| 146 | + char *ptr, *old_ptr; |
|---|
| 147 | |
|---|
| 148 | - if (!vs->has_update || vs->visible_y >= vs->ds->height || |
|---|
| 149 | - vs->visible_x >= vs->ds->width) |
|---|
| 150 | - return; |
|---|
| 151 | + ptr = row; |
|---|
| 152 | + old_ptr = old_row; |
|---|
| 153 | |
|---|
| 154 | - /* Count rectangles */ |
|---|
| 155 | - n_rectangles = 0; |
|---|
| 156 | - vnc_write_u8(vs, 0); /* msg id */ |
|---|
| 157 | - vnc_write_u8(vs, 0); |
|---|
| 158 | - saved_offset = vs->output.offset; |
|---|
| 159 | - vnc_write_u16(vs, 0); |
|---|
| 160 | + for (x = 0; x < X2DP_UP(vs, vs->ds->width); x++) { |
|---|
| 161 | + if (vs->dirty_row[y] & (1ULL << x)) { |
|---|
| 162 | + if (memcmp(old_ptr, ptr, tile_bytes)) { |
|---|
| 163 | + vs->has_update = 1; |
|---|
| 164 | + vs->update_row[y] |= (1ULL << x); |
|---|
| 165 | + memcpy(old_ptr, ptr, tile_bytes); |
|---|
| 166 | + } |
|---|
| 167 | + vs->dirty_row[y] &= ~(1ULL << x); |
|---|
| 168 | + } |
|---|
| 169 | |
|---|
| 170 | - maxy = vs->visible_y + vs->visible_h; |
|---|
| 171 | - if (maxy > vs->ds->height) |
|---|
| 172 | - maxy = vs->ds->height; |
|---|
| 173 | - maxx = vs->visible_x + vs->visible_w; |
|---|
| 174 | - if (maxx > vs->ds->width) |
|---|
| 175 | - maxx = vs->ds->width; |
|---|
| 176 | + ptr += tile_bytes; |
|---|
| 177 | + old_ptr += tile_bytes; |
|---|
| 178 | + } |
|---|
| 179 | + } |
|---|
| 180 | + |
|---|
| 181 | + row += vs->ds->linesize; |
|---|
| 182 | + old_row += vs->ds->linesize; |
|---|
| 183 | + } |
|---|
| 184 | |
|---|
| 185 | - for (y = vs->visible_y; y < maxy; y++) { |
|---|
| 186 | - int x; |
|---|
| 187 | - int last_x = -1; |
|---|
| 188 | - for (x = X2DP_DOWN(vs, vs->visible_x); |
|---|
| 189 | - x < X2DP_UP(vs, maxx); x++) { |
|---|
| 190 | - if (vs->update_row[y] & (1ULL << x)) { |
|---|
| 191 | - if (last_x == -1) |
|---|
| 192 | - last_x = x; |
|---|
| 193 | - vs->update_row[y] &= ~(1ULL << x); |
|---|
| 194 | - } else { |
|---|
| 195 | - if (last_x != -1) { |
|---|
| 196 | - int h = find_update_height(vs, y, maxy, last_x, x); |
|---|
| 197 | + if (!vs->has_update || vs->visible_y >= vs->ds->height || |
|---|
| 198 | + vs->visible_x >= vs->ds->width) |
|---|
| 199 | + goto backoff; |
|---|
| 200 | + |
|---|
| 201 | + /* Count rectangles */ |
|---|
| 202 | + n_rectangles = 0; |
|---|
| 203 | + vnc_write_u8(vs, 0); /* msg id */ |
|---|
| 204 | + vnc_write_u8(vs, 0); |
|---|
| 205 | + saved_offset = vs->output.offset; |
|---|
| 206 | + vnc_write_u16(vs, 0); |
|---|
| 207 | + |
|---|
| 208 | + maxy = vs->visible_y + vs->visible_h; |
|---|
| 209 | + if (maxy > vs->ds->height) |
|---|
| 210 | + maxy = vs->ds->height; |
|---|
| 211 | + maxx = vs->visible_x + vs->visible_w; |
|---|
| 212 | + if (maxx > vs->ds->width) |
|---|
| 213 | + maxx = vs->ds->width; |
|---|
| 214 | + |
|---|
| 215 | + for (y = vs->visible_y; y < maxy; y++) { |
|---|
| 216 | + int x; |
|---|
| 217 | + int last_x = -1; |
|---|
| 218 | + for (x = X2DP_DOWN(vs, vs->visible_x); |
|---|
| 219 | + x < X2DP_UP(vs, maxx); x++) { |
|---|
| 220 | + if (vs->update_row[y] & (1ULL << x)) { |
|---|
| 221 | + if (last_x == -1) |
|---|
| 222 | + last_x = x; |
|---|
| 223 | + vs->update_row[y] &= ~(1ULL << x); |
|---|
| 224 | + } else { |
|---|
| 225 | + if (last_x != -1) { |
|---|
| 226 | + int h = find_update_height(vs, y, maxy, last_x, x); |
|---|
| 227 | + if (h != 0) { |
|---|
| 228 | send_framebuffer_update(vs, DP2X(vs, last_x), y, |
|---|
| 229 | DP2X(vs, (x - last_x)), h); |
|---|
| 230 | n_rectangles++; |
|---|
| 231 | } |
|---|
| 232 | - last_x = -1; |
|---|
| 233 | } |
|---|
| 234 | + last_x = -1; |
|---|
| 235 | } |
|---|
| 236 | - if (last_x != -1) { |
|---|
| 237 | - int h = find_update_height(vs, y, maxy, last_x, x); |
|---|
| 238 | + } |
|---|
| 239 | + if (last_x != -1) { |
|---|
| 240 | + int h = find_update_height(vs, y, maxy, last_x, x); |
|---|
| 241 | + if (h != 0) { |
|---|
| 242 | send_framebuffer_update(vs, DP2X(vs, last_x), y, |
|---|
| 243 | DP2X(vs, (x - last_x)), h); |
|---|
| 244 | n_rectangles++; |
|---|
| 245 | } |
|---|
| 246 | } |
|---|
| 247 | - vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF; |
|---|
| 248 | - vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF; |
|---|
| 249 | + } |
|---|
| 250 | + vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF; |
|---|
| 251 | + vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF; |
|---|
| 252 | |
|---|
| 253 | - vs->has_update = 0; |
|---|
| 254 | - vs->need_update = 0; |
|---|
| 255 | - vnc_flush(vs); |
|---|
| 256 | - vs->slow_client = 0; |
|---|
| 257 | - } else |
|---|
| 258 | - vs->slow_client = 1; |
|---|
| 259 | + if (n_rectangles == 0) |
|---|
| 260 | + goto backoff; |
|---|
| 261 | + |
|---|
| 262 | + vs->has_update = 0; |
|---|
| 263 | + vnc_flush(vs); |
|---|
| 264 | + vs->last_update_time = now; |
|---|
| 265 | + |
|---|
| 266 | + vs->timer_interval /= 2; |
|---|
| 267 | + if (vs->timer_interval < VNC_REFRESH_INTERVAL_BASE) |
|---|
| 268 | + vs->timer_interval = VNC_REFRESH_INTERVAL_BASE; |
|---|
| 269 | + |
|---|
| 270 | + return; |
|---|
| 271 | + |
|---|
| 272 | + backoff: |
|---|
| 273 | + /* No update -> back off a bit */ |
|---|
| 274 | + vs->timer_interval += VNC_REFRESH_INTERVAL_INC; |
|---|
| 275 | + if (vs->timer_interval > VNC_REFRESH_INTERVAL_MAX) { |
|---|
| 276 | + vs->timer_interval = VNC_REFRESH_INTERVAL_MAX; |
|---|
| 277 | + if (now - vs->last_update_time >= VNC_MAX_UPDATE_INTERVAL) { |
|---|
| 278 | + /* Send a null update. If the client is no longer |
|---|
| 279 | + interested (e.g. minimised) it'll ignore this, and we |
|---|
| 280 | + can stop scanning the buffer until it sends another |
|---|
| 281 | + update request. */ |
|---|
| 282 | + /* It turns out that there's a bug in realvncviewer 4.1.2 |
|---|
| 283 | + which means that if you send a proper null update (with |
|---|
| 284 | + no update rectangles), it gets a bit out of sync and |
|---|
| 285 | + never sends any further requests, regardless of whether |
|---|
| 286 | + it needs one or not. Fix this by sending a single 1x1 |
|---|
| 287 | + update rectangle instead. */ |
|---|
| 288 | + vnc_write_u8(vs, 0); |
|---|
| 289 | + vnc_write_u8(vs, 0); |
|---|
| 290 | + vnc_write_u16(vs, 1); |
|---|
| 291 | + send_framebuffer_update(vs, 0, 0, 1, 1); |
|---|
| 292 | + vnc_flush(vs); |
|---|
| 293 | + vs->last_update_time = now; |
|---|
| 294 | + return; |
|---|
| 295 | + } |
|---|
| 296 | + } |
|---|
| 297 | + qemu_mod_timer(vs->timer, now + vs->timer_interval); |
|---|
| 298 | + return; |
|---|
| 299 | } |
|---|
| 300 | |
|---|
| 301 | static void vnc_update_client(void *opaque) |
|---|
| 302 | @@ -564,8 +612,10 @@ |
|---|
| 303 | |
|---|
| 304 | static void vnc_timer_init(VncState *vs) |
|---|
| 305 | { |
|---|
| 306 | - if (vs->timer == NULL) |
|---|
| 307 | + if (vs->timer == NULL) { |
|---|
| 308 | vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs); |
|---|
| 309 | + vs->timer_interval = VNC_REFRESH_INTERVAL_BASE; |
|---|
| 310 | + } |
|---|
| 311 | } |
|---|
| 312 | |
|---|
| 313 | static void vnc_dpy_refresh(DisplayState *ds) |
|---|
| 314 | @@ -625,7 +675,6 @@ |
|---|
| 315 | vs->csock = -1; |
|---|
| 316 | buffer_reset(&vs->input); |
|---|
| 317 | buffer_reset(&vs->output); |
|---|
| 318 | - vs->need_update = 0; |
|---|
| 319 | return 0; |
|---|
| 320 | } |
|---|
| 321 | return ret; |
|---|
| 322 | @@ -897,7 +946,6 @@ |
|---|
| 323 | int x_position, int y_position, |
|---|
| 324 | int w, int h) |
|---|
| 325 | { |
|---|
| 326 | - vs->need_update = 1; |
|---|
| 327 | if (!incremental) |
|---|
| 328 | framebuffer_set_updated(vs, x_position, y_position, w, h); |
|---|
| 329 | vs->visible_x = x_position; |
|---|
| 330 | @@ -1020,6 +1068,7 @@ |
|---|
| 331 | { |
|---|
| 332 | int i; |
|---|
| 333 | uint16_t limit; |
|---|
| 334 | + int64_t now; |
|---|
| 335 | |
|---|
| 336 | switch (data[0]) { |
|---|
| 337 | case 0: |
|---|
| 338 | @@ -1063,12 +1112,18 @@ |
|---|
| 339 | if (len == 1) |
|---|
| 340 | return 8; |
|---|
| 341 | |
|---|
| 342 | + vs->timer_interval = VNC_REFRESH_INTERVAL_BASE; |
|---|
| 343 | + qemu_advance_timer(vs->timer, |
|---|
| 344 | + qemu_get_clock(rt_clock) + vs->timer_interval); |
|---|
| 345 | key_event(vs, read_u8(data, 1), read_u32(data, 4)); |
|---|
| 346 | break; |
|---|
| 347 | case 5: |
|---|
| 348 | if (len == 1) |
|---|
| 349 | return 6; |
|---|
| 350 | |
|---|
| 351 | + vs->timer_interval = VNC_REFRESH_INTERVAL_BASE; |
|---|
| 352 | + qemu_advance_timer(vs->timer, |
|---|
| 353 | + qemu_get_clock(rt_clock) + vs->timer_interval); |
|---|
| 354 | pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4)); |
|---|
| 355 | break; |
|---|
| 356 | case 6: |
|---|
| 357 | Index: ioemu/vl.c |
|---|
| 358 | =================================================================== |
|---|
| 359 | --- ioemu.orig/vl.c 2007-05-03 10:07:56.000000000 +0100 |
|---|
| 360 | +++ ioemu/vl.c 2007-05-03 10:07:56.000000000 +0100 |
|---|
| 361 | @@ -725,6 +725,12 @@ |
|---|
| 362 | } |
|---|
| 363 | } |
|---|
| 364 | |
|---|
| 365 | +void qemu_advance_timer(QEMUTimer *ts, int64_t expire_time) |
|---|
| 366 | +{ |
|---|
| 367 | + if (ts->expire_time > expire_time || !qemu_timer_pending(ts)) |
|---|
| 368 | + qemu_mod_timer(ts, expire_time); |
|---|
| 369 | +} |
|---|
| 370 | + |
|---|
| 371 | /* modify the current timer so that it will be fired when current_time |
|---|
| 372 | >= expire_time. The corresponding callback will be called. */ |
|---|
| 373 | void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time) |
|---|
| 374 | Index: ioemu/vl.h |
|---|
| 375 | =================================================================== |
|---|
| 376 | --- ioemu.orig/vl.h 2007-05-03 10:07:56.000000000 +0100 |
|---|
| 377 | +++ ioemu/vl.h 2007-05-03 10:07:56.000000000 +0100 |
|---|
| 378 | @@ -407,6 +407,7 @@ |
|---|
| 379 | void qemu_free_timer(QEMUTimer *ts); |
|---|
| 380 | void qemu_del_timer(QEMUTimer *ts); |
|---|
| 381 | void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time); |
|---|
| 382 | +void qemu_advance_timer(QEMUTimer *ts, int64_t expire_time); |
|---|
| 383 | int qemu_timer_pending(QEMUTimer *ts); |
|---|
| 384 | |
|---|
| 385 | extern int64_t ticks_per_sec; |
|---|