summaryrefslogtreecommitdiffstats
path: root/x11vnc/xdamage.c
diff options
context:
space:
mode:
authorrunge <runge>2006-01-09 01:54:38 +0000
committerrunge <runge>2006-01-09 01:54:38 +0000
commit71f2ec79180185a6c3db0c87f9d53c491dc31e76 (patch)
tree67c341571cbeb1bd9a0744cc8eb03b30ef04f381 /x11vnc/xdamage.c
parentdef301266373e462f4a5e90eab443087ccfc7ccc (diff)
downloadlibtdevnc-71f2ec79180185a6c3db0c87f9d53c491dc31e76.tar.gz
libtdevnc-71f2ec79180185a6c3db0c87f9d53c491dc31e76.zip
x11vnc: the big split.
Diffstat (limited to 'x11vnc/xdamage.c')
-rw-r--r--x11vnc/xdamage.c532
1 files changed, 532 insertions, 0 deletions
diff --git a/x11vnc/xdamage.c b/x11vnc/xdamage.c
new file mode 100644
index 0000000..e54c0b9
--- /dev/null
+++ b/x11vnc/xdamage.c
@@ -0,0 +1,532 @@
+/* -- xdamage.c -- */
+
+#include "x11vnc.h"
+#include "xwrappers.h"
+#include "userinput.h"
+
+#if LIBVNCSERVER_HAVE_LIBXDAMAGE
+Damage xdamage = 0;
+#endif
+#ifndef XDAMAGE
+#define XDAMAGE 1
+#endif
+int use_xdamage = XDAMAGE; /* use the xdamage rects for scanline hints */
+int xdamage_present = 0;
+int xdamage_max_area = 20000; /* pixels */
+double xdamage_memory = 1.0; /* in units of NSCAN */
+int xdamage_tile_count = 0, xdamage_direct_count = 0;
+double xdamage_scheduled_mark = 0.0;
+sraRegionPtr xdamage_scheduled_mark_region = NULL;
+sraRegionPtr *xdamage_regions = NULL;
+int xdamage_ticker = 0;
+int XD_skip = 0, XD_tot = 0, XD_des = 0; /* for stats */
+
+void add_region_xdamage(sraRegionPtr new_region);
+void clear_xdamage_mark_region(sraRegionPtr markregion, int flush);
+int collect_xdamage(int scancnt, int call);
+int xdamage_hint_skip(int y);
+void initialize_xdamage(void);
+void create_xdamage_if_needed(void);
+void destroy_xdamage_if_needed(void);
+void check_xdamage_state(void);
+
+
+static void record_desired_xdamage_rect(int x, int y, int w, int h);
+
+
+static void record_desired_xdamage_rect(int x, int y, int w, int h) {
+ /*
+ * Unfortunately we currently can't trust an xdamage event
+ * to correspond to real screen damage. E.g. focus-in for
+ * mozilla (depending on wm) will mark the whole toplevel
+ * area as damaged, when only the border has changed.
+ * Similar things for terminal windows.
+ *
+ * This routine uses some heuristics to detect small enough
+ * damage regions that we will not have a performance problem
+ * if we believe them even though they are wrong. We record
+ * the corresponding tiles the damage regions touch.
+ */
+ int dt_x, dt_y, nt_x1, nt_y1, nt_x2, nt_y2, nt;
+ int ix, iy, cnt = 0;
+ int area = w*h, always_accept = 0;
+ /*
+ * XXX: not working yet, slow and overlaps with scan_display()
+ * probably slow because tall skinny rectangles very inefficient
+ * in general and in direct_fb_copy() (100X slower then horizontal).
+ */
+ int use_direct_fb_copy = 0;
+ int wh_min, wh_max;
+ static int first = 1, udfb = 0;
+ if (first) {
+ if (getenv("XD_DFC")) {
+ udfb = 1;
+ }
+ first = 0;
+ }
+ if (udfb) {
+ use_direct_fb_copy = 1;
+ }
+
+ if (xdamage_max_area <= 0) {
+ always_accept = 1;
+ }
+
+ if (!always_accept && area > xdamage_max_area) {
+ return;
+ }
+
+ dt_x = w / tile_x;
+ dt_y = h / tile_y;
+
+ if (w < h) {
+ wh_min = w;
+ wh_max = h;
+ } else {
+ wh_min = h;
+ wh_max = w;
+ }
+
+ if (!always_accept && dt_y >= 3 && area > 4000) {
+ /*
+ * if it is real it should be caught by a normal scanline
+ * poll, but we might as well keep if small (tall line?).
+ */
+ return;
+ }
+
+ if (use_direct_fb_copy) {
+ X_UNLOCK;
+ direct_fb_copy(x, y, x + w, y + h, 1);
+ xdamage_direct_count++;
+ X_LOCK;
+ } else if (0 && wh_min < tile_x/4 && wh_max > 30 * wh_min) {
+ /* try it for long, skinny rects, XXX still no good */
+ X_UNLOCK;
+ direct_fb_copy(x, y, x + w, y + h, 1);
+ xdamage_direct_count++;
+ X_LOCK;
+ } else {
+ nt_x1 = nfix( (x)/tile_x, ntiles_x);
+ nt_x2 = nfix((x+w)/tile_x, ntiles_x);
+ nt_y1 = nfix( (y)/tile_y, ntiles_y);
+ nt_y2 = nfix((y+h)/tile_y, ntiles_y);
+
+ /*
+ * loop over the rectangle of tiles (1 tile for a small
+ * input rect).
+ */
+ for (ix = nt_x1; ix <= nt_x2; ix++) {
+ for (iy = nt_y1; iy <= nt_y2; iy++) {
+ nt = ix + iy * ntiles_x;
+ cnt++;
+ if (! tile_has_xdamage_diff[nt]) {
+ XD_des++;
+ tile_has_xdamage_diff[nt] = 1;
+ }
+ /* not used: */
+ tile_row_has_xdamage_diff[iy] = 1;
+ xdamage_tile_count++;
+ }
+ }
+ }
+ if (debug_xdamage > 1) {
+ fprintf(stderr, "xdamage: desired: %dx%d+%d+%d\tA: %6d tiles="
+ "%02d-%02d/%02d-%02d tilecnt: %d\n", w, h, x, y,
+ w * h, nt_x1, nt_x2, nt_y1, nt_y2, cnt);
+ }
+}
+
+void add_region_xdamage(sraRegionPtr new_region) {
+ sraRegionPtr reg;
+ int prev_tick, nreg;
+
+ if (! xdamage_regions) {
+ return;
+ }
+
+ nreg = (xdamage_memory * NSCAN) + 1;
+ prev_tick = xdamage_ticker - 1;
+ if (prev_tick < 0) {
+ prev_tick = nreg - 1;
+ }
+
+ reg = xdamage_regions[prev_tick];
+ if (reg != NULL) {
+if (0) fprintf(stderr, "add_region_xdamage: prev_tick: %d reg %p\n", prev_tick, (void *)reg);
+ sraRgnOr(reg, new_region);
+ }
+}
+
+void clear_xdamage_mark_region(sraRegionPtr markregion, int flush) {
+#if LIBVNCSERVER_HAVE_LIBXDAMAGE
+ XEvent ev;
+ sraRegionPtr tmpregion;
+ int count = 0;
+
+ if (! xdamage_present || ! use_xdamage) {
+ return;
+ }
+ if (! xdamage) {
+ return;
+ }
+ if (! xdamage_base_event_type) {
+ return;
+ }
+
+ X_LOCK;
+ if (flush) {
+ XFlush(dpy);
+ }
+ while (XCheckTypedEvent(dpy, xdamage_base_event_type+XDamageNotify, &ev)) {
+ count++;
+ }
+ /* clear the whole damage region */
+ XDamageSubtract(dpy, xdamage, None, None);
+ X_UNLOCK;
+
+ if (debug_tiles || debug_xdamage) {
+ fprintf(stderr, "clear_xdamage_mark_region: %d\n", count);
+ }
+
+ if (! markregion) {
+ /* NULL means mark the whole display */
+ tmpregion = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
+ add_region_xdamage(tmpregion);
+ sraRgnDestroy(tmpregion);
+ } else {
+ add_region_xdamage(markregion);
+ }
+#else
+ if (0) flush++; /* compiler warnings */
+ if (0) markregion = NULL;
+#endif
+}
+
+int collect_xdamage(int scancnt, int call) {
+#if LIBVNCSERVER_HAVE_LIBXDAMAGE
+ XDamageNotifyEvent *dev;
+ XEvent ev;
+ sraRegionPtr tmpregion;
+ sraRegionPtr reg;
+ static int rect_count = 0;
+ int nreg, ccount = 0, dcount = 0, ecount = 0;
+ static time_t last_rpt = 0;
+ time_t now;
+ int x, y, w, h, x2, y2;
+ int i, dup, next, dup_max = 0;
+#define DUPSZ 32
+ int dup_x[DUPSZ], dup_y[DUPSZ], dup_w[DUPSZ], dup_h[DUPSZ];
+ double tm, dt;
+
+ if (scancnt) {} /* unused vars warning: */
+
+ if (! xdamage_present || ! use_xdamage) {
+ return 0;
+ }
+ if (! xdamage) {
+ return 0;
+ }
+ if (! xdamage_base_event_type) {
+ return 0;
+ }
+
+ dtime0(&tm);
+
+ nreg = (xdamage_memory * NSCAN) + 1;
+
+ if (call == 0) {
+ xdamage_ticker = (xdamage_ticker+1) % nreg;
+ xdamage_direct_count = 0;
+ reg = xdamage_regions[xdamage_ticker];
+ sraRgnMakeEmpty(reg);
+ } else {
+ reg = xdamage_regions[xdamage_ticker];
+ }
+
+
+ X_LOCK;
+if (0) XFlush(dpy);
+if (0) XEventsQueued(dpy, QueuedAfterFlush);
+ while (XCheckTypedEvent(dpy, xdamage_base_event_type+XDamageNotify, &ev)) {
+ /*
+ * TODO max cut off time in this loop?
+ * Could check QLength and if huge just mark the whole
+ * screen.
+ */
+ ecount++;
+ if (ev.type != xdamage_base_event_type + XDamageNotify) {
+ break;
+ }
+ dev = (XDamageNotifyEvent *) &ev;
+ if (dev->damage != xdamage) {
+ continue; /* not ours! */
+ }
+
+ x = dev->area.x;
+ y = dev->area.y;
+ w = dev->area.width;
+ h = dev->area.height;
+
+ /*
+ * we try to manually remove some duplicates because
+ * certain activities can lead to many 10's of dups
+ * in a row. The region work can be costly and reg is
+ * later used in xdamage_hint_skip loops, so it is good
+ * to skip them if possible.
+ */
+ dup = 0;
+ for (i=0; i < dup_max; i++) {
+ if (dup_x[i] == x && dup_y[i] == y && dup_w[i] == w &&
+ dup_h[i] == h) {
+ dup = 1;
+ break;
+ }
+ }
+ if (dup) {
+ dcount++;
+ continue;
+ }
+ if (dup_max < DUPSZ) {
+ next = dup_max;
+ dup_max++;
+ } else {
+ next = (next+1) % DUPSZ;
+ }
+ dup_x[next] = x;
+ dup_y[next] = y;
+ dup_w[next] = w;
+ dup_h[next] = h;
+
+ /* translate if needed */
+ if (clipshift) {
+ /* set coords relative to fb origin */
+ if (0 && rootshift) {
+ /*
+ * Note: not needed because damage is
+ * relative to subwin, not rootwin.
+ */
+ x = x - off_x;
+ y = y - off_y;
+ }
+ if (clipshift) {
+ x = x - coff_x;
+ y = y - coff_y;
+ }
+
+ x2 = x + w; /* upper point */
+ x = nfix(x, dpy_x); /* place both in fb area */
+ x2 = nfix(x2, dpy_x+1);
+ w = x2 - x; /* recompute w */
+
+ y2 = y + h;
+ y = nfix(y, dpy_y);
+ y2 = nfix(y2, dpy_y+1);
+ h = y2 - y;
+
+ if (w <= 0 || h <= 0) {
+ continue;
+ }
+ }
+ if (debug_xdamage > 2) {
+ fprintf(stderr, "xdamage: -> event %dx%d+%d+%d area:"
+ " %d dups: %d %s\n", w, h, x, y, w*h, dcount,
+ (w*h > xdamage_max_area) ? "TOO_BIG" : "");
+ }
+
+ record_desired_xdamage_rect(x, y, w, h);
+
+ tmpregion = sraRgnCreateRect(x, y, x + w, y + h);
+ sraRgnOr(reg, tmpregion);
+ sraRgnDestroy(tmpregion);
+ rect_count++;
+ ccount++;
+ }
+ /* clear the whole damage region for next time. XXX check */
+ if (call == 1) {
+ XDamageSubtract(dpy, xdamage, None, None);
+ }
+ X_UNLOCK;
+
+ if (0 && xdamage_direct_count) {
+ fb_push();
+ }
+
+ dt = dtime(&tm);
+ if ((debug_tiles > 1 && ecount) || (debug_tiles && ecount > 200)
+ || debug_xdamage > 1) {
+ fprintf(stderr, "collect_xdamage(%d): %.4f t: %.4f ev/dup/accept"
+ "/direct %d/%d/%d/%d\n", call, dt, tm - x11vnc_start, ecount,
+ dcount, ccount, xdamage_direct_count);
+ }
+ now = time(0);
+ if (! last_rpt) {
+ last_rpt = now;
+ }
+ if (now > last_rpt + 15) {
+ double rat = -1.0;
+
+ if (XD_tot) {
+ rat = ((double) XD_skip)/XD_tot;
+ }
+ if (debug_tiles || debug_xdamage) {
+ fprintf(stderr, "xdamage: == scanline skip/tot: "
+ "%04d/%04d =%.3f rects: %d desired: %d\n",
+ XD_skip, XD_tot, rat, rect_count, XD_des);
+ }
+
+ XD_skip = 0;
+ XD_tot = 0;
+ XD_des = 0;
+ rect_count = 0;
+ last_rpt = now;
+ }
+#else
+ if (0) scancnt++; /* compiler warnings */
+ if (0) call++;
+ if (0) record_desired_xdamage_rect(0, 0, 0, 0);
+#endif
+ return 0;
+}
+
+int xdamage_hint_skip(int y) {
+ static sraRegionPtr scanline = NULL;
+ sraRegionPtr reg, tmpl;
+ int ret, i, n, nreg;
+
+ if (! xdamage_present || ! use_xdamage) {
+ return 0; /* cannot skip */
+ }
+ if (! xdamage_regions) {
+ return 0; /* cannot skip */
+ }
+
+ if (! scanline) {
+ /* keep it around to avoid malloc etc, recreate */
+ scanline = sraRgnCreate();
+ }
+
+ tmpl = sraRgnCreateRect(0, y, dpy_x, y+1);
+
+ nreg = (xdamage_memory * NSCAN) + 1;
+ ret = 1;
+ for (i=0; i<nreg; i++) {
+ /* go back thru the history starting at most recent */
+ n = (xdamage_ticker + nreg - i) % nreg;
+ reg = xdamage_regions[n];
+ if (sraRgnEmpty(reg)) {
+ /* checking for emptiness is very fast */
+ continue;
+ }
+ sraRgnMakeEmpty(scanline);
+ sraRgnOr(scanline, tmpl);
+ if (sraRgnAnd(scanline, reg)) {
+ ret = 0;
+ break;
+ }
+ }
+ sraRgnDestroy(tmpl);
+
+ return ret;
+}
+
+void initialize_xdamage(void) {
+ sraRegionPtr *ptr;
+ int i, nreg;
+
+ if (! xdamage_present) {
+ use_xdamage = 0;
+ }
+ if (xdamage_regions) {
+ ptr = xdamage_regions;
+ while (*ptr != NULL) {
+ sraRgnDestroy(*ptr);
+ ptr++;
+ }
+ free(xdamage_regions);
+ xdamage_regions = NULL;
+ }
+ if (use_xdamage) {
+ nreg = (xdamage_memory * NSCAN) + 2;
+ xdamage_regions = (sraRegionPtr *)
+ malloc(nreg * sizeof(sraRegionPtr));
+ for (i = 0; i < nreg; i++) {
+ ptr = xdamage_regions+i;
+ if (i == nreg - 1) {
+ *ptr = NULL;
+ } else {
+ *ptr = sraRgnCreate();
+ sraRgnMakeEmpty(*ptr);
+ }
+ }
+ /* set so will be 0 in first collect_xdamage call */
+ xdamage_ticker = -1;
+ }
+}
+
+void create_xdamage_if_needed(void) {
+
+ if (raw_fb && ! dpy) return; /* raw_fb hack */
+
+#if LIBVNCSERVER_HAVE_LIBXDAMAGE
+ if (! xdamage) {
+ X_LOCK;
+ xdamage = XDamageCreate(dpy, window, XDamageReportRawRectangles);
+ XDamageSubtract(dpy, xdamage, None, None);
+ X_UNLOCK;
+ rfbLog("created xdamage object: 0x%lx\n", xdamage);
+ }
+#endif
+}
+
+void destroy_xdamage_if_needed(void) {
+
+ if (raw_fb && ! dpy) return; /* raw_fb hack */
+
+#if LIBVNCSERVER_HAVE_LIBXDAMAGE
+ if (xdamage) {
+ XEvent ev;
+ X_LOCK;
+ XDamageDestroy(dpy, xdamage);
+ XFlush(dpy);
+ if (xdamage_base_event_type) {
+ while (XCheckTypedEvent(dpy,
+ xdamage_base_event_type+XDamageNotify, &ev)) {
+ ;
+ }
+ }
+ X_UNLOCK;
+ rfbLog("destroyed xdamage object: 0x%lx\n", xdamage);
+ xdamage = 0;
+ }
+#endif
+}
+
+void check_xdamage_state(void) {
+ if (! xdamage_present) {
+ return;
+ }
+ /*
+ * Create or destroy the Damage object as needed, we don't want
+ * one if no clients are connected.
+ */
+ if (client_count && use_xdamage) {
+ create_xdamage_if_needed();
+ if (xdamage_scheduled_mark > 0.0 && dnow() >
+ xdamage_scheduled_mark) {
+ if (xdamage_scheduled_mark_region) {
+ mark_region_for_xdamage(
+ xdamage_scheduled_mark_region);
+ sraRgnDestroy(xdamage_scheduled_mark_region);
+ xdamage_scheduled_mark_region = NULL;
+ } else {
+ mark_for_xdamage(0, 0, dpy_x, dpy_y);
+ }
+ xdamage_scheduled_mark = 0.0;
+ }
+ } else {
+ destroy_xdamage_if_needed();
+ }
+}
+
+