diff options
author | runge <runge> | 2006-01-09 01:54:38 +0000 |
---|---|---|
committer | runge <runge> | 2006-01-09 01:54:38 +0000 |
commit | 71f2ec79180185a6c3db0c87f9d53c491dc31e76 (patch) | |
tree | 67c341571cbeb1bd9a0744cc8eb03b30ef04f381 /x11vnc/xdamage.c | |
parent | def301266373e462f4a5e90eab443087ccfc7ccc (diff) | |
download | libtdevnc-71f2ec79180185a6c3db0c87f9d53c491dc31e76.tar.gz libtdevnc-71f2ec79180185a6c3db0c87f9d53c491dc31e76.zip |
x11vnc: the big split.
Diffstat (limited to 'x11vnc/xdamage.c')
-rw-r--r-- | x11vnc/xdamage.c | 532 |
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(); + } +} + + |