/* -- 8to24.c -- */ #include "x11vnc.h" #include "cleanup.h" #include "scan.h" #include "util.h" #include "win_utils.h" int multivis_count = 0; int multivis_24count = 0; void check_for_multivis(void); void bpp8to24(int, int, int, int); void mark_8bpp(int); #if SKIP_8TO24 void check_for_multivis(void) {} void bpp8to24(int x, int y, int z, int t) {} void mark_8bpp(int x) {} #else /* lots... */ static void set_root_cmap(void); static int check_pointer_in_depth24(void); static void parse_cmap8to24(void); static void set_poll_fb(void); static int check_depth(Window win, Window top, int doall); static int check_depth_win(Window win, Window top, XWindowAttributes *attr); static XImage *p_xi(XImage *xi, Visual *visual, int win_depth, int *w); static int poll_line(int x1, int x2, int y1, int n, sraRegionPtr mod); static void poll_line_complement(int x1, int x2, int y1, sraRegionPtr mod); static int poll_8bpp(sraRegionPtr, int); static void poll_8bpp_complement(sraRegionPtr); static void mark_rgn_rects(sraRegionPtr mod); static int get_8bpp_regions(int validate); static int get_cmap(int j, Colormap cmap); static void do_8bpp_region(int n, sraRegionPtr mark); static XImage *cmap_xi(XImage *xi, Window win, int win_depth); static void transform_rect(sraRect rect, Window win, int win_depth, int cm); /* struct for keeping info about the 8bpp windows: */ typedef struct window8 { Window win; Window top; int depth; int x, y; int w, h; int map_state; Colormap cmap; Bool map_installed; int fetched; double last_fetched; sraRegionPtr clip_region; } window8bpp_t; enum mark_8bpp_modes { MARK_8BPP_ALL = 0, MARK_8BPP_POINTER, MARK_8BPP_TOP }; #define NCOLOR 256 static Colormap root_cmap = 0; static unsigned int root_rgb[NCOLOR]; static void set_root_cmap(void) { static time_t last_set = 0; time_t now = time(NULL); XWindowAttributes attr; static XColor color[NCOLOR]; int redo = 0; RAWFB_RET_VOID #if NO_X11 return; #else if (now > last_set + 10) { redo = 1; } if (! root_cmap || redo) { X_LOCK; if (! valid_window(window, &attr, 1)) { X_UNLOCK; return; } if (attr.colormap) { int i, ncells = NCOLOR; for (i=0; i < ncells; i++) { color[i].pixel = i; color[i].pad = 0; } last_set = now; root_cmap = attr.colormap; XQueryColors(dpy, root_cmap, color, ncells); for (i=0; i < ncells; i++) { unsigned int red, green, blue; /* strip out highest 8 bits of values: */ red = (color[i].red & 0xff00) >> 8; green = (color[i].green & 0xff00) >> 8; blue = (color[i].blue & 0xff00) >> 8; /* * the maxes should be at 255 already, * but just in case... */ red = (main_red_max * red )/255; green = (main_green_max * green)/255; blue = (main_blue_max * blue )/255; /* shift them over and or together for value */ red = red << main_red_shift; green = green << main_green_shift; blue = blue << main_blue_shift; /* store it in the array to be used later */ root_rgb[i] = red | green | blue; } } X_UNLOCK; } #endif /* NO_X11 */ } /* fixed size array. Will primarily hold visible 8bpp windows */ #define MAX_8BPP_WINDOWS 64 static window8bpp_t windows_8bpp[MAX_8BPP_WINDOWS]; static int db24 = 0; static int xgetimage_8to24 = 1; static double poll_8to24_delay = POLL_8TO24_DELAY; static double cache_win = 0.0; static int level2_8to24 = 0; static int check_pointer_in_depth24(void) { int tries = 0, in_24 = 0; XWindowAttributes attr; Window c, w; double now = dnow(); c = window; RAWFB_RET(0) if (now > last_keyboard_time + 1.0 && now > last_pointer_time + 1.0) { return 0; } X_LOCK; while (c && tries++ < 3) { c = query_pointer(c); if (valid_window(c, &attr, 1)) { if (attr.depth == 24) { in_24 = 1; break; } } } X_UNLOCK; if (in_24) { int x1, y1, x2, y2; X_LOCK; xtranslate(c, window, 0, 0, &x1, &y1, &w, 1); X_UNLOCK; x2 = x1 + attr.width; y2 = y1 + attr.height; x1 = nfix(x1, dpy_x); y1 = nfix(y1, dpy_y); x2 = nfix(x2, dpy_x+1); y2 = nfix(y2, dpy_y+1); mark_rect_as_modified(x1, y1, x2, y2, 0); if (db24 > 1) fprintf(stderr, "check_pointer_in_depth24 %d %d %d %d\n", x1, y1, x2, y2); return 1; } return 0; } static void parse_cmap8to24(void) { if (cmap8to24_str) { char *p, *str = strdup(cmap8to24_str); p = strtok(str, ","); /* defaults: */ db24 = 0; xgetimage_8to24 = 1; poll_8to24_delay = POLL_8TO24_DELAY; level2_8to24 = 0; cache_win = 0.0; while (p) { if (strstr(p, "dbg=") == p) { db24 = atoi(p + strlen("dbg=")); } else if (strstr(p, "poll=") == p) { poll_8to24_delay = atof(p + strlen("poll=")); } else if (strstr(p, "cachewin=") == p) { cache_win = atof(p + strlen("cachewin=")); } else if (!strcmp(p, "nogetimage")) { xgetimage_8to24 = 0; } else if (!strcmp(p, "level2")) { level2_8to24 = 1; } p = strtok(NULL, ","); } free(str); } else { if (getenv("DEBUG_8TO24") != NULL) { db24 = atoi(getenv("DEBUG_8TO24")); } if (getenv("NOXGETIMAGE_8TO24") != NULL) { xgetimage_8to24 = 0; } } } static char *poll8_fb = NULL, *poll24_fb = NULL; static int poll8_fb_w = 0, poll8_fb_h = 0; static int poll24_fb_w = 0, poll24_fb_h = 0; static void pfb(int fac, char **fb, int *w, int *h) { if (! *fb || *w != dpy_x || *h != dpy_y) { if (*fb) { free(*fb); } *fb = (char *) calloc(fac * dpy_x * dpy_y, 1); *w = dpy_x; *h = dpy_y; } } static void set_poll_fb(void) { /* create polling framebuffers or recreate if too small. */ if (! xgetimage_8to24) { return; /* this saves a bit of RAM */ } pfb(4, &poll24_fb, &poll24_fb_w, &poll24_fb_h); pfb(1, &poll8_fb, &poll8_fb_w, &poll8_fb_h); } int MV_glob = 0; int MV_count; int MV_hit; double MV_start; void check_for_multivis(void) { XWindowAttributes attr; int doall = 0; int k, i, cnt, diff; static int first = 1; static Window *stack_old = NULL; static int stack_old_len = 0; static double last_parse = 0.0; static double last_update = 0.0; static double last_clear = 0.0; static double last_poll = 0.0; static double last_fixup = 0.0; static double last_call = 0.0; static double last_query = 0.0; double now = dnow(); double delay; RAWFB_RET_VOID #if NO_X11 return; #else if (now > last_parse + 1.0) { last_parse = now; parse_cmap8to24(); } if (db24 > 2) fprintf(stderr, " check_for_multivis: %.4f\n", now - last_call); last_call = now; if (first) { int i; /* initialize 8bpp window table: */ for (i=0; i < MAX_8BPP_WINDOWS; i++) { windows_8bpp[i].win = None; windows_8bpp[i].top = None; windows_8bpp[i].map_state = IsUnmapped; windows_8bpp[i].cmap = (Colormap) 0; windows_8bpp[i].fetched = 0; windows_8bpp[i].last_fetched = -1.0; windows_8bpp[i].clip_region = NULL; } set_poll_fb(); first = 0; doall = 1; /* fetch everything first time */ } if (wireframe_in_progress) { return; } set_root_cmap(); /* * allocate an "old stack" list of all toplevels. we compare * this to the current stack to guess stacking order changes. */ if (!stack_old || stack_old_len < stack_list_len) { int n = stack_list_len; if (n < 256) { n = 256; } if (stack_old) { free(stack_old); } stack_old = (Window *) malloc(n*sizeof(Window)); stack_old_len = n; } /* fill the old stack with visible windows: */ cnt = 0; for (k=0; k < stack_list_num; k++) { if (stack_list[k].valid && stack_list[k].map_state == IsViewable) { stack_old[cnt++] = stack_list[k].win; } } /* snapshot + update the current stacking order: */ /* TUNABLE */ if (poll_8to24_delay >= POLL_8TO24_DELAY) { delay = 3.0 * poll_8to24_delay; } else { delay = 3.0 * POLL_8TO24_DELAY; /* 0.15 */ } if (doall || now > last_update + delay) { snapshot_stack_list(0, 0.0); update_stack_list(); last_update = now; } /* look for differences in the visible toplevels: */ diff = 0; cnt = 0; for (k=0; k < stack_list_num; k++) { if (stack_list[k].valid && stack_list[k].map_state == IsViewable) { if (stack_old[cnt] != stack_list[k].win) { diff = 1; break; } cnt++; } } multivis_count = 0; multivis_24count = 0; /* * every 10 seconds we try to clean out and also refresh the window * info in the the 8bpp window table: */ if (now > last_clear + 10) { last_clear = now; X_LOCK; for (i=0; i < MAX_8BPP_WINDOWS; i++) { Window w = windows_8bpp[i].win; if (! valid_window(w, &attr, 1)) { /* catch windows that went away: */ windows_8bpp[i].win = None; windows_8bpp[i].top = None; windows_8bpp[i].map_state = IsUnmapped; windows_8bpp[i].cmap = (Colormap) 0; windows_8bpp[i].fetched = 0; windows_8bpp[i].last_fetched = -1.0; } } X_UNLOCK; } MV_count = 0; MV_hit = 0; MV_start = dnow(); set_root_cmap(); /* loop over all toplevels, both 8 and 24 depths: */ X_LOCK; /* a giant lock around the whole activity */ for (k=0; k < stack_list_num; k++) { Window r, parent; Window *list0; Status rc; unsigned int nc0; int i1; XErrorHandler old_handler; double delay; Window win = stack_list[k].win; /* TUNABLE */ if (poll_8to24_delay >= POLL_8TO24_DELAY) { delay = 1.5 * poll_8to24_delay; } else { delay = 1.5 * POLL_8TO24_DELAY; /* 0.075 */ } if (now < last_query + delay) { break; } if (win == None) { continue; } if (stack_list[k].map_state != IsViewable) { int i; /* * if the toplevel became unmapped, mark it * for the children as well... */ for (i=0; i < MAX_8BPP_WINDOWS; i++) { if (windows_8bpp[i].top == win) { windows_8bpp[i].map_state = stack_list[k].map_state; } } } if (check_depth(win, win, doall)) { /* * returns 1 if no need to recurse down e.g. It * is 8bpp and we assume all lower one are too. */ continue; } /* we recurse up to two levels down from stack_list windows */ old_handler = XSetErrorHandler(trap_xerror); trapped_xerror = 0; rc = XQueryTree_wr(dpy, win, &r, &parent, &list0, &nc0); XSetErrorHandler(old_handler); if (! rc || trapped_xerror) { trapped_xerror = 0; continue; } trapped_xerror = 0; /* loop over grandchildren of rootwin: */ for (i1=0; i1 < (int) nc0; i1++) { Window win1 = list0[i1]; Window *list1; unsigned int nc1; int i2; if (check_depth(win1, win, doall)) { continue; } if (level2_8to24) { continue; } old_handler = XSetErrorHandler(trap_xerror); trapped_xerror = 0; rc = XQueryTree_wr(dpy, win1, &r, &parent, &list1, &nc1); XSetErrorHandler(old_handler); if (! rc || trapped_xerror) { trapped_xerror = 0; continue; } trapped_xerror = 0; /* loop over great-grandchildren of rootwin: */ for (i2=0; i2< (int) nc1; i2++) { Window win2 = list1[i2]; if (check_depth(win2, win, doall)) { continue; } /* more? Which wm does this? */ } if (nc1) { XFree_wr(list1); } } if (nc0) { XFree_wr(list0); } } X_UNLOCK; last_query = dnow(); MV_glob += MV_count; if (0) fprintf(stderr, "MV_count: %d hit: %d %.4f %10.2f\n", MV_count, MV_hit, last_query - MV_start, MV_glob / (last_query - x11vnc_start)); if (screen_fixup_8 > 0.0 && now > last_fixup + screen_fixup_8) { last_fixup = now; mark_8bpp(MARK_8BPP_ALL); last_poll = now; } else if (poll_8to24_delay > 0.0) { int area = -1; int validate = 0; if (diff && multivis_count) { validate = 1; } if (now > last_poll + poll_8to24_delay) { sraRegionPtr mod; last_poll = now; mod = sraRgnCreate(); area = poll_8bpp(mod, validate); if (depth == 24) { poll_8bpp_complement(mod); } mark_rgn_rects(mod); sraRgnDestroy(mod); } if (0 && area < dpy_x * dpy_y / 2 && diff && multivis_count) { mark_8bpp(MARK_8BPP_POINTER); last_poll = now; } } else if (diff && multivis_count) { mark_8bpp(MARK_8BPP_ALL); last_poll = now; } else if (depth == 8 && multivis_24count) { static double last_check = 0.0; if (now > last_check + 0.4) { last_check = now; if (check_pointer_in_depth24()) { last_poll = now; } } } if (0) fprintf(stderr, "done: %.4f\n", dnow() - last_query); #endif /* NO_X11 */ } #define VW_CACHE_MAX 1024 static XWindowAttributes vw_cache_attr[VW_CACHE_MAX]; static Window vw_cache_win[VW_CACHE_MAX]; static void set_attr(XWindowAttributes *attr, int j) { memcpy((void *) (vw_cache_attr+j), (void *) attr, sizeof(XWindowAttributes)); } #if 0 static int get_attr(XWindowAttributes *attr, int j) { memcpy((void *) attr, (void *) (vw_cache_attr+j), sizeof(XWindowAttributes)); return 1; } #endif static XWindowAttributes wattr; static XWindowAttributes *vw_lookup(Window win) { static double last_purge = 0.0; double now; int i, j, k; if (win == None) { return NULL; } now = dnow(); if (now > last_purge + cache_win) { last_purge = now; for (i=0; i= 0) { MV_hit++; return vw_cache_attr+j; } else if (k >= 0) { XWindowAttributes attr2; int rc = valid_window(win, &attr2, 1); if (rc) { vw_cache_win[k] = win; set_attr(&attr2, k); return vw_cache_attr+k; } else { return NULL; } } else { /* Full */ int rc = valid_window(win, &wattr, 1); if (rc) { return &wattr; } else { return NULL; } } } static int check_depth(Window win, Window top, int doall) { XWindowAttributes attr, *pattr; /* first see if it is (still) a valid window: */ MV_count++; if (cache_win > 0.0) { pattr = vw_lookup(win); if (pattr == NULL) { return 1; /* indicate done */ } } else { if (! valid_window(win, &attr, 1)) { return 1; /* indicate done */ } pattr = &attr; } if (! doall && pattr->map_state != IsViewable) { /* * store results anyway... this may lead to table * filling up, but currently this allows us to update * state of onetime mapped windows. */ check_depth_win(win, top, pattr); return 1; /* indicate done */ } else if (check_depth_win(win, top, pattr)) { return 1; /* indicate done */ } else { return 0; /* indicate not done */ } } static int check_depth_win(Window win, Window top, XWindowAttributes *attr) { int store_it = 0; /* * only store windows with depth not equal to the default visual's * depth note some windows can have depth == 0 ... (skip them). */ if (attr->depth > 0) { if (depth == 24 && attr->depth != 24) { store_it = 1; } else if (depth == 8 && root_cmap && attr->colormap != root_cmap) { store_it = 1; } } if (store_it) { int i, j = -1, none = -1, nomap = -1; int new = 0; if (attr->map_state == IsViewable) { /* count the visible ones: */ multivis_count++; if (attr->depth == 24) { multivis_24count++; } if (db24 > 1) fprintf(stderr, "multivis: 0x%lx %d\n", win, attr->depth); } /* try to find a table slot for this window: */ for (i=0; i < MAX_8BPP_WINDOWS; i++) { if (none < 0 && windows_8bpp[i].win == None) { /* found first None */ none = i; } if (windows_8bpp[i].win == win) { /* found myself */ j = i; break; } if (nomap < 0 && windows_8bpp[i].win != None && windows_8bpp[i].map_state != IsViewable) { /* found first unmapped */ nomap = i; } } if (j < 0) { if (attr->map_state != IsViewable) { /* no slot and not visible: not worth keeping */ return 1; } else if (none >= 0) { /* put it in the first None slot */ j = none; new = 1; } else if (nomap >=0) { /* put it in the first unmapped slot */ j = nomap; } /* otherwise we cannot store it... */ } if (db24 > 1) fprintf(stderr, "multivis: 0x%lx ms: %d j: %d no: %d nm: %d dep=%d\n", win, attr->map_state, j, none, nomap, attr->depth); /* store if if we found a slot j: */ if (j >= 0) { Window w; int x, y; int now_vis = 0; if (attr->map_state == IsViewable && windows_8bpp[j].map_state != IsViewable) { now_vis = 1; } if (db24 > 1) fprintf(stderr, "multivis: STORE 0x%lx j: %3d ms: %d dep=%d\n", win, j, attr->map_state, attr->depth); windows_8bpp[j].win = win; windows_8bpp[j].top = top; windows_8bpp[j].depth = attr->depth; windows_8bpp[j].map_state = attr->map_state; windows_8bpp[j].cmap = attr->colormap; windows_8bpp[j].map_installed = attr->map_installed; windows_8bpp[j].w = attr->width; windows_8bpp[j].h = attr->height; windows_8bpp[j].fetched = 1; windows_8bpp[j].last_fetched = dnow(); /* translate x y to be WRT the root window (not parent) */ xtranslate(win, window, 0, 0, &x, &y, &w, 1); windows_8bpp[j].x = x; windows_8bpp[j].y = y; if (new || now_vis) { if (db24) fprintf(stderr, "new/now_vis: 0x%lx %d/%d\n", win, new, now_vis); /* mark it immediately if a new one: */ X_UNLOCK; /* dont forget the giant lock */ mark_rect_as_modified(x, y, x + attr->width, y + attr->height, 0); X_LOCK; } } else { /* * Error: could not find a slot. * perhaps keep age and expire old ones?? */ if (db24) fprintf(stderr, "multivis: CANNOT STORE 0x%lx j=%d\n", win, j); for (i=0; i < MAX_8BPP_WINDOWS; i++) { if (db24 > 1) fprintf(stderr, " ------------ 0x%lx i=%d\n", windows_8bpp[i].win, i); } } return 1; } return 0; } static XImage *p_xi(XImage *xi, Visual *visual, int win_depth, int *w) { RAWFB_RET(NULL) #if NO_X11 return NULL; #else if (xi == NULL || *w < dpy_x) { char *d; if (xi) { XDestroyImage(xi); } if (win_depth == 8) { d = (char *) malloc(dpy_x * 1); } else { d = (char *) malloc(dpy_x * 4); } *w = dpy_x; xi = XCreateImage(dpy, visual, win_depth, ZPixmap, 0, d, dpy_x, 1, 8, 0); } return xi; #endif /* NO_X11 */ } static int poll_line(int x1, int x2, int y1, int n, sraRegionPtr mod) { int fac, n_off, w, xo, yo; char *poll_fb, *dst, *src; int w2, xl, xh, stride = 32; int inrun = 0, rx1 = -1, rx2 = -1; static XImage *xi8 = NULL, *xi24 = NULL, *xi_r; static int xi8_w = 0, xi24_w = 0; XErrorHandler old_handler = NULL; XImage *xi; Window c, win = windows_8bpp[n].win; static XWindowAttributes attr; static Window last_win = None; static double last_time = 0.0; double now; sraRegionPtr rect; int mx1, mx2, my1, my2; int ns = NSCAN/2; RAWFB_RET(1) #if NO_X11 return 1; #else if (win == None) { return 1; } if (windows_8bpp[n].map_state != IsViewable) { return 1; } if (! xgetimage_8to24) { return 1; } X_LOCK; now = dnow(); if (last_win != None && win == last_win && now < last_time + 0.5) { ; /* use previous attr */ } else { if (! valid_window(win, &attr, 1)) { X_UNLOCK; last_win = None; return 0; } last_time = now; last_win = win; } if (attr.depth != 8 && attr.depth != 24) { X_UNLOCK; return 1; } else if (attr.depth == 8) { xi = xi8 = p_xi(xi8, attr.visual, 8, &xi8_w); poll_fb = poll8_fb; fac = 1; n_off = poll8_fb_w * y1 + x1; } else { xi = xi24 = p_xi(xi24, attr.visual, 24, &xi24_w); poll_fb = poll24_fb; fac = 4; n_off = poll24_fb_w * y1 + x1; } old_handler = XSetErrorHandler(trap_xerror); trapped_xerror = 0; /* xtranslate() not used to save two XSetErrorHandler calls */ XTranslateCoordinates(dpy, win, window, 0, 0, &xo, &yo, &c); xo = x1 - xo; yo = y1 - yo; w = x2 - x1; if (trapped_xerror || xo < 0 || yo < 0 || xo + w > attr.width) { if (db24 > 2) fprintf(stderr, "avoid bad match...\n"); XSetErrorHandler(old_handler); trapped_xerror = 0; X_UNLOCK; return 0; } trapped_xerror = 0; xi_r = XGetSubImage(dpy, win, xo, yo, w, 1, AllPlanes, ZPixmap, xi, 0, 0); XSetErrorHandler(old_handler); X_UNLOCK; if (! xi_r || trapped_xerror) { trapped_xerror = 0; return 0; } trapped_xerror = 0; src = xi->data; dst = poll_fb + fac * n_off; inrun = 0; xl = x1; while (xl < x2) { xh = xl + stride; if (xh > x2) { xh = x2; } w2 = xh - xl; if (memcmp(dst, src, fac * w2)) { if (inrun) { rx2 = xh; } else { rx1 = xl; rx2 = xh; inrun = 1; } } else { if (inrun) { mx1 = rx1; mx2 = rx2; my1 = nfix(y1 - ns, dpy_y); my2 = nfix(y1 + ns, dpy_y+1); rect = sraRgnCreateRect(mx1, my1, mx2, my2); sraRgnOr(mod, rect); sraRgnDestroy(rect); inrun = 0; } } xl += stride; dst += fac * stride; src += fac * stride; } if (inrun) { mx1 = rx1; mx2 = rx2; my1 = nfix(y1 - ns, dpy_y); my2 = nfix(y1 + ns, dpy_y+1); rect = sraRgnCreateRect(mx1, my1, mx2, my2); sraRgnOr(mod, rect); sraRgnDestroy(rect); } return 1; #endif /* NO_X11 */ } static void poll_line_complement(int x1, int x2, int y1, sraRegionPtr mod) { int n_off, w, xl, xh, stride = 32; char *dst, *src; int inrun = 0, rx1 = -1, rx2 = -1; sraRegionPtr rect; int mx1, mx2, my1, my2; int ns = NSCAN/2; if (depth != 24) { return; } if (! cmap8to24_fb) { return; } if (! xgetimage_8to24) { return; } n_off = main_bytes_per_line * y1 + 4 * x1; src = main_fb + n_off; dst = cmap8to24_fb + n_off; inrun = 0; xl = x1; while (xl < x2) { xh = xl + stride; if (xh > x2) { xh = x2; } w = xh - xl; if (memcmp(dst, src, 4 * w)) { if (inrun) { rx2 = xh; } else { rx1 = xl; rx2 = xh; inrun = 1; } } else { if (inrun) { mx1 = rx1; mx2 = rx2; my1 = nfix(y1 - ns, dpy_y); my2 = nfix(y1 + ns, dpy_y+1); rect = sraRgnCreateRect(mx1, my1, mx2, my2); sraRgnOr(mod, rect); sraRgnDestroy(rect); inrun = 0; } } xl += stride; dst += 4 * stride; src += 4 * stride; } if (inrun) { mx1 = rx1; mx2 = rx2; my1 = nfix(y1 - ns, dpy_y); my2 = nfix(y1 + ns, dpy_y+1); rect = sraRgnCreateRect(mx1, my1, mx2, my2); sraRgnOr(mod, rect); sraRgnDestroy(rect); inrun = 0; } } #define CMAPMAX 64 static Colormap cmaps[CMAPMAX]; static int ncmaps; static int poll_8bpp(sraRegionPtr mod, int validate) { int i, y, ysh, map_count; static int ycnt = 0; sraRegionPtr line; sraRect rect; sraRectangleIterator *iter; int br = 0, area = 0; static double last_call = 0.0; map_count = get_8bpp_regions(validate); if (db24 > 1) fprintf(stderr, "poll_8bpp mc: %d\n", map_count); if (! map_count) { return 0; } set_poll_fb(); ysh = scanlines[(ycnt++) % NSCAN]; if (db24 > 2) fprintf(stderr, "poll_8bpp: ysh: %2d %.4f\n", ysh, dnow() - last_call); last_call = dnow(); for (i=0; i < MAX_8BPP_WINDOWS; i++) { sraRegionPtr reg = windows_8bpp[i].clip_region; if (! reg || sraRgnEmpty(reg)) { continue; } y = ysh; while (y < dpy_y) { line = sraRgnCreateRect(0, y, dpy_x, y+1); if (sraRgnAnd(line, reg)) { iter = sraRgnGetIterator(line); while (sraRgnIteratorNext(iter, &rect)) { if (! poll_line(rect.x1, rect.x2, rect.y1, i, mod)) { br = 1; break; /* exception */ } } sraRgnReleaseIterator(iter); } sraRgnDestroy(line); y += NSCAN; if (br) break; } if (br) break; } iter = sraRgnGetIterator(mod); while (sraRgnIteratorNext(iter, &rect)) { area += nabs((rect.x2 - rect.x1)*(rect.y2 - rect.y1)); } sraRgnReleaseIterator(iter); return area; } static void poll_8bpp_complement(sraRegionPtr mod) { int i, y, ysh; static int ycnt = 0; sraRegionPtr disp, line; sraRect rect; sraRectangleIterator *iter; disp = sraRgnCreateRect(0, 0, dpy_x, dpy_y); ysh = scanlines[(ycnt++) % NSCAN]; for (i=0; i < MAX_8BPP_WINDOWS; i++) { sraRegionPtr reg = windows_8bpp[i].clip_region; if (! reg) { continue; } if (windows_8bpp[i].map_state != IsViewable) { continue; } sraRgnSubtract(disp, reg); } y = ysh; while (y < dpy_y) { line = sraRgnCreateRect(0, y, dpy_x, y+1); if (sraRgnAnd(line, disp)) { iter = sraRgnGetIterator(line); while (sraRgnIteratorNext(iter, &rect)) { poll_line_complement(rect.x1, rect.x2, rect.y1, mod); } sraRgnReleaseIterator(iter); } sraRgnDestroy(line); y += NSCAN; } sraRgnDestroy(disp); } static void mark_rgn_rects(sraRegionPtr mod) { sraRect rect; sraRectangleIterator *iter; int area = 0; if (sraRgnEmpty(mod)) { return; } iter = sraRgnGetIterator(mod); while (sraRgnIteratorNext(iter, &rect)) { mark_rect_as_modified(rect.x1, rect.y1, rect.x2, rect.y2, 0); area += nabs((rect.x2 - rect.x1)*(rect.y2 - rect.y1)); } sraRgnReleaseIterator(iter); if (db24 > 1) fprintf(stderr, " mark_rgn_rects area: %d\n", area); } static int get_8bpp_regions(int validate) { XWindowAttributes attr; int i, k, mapcount = 0; /* initialize color map list */ ncmaps = 0; for (i=0; i < CMAPMAX; i++) { cmaps[i] = (Colormap) 0; } /* loop over the table of 8bpp windows: */ for (i=0; i < MAX_8BPP_WINDOWS; i++) { sraRegionPtr tmp_reg, tmp_reg2; Window c, w = windows_8bpp[i].win; int x, y; if (windows_8bpp[i].clip_region) { sraRgnDestroy(windows_8bpp[i].clip_region); } windows_8bpp[i].clip_region = NULL; if (w == None) { continue; } if (db24 > 1) fprintf(stderr, "get_8bpp_regions: 0x%lx ms=%d dep=%d i=%d\n", w, windows_8bpp[i].map_state, windows_8bpp[i].depth, i); if (validate) { /* * this could be slow: validating 8bpp windows each * time... */ X_LOCK; if (! valid_window(w, &attr, 1)) { X_UNLOCK; windows_8bpp[i].win = None; windows_8bpp[i].top = None; windows_8bpp[i].map_state = IsUnmapped; windows_8bpp[i].cmap = (Colormap) 0; windows_8bpp[i].fetched = 0; windows_8bpp[i].last_fetched = -1.0; continue; } X_UNLOCK; windows_8bpp[i].depth = attr.depth; windows_8bpp[i].map_state = attr.map_state; windows_8bpp[i].cmap = attr.colormap; windows_8bpp[i].map_installed = attr.map_installed; windows_8bpp[i].w = attr.width; windows_8bpp[i].h = attr.height; windows_8bpp[i].fetched = 1; windows_8bpp[i].last_fetched = dnow(); if (attr.map_state != IsViewable) { continue; } X_LOCK; xtranslate(w, window, 0, 0, &x, &y, &c, 1); X_UNLOCK; windows_8bpp[i].x = x; windows_8bpp[i].y = y; } else { /* this will be faster: no call to X server: */ if (windows_8bpp[i].map_state != IsViewable) { continue; } attr.depth = windows_8bpp[i].depth; attr.map_state = windows_8bpp[i].map_state; attr.colormap = windows_8bpp[i].cmap; attr.map_installed = windows_8bpp[i].map_installed; attr.width = windows_8bpp[i].w; attr.height = windows_8bpp[i].h; x = windows_8bpp[i].x; y = windows_8bpp[i].y; } mapcount++; /* tmp region for this 8bpp rectangle: */ tmp_reg = sraRgnCreateRect(nfix(x, dpy_x), nfix(y, dpy_y), nfix(x + attr.width, dpy_x+1), nfix(y + attr.height, dpy_y+1)); /* loop over all toplevels, top to bottom clipping: */ for (k = stack_list_num - 1; k >= 0; k--) { Window swin = stack_list[k].win; int sx, sy, sw, sh; if (db24 > 1 && stack_list[k].map_state == IsViewable) fprintf(stderr, "Stack win: 0x%lx %d iv=%d\n", swin, k, stack_list[k].map_state); if (swin == windows_8bpp[i].top) { /* found our top level: we skip the rest. */ if (db24 > 1) fprintf(stderr, "found top: 0x%lx %d iv=%d\n", swin, k, stack_list[k].map_state); break; } if (stack_list[k].map_state != IsViewable) { /* skip unmapped ones: */ continue; } /* make a temp rect for this toplevel: */ sx = stack_list[k].x; sy = stack_list[k].y; sw = stack_list[k].width; sh = stack_list[k].height; if (db24 > 1) fprintf(stderr, "subtract: 0x%lx %d -- %d %d %d %d\n", swin, k, sx, sy, sw, sh); tmp_reg2 = sraRgnCreateRect(nfix(sx, dpy_x), nfix(sy, dpy_y), nfix(sx + sw, dpy_x+1), nfix(sy + sh, dpy_y+1)); /* subtract it from the 8bpp window region */ sraRgnSubtract(tmp_reg, tmp_reg2); sraRgnDestroy(tmp_reg2); if (sraRgnEmpty(tmp_reg)) { break; } } if (sraRgnEmpty(tmp_reg)) { /* skip this 8bpp if completely clipped away: */ sraRgnDestroy(tmp_reg); continue; } /* otherwise, store any new colormaps: */ if (ncmaps < CMAPMAX && attr.colormap != (Colormap) 0) { int m, seen = 0; for (m=0; m < ncmaps; m++) { if (cmaps[m] == attr.colormap) { seen = 1; break; } } if (! seen && attr.depth == 8) { /* store only new ones: */ cmaps[ncmaps++] = attr.colormap; } } windows_8bpp[i].clip_region = tmp_reg; } return mapcount; } static XColor color[CMAPMAX][NCOLOR]; static unsigned int rgb[CMAPMAX][NCOLOR]; static int cmap_failed[CMAPMAX]; int histo[256]; static int get_cmap(int j, Colormap cmap) { int i, ncells; XErrorHandler old_handler = NULL; RAWFB_RET(0) #if NO_X11 return 0; #else if (0) { /* not working properly for depth 24... */ X_LOCK; ncells = CellsOfScreen(ScreenOfDisplay(dpy, scr)); X_UNLOCK; } else { ncells = NCOLOR; } if (db24 > 1) fprintf(stderr, "get_cmap: %d 0x%x\n", j, (unsigned int) cmap); /* ncells should "always" be 256. */ if (ncells > NCOLOR) { ncells = NCOLOR; } else if (ncells == 8) { /* hmmm. see set_colormap() */ ncells = NCOLOR; } /* initialize XColor array: */ for (i=0; i < ncells; i++) { color[j][i].pixel = i; color[j][i].pad = 0; } /* try to query the colormap, trap errors */ X_LOCK; trapped_xerror = 0; old_handler = XSetErrorHandler(trap_xerror); XQueryColors(dpy, cmap, color[j], ncells); XSetErrorHandler(old_handler); X_UNLOCK; if (trapped_xerror) { trapped_xerror = 0; return 0; } trapped_xerror = 0; /* now map each index to depth 24 RGB */ for (i=0; i < ncells; i++) { unsigned int red, green, blue; /* strip out highest 8 bits of values: */ red = (color[j][i].red & 0xff00) >> 8; green = (color[j][i].green & 0xff00) >> 8; blue = (color[j][i].blue & 0xff00) >> 8; /* * the maxes should be at 255 already, * but just in case... */ red = (main_red_max * red )/255; green = (main_green_max * green)/255; blue = (main_blue_max * blue )/255; if (db24 > 2) fprintf(stderr, " cmap[%02d][%03d]: %03d %03d %03d 0x%08x \n", j, i, red, green, blue, ( red << main_red_shift | green << main_green_shift | blue << main_blue_shift)); /* shift them over and or together for value */ red = red << main_red_shift; green = green << main_green_shift; blue = blue << main_blue_shift; /* store it in the array to be used later */ rgb[j][i] = red | green | blue; } return 1; #endif /* NO_X11 */ } static void do_8bpp_region(int n, sraRegionPtr mark) { int k, cm = -1, failed = 0; sraRectangleIterator *iter; sraRegionPtr clip; sraRect rect; if (! windows_8bpp[n].clip_region) { return; } if (windows_8bpp[n].win == None) { return; } if (windows_8bpp[n].map_state != IsViewable) { return; } if (db24 > 1) fprintf(stderr, "ncmaps: %d\n", ncmaps); /* see if XQueryColors failed: */ for (k=0; k rect.x2) { int tmp = rect.x2; rect.x2 = rect.x1; rect.x1 = tmp; } if (rect.y1 > rect.y2) { int tmp = rect.y2; rect.y2 = rect.y1; rect.y1 = tmp; } transform_rect(rect, windows_8bpp[n].win, windows_8bpp[n].depth, cm); } sraRgnReleaseIterator(iter); sraRgnDestroy(clip); } static XImage *cmap_xi(XImage *xi, Window win, int win_depth) { XWindowAttributes attr; char *d; #if NO_X11 return NULL; #else if (xi) { XDestroyImage(xi); } if (! dpy || ! valid_window(win, &attr, 1)) { return (XImage *) NULL; } if (attr.depth != win_depth) { return (XImage *) NULL; } else if (win_depth == 8) { d = (char *) malloc(dpy_x * dpy_y * 1); } else if (win_depth == 24) { d = (char *) malloc(dpy_x * dpy_y * 4); } else { return (XImage *) NULL; } return XCreateImage(dpy, attr.visual, win_depth, ZPixmap, 0, d, dpy_x, dpy_y, 8, 0); #endif /* NO_X11 */ } static void transform_rect(sraRect rect, Window win, int win_depth, int cm) { char *src, *dst, *poll; unsigned int *ui; unsigned char *uc; int ps, pixelsize = bpp/8; int poll_Bpl; int do_getimage = xgetimage_8to24; int line, n_off, j, h, w; unsigned int hi, idx; XWindowAttributes attr; XErrorHandler old_handler = NULL; if (db24 > 1) fprintf(stderr, "transform %4d %4d %4d %4d cm: %d\n", rect.x1, rect.y1, rect.x2, rect.y2, cm); RAWFB_RET_VOID #if NO_X11 return; #else /* now transform the pixels in this rectangle: */ n_off = main_bytes_per_line * rect.y1 + pixelsize * rect.x1; h = rect.y2 - rect.y1; w = rect.x2 - rect.x1; if (depth == 8) { /* need to fetch depth 24 data. */ do_getimage = 1; } #if 0 if (do_getimage) { X_LOCK; vw = valid_window(win, &attr, 1); X_UNLOCK; } if (do_getimage && vw) { #else if (do_getimage) { #endif static XImage *xi_8 = NULL; static XImage *xi_24 = NULL; XImage *xi = NULL, *xi_r; Window c; unsigned int wu, hu; int xo, yo; wu = (unsigned int) w; hu = (unsigned int) h; X_LOCK; #define GETSUBIMAGE #ifdef GETSUBIMAGE if (win_depth == 8) { if (xi_8 == NULL || xi_8->width != dpy_x || xi_8->height != dpy_y) { xi_8 = cmap_xi(xi_8, win, 8); } xi = xi_8; } else if (win_depth == 24) { if (xi_24 == NULL || xi_24->width != dpy_x || xi_24->height != dpy_y) { xi_24 = cmap_xi(xi_24, win, 24); } xi = xi_24; } #endif old_handler = XSetErrorHandler(trap_xerror); trapped_xerror = 0; XTranslateCoordinates(dpy, win, window, 0, 0, &xo, &yo, &c); xo = rect.x1 - xo; yo = rect.y1 - yo; if (db24 > 1) fprintf(stderr, "xywh: %d %d %d %d vs. %d %d\n", xo, yo, w, h, attr.width, attr.height); if (trapped_xerror || xo < 0 || yo < 0) { /* w > attr.width || h > attr.height */ XSetErrorHandler(old_handler); X_UNLOCK; trapped_xerror = 0; if (db24 > 1) fprintf(stderr, "skipping due to potential bad match...\n"); return; } trapped_xerror = 0; #ifndef GETSUBIMAGE xi = XGetImage(dpy, win, xo, yo, wu, hu, AllPlanes, ZPixmap); xi_r = xi; #else xi_r = XGetSubImage(dpy, win, xo, yo, wu, hu, AllPlanes, ZPixmap, xi, 0, 0); #endif XSetErrorHandler(old_handler); X_UNLOCK; if (! xi_r || trapped_xerror) { trapped_xerror = 0; if (db24 > 1) fprintf(stderr, "xi-fail: 0x%p trap=%d %d %d %d %d\n", (void *)xi, trapped_xerror, xo, yo, w, h); return; } else { if (db24 > 1) fprintf(stderr, "xi: 0x%p %d %d %d %d -- %d %d\n", (void *)xi, xo, yo, w, h, xi->width, xi->height); } trapped_xerror = 0; if (xi->depth != 8 && xi->depth != 24) { #ifndef GETSUBIMAGE X_LOCK; XDestroyImage(xi); X_UNLOCK; #endif if (db24) fprintf(stderr, "xi: wrong depth: %d\n", xi->depth); return; } set_poll_fb(); if (xi->depth == 8) { int ps1, ps2, fac; if (depth == 8) { ps1 = 1; ps2 = 4; fac = 4; } else { ps1 = 1; ps2 = pixelsize; fac = 1; } src = xi->data; dst = cmap8to24_fb + fac * n_off; poll = poll8_fb + poll8_fb_w * rect.y1 + rect.x1; poll_Bpl = poll8_fb_w * 1; /* line by line ... */ for (line = 0; line < h; line++) { /* pixel by pixel... */ for (j = 0; j < w; j++) { uc = (unsigned char *) (src + ps1 * j); ui = (unsigned int *) (dst + ps2 * j); idx = (int) (*uc); *ui = rgb[cm][idx]; *(poll + ps1 * j) = *uc; } src += xi->bytes_per_line; dst += main_bytes_per_line * fac; poll += poll_Bpl; } } else if (xi->depth == 24) { /* line by line ... */ int ps1 = 4, fac; if (depth == 8) { fac = 4; } else { fac = 1; /* will not happen */ } src = xi->data; dst = cmap8to24_fb + fac * n_off; poll = poll24_fb + (poll24_fb_w * rect.y1 + rect.x1)*4; poll_Bpl = poll24_fb_w * 4; for (line = 0; line < h; line++) { memcpy(dst, src, w * ps1); memcpy(poll, src, w * ps1); src += xi->bytes_per_line; dst += main_bytes_per_line * fac; poll += poll_Bpl; } } #ifndef GETSUBIMAGE X_LOCK; XDestroyImage(xi); X_UNLOCK; #endif } else if (! do_getimage) { int fac; if (depth == 8) { /* cooked up depth 24 TrueColor */ /* but currently disabled (high bits no useful?) */ ps = 4; fac = 4; src = cmap8to24_fb + 4 * n_off; } else { ps = pixelsize; fac = 1; src = cmap8to24_fb + n_off; } /* line by line ... */ for (line = 0; line < h; line++) { /* pixel by pixel... */ for (j = 0; j < w; j++) { /* grab 32 bit value */ ui = (unsigned int *) (src + ps * j); /* extract top 8 bits (FIXME: masks?) */ hi = (*ui) & 0xff000000; /* map to lookup index; rewrite pixel */ idx = hi >> 24; *ui = hi | rgb[cm][idx]; } src += main_bytes_per_line * fac; } } #endif /* NO_X11 */ } void bpp8to24(int x1, int y1, int x2, int y2) { char *src, *dst; unsigned char *uc; unsigned int *ui; int idx, pixelsize = bpp/8; int line, k, i, j, h, w; int n_off; sraRegionPtr rect; int validate = 1; static int last_map_count = 0, call_count = 0; static double last_get_8bpp_validate = 0.0; static double last_snapshot = 0.0; double now; double dt, d0 = 0.0, t2; RAWFB_RET_VOID if (! cmap8to24 || ! cmap8to24_fb) { /* hmmm, why were we called? */ return; } if (db24 > 1) fprintf(stderr, "bpp8to24 %d %d %d %d %.4f\n", x1, y1, x2, y2, dnow() - last_get_8bpp_validate); call_count++; /* clip to display just in case: */ x1 = nfix(x1, dpy_x); y1 = nfix(y1, dpy_y); x2 = nfix(x2, dpy_x+1); y2 = nfix(y2, dpy_y+1); if (wireframe_in_progress) { /* * draw_box() manages cmap8to24_fb for us so we get out as * soon as we can. No need to cp main_fb -> cmap8to24_fb. */ return; } /* copy from main_fb to cmap8to24_fb regardless of 8bpp windows: */ h = y2 - y1; w = x2 - x1; if (depth == 8) { /* need to cook up to depth 24 TrueColor */ /* pixelsize = 1 */ n_off = main_bytes_per_line * y1 + pixelsize * x1; src = main_fb + n_off; dst = cmap8to24_fb + 4 * n_off; set_root_cmap(); if (root_cmap) { int ps1 = 1, ps2 = 4; #if 0 unsigned int hi; #endif /* line by line ... */ for (line = 0; line < h; line++) { /* pixel by pixel... */ for (j = 0; j < w; j++) { uc = (unsigned char *) (src + ps1 * j); ui = (unsigned int *) (dst + ps2 * j); idx = (int) (*uc); #if 0 if (do_hibits) { hi = idx << 24; *ui = hi | rgb[0][idx]; } else { } #endif *ui = root_rgb[idx]; if (db24 > 2) histo[idx]++; } src += main_bytes_per_line; dst += main_bytes_per_line * 4; } } } else if (depth == 24) { /* pixelsize = 4 */ n_off = main_bytes_per_line * y1 + pixelsize * x1; src = main_fb + n_off; dst = cmap8to24_fb + n_off; /* otherwise, the pixel data as is */ for (line = 0; line < h; line++) { memcpy(dst, src, w * pixelsize); src += main_bytes_per_line; dst += main_bytes_per_line; } } if (last_map_count > MAX_8BPP_WINDOWS/4) { /* table is filling up... skip validating sometimes: */ int skip = 3; if (last_map_count > MAX_8BPP_WINDOWS/2) { skip = 6; } else if (last_map_count > 3*MAX_8BPP_WINDOWS/4) { skip = 12; } if (call_count % skip != 0) { validate = 0; } } if (db24 > 2) {for(i=0;i<256;i++){histo[i]=0;}} now = dnow(); dt = now - last_get_8bpp_validate; /* TUNABLES */ if (dt < 0.003) { ; /* XXX does this still give painting errors? */ } else { int snapit = 0; double delay1, delay2, delay3; if (poll_8to24_delay >= POLL_8TO24_DELAY) { delay1 = 1.0 * poll_8to24_delay; delay2 = 2.0 * poll_8to24_delay; delay3 = 10. * poll_8to24_delay; } else { delay1 = 1.0 * POLL_8TO24_DELAY; /* 0.05 */ delay2 = 2.0 * POLL_8TO24_DELAY; /* 0.1 */ delay3 = 10. * POLL_8TO24_DELAY; /* 0.5 */ } if (cache_win > 1.0) { delay2 *= 2; delay3 *= 2; } if (dt < delay1) { validate = 0; } if (last_map_count) { if (now > last_snapshot + delay2) { snapit = 1; } } else { if (now > last_snapshot + delay3) { snapit = 1; } } if (snapit) { /* less problems if we update the stack frequently */ snapshot_stack_list(0, 0.0); if (0) fprintf(stderr, "SNAP time: %.4f\n", dnow() - now); update_stack_list(); last_snapshot = dnow(); if (0) fprintf(stderr, "UPDA time: %.4f\n", last_snapshot - now); } if (0) t2 = dnow(); last_map_count = get_8bpp_regions(validate); if (validate) { last_get_8bpp_validate = dnow(); } if (0) fprintf(stderr, "get8bpp-%d: %.4f\n", validate, dnow() - t2); } if (db24) d0 = dnow(); if (db24 > 1) fprintf(stderr, "bpp8to24 w=%d h=%d m=%p c=%p r=%p ncmaps=%d\n", w, h, main_fb, cmap8to24_fb, rfb_fb, ncmaps); /* * now go back and transform and 8bpp regions to TrueColor in * cmap8to24_fb. */ if (last_map_count && (ncmaps || depth == 8)) { int i, j; int win[MAX_8BPP_WINDOWS]; int did[MAX_8BPP_WINDOWS]; int count = 0; /* * first, grab all of the associated colormaps from the * X server. Hopefully just 1 or 2... */ for (j=0; j 2) fprintf(stderr, "cmap %d %.4f\n", (int) cmaps[j], dnow() - d0); } for (i=0; i < MAX_8BPP_WINDOWS; i++) { sraRegionPtr reg = windows_8bpp[i].clip_region; if (reg) { rect = sraRgnCreateRect(x1, y1, x2, y2); if (sraRgnAnd(rect, reg)) { win[count] = i; did[count++] = 0; } sraRgnDestroy(rect); } } if (count) { rect = sraRgnCreateRect(x1, y1, x2, y2); /* try to apply lower windows first */ for (k=0; k < stack_list_num; k++) { Window swin = stack_list[k].win; for (j=0; j 2) {for(i=0; i<256;i++) {fprintf(stderr, " cmap histo[%03d] %d\n", i, histo[i]);}} } void mark_8bpp(int mode) { int i, cnt = 0; Window top = None; RAWFB_RET_VOID if (! cmap8to24 || !cmap8to24_fb) { return; } if (mode == MARK_8BPP_TOP) { int k; for (k = stack_list_num - 1; k >= 0; k--) { Window swin = stack_list[k].win; for (i=0; i < MAX_8BPP_WINDOWS; i++) { if (windows_8bpp[i].win == None) { continue; } if (windows_8bpp[i].map_state != IsViewable) { continue; } if (swin == windows_8bpp[i].top) { top = swin; break; } } if (top != None) { break; } } } /* for each mapped 8bpp window, mark it changed: */ for (i=0; i < MAX_8BPP_WINDOWS; i++) { int x1, y1, x2, y2, w, h, f = 32; f = 0; /* skip fuzz, may bring in other windows... */ if (windows_8bpp[i].win == None) { continue; } if (mode == MARK_8BPP_TOP) { if (windows_8bpp[i].top != top) { continue; } } if (windows_8bpp[i].map_state != IsViewable) { XWindowAttributes attr; int vw; X_LOCK; vw = valid_window(windows_8bpp[i].win, &attr, 1); X_UNLOCK; if (vw) { if (attr.map_state != IsViewable) { continue; } } else { continue; } } x1 = windows_8bpp[i].x; y1 = windows_8bpp[i].y; w = windows_8bpp[i].w; h = windows_8bpp[i].h; x2 = x1 + w; y2 = y1 + h; if (mode == MARK_8BPP_POINTER) { int b = 32; /* apply some fuzz for wm border */ if (cursor_x < x1 - b || cursor_y < y1 - b) { continue; } if (cursor_x > x2 + b || cursor_y > y2 + b) { continue; } } /* apply fuzz f around each one; constrain to screen */ x1 = nfix(x1 - f, dpy_x); y1 = nfix(y1 - f, dpy_y); x2 = nfix(x2 + f, dpy_x+1); y2 = nfix(y2 + f, dpy_y+1); if (db24 > 1) fprintf(stderr, "mark_8bpp: 0x%lx %d %d %d %d\n", windows_8bpp[i].win, x1, y1, x2, y2); mark_rect_as_modified(x1, y1, x2, y2, 0); cnt++; } if (cnt) { /* push it to viewers if possible. */ rfbPE(-1); } } #endif /* SKIP_8TO24 */