diff options
author | runge <runge> | 2006-01-15 03:58:48 +0000 |
---|---|---|
committer | runge <runge> | 2006-01-15 03:58:48 +0000 |
commit | 5993dd757e5af00af6565b3411ea10fd46eafbdf (patch) | |
tree | 7450fdde25310857c88221a719f026a1a09336f7 /x11vnc/8to24.c | |
parent | 60918e6664cd62ea3d71b48cdfda8b89ab82029c (diff) | |
download | libtdevnc-5993dd757e5af00af6565b3411ea10fd46eafbdf.tar.gz libtdevnc-5993dd757e5af00af6565b3411ea10fd46eafbdf.zip |
x11vnc: add -8to24 option for some multi-depth displays.
Diffstat (limited to 'x11vnc/8to24.c')
-rw-r--r-- | x11vnc/8to24.c | 797 |
1 files changed, 797 insertions, 0 deletions
diff --git a/x11vnc/8to24.c b/x11vnc/8to24.c new file mode 100644 index 0000000..5bea044 --- /dev/null +++ b/x11vnc/8to24.c @@ -0,0 +1,797 @@ +/* -- 8to24.c -- */ +#include "x11vnc.h" +#include "cleanup.h" +#include "scan.h" +#include "util.h" +#include "win_utils.h" + +int multivis_count = 0; + +void check_for_multivis(void); +void bpp8to24(int, int, int, int); +void mark_8bpp(void); + +static int check_depth(Window win, Window top, int doall); +static int check_depth_win(Window win, Window top, XWindowAttributes attr); + +/* 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; +} window8bpp_t; + +/* fixed size array. Will primarily hold visable 8bpp windows */ +#define MAX_8BPP_WINDOWS 64 +static window8bpp_t windows_8bpp[MAX_8BPP_WINDOWS]; + +static int db24 = 0; + +void check_for_multivis(void) { + XWindowAttributes attr; + int doall = 0; + int k, i, cnt, diff; + static int first = 1; + static double last_update = 0.0; + static double last_clear = 0.0; + double now = dnow(); + static Window *stack_old = NULL; + static int stack_old_len = 0; + + 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; + } + first = 0; + doall = 1; /* fetch everything first time */ + } + + /* + * 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 visable 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: */ + snapshot_stack_list(0, 0.25); + if (doall || now > last_update + 0.25) { + update_stack_list(); + last_update = now; + } + + /* look for differences in the visable 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++; + } + } + + /* + * if there are 8bpp visable and a stacking order change + * refresh vnc with coverage of the 8bpp regions: + */ + if (diff && multivis_count) { +if (db24 || 0) fprintf(stderr, "check_for_multivis stack diff: mark_all %f\n", now - x11vnc_start); + if (0) mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0); + if (1) mark_8bpp(); + } + + multivis_count = 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 ((db24 || 0) && w != None) fprintf(stderr, " windows_8bpp: 0x%lx i=%02d ms: %d\n", windows_8bpp[i].win, i, windows_8bpp[i].map_state); + if (w == None || ! 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; + } + } + X_UNLOCK; + } + + /* loop over all toplevels, both 8 and 24 depths: */ + for (k=0; k < stack_list_num; k++) { + Window r, parent; + Window *list0; + Status rc; + unsigned int nc0; + int i1; + + Window win = stack_list[k].win; + + 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 */ + X_LOCK; + rc = XQueryTree(dpy, win, &r, &parent, &list0, &nc0); + X_UNLOCK; + if (! rc) { + continue; + } + + /* 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; + } + X_LOCK; + rc = XQueryTree(dpy, win1, &r, &parent, &list1, &nc1); + X_UNLOCK; + if (! rc) { + continue; + } + /* 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) { + X_LOCK; + XFree(list1); + X_UNLOCK; + } + } + if (nc0) { + X_LOCK; + XFree(list0); + X_UNLOCK; + } + } +} + +static int check_depth(Window win, Window top, int doall) { + XWindowAttributes attr; + + X_LOCK; + /* first see if it is (still) a valid window: */ + if (! valid_window(win, &attr, 1)) { + X_UNLOCK; + return 1; /* indicate done */ + } + X_UNLOCK; + if (! doall && attr.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, attr); + return 1; /* indicate done */ + } else if (check_depth_win(win, top, attr)) { + return 1; /* indicate done */ + } else { + return 0; /* indicate not done */ + } +} + +static int check_depth_win(Window win, Window top, XWindowAttributes attr) { + + /* + * 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 != depth && attr.depth > 0) { + + int i, j = -1, none = -1, nomap = -1; + int new = 0; + if (attr.map_state == IsViewable) { + /* count the visable ones: */ + multivis_count++; +if (db24 || 0) 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 visable: 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 || 0) fprintf(stderr, "multivis: 0x%lx ms: %d j: %d no: %d nm: %d\n", win, attr.map_state, j, none, nomap); + + /* store if if we found a slot j: */ + if (j >= 0) { + Window w; + int x, y; + +if (db24 || 0) fprintf(stderr, "multivis: STORE 0x%lx j: %3d ms: %d\n", win, j, attr.map_state); + 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; + + /* 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; + + windows_8bpp[j].fetched = 1; + + if (new) { +if (db24) fprintf(stderr, "new: 0x%lx\n", win); + /* mark it immediately if a new one: */ + mark_rect_as_modified(x, y, x + attr.width, + y + attr.height, 0); + } + } else { + /* + * Error: could not find a slot. + * perhaps keep age and expire old ones?? + */ +if (db24 || 1) fprintf(stderr, "multivis: CANNOT STORE 0x%lx j=%d\n", win, j); + for (i=0; i < MAX_8BPP_WINDOWS; i++) { +if (db24 || 0) fprintf(stderr, " ------------ 0x%lx i=%d\n", windows_8bpp[i].win, i); + } + + } + return 1; + } + return 0; +} + +void bpp8to24(int x1, int y1, int x2, int y2) { + char *src; + char *dst; + int pixelsize = bpp/8; + int line, i, h, k, w; +# define CMAPMAX 64 + Colormap cmaps[CMAPMAX]; + int ncmaps, cmap_max = CMAPMAX; + sraRegionPtr rect, disp, region8bpp; + XWindowAttributes attr; + static int last_map_count = 0, call_count = 0; + static double last_validate = 0.0; + int validate = 1; + + if (! cmap8to24 || !cmap8to24_fb) { + /* hmmm, why were we called? */ + return; + } + + call_count++; + + /* initialize color map list */ + ncmaps = 0; + for (i=0; i < cmap_max; i++) { + cmaps[i] = (Colormap) 0; + } + + /* create regions for finding overlap, etc. */ + disp = sraRgnCreateRect(0, 0, dpy_x, dpy_y); + rect = sraRgnCreateRect(x1, y1, x2, y2); + region8bpp = sraRgnCreate(); + + X_LOCK; + XFlush(dpy); /* make sure X server is up to date WRT input, etc */ + X_UNLOCK; + + 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; + } + if (last_map_count > 3*MAX_8BPP_WINDOWS/4) { + skip = 12; + } + if (call_count % skip != 0) { + validate = 0; +fprintf(stderr, " bpp8to24: No validate: %d -- %d\n", skip, last_map_count); + } else { +fprintf(stderr, " bpp8to24: yesvalidate: %d -- %d\n", skip, last_map_count); + } + } + last_map_count = 0; + + /* loop over the table of 8bpp windows: */ + for (i=0; i < MAX_8BPP_WINDOWS; i++) { + sraRegionPtr tmp_reg, tmp_reg2; + Window w = windows_8bpp[i].win; + Window c; + int x, y; + + if (wireframe_in_progress) { + break; /* skip updates during wireframe drag */ + } + + if (w == None) { + continue; + } + +if (db24 || 0) fprintf(stderr, "bpp8to24: 0x%lx ms=%d i=%d\n", w, windows_8bpp[i].map_state, i); + if (validate) { + /* + * this could be slow: validating 8bpp windows each + * time... + */ + last_validate = dnow(); + + 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; + continue; + } + X_UNLOCK; + if (attr.map_state != IsViewable) { + continue; + } + + X_LOCK; + xtranslate(w, window, 0, 0, &x, &y, &c, 1); + X_UNLOCK; + } else { + /* this would be faster: no call to X server: */ + if (windows_8bpp[i].map_state != IsViewable) { + continue; + } + x = windows_8bpp[i].x; + y = windows_8bpp[i].y; + attr.width = windows_8bpp[i].w; + attr.height = windows_8bpp[i].h; + attr.map_state = windows_8bpp[i].map_state; + attr.colormap = windows_8bpp[i].cmap; + + } + last_map_count++; + + /* tmp region for this 8bpp rectangle: */ + tmp_reg = sraRgnCreateRect(x, y, x + attr.width, + y + attr.height); + + /* clip to display screen: */ + sraRgnAnd(tmp_reg, disp); + + /* find overlap with mark region in rect: */ + sraRgnAnd(tmp_reg, rect); + if (sraRgnEmpty(tmp_reg)) { + /* skip if no overlap: */ + sraRgnDestroy(tmp_reg); + continue; + } + + /* 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) 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 clip the rest. */ +if (db24) 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; + + tmp_reg2 = sraRgnCreateRect(sx, sy, sx + sw, sy + sh); + sraRgnAnd(tmp_reg2, disp); + + /* subtrace it from the 8bpp window region */ + sraRgnSubtract(tmp_reg, tmp_reg2); + sraRgnDestroy(tmp_reg2); + } + + if (sraRgnEmpty(tmp_reg)) { + /* skip this 8bpp if completely clipped away: */ + sraRgnDestroy(tmp_reg); +if (db24) fprintf(stderr, "Empty tmp_reg\n"); + continue; + } + + /* otherwise, store any new colormaps: */ + if (ncmaps < cmap_max && attr.colormap != (Colormap) 0) { + int m, sawit = 0; + for (m=0; m < ncmaps; m++) { + if (cmaps[m] == attr.colormap) { + sawit = 1; + break; + } + } + if (! sawit) { + /* store only new ones: */ + cmaps[ncmaps++] = attr.colormap; + } + } + + /* now include this region with the full 8bpp region: */ + sraRgnOr(region8bpp, tmp_reg); + sraRgnDestroy(tmp_reg); + } + + /* copy from main_fb to cmap8to24_fb regardless of 8bpp windows: */ + src = main_fb + main_bytes_per_line * y1 + pixelsize * x1; + dst = cmap8to24_fb + main_bytes_per_line * y1 + pixelsize * x1; + h = y2 - y1; + w = x2 - x1; + + for (line = 0; line < h; line++) { + memcpy(dst, src, w * pixelsize); + src += main_bytes_per_line; + dst += main_bytes_per_line; + } + +if (db24) fprintf(stderr, "bpp8to24 w=%d h=%d m=%p c=%p r=%p \n", w, h, main_fb, cmap8to24_fb, rfb_fb); + + /* + * now go back and tranform and 8bpp regions to TrueColor in + * cmap8to24_fb. we have to guess the best colormap to use if + * there is more than one... + */ +#define NCOLOR 256 + if (! sraRgnEmpty(region8bpp) && ncmaps) { + sraRectangleIterator *iter; + sraRect rect; + int i, j, ncells; + int cmap_failed[CMAPMAX]; + static XColor color[CMAPMAX][NCOLOR]; + static unsigned int rgb[CMAPMAX][NCOLOR]; + XErrorHandler old_handler; + +#if 0 + /* not working properly for depth 24... */ + X_LOCK; + ncells = CellsOfScreen(ScreenOfDisplay(dpy, scr)); + X_UNLOCK; +#else + ncells = NCOLOR; +#endif + /* ncells should "always" be 256. */ + if (ncells > NCOLOR) { + ncells = NCOLOR; + } else if (ncells == 8) { + /* hmmm. see set_colormap() */ + ncells = NCOLOR; + } + + /* + * first, grab all of the associated colormaps from the + * X server. Hopefully just 1 or 2... + */ + for (j=0; j<ncmaps; j++) { + +if (db24) fprintf(stderr, "cmap %d\n", (int) cmaps[j]); + + /* 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, cmaps[j], color[j], ncells); + XSetErrorHandler(old_handler); + X_UNLOCK; + if (trapped_xerror) { + /* + * results below will be indefinite... + * need to exclude this one. + */ + trapped_xerror = 0; + cmap_failed[j] = 1; + continue; + } + trapped_xerror = 0; + cmap_failed[j] = 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; + + /* shift them over and or together for value */ + red = red << main_red_shift; + green = green << main_green_shift; + blue = blue << main_blue_shift; + + rgb[j][i] = red | green | blue; + } + } + + /* loop over the rectangles making up region8bpp */ + iter = sraRgnGetIterator(region8bpp); + while (sraRgnIteratorNext(iter, &rect)) { + double score, max_score = -1.0; + int n, m, best; + + if (rect.x1 > 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; + } + +if (db24 || 0) fprintf(stderr, "ncmaps: %d\n", ncmaps); + + /* + * try to pick the "best" colormap to use for + * this rectangle (often wrong... let them + * iconify or move the trouble windows, etc.) + */ + best = -1; + + for (m=0; m < MAX_8BPP_WINDOWS; m++) { + int mx1, my1, mx2, my2; + int k, failed = 0; + if (windows_8bpp[m].win == None) { + continue; + } + if (windows_8bpp[m].map_state != IsViewable) { + continue; + } + + /* see if XQueryColors failed: */ + for (k=0; k<ncmaps; k++) { + if (windows_8bpp[m].cmap == cmaps[k] + && cmap_failed[k]) { + failed = 1; + } + } + if (failed) { + continue; + } + + /* rectangle coords for this 8bpp win: */ + mx1 = windows_8bpp[m].x; + my1 = windows_8bpp[m].y; + mx2 = windows_8bpp[m].x + windows_8bpp[m].w; + my2 = windows_8bpp[m].y + windows_8bpp[m].h; + + /* use overlap as score: */ + score = rect_overlap(mx1, my1, mx2, my2, + rect.x1, rect.y1, rect.x2, rect.y2); + + if (score > max_score) { + max_score = score; + best = m; + } + +if (db24 || 0) fprintf(stderr, "cmap_score: 0x%x %.3f %.3f\n", (int) windows_8bpp[m].cmap, score, max_score); + + } + + if (best < 0) { + /* hmmm, use the first one then... */ + best = 0; + } else { + int ok = 0; + /* + * find the cmap corresponding to best window + * note we reset best from the windows_8bpp + * index to the cmaps[]. + */ + for (m=0; m < ncmaps; m++) { + if (cmaps[m] == windows_8bpp[best].cmap) { + ok = 1; + best = m; + } + } + if (! ok) { + best = 0; + } + } + + +if (db24) fprintf(stderr, "transform %d %d %d %d\n", rect.x1, rect.y1, rect.x2, rect.y2); + + /* now tranform the pixels in this rectangle: */ + n = main_bytes_per_line * rect.y1 + pixelsize * rect.x1; + + src = cmap8to24_fb + n; + h = rect.y2 - rect.y1; + w = rect.x2 - rect.x1; + + for (line = 0; line < h; line++) { + /* line by line ... */ + for (j = 0; j < w; j++) { + /* pixel by pixel... */ + unsigned int *ui; + unsigned int hi, idx; + + /* grab 32 bit value */ + ui = (unsigned int *) (src + pixelsize * j); + + /* extract top 8 bits (FIXME: masks?) */ + hi = (*ui) & 0xff000000; + + /* map to lookup index; rewrite pixel: */ + idx = hi >> 24; + *ui = hi | rgb[best][idx]; + } + src += main_bytes_per_line; + } + } + sraRgnReleaseIterator(iter); + } + + /* cleanup */ + sraRgnDestroy(disp); + sraRgnDestroy(rect); + sraRgnDestroy(region8bpp); +} + +void mark_8bpp(void) { + int i, cnt = 0; + + if (! cmap8to24 || !cmap8to24_fb) { + return; + } + + /* 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 (windows_8bpp[i].map_state != IsViewable) { + continue; + } + + x1 = windows_8bpp[i].x; + y1 = windows_8bpp[i].y; + w = windows_8bpp[i].w; + h = windows_8bpp[i].h; + + /* apply a fuzz f around each one... constrain to screen */ + x2 = x1 + w; + y2 = y1 + h; + x1 = nfix(x1 - f, dpy_x); + y1 = nfix(y1 - f, dpy_y); + x2 = nfix(x2 + f, dpy_x); + y2 = nfix(y2 + f, dpy_y); + +if (db24 || 0) 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); + } +} + |