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; |
---|