Home | History | Annotate | Download | only in x11vnc
      1 /*
      2    Copyright (C) 2002-2010 Karl J. Runge <runge (at) 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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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