1 /*
2    Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com>
3    All rights reserved.
4 
5 This file is part of x11vnc.
6 
7 x11vnc is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or (at
10 your option) any later version.
11 
12 x11vnc is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with x11vnc; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
20 or see <http://www.gnu.org/licenses/>.
21 
22 In addition, as a special exception, Karl J. Runge
23 gives permission to link the code of its release of x11vnc with the
24 OpenSSL project's "OpenSSL" library (or with modified versions of it
25 that use the same license as the "OpenSSL" library), and distribute
26 the linked executables.  You must obey the GNU General Public License
27 in all respects for all of the code used other than "OpenSSL".  If you
28 modify this file, you may extend this exception to your version of the
29 file, but you are not obligated to do so.  If you do not wish to do
30 so, delete this exception statement from your version.
31 */
32 
33 /* -- xdamage.c -- */
34 
35 #include "x11vnc.h"
36 #include "xwrappers.h"
37 #include "userinput.h"
38 #include "unixpw.h"
39 
40 #if LIBVNCSERVER_HAVE_LIBXDAMAGE
41 Damage xdamage = 0;
42 #endif
43 
44 #ifndef XDAMAGE
45 #define XDAMAGE 1
46 #endif
47 int use_xdamage = XDAMAGE;	/* use the xdamage rects for scanline hints */
48 int xdamage_present = 0;
49 
50 #ifdef MACOSX
51 int xdamage_max_area = 50000;
52 #else
53 int xdamage_max_area = 20000;	/* pixels */
54 #endif
55 
56 double xdamage_memory = 1.0;	/* in units of NSCAN */
57 int xdamage_tile_count = 0, xdamage_direct_count = 0;
58 double xdamage_scheduled_mark = 0.0;
59 double xdamage_crazy_time = 0.0;
60 double xdamage_crazy_delay = 300.0;
61 sraRegionPtr xdamage_scheduled_mark_region = NULL;
62 sraRegionPtr *xdamage_regions = NULL;
63 int xdamage_ticker = 0;
64 int XD_skip = 0, XD_tot = 0, XD_des = 0;	/* for stats */
65 
66 void add_region_xdamage(sraRegionPtr new_region);
67 void clear_xdamage_mark_region(sraRegionPtr markregion, int flush);
68 int collect_non_X_xdamage(int x_in, int y_in, int w_in, int h_in, int call);
69 int collect_xdamage(int scancnt, int call);
70 int xdamage_hint_skip(int y);
71 void initialize_xdamage(void);
72 void create_xdamage_if_needed(int force);
73 void destroy_xdamage_if_needed(void);
74 void check_xdamage_state(void);
75 
76 static void record_desired_xdamage_rect(int x, int y, int w, int h);
77 
78 
record_desired_xdamage_rect(int x,int y,int w,int h)79 static void record_desired_xdamage_rect(int x, int y, int w, int h) {
80 	/*
81 	 * Unfortunately we currently can't trust an xdamage event
82 	 * to correspond to real screen damage.  E.g. focus-in for
83 	 * mozilla (depending on wm) will mark the whole toplevel
84 	 * area as damaged, when only the border has changed.
85 	 * Similar things for terminal windows.
86 	 *
87 	 * This routine uses some heuristics to detect small enough
88 	 * damage regions that we will not have a performance problem
89 	 * if we believe them even though they are wrong.  We record
90 	 * the corresponding tiles the damage regions touch.
91 	 */
92 	int dt_x, dt_y, nt_x1, nt_y1, nt_x2, nt_y2, nt;
93 	int ix, iy, cnt = 0;
94 	int area = w*h, always_accept = 0;
95 	/*
96 	 * XXX: not working yet, slow and overlaps with scan_display()
97 	 * probably slow because tall skinny rectangles very inefficient
98 	 * in general and in direct_fb_copy() (100X slower then horizontal).
99 	 */
100 	int use_direct_fb_copy = 0;
101 	int wh_min, wh_max;
102 	static int first = 1, udfb = 0;
103 
104 	/* compiler warning: */
105 	nt_x1 = 0; nt_y1 = 0; nt_x2 = 0; nt_y2 = 0;
106 
107 	if (first) {
108 		if (getenv("XD_DFC")) {
109 			udfb = 1;
110 		}
111 		first = 0;
112 	}
113 	if (udfb) {
114 		use_direct_fb_copy = 1;
115 	}
116 
117 	if (xdamage_max_area <= 0) {
118 		always_accept = 1;
119 	}
120 
121 	if (!always_accept && area > xdamage_max_area) {
122 		return;
123 	}
124 
125 	dt_x = w / tile_x;
126 	dt_y = h / tile_y;
127 
128 	if (w < h) {
129 		wh_min = w;
130 		wh_max = h;
131 	} else {
132 		wh_min = h;
133 		wh_max = w;
134 	}
135 
136 	if (!always_accept && dt_y >= 3 && area > 4000)  {
137 		/*
138 		 * if it is real it should be caught by a normal scanline
139 		 * poll, but we might as well keep if small (tall line?).
140 		 */
141 		return;
142 	}
143 
144 	if (use_direct_fb_copy) {
145 		X_UNLOCK;
146 		direct_fb_copy(x, y, x + w, y + h, 1);
147 		xdamage_direct_count++;
148 		X_LOCK;
149 	} else if (0 && wh_min < tile_x/4 && wh_max > 30 * wh_min) {
150 		/* try it for long, skinny rects, XXX still no good */
151 		X_UNLOCK;
152 		direct_fb_copy(x, y, x + w, y + h, 1);
153 		xdamage_direct_count++;
154 		X_LOCK;
155 	} else {
156 
157 		if (ntiles_x == 0 || ntiles_y == 0) {
158 			/* too early. */
159 			return;
160 		}
161 		nt_x1 = nfix(  (x)/tile_x, ntiles_x);
162 		nt_x2 = nfix((x+w)/tile_x, ntiles_x);
163 		nt_y1 = nfix(  (y)/tile_y, ntiles_y);
164 		nt_y2 = nfix((y+h)/tile_y, ntiles_y);
165 
166 		/*
167 		 * loop over the rectangle of tiles (1 tile for a small
168 		 * input rect).
169 		 */
170 		for (ix = nt_x1; ix <= nt_x2; ix++) {
171 			for (iy = nt_y1; iy <= nt_y2; iy++) {
172 				nt = ix + iy * ntiles_x;
173 				cnt++;
174 				if (! tile_has_xdamage_diff[nt]) {
175 					XD_des++;
176 					tile_has_xdamage_diff[nt] = 1;
177 				}
178 				/* not used: */
179 				tile_row_has_xdamage_diff[iy] = 1;
180 				xdamage_tile_count++;
181 			}
182 		}
183 	}
184 	if (debug_xdamage > 1) {
185 		fprintf(stderr, "xdamage: desired: %dx%d+%d+%d\tA: %6d  tiles="
186 		    "%02d-%02d/%02d-%02d  tilecnt: %d\n", w, h, x, y,
187 		    w * h, nt_x1, nt_x2, nt_y1, nt_y2, cnt);
188 	}
189 }
190 
add_region_xdamage(sraRegionPtr new_region)191 void add_region_xdamage(sraRegionPtr new_region) {
192 	sraRegionPtr reg;
193 	int prev_tick, nreg;
194 
195 	if (! xdamage_regions) {
196 		return;
197 	}
198 
199 	nreg = (xdamage_memory * NSCAN) + 1;
200 	prev_tick = xdamage_ticker - 1;
201 	if (prev_tick < 0) {
202 		prev_tick = nreg - 1;
203 	}
204 
205 	reg = xdamage_regions[prev_tick];
206 	if (reg != NULL && new_region != NULL) {
207 if (debug_xdamage > 1) fprintf(stderr, "add_region_xdamage: prev_tick: %d reg %p  new_region %p\n", prev_tick, (void *)reg, (void *)new_region);
208 		sraRgnOr(reg, new_region);
209 	}
210 }
211 
clear_xdamage_mark_region(sraRegionPtr markregion,int flush)212 void clear_xdamage_mark_region(sraRegionPtr markregion, int flush) {
213 #if LIBVNCSERVER_HAVE_LIBXDAMAGE
214 	XEvent ev;
215 	sraRegionPtr tmpregion;
216 	int count = 0;
217 
218 	RAWFB_RET_VOID
219 
220 	if (! xdamage_present || ! use_xdamage) {
221 		return;
222 	}
223 	if (! xdamage) {
224 		return;
225 	}
226 	if (! xdamage_base_event_type) {
227 		return;
228 	}
229 	if (unixpw_in_progress) return;
230 
231 	X_LOCK;
232 	if (flush) {
233 		XFlush_wr(dpy);
234 	}
235 	while (XCheckTypedEvent(dpy, xdamage_base_event_type+XDamageNotify, &ev)) {
236 		count++;
237 	}
238 	/* clear the whole damage region */
239 	XDamageSubtract(dpy, xdamage, None, None);
240 	X_UNLOCK;
241 
242 	if (debug_tiles || debug_xdamage) {
243 		fprintf(stderr, "clear_xdamage_mark_region: %d\n", count);
244 	}
245 
246 	if (! markregion) {
247 		/* NULL means mark the whole display */
248 		tmpregion = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
249 		add_region_xdamage(tmpregion);
250 		sraRgnDestroy(tmpregion);
251 	} else {
252 		add_region_xdamage(markregion);
253 	}
254 #else
255 	if (0) flush++;        /* compiler warnings */
256 	if (0) markregion = NULL;
257 #endif
258 }
259 
collect_non_X_xdamage(int x_in,int y_in,int w_in,int h_in,int call)260 int collect_non_X_xdamage(int x_in, int y_in, int w_in, int h_in, int call) {
261 	sraRegionPtr tmpregion;
262 	sraRegionPtr reg;
263 	static int rect_count = 0;
264 	int nreg, ccount = 0, dcount = 0, ecount = 0;
265 	static time_t last_rpt = 0;
266 	time_t now;
267 	double tm, dt;
268 	int x, y, w, h, x2, y2;
269 
270 if (call && debug_xdamage > 1) fprintf(stderr, "collect_non_X_xdamage: %d %d %d %d - %d / %d\n", x_in, y_in, w_in, h_in, call, use_xdamage);
271 
272 	if (! use_xdamage) {
273 		return 0;
274 	}
275 	if (! xdamage_regions) {
276 		return 0;
277 	}
278 
279 	dtime0(&tm);
280 
281 	nreg = (xdamage_memory * NSCAN) + 1;
282 
283 	if (call == 0) {
284 		xdamage_ticker = (xdamage_ticker+1) % nreg;
285 		xdamage_direct_count = 0;
286 		reg = xdamage_regions[xdamage_ticker];
287 		if (reg != NULL) {
288 			sraRgnMakeEmpty(reg);
289 		}
290 	} else {
291 		if (xdamage_ticker < 0) {
292 			xdamage_ticker = 0;
293 		}
294 		reg = xdamage_regions[xdamage_ticker];
295 	}
296 	if (reg == NULL) {
297 		return 0;
298 	}
299 
300 	if (x_in < 0) {
301 		return 0;
302 	}
303 
304 
305 	x = x_in;
306 	y = y_in;
307 	w = w_in;
308 	h = h_in;
309 
310 	/* translate if needed */
311 	if (clipshift) {
312 		/* set coords relative to fb origin */
313 		if (0 && rootshift) {
314 			/*
315 			 * Note: not needed because damage is
316 			 * relative to subwin, not rootwin.
317 			 */
318 			x = x - off_x;
319 			y = y - off_y;
320 		}
321 		if (clipshift) {
322 			x = x - coff_x;
323 			y = y - coff_y;
324 		}
325 
326 		x2 = x + w;		/* upper point */
327 		x  = nfix(x,  dpy_x);	/* place both in fb area */
328 		x2 = nfix(x2, dpy_x+1);
329 		w = x2 - x;		/* recompute w */
330 
331 		y2 = y + h;
332 		y  = nfix(y,  dpy_y);
333 		y2 = nfix(y2, dpy_y+1);
334 		h = y2 - y;
335 
336 		if (w <= 0 || h <= 0) {
337 			return 0;
338 		}
339 	}
340 	if (debug_xdamage > 2) {
341 		fprintf(stderr, "xdamage: -> event %dx%d+%d+%d area:"
342 		    " %d  dups: %d  %s reg: %p\n", w, h, x, y, w*h, dcount,
343 		    (w*h > xdamage_max_area) ? "TOO_BIG" : "", (void *)reg);
344 	}
345 
346 	record_desired_xdamage_rect(x, y, w, h);
347 
348 	tmpregion = sraRgnCreateRect(x, y, x + w, y + h);
349 	sraRgnOr(reg, tmpregion);
350 	sraRgnDestroy(tmpregion);
351 	rect_count++;
352 	ccount++;
353 
354 	if (0 && xdamage_direct_count) {
355 		fb_push();
356 	}
357 
358 	dt = dtime(&tm);
359 	if ((debug_tiles > 1 && ecount) || (debug_tiles && ecount > 200)
360 	    || debug_xdamage > 1) {
361 		fprintf(stderr, "collect_non_X_xdamage(%d): %.4f t: %.4f ev/dup/accept"
362 		    "/direct %d/%d/%d/%d\n", call, dt, tm - x11vnc_start, ecount,
363 		    dcount, ccount, xdamage_direct_count);
364 	}
365 	now = time(NULL);
366 	if (! last_rpt) {
367 		last_rpt = now;
368 	}
369 	if (now > last_rpt + 15) {
370 		double rat = -1.0;
371 
372 		if (XD_tot) {
373 			rat = ((double) XD_skip)/XD_tot;
374 		}
375 		if (debug_tiles || debug_xdamage) {
376 			fprintf(stderr, "xdamage: == scanline skip/tot: "
377 			    "%04d/%04d =%.3f  rects: %d  desired: %d\n",
378 			    XD_skip, XD_tot, rat, rect_count, XD_des);
379 		}
380 
381 		XD_skip = 0;
382 		XD_tot  = 0;
383 		XD_des  = 0;
384 		rect_count = 0;
385 		last_rpt = now;
386 	}
387 	return 0;
388 }
389 
collect_xdamage(int scancnt,int call)390 int collect_xdamage(int scancnt, int call) {
391 #if LIBVNCSERVER_HAVE_LIBXDAMAGE
392 	XDamageNotifyEvent *dev;
393 	XEvent ev;
394 	sraRegionPtr tmpregion;
395 	sraRegionPtr reg;
396 	static int rect_count = 0;
397 	int nreg, ccount = 0, dcount = 0, ecount = 0;
398 	static time_t last_rpt = 0;
399 	time_t now;
400 	int x, y, w, h, x2, y2;
401 	int i, dup, next = 0, dup_max = 0;
402 #define DUPSZ 32
403 	int dup_x[DUPSZ], dup_y[DUPSZ], dup_w[DUPSZ], dup_h[DUPSZ];
404 	double tm, dt;
405 	int mark_all = 0, retries = 0, too_many = 1000, tot_ev = 0;
406 
407 	RAWFB_RET(0)
408 
409 	if (scancnt) {} /* unused vars warning: */
410 
411 	if (! xdamage_present || ! use_xdamage) {
412 		return 0;
413 	}
414 	if (! xdamage) {
415 		return 0;
416 	}
417 	if (! xdamage_base_event_type) {
418 		return 0;
419 	}
420 	if (! xdamage_regions) {
421 		return 0;
422 	}
423 
424 	dtime0(&tm);
425 
426 	nreg = (xdamage_memory * NSCAN) + 1;
427 
428 	if (call == 0) {
429 		xdamage_ticker = (xdamage_ticker+1) % nreg;
430 		xdamage_direct_count = 0;
431 		reg = xdamage_regions[xdamage_ticker];
432 		if (reg != NULL) {
433 			sraRgnMakeEmpty(reg);
434 		}
435 	} else {
436 		if (xdamage_ticker < 0) {
437 			xdamage_ticker = 0;
438 		}
439 		reg = xdamage_regions[xdamage_ticker];
440 	}
441 	if (reg == NULL) {
442 		return 0;
443 	}
444 
445 
446 	X_LOCK;
447 if (0)	XFlush_wr(dpy);
448 if (0)	XEventsQueued(dpy, QueuedAfterFlush);
449 
450 	come_back_for_more:
451 
452 	while (XCheckTypedEvent(dpy, xdamage_base_event_type+XDamageNotify, &ev)) {
453 		/*
454 		 * TODO max cut off time in this loop?
455 		 * Could check QLength and if huge just mark the whole
456 		 * screen.
457 		 */
458 		ecount++;
459 		tot_ev++;
460 
461 		if (mark_all) {
462 			continue;
463 		}
464 		if (ecount == too_many) {
465 			int nqa = XEventsQueued(dpy, QueuedAlready);
466 			if (nqa >= too_many) {
467 				static double last_msg = 0.0;
468 				tmpregion = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
469 				sraRgnOr(reg, tmpregion);
470 				sraRgnDestroy(tmpregion);
471 				if (dnow() > last_msg + xdamage_crazy_delay) {
472 					rfbLog("collect_xdamage: too many xdamage events %d+%d\n", ecount, nqa);
473 					last_msg = dnow();
474 				}
475 				mark_all = 1;
476 			}
477 		}
478 
479 		if (ev.type != xdamage_base_event_type + XDamageNotify) {
480 			break;
481 		}
482 		dev = (XDamageNotifyEvent *) &ev;
483 		if (dev->damage != xdamage) {
484 			continue;	/* not ours! */
485 		}
486 
487 		x = dev->area.x;
488 		y = dev->area.y;
489 		w = dev->area.width;
490 		h = dev->area.height;
491 
492 		/*
493 		 * we try to manually remove some duplicates because
494 		 * certain activities can lead to many 10's of dups
495 		 * in a row.  The region work can be costly and reg is
496 		 * later used in xdamage_hint_skip loops, so it is good
497 		 * to skip them if possible.
498 		 */
499 		dup = 0;
500 		for (i=0; i < dup_max; i++) {
501 			if (dup_x[i] == x && dup_y[i] == y && dup_w[i] == w &&
502 			    dup_h[i] == h) {
503 				dup = 1;
504 				break;
505 			}
506 		}
507 		if (dup) {
508 			dcount++;
509 			continue;
510 		}
511 		if (dup_max < DUPSZ) {
512 			next = dup_max;
513 			dup_max++;
514 		} else {
515 			next = (next+1) % DUPSZ;
516 		}
517 		dup_x[next] = x;
518 		dup_y[next] = y;
519 		dup_w[next] = w;
520 		dup_h[next] = h;
521 
522 		/* translate if needed */
523 		if (clipshift) {
524 			/* set coords relative to fb origin */
525 			if (0 && rootshift) {
526 				/*
527 				 * Note: not needed because damage is
528 				 * relative to subwin, not rootwin.
529 				 */
530 				x = x - off_x;
531 				y = y - off_y;
532 			}
533 			if (clipshift) {
534 				x = x - coff_x;
535 				y = y - coff_y;
536 			}
537 
538 			x2 = x + w;		/* upper point */
539 			x  = nfix(x,  dpy_x);	/* place both in fb area */
540 			x2 = nfix(x2, dpy_x+1);
541 			w = x2 - x;		/* recompute w */
542 
543 			y2 = y + h;
544 			y  = nfix(y,  dpy_y);
545 			y2 = nfix(y2, dpy_y+1);
546 			h = y2 - y;
547 
548 			if (w <= 0 || h <= 0) {
549 				continue;
550 			}
551 		}
552 		if (debug_xdamage > 2) {
553 			fprintf(stderr, "xdamage: -> event %dx%d+%d+%d area:"
554 			    " %d  dups: %d  %s\n", w, h, x, y, w*h, dcount,
555 			    (w*h > xdamage_max_area) ? "TOO_BIG" : "");
556 		}
557 
558 		record_desired_xdamage_rect(x, y, w, h);
559 
560 		tmpregion = sraRgnCreateRect(x, y, x + w, y + h);
561 		sraRgnOr(reg, tmpregion);
562 		sraRgnDestroy(tmpregion);
563 		rect_count++;
564 		ccount++;
565 	}
566 
567 	if (mark_all) {
568 		if (ecount + XEventsQueued(dpy, QueuedAlready) >= 3 * too_many && retries < 3) {
569 			retries++;
570 			XFlush_wr(dpy);
571 			usleep(20 * 1000);
572 			XFlush_wr(dpy);
573 			ecount = 0;
574 			goto come_back_for_more;
575 		}
576 	}
577 
578 	/* clear the whole damage region for next time. XXX check */
579 	if (call == 1) {
580 		XDamageSubtract(dpy, xdamage, None, None);
581 	}
582 	X_UNLOCK;
583 
584 	if (tot_ev > 20 * too_many) {
585 		rfbLog("collect_xdamage: xdamage has gone crazy (screensaver or game?) ev: %d ret: %d\n", tot_ev, retries);
586 		rfbLog("collect_xdamage: disabling xdamage for %d seconds.\n", (int) xdamage_crazy_delay);
587 		destroy_xdamage_if_needed();
588 		X_LOCK;
589 		XSync(dpy, False);
590 		while (XCheckTypedEvent(dpy, xdamage_base_event_type+XDamageNotify, &ev)) {
591 			;
592 		}
593 		X_UNLOCK;
594 		xdamage_crazy_time = dnow();
595 	}
596 
597 	if (0 && xdamage_direct_count) {
598 		fb_push();
599 	}
600 
601 	dt = dtime(&tm);
602 	if ((debug_tiles > 1 && ecount) || (debug_tiles && ecount > 200)
603 	    || debug_xdamage > 1) {
604 		fprintf(stderr, "collect_xdamage(%d): %.4f t: %.4f ev/dup/accept"
605 		    "/direct %d/%d/%d/%d\n", call, dt, tm - x11vnc_start, ecount,
606 		    dcount, ccount, xdamage_direct_count);
607 	}
608 	now = time(NULL);
609 	if (! last_rpt) {
610 		last_rpt = now;
611 	}
612 	if (now > last_rpt + 15) {
613 		double rat = -1.0;
614 
615 		if (XD_tot) {
616 			rat = ((double) XD_skip)/XD_tot;
617 		}
618 		if (debug_tiles || debug_xdamage) {
619 			fprintf(stderr, "xdamage: == scanline skip/tot: "
620 			    "%04d/%04d =%.3f  rects: %d  desired: %d\n",
621 			    XD_skip, XD_tot, rat, rect_count, XD_des);
622 		}
623 
624 		XD_skip = 0;
625 		XD_tot  = 0;
626 		XD_des  = 0;
627 		rect_count = 0;
628 		last_rpt = now;
629 	}
630 #else
631 	if (0) scancnt++;	/* compiler warnings */
632 	if (0) call++;
633 	if (0) record_desired_xdamage_rect(0, 0, 0, 0);
634 #endif
635 	return 0;
636 }
637 
xdamage_hint_skip(int y)638 int xdamage_hint_skip(int y) {
639 	static sraRegionPtr scanline = NULL;
640 	static sraRegionPtr tmpl_y = NULL;
641 	int fast_tmpl = 1;
642 	sraRegionPtr reg, tmpl;
643 	int ret, i, n, nreg;
644 #ifndef NO_NCACHE
645 	static int ncache_no_skip = 0;
646 	static double last_ncache_no_skip = 0.0;
647 	static double last_ncache_no_skip_long = 0.0, ncache_fac = 0.25;
648 #endif
649 
650 	if (! xdamage_present || ! use_xdamage) {
651 		return 0;	/* cannot skip */
652 	}
653 	if (! xdamage_regions) {
654 		return 0;	/* cannot skip */
655 	}
656 
657 	if (! scanline) {
658 		/* keep it around to avoid malloc etc, recreate */
659 		scanline = sraRgnCreate();
660 	}
661 	if (! tmpl_y) {
662 		tmpl_y = sraRgnCreateRect(0, 0, dpy_x, 1);
663 	}
664 
665 	nreg = (xdamage_memory * NSCAN) + 1;
666 
667 #ifndef NO_NCACHE
668 	if (ncache > 0) {
669 		if (ncache_no_skip == 0) {
670 			double now = g_now;
671 			if (now > last_ncache_no_skip + 8.0) {
672 				ncache_no_skip = 1;
673 			} else if (now < last_bs_restore + 0.5) {
674 				ncache_no_skip = 1;
675 			} else if (now < last_su_restore + 0.5) {
676 				ncache_no_skip = 1;
677 			} else if (now < last_copyrect + 0.5) {
678 				ncache_no_skip = 1;
679 			}
680 			if (ncache_no_skip) {
681 				last_ncache_no_skip = dnow();
682 				if (now > last_ncache_no_skip_long + 60.0) {
683 					ncache_fac = 2.0;
684 					last_ncache_no_skip_long = now;
685 				} else {
686 					ncache_fac = 0.25;
687 				}
688 				return 0;
689 			}
690 		} else {
691 			if (ncache_no_skip++ >= ncache_fac*nreg + 4) {
692 				ncache_no_skip = 0;
693 			} else {
694 				return 0;
695 			}
696 		}
697 	}
698 #endif
699 
700 	if (fast_tmpl) {
701 		sraRgnOffset(tmpl_y, 0, y);
702 		tmpl = tmpl_y;
703 	} else {
704 		tmpl = sraRgnCreateRect(0, y, dpy_x, y+1);
705 	}
706 
707 	ret = 1;
708 	for (i=0; i<nreg; i++) {
709 		/* go back thru the history starting at most recent */
710 		n = (xdamage_ticker + nreg - i) % nreg;
711 		reg = xdamage_regions[n];
712 		if (reg == NULL) {
713 			continue;
714 		}
715 		if (sraRgnEmpty(reg)) {
716 			/* checking for emptiness is very fast */
717 			continue;
718 		}
719 		sraRgnMakeEmpty(scanline);
720 		sraRgnOr(scanline, tmpl);
721 		if (sraRgnAnd(scanline, reg)) {
722 			ret = 0;
723 			break;
724 		}
725 	}
726 	if (fast_tmpl) {
727 		sraRgnOffset(tmpl_y, 0, -y);
728 	} else {
729 		sraRgnDestroy(tmpl);
730 	}
731 if (0) fprintf(stderr, "xdamage_hint_skip: %d -> %d\n", y, ret);
732 
733 	return ret;
734 }
735 
initialize_xdamage(void)736 void initialize_xdamage(void) {
737 	sraRegionPtr *ptr;
738 	int i, nreg;
739 
740 	if (! xdamage_present) {
741 		use_xdamage = 0;
742 	}
743 	if (xdamage_regions)  {
744 		ptr = xdamage_regions;
745 		while (*ptr != NULL) {
746 			sraRgnDestroy(*ptr);
747 			ptr++;
748 		}
749 		free(xdamage_regions);
750 		xdamage_regions = NULL;
751 	}
752 	if (use_xdamage) {
753 		nreg = (xdamage_memory * NSCAN) + 2;
754 		xdamage_regions = (sraRegionPtr *)
755 		    malloc(nreg * sizeof(sraRegionPtr));
756 		for (i = 0; i < nreg; i++) {
757 			ptr = xdamage_regions+i;
758 			if (i == nreg - 1) {
759 				*ptr = NULL;
760 			} else {
761 				*ptr = sraRgnCreate();
762 				sraRgnMakeEmpty(*ptr);
763 			}
764 		}
765 		/* set so will be 0 in first collect_xdamage call */
766 		xdamage_ticker = -1;
767 	}
768 }
769 
create_xdamage_if_needed(int force)770 void create_xdamage_if_needed(int force) {
771 
772 	RAWFB_RET_VOID
773 
774 	if (force) {}
775 
776 #if LIBVNCSERVER_HAVE_LIBXDAMAGE
777 	if (! xdamage || force) {
778 		X_LOCK;
779 		xdamage = XDamageCreate(dpy, window, XDamageReportRawRectangles);
780 		XDamageSubtract(dpy, xdamage, None, None);
781 		X_UNLOCK;
782 		rfbLog("created   xdamage object: 0x%lx\n", xdamage);
783 	}
784 #endif
785 }
786 
destroy_xdamage_if_needed(void)787 void destroy_xdamage_if_needed(void) {
788 
789 	RAWFB_RET_VOID
790 
791 #if LIBVNCSERVER_HAVE_LIBXDAMAGE
792 	if (xdamage) {
793 		XEvent ev;
794 		X_LOCK;
795 		XDamageDestroy(dpy, xdamage);
796 		XFlush_wr(dpy);
797 		if (xdamage_base_event_type) {
798 			while (XCheckTypedEvent(dpy,
799 			    xdamage_base_event_type+XDamageNotify, &ev)) {
800 				;
801 			}
802 		}
803 		X_UNLOCK;
804 		rfbLog("destroyed xdamage object: 0x%lx\n", xdamage);
805 		xdamage = 0;
806 	}
807 #endif
808 }
809 
check_xdamage_state(void)810 void check_xdamage_state(void) {
811 	if (! xdamage_present) {
812 		return;
813 	}
814 	/*
815 	 * Create or destroy the Damage object as needed, we don't want
816 	 * one if no clients are connected.
817 	 */
818 	if (xdamage_crazy_time > 0.0 && dnow() < xdamage_crazy_time + xdamage_crazy_delay) {
819 		return;
820 	}
821 	if (client_count && use_xdamage) {
822 		create_xdamage_if_needed(0);
823 		if (xdamage_scheduled_mark > 0.0 && dnow() >
824 		    xdamage_scheduled_mark) {
825 			if (xdamage_scheduled_mark_region) {
826 				mark_region_for_xdamage(
827 				    xdamage_scheduled_mark_region);
828 				sraRgnDestroy(xdamage_scheduled_mark_region);
829 				xdamage_scheduled_mark_region = NULL;
830 			} else {
831 				mark_for_xdamage(0, 0, dpy_x, dpy_y);
832 			}
833 			xdamage_scheduled_mark = 0.0;
834 		}
835 	} else {
836 		destroy_xdamage_if_needed();
837 	}
838 }
839 
840 
841