You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2019 lines
44 KiB
2019 lines
44 KiB
/* -- 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<VW_CACHE_MAX; i++) {
|
|
vw_cache_win[i] = None;
|
|
}
|
|
}
|
|
|
|
j = -1;
|
|
k = -1;
|
|
for (i=0; i<VW_CACHE_MAX; i++) {
|
|
if (vw_cache_win[i] == win) {
|
|
j = i;
|
|
break;
|
|
} else if (vw_cache_win[i] == None) {
|
|
k = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (j >= 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<ncmaps; k++) {
|
|
if (windows_8bpp[n].cmap == cmaps[k]) {
|
|
cm = k;
|
|
if (cmap_failed[k]) {
|
|
failed = 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (windows_8bpp[n].depth == 8) { /* 24 won't have a cmap */
|
|
if (failed || cm == -1) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
clip = sraRgnCreateRgn(mark);
|
|
sraRgnAnd(clip, windows_8bpp[n].clip_region);
|
|
|
|
/* loop over the rectangles making up region */
|
|
iter = sraRgnGetIterator(clip);
|
|
while (sraRgnIteratorNext(iter, &rect)) {
|
|
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;
|
|
}
|
|
|
|
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<ncmaps; j++) {
|
|
if (! get_cmap(j, cmaps[j])) {
|
|
cmap_failed[j] = 1;
|
|
} else {
|
|
cmap_failed[j] = 0;
|
|
}
|
|
if (db24 > 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<count; j++) {
|
|
i = win[j];
|
|
if (did[j]) {
|
|
continue;
|
|
}
|
|
if (windows_8bpp[i].top == swin) {
|
|
do_8bpp_region(i, rect);
|
|
did[j] = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
for (j=0; j<count; j++) {
|
|
if (! did[j]) {
|
|
i = win[j];
|
|
do_8bpp_region(i, rect);
|
|
did[j] = 1;
|
|
}
|
|
}
|
|
sraRgnDestroy(rect);
|
|
}
|
|
}
|
|
if (0) fprintf(stderr, "done time: %.4f\n", dnow() - d0);
|
|
|
|
if (db24 > 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 */
|
|
|