/* -- userinput.c -- */ #include "x11vnc.h" #include "xwrappers.h" #include "xdamage.h" #include "xrecord.h" #include "xinerama.h" #include "win_utils.h" #include "user.h" #include "scan.h" #include "cleanup.h" #include "pointer.h" #include "rates.h" #include "keyboard.h" #include "solid.h" #include "xrandr.h" #include "8to24.h" #include "unixpw.h" #include "macosx.h" #include "macosxCGS.h" #include "cursor.h" #include "screen.h" /* * user input handling heuristics */ int defer_update_nofb = 6; /* defer a shorter time under -nofb */ int last_scroll_type = SCR_NONE; int get_wm_frame_pos(int *px, int *py, int *x, int *y, int *w, int *h, Window *frame, Window *win); void parse_scroll_copyrect(void); void parse_fixscreen(void); void parse_wireframe(void); void set_wirecopyrect_mode(char *str); void set_scrollcopyrect_mode(char *str); void initialize_scroll_keys(void); void initialize_scroll_matches(void); void initialize_scroll_term(void); void initialize_max_keyrepeat(void); int direct_fb_copy(int x1, int y1, int x2, int y2, int mark); void fb_push(void); int fb_push_wait(double max_wait, int flags); void eat_viewonly_input(int max_eat, int keep); void mark_for_xdamage(int x, int y, int w, int h); void mark_region_for_xdamage(sraRegionPtr region); void set_xdamage_mark(int x, int y, int w, int h); int near_wm_edge(int x, int y, int w, int h, int px, int py); int near_scrollbar_edge(int x, int y, int w, int h, int px, int py); void check_fixscreen(void); int check_xrecord(void); int check_wireframe(void); int fb_update_sent(int *count); int check_user_input(double dt, double dtr, int tile_diffs, int *cnt); void do_copyregion(sraRegionPtr region, int dx, int dy, int mode); int check_ncache(int reset, int mode); int find_rect(int idx, int x, int y, int w, int h); static void get_client_regions(int *req, int *mod, int *cpy, int *num) ; static void parse_scroll_copyrect_str(char *scr); static void parse_wireframe_str(char *wf); static void destroy_str_list(char **list); static void draw_box(int x, int y, int w, int h, int restore); static int do_bdpush(Window wm_win, int x0, int y0, int w0, int h0, int bdx, int bdy, int bdskinny); static int set_ypad(void); static void scale_mark(int x1, int y1, int x2, int y2, int mark); static int push_scr_ev(double *age, int type, int bdpush, int bdx, int bdy, int bdskinny, int first_push); static int crfix(int x, int dx, int Lx); static int scrollability(Window win, int set); static int eat_pointer(int max_ptr_eat, int keep); static void set_bdpush(int type, double *last_bdpush, int *pushit); static int repeat_check(double last_key_scroll); static int check_xrecord_keys(void); static int check_xrecord_mouse(void); static int try_copyrect(Window frame, int x, int y, int w, int h, int dx, int dy, int *obscured, sraRegionPtr extra_clip, double max_wait, int *nbatch); static int wireframe_mod_state(); static void check_user_input2(double dt); static void check_user_input3(double dt, double dtr, int tile_diffs); static void check_user_input4(double dt, double dtr, int tile_diffs); winattr_t *cache_list; int lookup_win_index(Window); /* * For -wireframe: find the direct child of rootwin that has the * pointer, assume that is the WM frame that contains the application * (i.e. wm reparents the app toplevel) return frame position and size * if successful. */ int get_wm_frame_pos(int *px, int *py, int *x, int *y, int *w, int *h, Window *frame, Window *win) { Window r, c; XWindowAttributes attr; Bool ret; int rootx, rooty, wx, wy; unsigned int mask; #ifdef MACOSX if (macosx_console) { return macosx_get_wm_frame_pos(px, py, x, y, w, h, frame, win); } #endif RAWFB_RET(0) #if NO_X11 return 0; #else ret = XQueryPointer_wr(dpy, rootwin, &r, &c, &rootx, &rooty, &wx, &wy, &mask); *frame = c; /* current pointer position is returned too */ *px = rootx; *py = rooty; if (!ret || ! c || c == rootwin) { /* no immediate child */ return 0; } /* child window position and size */ if (! valid_window(c, &attr, 1)) { return 0; } *x = attr.x; *y = attr.y; *w = attr.width; *h = attr.height; if (win != NULL) { *win = descend_pointer(5, c, NULL, 0); } return 1; #endif /* NO_X11 */ } static int scrollcopyrect_top, scrollcopyrect_bot; static int scrollcopyrect_left, scrollcopyrect_right; static double scr_key_time, scr_key_persist; static double scr_mouse_time, scr_mouse_persist, scr_mouse_maxtime; static double scr_mouse_pointer_delay; static double scr_key_bdpush_time, scr_mouse_bdpush_time; static void parse_scroll_copyrect_str(char *scr) { char *p, *str; int i; char *part[10]; for (i=0; i<10; i++) { part[i] = NULL; } if (scr == NULL || *scr == '\0') { return; } str = strdup(scr); p = strtok(str, ","); i = 0; while (p) { part[i++] = strdup(p); p = strtok(NULL, ","); } free(str); /* * Top, Bottom, Left, Right tolerances for scrollbar locations. */ if ((str = part[0]) != NULL) { int t, b, l, r; if (sscanf(str, "%d+%d+%d+%d", &t, &b, &l, &r) == 4) { scrollcopyrect_top = t; scrollcopyrect_bot = b; scrollcopyrect_left = l; scrollcopyrect_right = r; } free(str); } /* key scrolling timing heuristics. */ if ((str = part[1]) != NULL) { double t1, t2, t3; if (sscanf(str, "%lf+%lf+%lf", &t1, &t2, &t3) == 3) { scr_key_time = t1; scr_key_persist = t2; scr_key_bdpush_time = t3; } free(str); } /* mouse scrolling timing heuristics. */ if ((str = part[2]) != NULL) { double t1, t2, t3, t4, t5; if (sscanf(str, "%lf+%lf+%lf+%lf+%lf", &t1, &t2, &t3, &t4, &t5) == 5) { scr_mouse_time = t1; scr_mouse_persist = t2; scr_mouse_bdpush_time = t3; scr_mouse_pointer_delay = t4; scr_mouse_maxtime = t5; } free(str); } } void parse_scroll_copyrect(void) { parse_scroll_copyrect_str(SCROLL_COPYRECT_PARMS); if (! scroll_copyrect_str) { scroll_copyrect_str = strdup(SCROLL_COPYRECT_PARMS); } parse_scroll_copyrect_str(scroll_copyrect_str); } void parse_fixscreen(void) { char *str, *p; screen_fixup_V = 0.0; screen_fixup_C = 0.0; screen_fixup_X = 0.0; screen_fixup_8 = 0.0; if (! screen_fixup_str) { return; } str = strdup(screen_fixup_str); p = strtok(str, ","); while (p) { double t; if (*p == 'V' && sscanf(p, "V=%lf", &t) == 1) { screen_fixup_V = t; } else if (*p == 'C' && sscanf(p, "C=%lf", &t) == 1) { screen_fixup_C = t; } else if (*p == 'X' && sscanf(p, "X=%lf", &t) == 1) { screen_fixup_X = t; } else if (*p == 'X' && sscanf(p, "8=%lf", &t) == 1) { screen_fixup_8 = t; } p = strtok(NULL, ","); } free(str); if (screen_fixup_V < 0.0) screen_fixup_V = 0.0; if (screen_fixup_C < 0.0) screen_fixup_C = 0.0; if (screen_fixup_X < 0.0) screen_fixup_X = 0.0; if (screen_fixup_8 < 0.0) screen_fixup_8 = 0.0; } /* WIREFRAME_PARMS "0xff,2,0,30+6+6+6,Alt,0.05+0.3+2.0,8" shade,linewidth,percent,T+B+L+R,mods,t1+t2+t3+t4 */ #define LW_MAX 8 static unsigned long wireframe_shade = 0xff; static int wireframe_lw; static double wireframe_frac; static int wireframe_top, wireframe_bot, wireframe_left, wireframe_right; static double wireframe_t1, wireframe_t2, wireframe_t3, wireframe_t4; static char *wireframe_mods = NULL; /* * Parse the gory -wireframe string for parameters. */ static void parse_wireframe_str(char *wf) { char *p, *str; int i; char *part[10]; for (i=0; i<10; i++) { part[i] = NULL; } if (wf == NULL || *wf == '\0') { return; } str = strdup(wf); /* leading ",", make it start with ignorable string "z" */ if (*str == ',') { char *tmp = (char *) malloc(strlen(str)+2); strcpy(tmp, "z"); strcat(tmp, str); free(str); str = tmp; } p = strtok(str, ","); i = 0; while (p) { part[i++] = strdup(p); p = strtok(NULL, ","); } free(str); /* Wireframe shade, color, RGB: */ if ((str = part[0]) != NULL) { unsigned long n; int r, g, b, ok = 0; XColor cdef; Colormap cmap; if (dpy && (bpp == 32 || bpp == 16)) { #if !NO_X11 cmap = DefaultColormap (dpy, scr); if (XParseColor(dpy, cmap, str, &cdef) && XAllocColor(dpy, cmap, &cdef)) { r = cdef.red >> 8; g = cdef.green >> 8; b = cdef.blue >> 8; if (r == 0 && g == 0) { g = 1; /* need to be > 255 */ } n = 0; n |= (r << main_red_shift); n |= (g << main_green_shift); n |= (b << main_blue_shift); wireframe_shade = n; ok = 1; } #endif } if (ok) { ; } else if (sscanf(str, "0x%lx", &n) == 1) { wireframe_shade = n; } else if (sscanf(str, "%lu", &n) == 1) { wireframe_shade = n; } else if (sscanf(str, "%lx", &n) == 1) { wireframe_shade = n; } free(str); } /* linewidth: # of pixels wide for the wireframe lines */ if ((str = part[1]) != NULL) { int n; if (sscanf(str, "%d", &n) == 1) { wireframe_lw = n; if (wireframe_lw < 1) { wireframe_lw = 1; } if (wireframe_lw > LW_MAX) { wireframe_lw = LW_MAX; } } free(str); } /* percentage cutoff for opaque move/resize (like WM's) */ if ((str = part[2]) != NULL) { if (*str == '\0') { ; } else if (strchr(str, '.')) { wireframe_frac = atof(str); } else { wireframe_frac = ((double) atoi(str))/100.0; } free(str); } /* * Top, Bottom, Left, Right tolerances to guess the wm frame is * being grabbed (Top is traditionally bigger, i.e. titlebar): */ if ((str = part[3]) != NULL) { int t, b, l, r; if (sscanf(str, "%d+%d+%d+%d", &t, &b, &l, &r) == 4) { wireframe_top = t; wireframe_bot = b; wireframe_left = l; wireframe_right = r; } free(str); } /* * wireframe in interior with Modifier down. * 0 => no mods * 1 => all mods * Shift,Alt,Control,Meta,Super,Hyper */ if (wireframe_mods) { free(wireframe_mods); } wireframe_mods = NULL; if ((str = part[4]) != NULL) { if (*str == '0' || !strcmp(str, "none")) { ; } else if (*str == '1' || !strcmp(str, "all")) { wireframe_mods = strdup("all"); } else if (!strcmp(str, "Alt") || !strcmp(str, "Shift") || !strcmp(str, "Control") || !strcmp(str, "Meta") || !strcmp(str, "Super") || !strcmp(str, "Hyper")) { wireframe_mods = strdup(str); } } /* check_wireframe() timing heuristics. */ if ((str = part[5]) != NULL) { double t1, t2, t3, t4; if (sscanf(str, "%lf+%lf+%lf+%lf", &t1, &t2, &t3, &t4) == 4) { wireframe_t1 = t1; wireframe_t2 = t2; wireframe_t3 = t3; wireframe_t4 = t4; } free(str); } } /* * First parse the defaults and apply any user supplied ones (may be a subset) */ void parse_wireframe(void) { parse_wireframe_str(WIREFRAME_PARMS); if (! wireframe_str) { wireframe_str = strdup(WIREFRAME_PARMS); } parse_wireframe_str(wireframe_str); } /* * Set wireframe_copyrect based on desired mode. */ void set_wirecopyrect_mode(char *str) { char *orig = wireframe_copyrect; if (str == NULL || *str == '\0') { wireframe_copyrect = strdup(wireframe_copyrect_default); } else if (!strcmp(str, "always") || !strcmp(str, "all")) { wireframe_copyrect = strdup("always"); } else if (!strcmp(str, "top")) { wireframe_copyrect = strdup("top"); } else if (!strcmp(str, "never") || !strcmp(str, "none")) { wireframe_copyrect = strdup("never"); } else { if (! wireframe_copyrect) { wireframe_copyrect = strdup(wireframe_copyrect_default); } else { orig = NULL; } rfbLog("unknown -wirecopyrect mode: %s, using: %s\n", str, wireframe_copyrect); } if (orig) { free(orig); } } /* * Set scroll_copyrect based on desired mode. */ void set_scrollcopyrect_mode(char *str) { char *orig = scroll_copyrect; if (str == NULL || *str == '\0') { scroll_copyrect = strdup(scroll_copyrect_default); } else if (!strcmp(str, "always") || !strcmp(str, "all") || !strcmp(str, "both")) { scroll_copyrect = strdup("always"); } else if (!strcmp(str, "keys") || !strcmp(str, "keyboard")) { scroll_copyrect = strdup("keys"); } else if (!strcmp(str, "mouse") || !strcmp(str, "pointer")) { scroll_copyrect = strdup("mouse"); } else if (!strcmp(str, "never") || !strcmp(str, "none")) { scroll_copyrect = strdup("never"); } else { if (! scroll_copyrect) { scroll_copyrect = strdup(scroll_copyrect_default); } else { orig = NULL; } rfbLog("unknown -scrollcopyrect mode: %s, using: %s\n", str, scroll_copyrect); } if (orig) { free(orig); } } void initialize_scroll_keys(void) { char *str, *p; int i, nkeys = 0, saw_builtin = 0; int ks_max = 2 * 0xFFFF; if (scroll_key_list) { free(scroll_key_list); scroll_key_list = NULL; } if (! scroll_key_list_str || *scroll_key_list_str == '\0') { return; } if (strstr(scroll_key_list_str, "builtin")) { int k; /* add in number of keysyms builtin gives */ for (k=1; k 40) { max_keyrepeat_hi = 40; } } typedef struct saveline { int x0, y0, x1, y1; int shift; int vert; int saved; char *data; } saveline_t; /* * Draw the wireframe box onto the framebuffer. Saves the real * framebuffer data to some storage lines. Restores previous lines. * use restore = 1 to clean up (done with animation). * This works with -scale. */ static void draw_box(int x, int y, int w, int h, int restore) { int x0, y0, x1, y1, i, pixelsize = bpp/8; char *dst, *src, *use_fb; static saveline_t *save[4]; static int first = 1, len = 0; int max = dpy_x > dpy_y ? dpy_x : dpy_y; int use_Bpl, lw = wireframe_lw; unsigned long shade = wireframe_shade; int color = 0; unsigned short us = 0; unsigned long ul = 0; if (clipshift) { x -= coff_x; y -= coff_y; } /* handle -8to24 mode: use 2nd fb only */ use_fb = main_fb; use_Bpl = main_bytes_per_line; if (cmap8to24 && cmap8to24_fb) { use_fb = cmap8to24_fb; pixelsize = 4; if (depth == 8) { use_Bpl *= 4; } } if (max > len) { /* create/resize storage lines: */ for (i=0; i<4; i++) { len = max; if (! first && save[i]) { if (save[i]->data) { free(save[i]->data); save[i]->data = NULL; } free(save[i]); } save[i] = (saveline_t *) malloc(sizeof(saveline_t)); save[i]->saved = 0; save[i]->data = (char *) malloc( (LW_MAX+1)*len*4 ); /* * Four types of lines: * 0) top horizontal * 1) bottom horizontal * 2) left vertical * 3) right vertical * * shift means shifted by width or height. */ if (i == 0) { save[i]->vert = 0; save[i]->shift = 0; } else if (i == 1) { save[i]->vert = 0; save[i]->shift = 1; } else if (i == 2) { save[i]->vert = 1; save[i]->shift = 0; } else if (i == 3) { save[i]->vert = 1; save[i]->shift = 1; } } } first = 0; /* * restore any saved lines. see below for algorithm and * how x0, etc. are used. we just reverse those steps. */ for (i=0; i<4; i++) { int s = save[i]->shift; int yu, y_min = -1, y_max = -1; int y_start, y_stop, y_step; if (! save[i]->saved) { continue; } x0 = save[i]->x0; y0 = save[i]->y0; x1 = save[i]->x1; y1 = save[i]->y1; if (save[i]->vert) { y_start = y0+lw; y_stop = y1-lw; y_step = lw*pixelsize; } else { y_start = y0 - s*lw; y_stop = y_start + lw; y_step = max*pixelsize; } for (yu = y_start; yu < y_stop; yu++) { if (x0 == x1) { continue; } if (yu < 0 || yu >= dpy_y) { continue; } if (y_min < 0 || yu < y_min) { y_min = yu; } if (y_max < 0 || yu > y_max) { y_max = yu; } src = save[i]->data + (yu-y_start)*y_step; dst = use_fb + yu*use_Bpl + x0*pixelsize; memcpy(dst, src, (x1-x0)*pixelsize); } if (y_min >= 0) { mark_rect_as_modified(x0, y_min, x1, y_max+1, 0); } save[i]->saved = 0; } if (restore) { return; } if (0) fprintf(stderr, " DrawBox: %dx%d+%d+%d\n", w, h, x, y); /* * work out shade/color for the wireframe line, could be a color * for 16bpp or 24bpp. */ if (shade > 255) { if (pixelsize == 2) { us = (unsigned short) (shade & 0xffff); color = 1; } else if (pixelsize == 4) { ul = (unsigned long) shade; color = 1; } else { shade = shade % 256; } } for (i=0; i<4; i++) { int s = save[i]->shift; int yu, y_min = -1, y_max = -1; int yblack = -1, xblack1 = -1, xblack2 = -1; int y_start, y_stop, y_step; if (save[i]->vert) { /* * make the narrow x's be on the screen, let * the y's hang off (not drawn). */ save[i]->x0 = x0 = nfix(x + s*w - s*lw, dpy_x); save[i]->y0 = y0 = y; save[i]->x1 = x1 = nfix(x + s*w - s*lw + lw, dpy_x); save[i]->y1 = y1 = y + h; /* * start and stop a linewidth away from true edge, * to avoid interfering with horizontal lines. */ y_start = y0+lw; y_stop = y1-lw; y_step = lw*pixelsize; /* draw a black pixel for the border if lw > 1 */ if (s) { xblack1 = x1-1; } else { xblack1 = x0; } } else { /* * make the wide x's be on the screen, let the y's * hang off (not drawn). */ save[i]->x0 = x0 = nfix(x, dpy_x); save[i]->y0 = y0 = y + s*h; save[i]->x1 = x1 = nfix(x + w, dpy_x); save[i]->y1 = y1 = y0 + lw; y_start = y0 - s*lw; y_stop = y_start + lw; y_step = max*pixelsize; /* draw a black pixels for the border if lw > 1 */ if (s) { yblack = y_stop - 1; } else { yblack = y_start; } xblack1 = x0; xblack2 = x1-1; } /* now loop over the allowed y's for either case */ for (yu = y_start; yu < y_stop; yu++) { if (x0 == x1) { continue; } if (yu < 0 || yu >= dpy_y) { continue; } /* record min and max y's for marking rectangle: */ if (y_min < 0 || yu < y_min) { y_min = yu; } if (y_max < 0 || yu > y_max) { y_max = yu; } /* save fb data for this line: */ save[i]->saved = 1; src = use_fb + yu*use_Bpl + x0*pixelsize; dst = save[i]->data + (yu-y_start)*y_step; memcpy(dst, src, (x1-x0)*pixelsize); /* apply the shade/color to make the wireframe line: */ if (! color) { memset(src, shade, (x1-x0)*pixelsize); } else { char *csrc = src; unsigned short *usp; unsigned long *ulp; int k; for (k=0; k < x1 - x0; k++) { if (pixelsize == 4) { ulp = (unsigned long *)csrc; *ulp = ul; } else if (pixelsize == 2) { usp = (unsigned short *)csrc; *usp = us; } csrc += pixelsize; } } /* apply black border for lw >= 2 */ if (lw > 1) { if (yu == yblack) { memset(src, 0, (x1-x0)*pixelsize); } if (xblack1 >= 0) { src = src + (xblack1 - x0)*pixelsize; memset(src, 0, pixelsize); } if (xblack2 >= 0) { src = src + (xblack2 - x0)*pixelsize; memset(src, 0, pixelsize); } } } /* mark it for sending: */ if (save[i]->saved) { mark_rect_as_modified(x0, y_min, x1, y_max+1, 0); } } } int direct_fb_copy(int x1, int y1, int x2, int y2, int mark) { char *src, *dst; int y, pixelsize = bpp/8; int xmin = -1, xmax = -1, ymin = -1, ymax = -1; int do_cmp = 2; double tm; int db = 0; if (db) dtime0(&tm); x1 = nfix(x1, dpy_x); y1 = nfix(y1, dpy_y); x2 = nfix(x2, dpy_x+1); y2 = nfix(y2, dpy_y+1); if (x1 == x2) { return 1; } if (y1 == y2) { return 1; } X_LOCK; for (y = y1; y < y2; y++) { XRANDR_SET_TRAP_RET(0, "direct_fb_copy-set"); copy_image(scanline, x1, y, x2 - x1, 1); XRANDR_CHK_TRAP_RET(0, "direct_fb_copy-chk"); src = scanline->data; dst = main_fb + y * main_bytes_per_line + x1 * pixelsize; if (do_cmp == 0 || !mark) { memcpy(dst, src, (x2 - x1)*pixelsize); } else if (do_cmp == 1) { if (memcmp(dst, src, (x2 - x1)*pixelsize)) { if (ymin == -1 || y < ymin) { ymin = y; } if (ymax == -1 || y > ymax) { ymax = y; } memcpy(dst, src, (x2 - x1)*pixelsize); } } else if (do_cmp == 2) { int n, shift, xlo, xhi, k, block = 32; char *dst2, *src2; for (k=0; k*block < (x2 - x1); k++) { shift = k*block; xlo = x1 + shift; xhi = xlo + block; if (xhi > x2) { xhi = x2; } n = xhi - xlo; if (n < 1) { continue; } src2 = src + shift*pixelsize; dst2 = dst + shift*pixelsize; if (memcmp(dst2, src2, n*pixelsize)) { if (ymin == -1 || y < ymin) { ymin = y; } if (ymax == -1 || y > ymax) { ymax = y; } if (xmin == -1 || xlo < xmin) { xmin = xlo; } if (xmax == -1 || xhi > xmax) { xmax = xhi; } memcpy(dst2, src2, n*pixelsize); } } } } X_UNLOCK; if (do_cmp == 0) { xmin = x1; ymin = y1; xmax = x2; ymax = y2; } else if (do_cmp == 1) { xmin = x1; xmax = x2; } if (xmin < 0 || ymin < 0 || xmax < 0 || xmin < 0) { /* no diffs */ return 1; } if (xmax < x2) { xmax++; } if (ymax < y2) { ymax++; } if (mark) { mark_rect_as_modified(xmin, ymin, xmax, ymax, 0); } if (db) { fprintf(stderr, "direct_fb_copy: %dx%d+%d+%d - %d %.4f\n", x2 - x1, y2 - y1, x1, y1, mark, dtime(&tm)); } return 1; } static int do_bdpush(Window wm_win, int x0, int y0, int w0, int h0, int bdx, int bdy, int bdskinny) { XWindowAttributes attr; sraRectangleIterator *iter; sraRect rect; sraRegionPtr frame, whole, tmpregion; int tx1, ty1, tx2, ty2; static Window last_wm_win = None; static int last_x, last_y, last_w, last_h; int do_fb_push = 0; int db = debug_scroll; if (wm_win == last_wm_win) { attr.x = last_x; attr.y = last_y; attr.width = last_w; attr.height = last_h; } else { if (!valid_window(wm_win, &attr, 1)) { return do_fb_push; } last_wm_win = wm_win; last_x = attr.x; last_y = attr.y; last_w = attr.width; last_h = attr.height; } if (db > 1) fprintf(stderr, "BDP %d %d %d %d %d %d %d %d %d %d %d\n", x0, y0, w0, h0, bdx, bdy, bdskinny, last_x, last_y, last_w, last_h); /* wm frame: */ tx1 = attr.x; ty1 = attr.y; tx2 = attr.x + attr.width; ty2 = attr.y + attr.height; whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y); if (clipshift) { sraRgnOffset(whole, coff_x, coff_y); } if (subwin) { sraRgnOffset(whole, off_x, off_y); } frame = sraRgnCreateRect(tx1, ty1, tx2, ty2); sraRgnAnd(frame, whole); /* scrolling window: */ tmpregion = sraRgnCreateRect(x0, y0, x0 + w0, y0 + h0); sraRgnAnd(tmpregion, whole); sraRgnSubtract(frame, tmpregion); sraRgnDestroy(tmpregion); if (!sraRgnEmpty(frame)) { double dt = 0.0, dm; dtime0(&dm); iter = sraRgnGetIterator(frame); while (sraRgnIteratorNext(iter, &rect)) { tx1 = rect.x1; ty1 = rect.y1; tx2 = rect.x2; ty2 = rect.y2; if (bdskinny > 0) { int ok = 0; if (nabs(ty2-ty1) <= bdskinny) { ok = 1; } if (nabs(tx2-tx1) <= bdskinny) { ok = 1; } if (! ok) { continue; } } if (bdx >= 0) { if (bdx < tx1 || tx2 <= bdx) { continue; } } if (bdy >= 0) { if (bdy < ty1 || ty2 <= bdy) { continue; } } if (clipshift) { tx1 -= coff_x; ty1 -= coff_y; tx2 -= coff_x; ty2 -= coff_y; } if (subwin) { tx1 -= off_x; ty1 -= off_y; tx2 -= off_x; ty2 -= off_y; } direct_fb_copy(tx1, ty1, tx2, ty2, 1); do_fb_push++; dt += dtime(&dm); if (db > 1) fprintf(stderr, " BDP(%d,%d-%d,%d) dt: %.4f\n", tx1, ty1, tx2, ty2, dt); } sraRgnReleaseIterator(iter); } sraRgnDestroy(whole); sraRgnDestroy(frame); return do_fb_push; } static int set_ypad(void) { int ev, ev_tot = scr_ev_cnt; static Window last_win = None; static double last_time = 0.0; static int y_accum = 0, last_sign = 0; double now, cut = 0.1; int dy_sum = 0, ys = 0, sign; int font_size = 15; int win_y, scr_y, loc_cut = 4*font_size, y_cut = 10*font_size; if (!xrecord_set_by_keys || !xrecord_name_info) { return 0; } if (xrecord_name_info[0] == '\0') { return 0; } if (! ev_tot) { return 0; } if (xrecord_keysym == NoSymbol) { return 0; } if (!xrecord_scroll_keysym(xrecord_keysym)) { return 0; } if (!scroll_term) { return 0; } if (!match_str_list(xrecord_name_info, scroll_term)) { return 0; } for (ev=0; ev < ev_tot; ev++) { dy_sum += nabs(scr_ev[ev].dy); if (scr_ev[ev].dy < 0) { ys--; } else if (scr_ev[ev].dy > 0) { ys++; } else { ys = 0; break; } if (scr_ev[ev].win != scr_ev[0].win) { ys = 0; break; } if (scr_ev[ev].dx != 0) { ys = 0; break; } } if (ys != ev_tot && ys != -ev_tot) { return 0; } if (ys < 0) { sign = -1; } else { sign = 1; } if (sign > 0) { /* * this case is not as useful as scrolling near the * bottom of a terminal. But there are problems for it too. */ return 0; } win_y = scr_ev[0].win_y + scr_ev[0].win_h; scr_y = scr_ev[0].y + scr_ev[0].h; if (nabs(scr_y - win_y) > loc_cut) { /* require it to be near the bottom. */ return 0; } now = dnow(); if (now < last_time + cut) { int ok = 1; if (last_win && scr_ev[0].win != last_win) { ok = 0; } if (last_sign && sign != last_sign) { ok = 0; } if (! ok) { last_win = None; last_sign = 0; y_accum = 0; last_time = 0.0; return 0; } } else { last_win = None; last_sign = 0; last_time = 0.0; y_accum = 0; } y_accum += sign * dy_sum; if (4 * nabs(y_accum) > scr_ev[0].h && y_cut) { ; /* TBD */ } last_sign = sign; last_win = scr_ev[0].win; last_time = now; return y_accum; } static void scale_mark(int x1, int y1, int x2, int y2, int mark) { int s = 2; x1 = nfix(x1 - s, dpy_x); y1 = nfix(y1 - s, dpy_y); x2 = nfix(x2 + s, dpy_x+1); y2 = nfix(y2 + s, dpy_y+1); scale_and_mark_rect(x1, y1, x2, y2, mark); } #define PUSH_TEST(n) \ if (n) { \ double dt = 0.0, tm; dtime0(&tm); \ fprintf(stderr, "PUSH---\n"); \ while (dt < 2.0) { rfbPE(50000); dt += dtime(&tm); } \ fprintf(stderr, "---PUSH\n"); \ } int batch_dxs[], batch_dys[]; sraRegionPtr batch_reg[]; void batch_copyregion(sraRegionPtr* region, int *dx, int *dy, int ncr, double delay); static int push_scr_ev(double *age, int type, int bdpush, int bdx, int bdy, int bdskinny, int first_push) { Window frame, win, win0; int x, y, w, h, wx, wy, ww, wh, dx, dy; int x0, y0, w0, h0; int nx, ny, nw, nh; int dret = 1, do_fb_push = 0, obscured; int ev, ev_tot = scr_ev_cnt; double tm, dt, st, waittime = 0.125; double max_age = *age; int db = debug_scroll, rrate = get_read_rate(); sraRegionPtr backfill, whole, tmpregion, tmpregion2; int link, latency, netrate; int ypad = 0; double last_scroll_event_save = last_scroll_event; int fast_push = 0, rc; /* we return the oldest one. */ *age = 0.0; if (ev_tot == 0) { return dret; } link = link_rate(&latency, &netrate); if (link == LR_DIALUP) { waittime *= 5; } else if (link == LR_BROADBAND) { waittime *= 3; } else if (latency > 80 || netrate < 40) { waittime *= 3; } backfill = sraRgnCreate(); whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y); if (clipshift) { sraRgnOffset(whole, coff_x, coff_y); } if (subwin) { sraRgnOffset(whole, off_x, off_y); } win0 = scr_ev[0].win; x0 = scr_ev[0].win_x; y0 = scr_ev[0].win_y; w0 = scr_ev[0].win_w; h0 = scr_ev[0].win_h; ypad = set_ypad(); if (db) fprintf(stderr, "ypad: %d dy[0]: %d ev_tot: %d\n", ypad, scr_ev[0].dy, ev_tot); for (ev=0; ev < ev_tot; ev++) { double ag; x = scr_ev[ev].x; y = scr_ev[ev].y; w = scr_ev[ev].w; h = scr_ev[ev].h; dx = scr_ev[ev].dx; dy = scr_ev[ev].dy; win = scr_ev[ev].win; wx = scr_ev[ev].win_x; wy = scr_ev[ev].win_y; ww = scr_ev[ev].win_w; wh = scr_ev[ev].win_h; nx = scr_ev[ev].new_x; ny = scr_ev[ev].new_y; nw = scr_ev[ev].new_w; nh = scr_ev[ev].new_h; st = scr_ev[ev].t; ag = (dnow() - servertime_diff) - st; if (ag > *age) { *age = ag; } if (dabs(ag) > max_age) { if (db) fprintf(stderr, "push_scr_ev: TOO OLD: %.4f :: (%.4f - %.4f) " "- %.4f \n", ag, dnow(), servertime_diff, st); dret = 0; break; } else { if (db) fprintf(stderr, "push_scr_ev: AGE: %.4f\n", ag); } if (win != win0) { if (db) fprintf(stderr, "push_scr_ev: DIFF WIN: 0x%lx != 0x%lx\n", win, win0); dret = 0; break; } if (wx != x0 || wy != y0) { if (db) fprintf(stderr, "push_scr_ev: WIN SHIFT: %d %d, %d %d", wx, x0, wy, y0); dret = 0; break; } if (ww != w0 || wh != h0) { if (db) fprintf(stderr, "push_scr_ev: WIN RESIZE: %d %d, %d %d", ww, w0, wh, h0); dret = 0; break; } if (w < 1 || h < 1 || ww < 1 || wh < 1) { if (db) fprintf(stderr, "push_scr_ev: NEGATIVE h/w: %d %d %d %d\n", w, h, ww, wh); dret = 0; break; } if (db > 1) fprintf(stderr, "push_scr_ev: got: %d x: %4d y: %3d" " w: %4d h: %3d dx: %d dy: %d %dx%d+%d+%d win: 0x%lx\n", ev, x, y, w, h, dx, dy, w, h, x, y, win); if (db > 1) fprintf(stderr, "------------ got: %d x: %4d y: %3d" " w: %4d h: %3d %dx%d+%d+%d\n", ev, wx, wy, ww, wh, ww, wh, wx, wy); if (db > 1) fprintf(stderr, "------------ got: %d x: %4d y: %3d" " w: %4d h: %3d %dx%d+%d+%d\n", ev, nx, ny, nw, nh, nw, nh, nx, ny); frame = None; if (xrecord_wm_window) { frame = xrecord_wm_window; } if (! frame) { X_LOCK; frame = query_pointer(rootwin); X_UNLOCK; } if (! frame) { frame = win; } dtime0(&tm); tmpregion = sraRgnCreateRect(0, 0, dpy_x, dpy_y); if (clipshift) { sraRgnOffset(tmpregion, coff_x, coff_y); } if (subwin) { sraRgnOffset(tmpregion, off_x, off_y); } tmpregion2 = sraRgnCreateRect(wx, wy, wx+ww, wy+wh); sraRgnAnd(tmpregion2, whole); sraRgnSubtract(tmpregion, tmpregion2); sraRgnDestroy(tmpregion2); /* do the wm frame just incase the above is bogus too. */ if (frame && frame != win) { int k, gotk = -1; for (k = stack_list_num - 1; k >= 0; k--) { if (stack_list[k].win == frame && stack_list[k].fetched && stack_list[k].valid && stack_list[k].map_state == IsViewable) { gotk = k; break; } } if (gotk != -1) { int tx1, ty1, tx2, ty2; tx1 = stack_list[gotk].x; ty1 = stack_list[gotk].y; tx2 = tx1 + stack_list[gotk].width; ty2 = ty1 + stack_list[gotk].height; tmpregion2 = sraRgnCreateRect(tx1,ty1,tx2,ty2); sraRgnAnd(tmpregion2, whole); sraRgnSubtract(tmpregion, tmpregion2); sraRgnDestroy(tmpregion2); } } /* * XXX Need to also clip: * children of win * siblings of win higher in stacking order. * ignore for now... probably will make some apps * act very strangely. */ if (ypad) { if (ypad < 0) { if (h > -ypad) { h += ypad; } else { ypad = 0; } } else { if (h > ypad) { y += ypad; } else { ypad = 0; } } } if (fast_push) { int k, nbatch = 0; double delay, d1 = 0.1, d2 = 0.02; rc = try_copyrect(frame, x, y, w, h, dx, dy, &obscured, tmpregion, waittime, &nbatch); if (first_push) { delay = d1; } else { delay = d2; } batch_copyregion(batch_reg, batch_dxs, batch_dys, nbatch, delay); for (k=0; k < nbatch; k++) { sraRgnDestroy(batch_reg[k]); } fb_push(); } else { rc = try_copyrect(frame, x, y, w, h, dx, dy, &obscured, tmpregion, waittime, NULL); if (rc) { last_scroll_type = type; dtime0(&last_scroll_event); do_fb_push++; urgent_update = 1; sraRgnDestroy(tmpregion); PUSH_TEST(0); } } if (! rc) { dret = 0; sraRgnDestroy(tmpregion); break; } dt = dtime(&tm); if (0) fprintf(stderr, " try_copyrect dt: %.4f\n", dt); if (ev > 0) { sraRgnOffset(backfill, dx, dy); sraRgnAnd(backfill, whole); } if (ypad) { if (ypad < 0) { ny += ypad; nh -= ypad; } else { ; } } tmpregion = sraRgnCreateRect(nx, ny, nx + nw, ny + nh); sraRgnAnd(tmpregion, whole); sraRgnOr(backfill, tmpregion); sraRgnDestroy(tmpregion); } /* try to update the backfill region (new window contents) */ if (dret != 0) { double est, win_area = 0.0, area = 0.0; sraRectangleIterator *iter; sraRect rect; int tx1, ty1, tx2, ty2; tmpregion = sraRgnCreateRect(x0, y0, x0 + w0, y0 + h0); sraRgnAnd(tmpregion, whole); sraRgnAnd(backfill, tmpregion); iter = sraRgnGetIterator(tmpregion); while (sraRgnIteratorNext(iter, &rect)) { tx1 = rect.x1; ty1 = rect.y1; tx2 = rect.x2; ty2 = rect.y2; win_area += (tx2 - tx1)*(ty2 - ty1); } sraRgnReleaseIterator(iter); sraRgnDestroy(tmpregion); iter = sraRgnGetIterator(backfill); while (sraRgnIteratorNext(iter, &rect)) { tx1 = rect.x1; ty1 = rect.y1; tx2 = rect.x2; ty2 = rect.y2; area += (tx2 - tx1)*(ty2 - ty1); } sraRgnReleaseIterator(iter); est = (area * (bpp/8)) / (1000000.0 * rrate); if (db) fprintf(stderr, " area %.1f win_area %.1f est: %.4f", area, win_area, est); if (area > 0.90 * win_area) { if (db) fprintf(stderr, " AREA_TOO_MUCH"); dret = 0; } else if (est > 0.6) { if (db) fprintf(stderr, " EST_TOO_LARGE"); dret = 0; } else if (area <= 0.0) { ; } else { dtime0(&tm); iter = sraRgnGetIterator(backfill); while (sraRgnIteratorNext(iter, &rect)) { tx1 = rect.x1; ty1 = rect.y1; tx2 = rect.x2; ty2 = rect.y2; if (clipshift) { tx1 -= coff_x; ty1 -= coff_y; tx2 -= coff_x; ty2 -= coff_y; } if (subwin) { tx1 -= off_x; ty1 -= off_y; tx2 -= off_x; ty2 -= off_y; } tx1 = nfix(tx1, dpy_x); ty1 = nfix(ty1, dpy_y); tx2 = nfix(tx2, dpy_x+1); ty2 = nfix(ty2, dpy_y+1); dtime(&tm); if (db) fprintf(stderr, " DFC(%d,%d-%d,%d)", tx1, ty1, tx2, ty2); direct_fb_copy(tx1, ty1, tx2, ty2, 1); if (fast_push) { fb_push(); } //fb_push(); do_fb_push++; PUSH_TEST(0); } sraRgnReleaseIterator(iter); dt = dtime(&tm); if (db) fprintf(stderr, " dfc---- dt: %.4f", dt); } if (db && dret) fprintf(stderr, " **** dret=%d", dret); if (db && !dret) fprintf(stderr, " ---- dret=%d", dret); if (db) fprintf(stderr, "\n"); } if (db && bdpush) fprintf(stderr, "BDPUSH-TIME: 0x%lx\n", xrecord_wm_window); if (bdpush && xrecord_wm_window != None) { int x, y, w, h; x = scr_ev[0].x; y = scr_ev[0].y; w = scr_ev[0].w; h = scr_ev[0].h; do_fb_push += do_bdpush(xrecord_wm_window, x, y, w, h, bdx, bdy, bdskinny); if (fast_push) { fb_push(); } } if (do_fb_push) { dtime0(&tm); fb_push(); dt = dtime(&tm); if (0) fprintf(stderr, " fb_push dt: %.4f", dt); if (scaling) { static double last_time = 0.0; double now = dnow(), delay = 0.4, first_wait = 3.0; double trate; int repeating, lat, rate; int link = link_rate(&lat, &rate); int skip_first = 0; if (link == LR_DIALUP || rate < 35) { delay *= 4; } else if (link != LR_LAN || rate < 100) { delay *= 2; } trate = typing_rate(0.0, &repeating); if (xrecord_set_by_mouse || repeating >= 3) { if (now > last_scroll_event_save + first_wait) { skip_first = 1; } } if (skip_first) { /* * try not to send the first one, but a * single keystroke scroll would be OK. */ } else if (now > last_time + delay) { scale_mark(x0, y0, x0 + w0, y0 + h0, 1); last_copyrect_fix = now; } last_time = now; } } sraRgnDestroy(backfill); sraRgnDestroy(whole); return dret; } static void get_client_regions(int *req, int *mod, int *cpy, int *num) { rfbClientIteratorPtr i; rfbClientPtr cl; *req = 0; *mod = 0; *cpy = 0; *num = 0; i = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(i)) ) { *req += sraRgnCountRects(cl->requestedRegion); *mod += sraRgnCountRects(cl->modifiedRegion); *cpy += sraRgnCountRects(cl->copyRegion); *num += 1; } rfbReleaseClientIterator(i); } /* * Wrapper to apply the rfbDoCopyRegion taking into account if scaling * is being done. Note that copyrect under the scaling case is often * only approximate. */ int DCR_Normal = 0; int DCR_FBOnly = 1; int DCR_Direct = 2; void do_copyregion(sraRegionPtr region, int dx, int dy, int mode) { sraRectangleIterator *iter; sraRect rect; int Bpp0 = bpp/8, Bpp; int x1, y1, x2, y2, w, stride, stride0; int sx1, sy1, sx2, sy2, sdx, sdy; int req, mod, cpy, ncli; char *dst = NULL, *src = NULL; last_copyrect = dnow(); if (rfb_fb == main_fb && ! rotating && mode == DCR_Normal) { /* normal case, no -scale or -8to24 */ get_client_regions(&req, &mod, &cpy, &ncli); if (debug_scroll > 1) fprintf(stderr, ">>>-rfbDoCopyRect req: %d mod: %d cpy: %d\n", req, mod, cpy); rfbDoCopyRegion(screen, region, dx, dy); get_client_regions(&req, &mod, &cpy, &ncli); if (debug_scroll > 1) fprintf(stderr, "<<<-rfbDoCopyRect req: %d mod: %d cpy: %d\n", req, mod, cpy); return; } /* rarer case, we need to call rfbDoCopyRect with scaled xy */ stride0 = dpy_x * Bpp0; iter = sraRgnGetReverseIterator(region, dx < 0, dy < 0); while(sraRgnIteratorNext(iter, &rect)) { int j, c, t; x1 = rect.x1; y1 = rect.y1; x2 = rect.x2; y2 = rect.y2; for (c= 0; c < 2; c++) { Bpp = Bpp0; stride = stride0; if (c == 0) { dst = main_fb + y1*stride + x1*Bpp; src = main_fb + (y1-dy)*stride + (x1-dx)*Bpp; } else if (c == 1) { if (! cmap8to24_fb || cmap8to24_fb == rfb_fb) { continue; } if (cmap8to24 && depth == 8) { Bpp = 4 * Bpp0; stride = 4 * stride0; } dst = cmap8to24_fb + y1*stride + x1*Bpp; src = cmap8to24_fb + (y1-dy)*stride + (x1-dx)*Bpp; } w = (x2 - x1)*Bpp; if (dy < 0) { for (j=y1; j=y1; j--) { memmove(dst, src, w); dst -= stride; src -= stride; } } } if (mode == DCR_FBOnly) { continue; } if (scaling) { sx1 = ((double) x1 / dpy_x) * scaled_x; sy1 = ((double) y1 / dpy_y) * scaled_y; sx2 = ((double) x2 / dpy_x) * scaled_x; sy2 = ((double) y2 / dpy_y) * scaled_y; sdx = ((double) dx / dpy_x) * scaled_x; sdy = ((double) dy / dpy_y) * scaled_y; } else { sx1 = x1; sy1 = y1; sx2 = x2; sy2 = y2; sdx = dx; sdy = dy; } if (0) fprintf(stderr, "s... %d %d %d %d %d %d\n", sx1, sy1, sx2, sy2, sdx, sdy); if (rotating) { rotate_coords(sx1, sy1, &sx1, &sy1, -1, -1); rotate_coords(sx2, sy2, &sx2, &sy2, -1, -1); if (rotating == ROTATE_X) { sdx = -sdx; } else if (rotating == ROTATE_Y) { sdy = -sdy; } else if (rotating == ROTATE_XY) { sdx = -sdx; sdy = -sdy; } else if (rotating == ROTATE_90) { t = sdx; sdx = -sdy; sdy = t; } else if (rotating == ROTATE_90X) { t = sdx; sdx = sdy; sdy = t; } else if (rotating == ROTATE_90Y) { t = sdx; sdx = -sdy; sdy = -t; } else if (rotating == ROTATE_270) { t = sdx; sdx = sdy; sdy = -t; } } /* XXX -1? */ if (sx2 < 0) sx2 = 0; if (sy2 < 0) sy2 = 0; if (sx2 < sx1) { t = sx1; sx1 = sx2; sx2 = t; } if (sy2 < sy1) { t = sy1; sy1 = sy2; sy2 = t; } if (0) fprintf(stderr, "s... %d %d %d %d %d %d\n", sx1, sy1, sx2, sy2, sdx, sdy); if (mode == DCR_Direct) { rfbClientIteratorPtr i; rfbClientPtr cl; sraRegionPtr r = sraRgnCreateRect(sx1, sy1, sx2, sy2); i = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(i)) ) { rfbSendCopyRegion(cl, r, sdx, sdy); } rfbReleaseClientIterator(i); sraRgnDestroy(r); } else { rfbDoCopyRect(screen, sx1, sy1, sx2, sy2, sdx, sdy); } } sraRgnReleaseIterator(iter); } void batch_copyregion(sraRegionPtr* region, int *dx, int *dy, int ncr, double delay) { rfbClientIteratorPtr i; rfbClientPtr cl; int k, direct, mode, nrects = 0; /* XXX Y */ if (delay < 0.0) { delay = 0.1; } fb_push_wait(delay, FB_COPY|FB_MOD); for (k=0; k < ncr; k++) { nrects += sraRgnCountRects(region[k]); } i = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(i)) ) { rfbFramebufferUpdateMsg *fu = (rfbFramebufferUpdateMsg *)cl->updateBuf; fu->nRects = Swap16IfLE((uint16_t)(nrects)); fu->type = rfbFramebufferUpdate; if (cl->ublen != 0) fprintf(stderr, "batch_copyregion: *** ublen != 0: %d\n", cl->ublen); cl->ublen = sz_rfbFramebufferUpdateMsg; } rfbReleaseClientIterator(i); if (rfb_fb == main_fb && !rotating) { direct = 0; mode = DCR_FBOnly; } else { direct = 1; mode = DCR_Direct; } for (k=0; k < ncr; k++) { do_copyregion(region[k], dx[k], dy[k], mode); } i = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(i)) ) { if (!direct) { for (k=0; k < ncr; k++) { rfbSendCopyRegion(cl, region[k], dx[k], dy[k]); } } rfbSendUpdateBuf(cl); } rfbReleaseClientIterator(i); fprintf(stderr, "batch_copyregion: nrects: %d nregions: %d\n", nrects, ncr); } void fb_push0(void) { char *httpdir = screen->httpDir; int defer = screen->deferUpdateTime; int req0, mod0, cpy0, req1, mod1, cpy1, ncli; int db = (debug_scroll || debug_wireframe); screen->httpDir = NULL; screen->deferUpdateTime = 0; if (db) get_client_regions(&req0, &mod0, &cpy0, &ncli); rfbPE(0); screen->httpDir = httpdir; screen->deferUpdateTime = defer; if (db) { get_client_regions(&req1, &mod1, &cpy1, &ncli); fprintf(stderr, "\nFB_push: req: %d/%d mod: %d/%d cpy: %d/%d %.4f\n", req0, req1, mod0, mod1, cpy0, cpy1, dnowx()); } } void fb_push(void) { int req0, mod0, cpy0, req1, mod1, cpy1, ncli; int db = (debug_scroll || debug_wireframe); rfbClientIteratorPtr i; rfbClientPtr cl; /* XXX Y */ db = 0; if (db) get_client_regions(&req0, &mod0, &cpy0, &ncli); i = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(i)) ) { if (cl->sock >= 0 && !cl->onHold && FB_UPDATE_PENDING(cl) && !sraRgnEmpty(cl->requestedRegion)) { if (!rfbSendFramebufferUpdate(cl, cl->modifiedRegion)) { fprintf(stderr, "*** rfbSendFramebufferUpdate FAILED #1\n"); if (cl->ublen) fprintf(stderr, "*** fb_push ublen not zero: %d\n", cl->ublen); break; } if (cl->ublen) fprintf(stderr, "*** fb_push ublen not zero: %d\n", cl->ublen); } } rfbReleaseClientIterator(i); if (db) { get_client_regions(&req1, &mod1, &cpy1, &ncli); fprintf(stderr, "\nFB_push: req: %d/%d mod: %d/%d cpy: %d/%d %.4f\n", req0, req1, mod0, mod1, cpy0, cpy1, dnowx()); } } int fb_push_wait0(double max_wait, int flags) { double tm, dt = 0.0; int req, mod, cpy, ncli; int ok = 0; dtime0(&tm); while (dt < max_wait) { int done = 1; rfbCFD(0); get_client_regions(&req, &mod, &cpy, &ncli); if (flags & FB_COPY && cpy) { done = 0; } if (flags & FB_MOD && mod) { done = 0; } if (flags & FB_REQ && req) { done = 0; } if (done) { ok = 1; break; } usleep(1000); fb_push(); dt += dtime(&tm); } return ok; } int fb_push_wait(double max_wait, int flags) { double tm, dt = 0.0; int req, mod, cpy, ncli; int ok = 0, first = 1; dtime0(&tm); while (dt < max_wait) { int done = 1; fb_push(); get_client_regions(&req, &mod, &cpy, &ncli); if (flags & FB_COPY && cpy) { done = 0; } if (flags & FB_MOD && mod) { done = 0; } if (flags & FB_REQ && req) { done = 0; } if (done) { ok = 1; break; } if (first) { first = 0; continue; } rfbCFD(0); usleep(1000); dt += dtime(&tm); } return ok; } /* * utility routine for CopyRect of the window (but not CopyRegion) */ static int crfix(int x, int dx, int Lx) { /* adjust x so that copy source is on screen */ if (dx > 0) { if (x-dx < 0) { /* off on the left */ x = dx; } } else { if (x-dx >= Lx) { /* off on the right */ x = Lx + dx - 1; } } return x; } typedef struct scroll_result { Window win; double time; int result; } scroll_result_t; #define SCR_RESULTS_MAX 256 static scroll_result_t scroll_results[SCR_RESULTS_MAX]; static int scrollability(Window win, int set) { double oldest = -1.0; int i, index = -1, next_index = -1; static int first = 1; if (first) { for (i=0; i= 0 && scroll_results[i].win == None) { break; } } if (set == SCR_SUCCESS) { set = 1; } else if (set == SCR_FAIL) { set = -1; } else { set = 0; } if (index == -1) { scroll_results[next_index].win = win; scroll_results[next_index].time = dnow(); scroll_results[next_index].result = set; } else { if (scroll_results[index].result == 1) { /* * once a success, always a success, until they * forget about us... */ set = 1; } else { scroll_results[index].result = set; } scroll_results[index].time = dnow(); } return set; } void eat_viewonly_input(int max_eat, int keep) { int i, gp, gk; for (i=0; i gp) { if (debug_pointer) { rfbLog("eat_viewonly_input: pointer: %d\n", i); } cont++; } if (got_keyboard_calls > gk) { if (debug_keyboard) { rfbLog("eat_viewonly_input: keyboard: %d\n", i); } cont++; } if (i >= keep - 1 && ! cont) { break; } } } static int eat_pointer(int max_ptr_eat, int keep) { int i, count = 0, gp = got_pointer_input; for (i=0; i gp) { count++; if (0) fprintf(stderr, "GP*-%d\n", i); gp = got_pointer_input; } else if (i > keep) { break; } } return count; } static void set_bdpush(int type, double *last_bdpush, int *pushit) { double now, delay = 0.0; int link, latency, netrate; *pushit = 0; if (type == SCR_MOUSE) { delay = scr_mouse_bdpush_time; } else if (type == SCR_KEY) { delay = scr_key_bdpush_time; } link = link_rate(&latency, &netrate); if (link == LR_DIALUP) { delay *= 1.5; } else if (link == LR_BROADBAND) { delay *= 1.25; } dtime0(&now); if (delay > 0.0 && now > *last_bdpush + delay) { *pushit = 1; *last_bdpush = now; } } void mark_for_xdamage(int x, int y, int w, int h) { int tx1, ty1, tx2, ty2; sraRegionPtr tmpregion; if (! use_xdamage) { return; } tx1 = nfix(x, dpy_x); ty1 = nfix(y, dpy_y); tx2 = nfix(x + w, dpy_x+1); ty2 = nfix(y + h, dpy_y+1); tmpregion = sraRgnCreateRect(tx1, ty1, tx2, ty2); add_region_xdamage(tmpregion); sraRgnDestroy(tmpregion); } void mark_region_for_xdamage(sraRegionPtr region) { sraRectangleIterator *iter; sraRect rect; iter = sraRgnGetIterator(region); while (sraRgnIteratorNext(iter, &rect)) { int x1 = rect.x1; int y1 = rect.y1; int x2 = rect.x2; int y2 = rect.y2; mark_for_xdamage(x1, y1, x2 - x1, y2 - y1); } sraRgnReleaseIterator(iter); } void set_xdamage_mark(int x, int y, int w, int h) { sraRegionPtr region; if (! use_xdamage) { return; } mark_for_xdamage(x, y, w, h); if (xdamage_scheduled_mark == 0.0) { xdamage_scheduled_mark = dnow() + 2.0; } if (xdamage_scheduled_mark_region == NULL) { xdamage_scheduled_mark_region = sraRgnCreate(); } region = sraRgnCreateRect(x, y, x + w, y + w); sraRgnOr(xdamage_scheduled_mark_region, region); sraRgnDestroy(region); } static int repeat_check(double last_key_scroll) { int repeating; double rate = typing_rate(0.0, &repeating); double now = dnow(), delay = 0.5; if (rate > 2.0 && repeating && now > last_key_scroll + delay) { return 0; } else { return 1; } } static int check_xrecord_keys(void) { static int last_wx, last_wy, last_ww, last_wh; double spin = 0.0, tm, tnow; int scr_cnt = 0, input = 0, scroll_rep; int get_out, got_one = 0, flush1 = 0, flush2 = 0; int gk, gk0, ret = 0, db = debug_scroll; int fail = 0; int link, latency, netrate; static double last_key_scroll = 0.0; static double persist_start = 0.0; static double last_bdpush = 0.0; static int persist_count = 0; int scroll_keysym = 0; double last_scroll, scroll_persist = scr_key_persist; double spin_fac = 1.0, scroll_fac = 2.0, noscroll_fac = 0.75; double max_spin, max_long_spin = 0.3; double set_repeat_in; static double set_repeat = 0.0; RAWFB_RET(0) if (unixpw_in_progress) return 0; set_repeat_in = set_repeat; set_repeat = 0.0; get_out = 1; if (got_keyboard_input) { get_out = 0; } dtime0(&tnow); if (tnow < last_key_scroll + scroll_persist) { get_out = 0; } if (set_repeat_in > 0.0 && tnow < last_key_scroll + set_repeat_in) { get_out = 0; } if (get_out) { persist_start = 0.0; persist_count = 0; last_bdpush = 0.0; if (xrecording) { xrecord_watch(0, SCR_KEY); } return 0; } #if 0 /* not used for keyboard yet */ scroll_rep = scrollability(xrecord_ptr_window, SCR_NONE) + 1; if (scroll_rep == 1) { scroll_rep = 2; /* if no info, assume the best. */ } #endif scroll_keysym = xrecord_scroll_keysym(last_rfb_keysym); max_spin = scr_key_time; if (set_repeat_in > 0.0 && tnow < last_key_scroll + 2*set_repeat_in) { max_spin = 2 * set_repeat_in; } else if (tnow < last_key_scroll + scroll_persist) { max_spin = 1.25*(tnow - last_key_scroll); } else if (tnow < last_key_to_button_remap_time + 1.5*scroll_persist) { /* mostly a hack I use for testing -remap key -> btn4/btn5 */ max_spin = scroll_persist; } else if (scroll_keysym) { if (repeat_check(last_key_scroll)) { spin_fac = scroll_fac; } else { spin_fac = noscroll_fac; } } if (max_spin > max_long_spin) { max_spin = max_long_spin; } /* XXX use this somehow */ if (0) link = link_rate(&latency, &netrate); gk = gk0 = got_keyboard_input; dtime0(&tm); if (db) fprintf(stderr, "check_xrecord_keys: BEGIN LOOP: scr_ev_cnt: " "%d max: %.3f %.4f\n", scr_ev_cnt, max_spin, tm - x11vnc_start); while (1) { if (scr_ev_cnt) { got_one = 1; scrollability(xrecord_ptr_window, SCR_SUCCESS); scroll_rep = 2; dtime0(&last_scroll); last_key_scroll = last_scroll; scr_cnt++; break; } X_LOCK; flush1 = 1; XFlush_wr(dpy); X_UNLOCK; if (set_repeat_in > 0.0) { max_keyrepeat_time = set_repeat_in; } if (use_threads) { usleep(1000); } else { rfbCFD(1000); } spin += dtime(&tm); X_LOCK; if (got_keyboard_input > gk) { gk = got_keyboard_input; input++; if (set_repeat_in) { ; } else if (xrecord_scroll_keysym(last_rfb_keysym)) { if (repeat_check(last_key_scroll)) { spin_fac = scroll_fac; } else { spin_fac = noscroll_fac; } } if (0 || db) fprintf(stderr, "check_xrecord: more keys: %.3f 0x%x " " %.4f %s %s\n", spin, last_rfb_keysym, last_rfb_keytime - x11vnc_start, last_rfb_down ? "down":"up ", last_rfb_key_accepted ? "accept":"skip"); flush2 = 1; XFlush_wr(dpy); } #if LIBVNCSERVER_HAVE_RECORD SCR_LOCK; XRecordProcessReplies(rdpy_data); SCR_UNLOCK; #endif X_UNLOCK; if (spin >= max_spin * spin_fac) { if (0 || db) fprintf(stderr, "check_xrecord: SPIN-OUT: %.3f/%.3f\n", spin, max_spin * spin_fac); fail = 1; break; } } max_keyrepeat_time = 0.0; if (scr_ev_cnt) { int dret, ev = scr_ev_cnt - 1; int bdx, bdy, bdskinny, bdpush = 0; double max_age = 0.25, age, tm, dt; static double last_scr_ev = 0.0; last_wx = scr_ev[ev].win_x; last_wy = scr_ev[ev].win_y; last_ww = scr_ev[ev].win_w; last_wh = scr_ev[ev].win_h; /* assume scrollbar on rhs: */ bdx = last_wx + last_ww + 3; bdy = last_wy + last_wh/2; bdskinny = 32; if (persist_start == 0.0) { bdpush = 0; } else { set_bdpush(SCR_KEY, &last_bdpush, &bdpush); } dtime0(&tm); age = max_age; dret = push_scr_ev(&age, SCR_KEY, bdpush, bdx, bdy, bdskinny, 1); dt = dtime(&tm); ret = 1 + dret; scr_ev_cnt = 0; if (ret == 2 && xrecord_scroll_keysym(last_rfb_keysym)) { int repeating; double time_lo = 1.0/max_keyrepeat_lo; double time_hi = 1.0/max_keyrepeat_hi; double rate = typing_rate(0.0, &repeating); if (0 || db) fprintf(stderr, "Typing: dt: %.4f rate: %.1f\n", dt, rate); if (repeating) { /* n.b. the "quantum" is about 1/30 sec. */ max_keyrepeat_time = 1.0*dt; if (max_keyrepeat_time > time_lo || max_keyrepeat_time < time_hi) { max_keyrepeat_time = 0.0; } else { set_repeat = max_keyrepeat_time; if (0 || db) fprintf(stderr, "set max_keyrepeat_time: %.2f\n", max_keyrepeat_time); } } } last_scr_ev = dnow(); } if ((got_one && ret < 2) || persist_count) { set_xdamage_mark(last_wx, last_wy, last_ww, last_wh); } if (fail) { scrollability(xrecord_ptr_window, SCR_FAIL); } if (xrecording) { if (ret < 2) { xrecord_watch(0, SCR_KEY); } } if (ret == 2) { if (persist_start == 0.0) { dtime(&persist_start); last_bdpush = persist_start; } } else { persist_start = 0.0; last_bdpush = 0.0; } /* since we've flushed it, we might as well avoid -input_skip */ if (flush1 || flush2) { got_keyboard_input = 0; got_pointer_input = 0; } return ret; } static int check_xrecord_mouse(void) { static int last_wx, last_wy, last_ww, last_wh; double spin = 0.0, tm, tnow; int i, scr_cnt = 0, input = 0, scroll_rep; int get_out, got_one = 0, flush1 = 0, flush2 = 0; int gp, gp0, ret = 0, db = debug_scroll; int gk, gk0; int fail = 0; int link, latency, netrate; int start_x, start_y, last_x, last_y; static double last_mouse_scroll = 0.0; double last_scroll; double max_spin[3], max_long[3], persist[3]; double flush1_time = 0.01; static double last_flush = 0.0; double last_bdpush = 0.0, button_up_time = 0.0; int button_mask_save; int already_down = 0, max_ptr_eat = 20; static int want_back_in = 0; int came_back_in; int first_push = 1; int scroll_wheel = 0; int btn4 = (1<<3); int btn5 = (1<<4); RAWFB_RET(0) get_out = 1; if (button_mask) { get_out = 0; } if (want_back_in) { get_out = 0; } dtime0(&tnow); if (0) fprintf(stderr, "check_xrecord_mouse: IN xrecording: %d\n", xrecording); if (get_out) { if (xrecording) { xrecord_watch(0, SCR_MOUSE); } return 0; } scroll_rep = scrollability(xrecord_ptr_window, SCR_NONE) + 1; if (scroll_rep == 1) { scroll_rep = 2; /* if no info, assume the best. */ } if (button_mask_prev) { already_down = 1; } if (want_back_in) { came_back_in = 1; first_push = 0; } else { came_back_in = 0; } want_back_in = 0; if (button_mask & (btn4|btn5)) { scroll_wheel = 1; } /* * set up times for the various "reputations" * * 0 => -1, has been tried but never found a scroll. * 1 => 0, has not been tried. * 2 => +1, has been tried and found a scroll. */ /* first spin-out time (no events) */ max_spin[0] = 1*scr_mouse_time; max_spin[1] = 2*scr_mouse_time; max_spin[2] = 4*scr_mouse_time; if (!already_down) { for (i=0; i<3; i++) { max_spin[i] *= 1.5; } } /* max time between events */ persist[0] = 1*scr_mouse_persist; persist[1] = 2*scr_mouse_persist; persist[2] = 4*scr_mouse_persist; /* absolute max time in the loop */ max_long[0] = scr_mouse_maxtime; max_long[1] = scr_mouse_maxtime; max_long[2] = scr_mouse_maxtime; pointer_flush_delay = scr_mouse_pointer_delay; /* slow links: */ link = link_rate(&latency, &netrate); if (link == LR_DIALUP) { for (i=0; i<3; i++) { max_spin[i] *= 2.0; } pointer_flush_delay *= 2; } else if (link == LR_BROADBAND) { pointer_flush_delay *= 2; } gp = gp0 = got_pointer_input; gk = gk0 = got_keyboard_input; dtime0(&tm); /* * this is used for border pushes (bdpush) to guess location * of scrollbar (region rects containing this point are pushed). */ last_x = start_x = cursor_x; last_y = start_y = cursor_y; if (db) fprintf(stderr, "check_xrecord_mouse: BEGIN LOOP: scr_ev_cnt: " "%d max: %.3f %.4f\n", scr_ev_cnt, max_spin[scroll_rep], tm - x11vnc_start); while (1) { double spin_check; if (scr_ev_cnt) { int dret, ev = scr_ev_cnt - 1; int bdpush = 0, bdx, bdy, bdskinny; double tm, dt, age = 0.35; got_one = 1; scrollability(xrecord_ptr_window, SCR_SUCCESS); scroll_rep = 2; scr_cnt++; dtime0(&last_scroll); last_mouse_scroll = last_scroll; if (last_bdpush == 0.0) { last_bdpush = last_scroll; } bdx = start_x; bdy = start_y; if (clipshift) { bdx += coff_x; bdy += coff_y; } if (subwin) { bdx += off_x; bdy += off_y; } bdskinny = 32; set_bdpush(SCR_MOUSE, &last_bdpush, &bdpush); dtime0(&tm); dret = push_scr_ev(&age, SCR_MOUSE, bdpush, bdx, bdy, bdskinny, first_push); if (first_push) first_push = 0; ret = 1 + dret; dt = dtime(&tm); if (db) fprintf(stderr, " dret: %d scr_ev_cnt: %d dt: %.4f\n", dret, scr_ev_cnt, dt); last_wx = scr_ev[ev].win_x; last_wy = scr_ev[ev].win_y; last_ww = scr_ev[ev].win_w; last_wh = scr_ev[ev].win_h; scr_ev_cnt = 0; if (! dret) { break; } if (0 && button_up_time > 0.0) { /* we only take 1 more event with button up */ if (db) fprintf(stderr, "check_xrecord: BUTTON_UP_SCROLL: %.3f\n", spin); break; } } if (! flush1) { if (! already_down || (!scr_cnt && spin>flush1_time)) { flush1 = 1; X_LOCK; XFlush_wr(dpy); X_UNLOCK; dtime0(&last_flush); } } if (use_threads) { usleep(1000); } else { rfbCFD(1000); rfbCFD(0); } spin += dtime(&tm); if (got_pointer_input > gp) { flush2 = 1; input += eat_pointer(max_ptr_eat, 1); gp = got_pointer_input; } if (got_keyboard_input > gk) { gk = got_keyboard_input; input++; } X_LOCK; #if LIBVNCSERVER_HAVE_RECORD SCR_LOCK; XRecordProcessReplies(rdpy_data); SCR_UNLOCK; #endif X_UNLOCK; if (! input) { spin_check = 1.5 * max_spin[scroll_rep]; } else { spin_check = max_spin[scroll_rep]; } if (button_up_time > 0.0) { if (tm > button_up_time + max_spin[scroll_rep]) { if (db) fprintf(stderr, "check_xrecord: SPIN-OUT-BUTTON_UP: %.3f/%.3f\n", spin, tm - button_up_time); break; } } else if (!scr_cnt) { if (spin >= spin_check) { if (db) fprintf(stderr, "check_xrecord: SPIN-OUT-1: %.3f/%.3f\n", spin, spin_check); fail = 1; break; } } else { if (tm >= last_scroll + persist[scroll_rep]) { if (db) fprintf(stderr, "check_xrecord: SPIN-OUT-2: %.3f/%.3f\n", spin, tm - last_scroll); break; } } if (spin >= max_long[scroll_rep]) { if (db) fprintf(stderr, "check_xrecord: SPIN-OUT-3: %.3f/%.3f\n", spin, max_long[scroll_rep]); break; } if (! button_mask) { int doflush = 0; if (button_up_time > 0.0) { ; } else if (came_back_in) { dtime0(&button_up_time); doflush = 1; } else if (scroll_wheel) { if (db) fprintf(stderr, "check_xrecord: SCROLL-WHEEL-BUTTON-UP-KEEP-GOING: %.3f/%.3f %d/%d %d/%d\n", spin, max_long[scroll_rep], last_x, last_y, cursor_x, cursor_y); doflush = 1; dtime0(&button_up_time); } else if (last_x == cursor_x && last_y == cursor_y) { if (db) fprintf(stderr, "check_xrecord: BUTTON-UP: %.3f/%.3f %d/%d %d/%d\n", spin, max_long[scroll_rep], last_x, last_y, cursor_x, cursor_y); break; } else { if (db) fprintf(stderr, "check_xrecord: BUTTON-UP-KEEP-GOING: %.3f/%.3f %d/%d %d/%d\n", spin, max_long[scroll_rep], last_x, last_y, cursor_x, cursor_y); doflush = 1; dtime0(&button_up_time); } if (doflush) { flush1 = 1; X_LOCK; XFlush_wr(dpy); X_UNLOCK; dtime0(&last_flush); } } last_x = cursor_x; last_y = cursor_y; } if (got_one) { set_xdamage_mark(last_wx, last_wy, last_ww, last_wh); } if (fail) { scrollability(xrecord_ptr_window, SCR_FAIL); } /* flush any remaining pointer events. */ button_mask_save = button_mask; pointer_queued_sent = 0; last_x = cursor_x; last_y = cursor_y; pointer(-1, 0, 0, NULL); pointer_flush_delay = 0.0; if (xrecording && pointer_queued_sent && button_mask_save && (last_x != cursor_x || last_y != cursor_y) ) { if (db) fprintf(stderr, " pointer() push yields events on: ret=%d\n", ret); if (ret == 2) { if (db) fprintf(stderr, " we decide to send ret=3\n"); want_back_in = 1; ret = 3; flush2 = 1; } else { if (ret) { ret = 1; } else { ret = 0; } xrecord_watch(0, SCR_MOUSE); } } else { if (ret) { ret = 1; } else { ret = 0; } if (xrecording) { xrecord_watch(0, SCR_MOUSE); } } if (flush2) { X_LOCK; XFlush_wr(dpy); XFlush_wr(rdpy_ctrl); X_UNLOCK; flush2 = 1; dtime0(&last_flush); if (db) fprintf(stderr, "FLUSH-2\n"); } /* since we've flushed it, we might as well avoid -input_skip */ if (flush1 || flush2) { got_keyboard_input = 0; got_pointer_input = 0; } if (ret) { return ret; } else if (scr_cnt) { return 1; } else { return 0; } } int check_xrecord(void) { int watch_keys = 0, watch_mouse = 0, consider_mouse; static int mouse_wants_back_in = 0; RAWFB_RET(0) if (! use_xrecord) { return 0; } if (unixpw_in_progress) return 0; if (skip_cr_when_scaling("scroll")) { return 0; } if (0) fprintf(stderr, "check_xrecord: IN xrecording: %d\n", xrecording); if (! xrecording) { return 0; } if (!strcmp(scroll_copyrect, "always")) { watch_keys = 1; watch_mouse = 1; } else if (!strcmp(scroll_copyrect, "keys")) { watch_keys = 1; } else if (!strcmp(scroll_copyrect, "mouse")) { watch_mouse = 1; } if (button_mask || mouse_wants_back_in) { consider_mouse = 1; } else { consider_mouse = 0; } if (0) fprintf(stderr, "check_xrecord: button_mask: %d mouse_wants_back_in: %d\n", button_mask, mouse_wants_back_in); if (watch_mouse && consider_mouse && xrecord_set_by_mouse) { int ret = check_xrecord_mouse(); if (ret == 3) { mouse_wants_back_in = 1; } else { mouse_wants_back_in = 0; } return ret; } else if (watch_keys && xrecord_set_by_keys) { mouse_wants_back_in = 0; return check_xrecord_keys(); } else { mouse_wants_back_in = 0; return 0; } } #define DB_SET \ int db = 0; \ int db2 = 0; \ if (debug_wireframe == 1) { \ db = 1; \ } \ if (debug_wireframe == 2) { \ db2 = 1; \ } \ if (debug_wireframe == 3) { \ db = 1; \ db2 = 1; \ } #define NBATCHMAX 1024 int batch_dxs[NBATCHMAX], batch_dys[NBATCHMAX]; sraRegionPtr batch_reg[NBATCHMAX]; static int try_copyrect(Window frame, int x, int y, int w, int h, int dx, int dy, int *obscured, sraRegionPtr extra_clip, double max_wait, int *nbatch) { static int dt_bad = 0; static time_t dt_bad_check = 0; int x1, y1, x2, y2, sent_copyrect = 0; int req, mod, cpy, ncli; double tm, dt; DB_SET if (nbatch == NULL) { get_client_regions(&req, &mod, &cpy, &ncli); if (cpy) { /* one is still pending... try to force it out: */ if (!fb_push_wait(max_wait, FB_COPY)) { fb_push_wait(max_wait/2, FB_COPY); } get_client_regions(&req, &mod, &cpy, &ncli); } if (cpy) { return 0; } } *obscured = 0; /* * XXX KDE and xfce do some weird things with the * stacking, it does not match XQueryTree. Work around * it for now by CopyRect-ing the *whole* on-screen * rectangle (whether obscured or not!) */ if (time(NULL) > dt_bad_check + 5) { char *dt = guess_desktop(); if (!strcmp(dt, "kde")) { dt_bad = 1; } else if (!strcmp(dt, "xfce")) { dt_bad = 1; } else { dt_bad = 0; } dt_bad_check = time(NULL); } if (clipshift) { x -= coff_x; y -= coff_y; } if (subwin) { x -= off_x; y -= off_y; } if (db2) fprintf(stderr, "try_copyrect: 0x%lx bad: %d stack_list_num: %d\n", frame, dt_bad, stack_list_num); if (dt_bad && wireframe_in_progress) { sraRegionPtr rect; /* send the whole thing... */ x1 = crfix(nfix(x, dpy_x), dx, dpy_x); y1 = crfix(nfix(y, dpy_y), dy, dpy_y); x2 = crfix(nfix(x+w, dpy_x+1), dx, dpy_x+1); y2 = crfix(nfix(y+h, dpy_y+1), dy, dpy_y+1); rect = sraRgnCreateRect(x1, y1, x2, y2); if (blackouts) { int i; sraRegionPtr bo_rect; for (i=0; i= 0; k--) { Window swin; if (0 && dt_bad) { break; } swin = stack_list[k].win; if (db2) fprintf(stderr, "sw: %d/%lx\n", k, swin); if (swin == frame) { if (db2) { saw_me = 1; fprintf(stderr, " ----------\n"); } else { break; } } #if 0 fprintf(stderr, "bo: %d/%lx\n", k, swin); #endif /* skip some unwanted cases: */ #ifndef MACOSX if (swin == None) { continue; } #endif if (boff <= (int) swin && (int) swin < boff + bwin) { ; /* blackouts */ } else if (! stack_list[k].fetched || stack_list[k].time > tm + 2.0) { if (!valid_window(swin, &attr, 1)) { stack_list[k].valid = 0; } else { stack_list[k].valid = 1; stack_list[k].x = attr.x; stack_list[k].y = attr.y; stack_list[k].width = attr.width; stack_list[k].height = attr.height; stack_list[k].depth = attr.depth; stack_list[k].class = attr.class; stack_list[k].backing_store = attr.backing_store; stack_list[k].map_state = attr.map_state; } stack_list[k].fetched = 1; stack_list[k].time = tm; } if (!stack_list[k].valid) { continue; } attr.x = stack_list[k].x; attr.y = stack_list[k].y; attr.depth = stack_list[k].depth; attr.width = stack_list[k].width; attr.height = stack_list[k].height; attr.map_state = stack_list[k].map_state; if (attr.map_state != IsViewable) { continue; } if (clipshift) { attr.x -= coff_x; attr.y -= coff_y; } if (subwin) { attr.x -= off_x; attr.y -= off_y; } /* * first subtract any overlap from the initial * window rectangle */ /* clip the window to the visible screen: */ tx1 = nfix(attr.x, dpy_x); ty1 = nfix(attr.y, dpy_y); tx2 = nfix(attr.x + attr.width, dpy_x+1); ty2 = nfix(attr.y + attr.height, dpy_y+1); if (db2) fprintf(stderr, " tmp_win-1: %4d %3d, %4d %3d 0x%lx\n", tx1, ty1, tx2, ty2, swin); if (db2 && saw_me) continue; /* see if window clips us: */ tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2); if (sraRgnAnd(tmp_win, moved_win)) { *obscured = 1; if (db2) fprintf(stderr, " : clips it.\n"); } sraRgnDestroy(tmp_win); /* subtract it from our region: */ tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2); sraRgnSubtract(moved_win, tmp_win); sraRgnDestroy(tmp_win); /* * next, subtract from the initial window rectangle * anything that would clip it. */ /* clip the window to the visible screen: */ tx1 = nfix(attr.x - dx, dpy_x); ty1 = nfix(attr.y - dy, dpy_y); tx2 = nfix(attr.x - dx + attr.width, dpy_x+1); ty2 = nfix(attr.y - dy + attr.height, dpy_y+1); if (db2) fprintf(stderr, " tmp_win-2: %4d %3d, %4d %3d 0x%lx\n", tx1, ty1, tx2, ty2, swin); if (db2 && saw_me) continue; /* subtract it from our region: */ tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2); sraRgnSubtract(moved_win, tmp_win); sraRgnDestroy(tmp_win); } X_UNLOCK; if (extra_clip && ! sraRgnEmpty(extra_clip)) { whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y); if (clipshift) { sraRgnOffset(extra_clip, -coff_x, -coff_y); } if (subwin) { sraRgnOffset(extra_clip, -off_x, -off_y); } iter = sraRgnGetIterator(extra_clip); while (sraRgnIteratorNext(iter, &rect)) { /* clip the window to the visible screen: */ tx1 = rect.x1; ty1 = rect.y1; tx2 = rect.x2; ty2 = rect.y2; tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2); sraRgnAnd(tmp_win, whole); /* see if window clips us: */ if (sraRgnAnd(tmp_win, moved_win)) { *obscured = 1; } sraRgnDestroy(tmp_win); /* subtract it from our region: */ tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2); sraRgnSubtract(moved_win, tmp_win); sraRgnDestroy(tmp_win); /* * next, subtract from the initial window rectangle * anything that would clip it. */ tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2); sraRgnOffset(tmp_win, -dx, -dy); /* clip the window to the visible screen: */ sraRgnAnd(tmp_win, whole); /* subtract it from our region: */ sraRgnSubtract(moved_win, tmp_win); sraRgnDestroy(tmp_win); } sraRgnReleaseIterator(iter); sraRgnDestroy(whole); } dt = dtime(&tm); if (db2) fprintf(stderr, " stack_work dt: %.4f\n", dt); if (*obscured && !strcmp(wireframe_copyrect, "top")) { ; /* cannot send CopyRegion */ } else if (! sraRgnEmpty(moved_win)) { sraRegionPtr whole, shifted_region; whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y); shifted_region = sraRgnCreateRgn(moved_win); sraRgnOffset(shifted_region, dx, dy); sraRgnAnd(shifted_region, whole); sraRgnDestroy(whole); /* now send the CopyRegion: */ if (! sraRgnEmpty(shifted_region)) { dtime0(&tm); if (!nbatch) { do_copyregion(shifted_region, dx, dy, 0); } else { batch_dxs[*nbatch] = dx; batch_dys[*nbatch] = dy; batch_reg[*nbatch] = sraRgnCreateRgn(shifted_region); (*nbatch)++; } dt = dtime(&tm); if (0 || db2) fprintf(stderr, "do_copyregion: %d %d %d %d dx: %d dy: %d dt: %.4f\n", tx1, ty1, tx2, ty2, dx, dy, dt); sent_copyrect = 1; } sraRgnDestroy(shifted_region); } sraRgnDestroy(moved_win); } return sent_copyrect; } int near_wm_edge(int x, int y, int w, int h, int px, int py) { /* heuristics: */ int wf_t = wireframe_top; int wf_b = wireframe_bot; int wf_l = wireframe_left; int wf_r = wireframe_right; int near_edge = 0; if (wf_t || wf_b || wf_l || wf_r) { if (nabs(y - py) < wf_t) { near_edge = 1; } if (nabs(y + h - py) < wf_b) { near_edge = 1; } if (nabs(x - px) < wf_l) { near_edge = 1; } if (nabs(x + w - px) < wf_r) { near_edge = 1; } } else { /* all zero; always "near" edge: */ near_edge = 1; } return near_edge; } int near_scrollbar_edge(int x, int y, int w, int h, int px, int py) { /* heuristics: */ int sb_t = scrollcopyrect_top; int sb_b = scrollcopyrect_bot; int sb_l = scrollcopyrect_left; int sb_r = scrollcopyrect_right; int near_edge = 0; if (sb_t || sb_b || sb_l || sb_r) { if (nabs(y - py) < sb_t) { near_edge = 1; } if (nabs(y + h - py) < sb_b) { near_edge = 1; } if (nabs(x - px) < sb_l) { near_edge = 1; } if (nabs(x + w - px) < sb_r) { near_edge = 1; } } else { /* all zero; always "near" edge: */ near_edge = 1; } return near_edge; } void check_fixscreen(void) { double now = dnow(); int didfull = 0, db = 0; if (!client_count) { return; } if (unixpw_in_progress) return; if (screen_fixup_X > 0.0) { static double last = 0.0; if (now > last + screen_fixup_X) { if (db) rfbLog("doing screen_fixup_X\n"); do_copy_screen = 1; last = now; didfull = 1; } } if (screen_fixup_V > 0.0) { static double last = 0.0; if (now > last + screen_fixup_V) { if (! didfull) { refresh_screen(0); if (db) rfbLog("doing screen_fixup_V\n"); } last = now; didfull = 1; } } if (screen_fixup_C > 0.0) { static double last = 0.0; if (last_copyrect_fix < last_copyrect && now > last_copyrect + screen_fixup_C) { if (! didfull) { refresh_screen(0); if (db) rfbLog("doing screen_fixup_C\n"); } last_copyrect_fix = now; last = now; didfull = 1; } } if (scaling && last_copyrect_fix < last_copyrect) { static double last = 0.0; double delay = 3.0; if (now > last + delay) { if (! didfull) { scale_and_mark_rect(0, 0, dpy_x, dpy_y, 1); if (db) rfbLog("doing scale screen_fixup\n"); } last_copyrect_fix = now; last = now; didfull = 1; } } } static int wireframe_mod_state() { if (! wireframe_mods) { return 0; } if (!strcmp(wireframe_mods, "all")) { if (track_mod_state(NoSymbol, FALSE, FALSE)) { return 1; } else { return 0; } } else if (!strcmp(wireframe_mods, "Alt")) { if (track_mod_state(XK_Alt_L, FALSE, FALSE) == 1) { return 1; } else if (track_mod_state(XK_Alt_R, FALSE, FALSE) == 1) { return 1; } } else if (!strcmp(wireframe_mods, "Shift")) { if (track_mod_state(XK_Shift_L, FALSE, FALSE) == 1) { return 1; } else if (track_mod_state(XK_Shift_R, FALSE, FALSE) == 1) { return 1; } } else if (!strcmp(wireframe_mods, "Control")) { if (track_mod_state(XK_Control_L, FALSE, FALSE) == 1) { return 1; } else if (track_mod_state(XK_Control_R, FALSE, FALSE) == 1) { return 1; } } else if (!strcmp(wireframe_mods, "Meta")) { if (track_mod_state(XK_Meta_L, FALSE, FALSE) == 1) { return 1; } else if (track_mod_state(XK_Meta_R, FALSE, FALSE) == 1) { return 1; } } else if (!strcmp(wireframe_mods, "Super")) { if (track_mod_state(XK_Super_L, FALSE, FALSE) == 1) { return 1; } else if (track_mod_state(XK_Super_R, FALSE, FALSE) == 1) { return 1; } } else if (!strcmp(wireframe_mods, "Hyper")) { if (track_mod_state(XK_Hyper_L, FALSE, FALSE) == 1) { return 1; } else if (track_mod_state(XK_Hyper_R, FALSE, FALSE) == 1) { return 1; } } return 0; } static int NPP_nreg = 0; static sraRegionPtr NPP_roffscreen = NULL; static sraRegionPtr NPP_r_bs_tmp = NULL; static Window NPP_nwin = None; void clear_win_events(void) { #if !NO_X11 if (dpy && NPP_nwin != None) { XEvent ev; XErrorHandler old_handler; old_handler = XSetErrorHandler(trap_xerror); trapped_xerror = 0; while (XCheckTypedWindowEvent(dpy, NPP_nwin, ConfigureNotify, &ev)) { fprintf(stderr, "."); if (trapped_xerror) { break; } trapped_xerror = 0; } while (XCheckTypedWindowEvent(dpy, NPP_nwin, VisibilityNotify, &ev)) { fprintf(stderr, "+"); if (trapped_xerror) { break; } trapped_xerror = 0; } XSetErrorHandler(old_handler); fprintf(stderr, " 0x%x\n", (unsigned int) NPP_nwin); } #endif } void push_borders(sraRect *rects, int nrect) { int i, s = 2; sraRegionPtr r0, r1, r2; r0 = sraRgnCreate(); r1 = sraRgnCreateRect(0, 0, dpy_x, dpy_y); for (i=0; i 0 && h > 0 && w * h > 64 * 64) { r2 = sraRgnCreateRect(x - s, y , x , y + h); sraRgnOr(r0, r2); sraRgnDestroy(r2); r2 = sraRgnCreateRect(x + w, y , x + w + s, y + h); sraRgnOr(r0, r2); sraRgnDestroy(r2); r2 = sraRgnCreateRect(x - s, y - s, x + w + s, y + s); sraRgnOr(r0, r2); sraRgnDestroy(r2); r2 = sraRgnCreateRect(x - s, y , x + w + s, y + h + s); sraRgnOr(r0, r2); sraRgnDestroy(r2); } } sraRgnAnd(r0, r1); if (!sraRgnEmpty(r0)) { double d = dnow(); sraRectangleIterator *iter; sraRect rect; int db = 0; if (db) fprintf(stderr, "SCALE_BORDER\n"); fb_push_wait(0.05, FB_MOD|FB_COPY); iter = sraRgnGetIterator(r0); while (sraRgnIteratorNext(iter, &rect)) { /* clip the window to the visible screen: */ int tx1 = rect.x1; int ty1 = rect.y1; int tx2 = rect.x2; int ty2 = rect.y2; scale_and_mark_rect(tx1, ty1, tx2, ty2, 1); } sraRgnReleaseIterator(iter); if (db) fprintf(stderr, "SCALE_BORDER %.4f\n", dnow() - d); fb_push_wait(0.1, FB_MOD|FB_COPY); if (db) fprintf(stderr, "SCALE_BORDER %.4f\n", dnow() - d); } sraRgnDestroy(r0); sraRgnDestroy(r1); } void ncache_pre_portions(Window orig_frame, Window frame, int *nidx_in, int try_batch, int *use_batch, int orig_x, int orig_y, int orig_w, int orig_h, int x, int y, int w, int h, double ntim) { int nidx, np = ncache_pad; if (!ntim) {} *use_batch = 0; *nidx_in = -1; NPP_nreg = 0; NPP_roffscreen = NULL; NPP_r_bs_tmp = NULL; NPP_nwin = None; if (ncache <= 0) { return; } if (rotating) { try_batch = 0; } if (*nidx_in == -1) { nidx = lookup_win_index(orig_frame); NPP_nwin = orig_frame; if (nidx < 0) { nidx = lookup_win_index(frame); NPP_nwin = frame; } } else { nidx = *nidx_in; } if (nidx > 0) { sraRegionPtr r0, r1, r2; int dx, dy; int bs_x = cache_list[nidx].bs_x; int bs_y = cache_list[nidx].bs_y; int bs_w = cache_list[nidx].bs_w; int bs_h = cache_list[nidx].bs_h; *nidx_in = nidx; if (bs_x < 0) { if (!find_rect(nidx, x, y, w, h)) { nidx = -1; return; } bs_x = cache_list[nidx].bs_x; bs_y = cache_list[nidx].bs_y; bs_w = cache_list[nidx].bs_w; bs_h = cache_list[nidx].bs_h; } if (bs_x < 0) { nidx = -1; return; } if (try_batch) { *use_batch = 1; } if (ncache_pad) { orig_x -= np; orig_y -= np; orig_w += 2 * np; orig_h += 2 * np; x -= np; y -= np; w += 2 * np; h += 2 * np; } if (clipshift) { orig_x -= coff_x; orig_y -= coff_y; x -= coff_x; y -= coff_y; } r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y); r2 = sraRgnCreateRect(orig_x, orig_y, orig_x + orig_w, orig_y + orig_h); sraRgnSubtract(r2, r0); if (! sraRgnEmpty(r2) && cache_list[nidx].bs_time > 0.0) { /* some is initially offscreen */ dx = bs_x - orig_x; dy = bs_y - orig_y; sraRgnOffset(r2, dx, dy); dx = 0; dy = dpy_y; sraRgnOffset(r2, dx, dy); //fprintf(stderr, "FB_COPY: %.4f 1) offscreen check:\n", dnow() - ntim); /* 0) save it in the invalid (offscreen) SU portion */ if (! *use_batch) { do_copyregion(r2, dx, dy, 0); if (! fb_push_wait(0.2, FB_COPY)) { fb_push_wait(0.1, FB_COPY); } } else { batch_dxs[NPP_nreg] = dx; batch_dys[NPP_nreg] = dy; batch_reg[NPP_nreg++] = sraRgnCreateRgn(r2); } NPP_roffscreen = sraRgnCreateRgn(r2); } sraRgnDestroy(r2); /* 1) use bs for temp storage of the new save under. */ r1 = sraRgnCreateRect(x, y, x + w, y + h); sraRgnAnd(r1, r0); dx = bs_x - x; dy = bs_y - y; sraRgnOffset(r1, dx, dy); //fprintf(stderr, "FB_COPY: %.4f 1) use tmp bs:\n", dnow() - ntim); if (! *use_batch) { do_copyregion(r1, dx, dy, 0); if (! fb_push_wait(0.2, FB_COPY)) { //fprintf(stderr, "FB_COPY: %.4f 1) FAILED.\n", dnow() - ntim); fb_push_wait(0.1, FB_COPY); } } else { batch_dxs[NPP_nreg] = dx; batch_dys[NPP_nreg] = dy; batch_reg[NPP_nreg++] = sraRgnCreateRgn(r1); } NPP_r_bs_tmp = sraRgnCreateRgn(r1); sraRgnDestroy(r0); sraRgnDestroy(r1); } } void ncache_post_portions(int nidx, int use_batch, int orig_x, int orig_y, int orig_w, int orig_h, int x, int y, int w, int h, double batch_delay, double ntim) { int np = ncache_pad; if (ncache > 0 && nidx >= 0) { sraRegionPtr r0, r1, r2, r3; int dx, dy; int su_x = cache_list[nidx].su_x; int su_y = cache_list[nidx].su_y; int su_w = cache_list[nidx].su_w; int su_h = cache_list[nidx].su_h; int bs_x = cache_list[nidx].bs_x; int bs_y = cache_list[nidx].bs_y; int bs_w = cache_list[nidx].bs_w; int bs_h = cache_list[nidx].bs_h; int some_su = 0; //fprintf(stderr, "su: %dx%d+%d+%d bs: %dx%d+%d+%d\n", su_w, su_h, su_x, su_y, bs_w, bs_h, bs_x, bs_y); if (bs_x < 0) { if (!find_rect(nidx, x, y, w, h)) { return; } su_x = cache_list[nidx].su_x; su_y = cache_list[nidx].su_y; su_w = cache_list[nidx].su_w; su_h = cache_list[nidx].su_h; bs_x = cache_list[nidx].bs_x; bs_y = cache_list[nidx].bs_y; bs_w = cache_list[nidx].bs_w; bs_h = cache_list[nidx].bs_h; } if (bs_x < 0) { return; } if (ncache_pad) { orig_x -= np; orig_y -= np; orig_w += 2 * np; orig_h += 2 * np; x -= np; y -= np; w += 2 * np; h += 2 * np; } if (clipshift) { orig_x -= coff_x; orig_y -= coff_y; x -= coff_x; y -= coff_y; } r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y); /* 0b) copy this bs part stored in saveunder */ if (NPP_roffscreen != NULL) { dx = x - su_x; dy = y - su_y; sraRgnOffset(NPP_roffscreen, dx, dy); sraRgnAnd(NPP_roffscreen, r0); if (! use_batch) { do_copyregion(NPP_roffscreen, dx, dy, 0); if (!fb_push_wait(0.2, FB_COPY)) { fb_push_wait(0.1, FB_COPY); } } else { batch_dxs[NPP_nreg] = dx; batch_dys[NPP_nreg] = dy; batch_reg[NPP_nreg++] = sraRgnCreateRgn(NPP_roffscreen); } sraRgnDestroy(NPP_roffscreen); } /* 3) copy from the saveunder to where orig win was */ r1 = sraRgnCreateRect(orig_x, orig_y, orig_x + orig_w, orig_y + orig_h); sraRgnAnd(r1, r0); r2 = sraRgnCreateRect(x+np, y+np, x + w-np, y + h-np); sraRgnAnd(r2, r0); sraRgnSubtract(r1, r2); dx = orig_x - su_x; dy = orig_y - su_y; //fprintf(stderr, "FB_COPY: %.4f 3) sent_copyrect: su_restore: %d %d\n", dnow() - ntim, dx, dy); if (cache_list[nidx].su_time == 0.0) { ; } else if (! use_batch) { do_copyregion(r1, dx, dy, 0); if (!fb_push_wait(0.2, FB_COPY)) { //fprintf(stderr, "FB_COPY: %.4f 3) FAILED.\n", dnow() - ntim); fb_push_wait(0.1, FB_COPY); } } else { batch_dxs[NPP_nreg] = dx; batch_dys[NPP_nreg] = dy; batch_reg[NPP_nreg++] = sraRgnCreateRgn(r1); } //fprintf(stderr, "sent_copyrect: %.4f su_restore: done.\n", dnow() - ntim); sraRgnDestroy(r0); sraRgnDestroy(r1); sraRgnDestroy(r2); /* 4) if overlap between orig and displaced, move the corner that will still be su: */ r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y); r1 = sraRgnCreateRect(orig_x, orig_y, orig_x + orig_w, orig_y + orig_h); sraRgnAnd(r1, r0); r2 = sraRgnCreateRect(x, y, x + w, y + h); sraRgnAnd(r2, r0); r3 = NULL; if (sraRgnAnd(r2, r1) && cache_list[nidx].su_time > 0.0) { int dx2 = su_x - orig_x; int dy2 = su_y - orig_y; r3 = sraRgnCreateRgn(r2); sraRgnOffset(r2, dx2, dy2); dx = su_x - x; dy = su_y - y; sraRgnOffset(r3, dx, dy); dx = dx - dx2; dy = dy - dy2; //fprintf(stderr, "FB_COPY: %.4f 4) move overlap inside su:\n", dnow() - ntim); if (! use_batch) { do_copyregion(r3, dx, dy, 0); if (!fb_push_wait(0.2, FB_COPY)) { //fprintf(stderr, "FB_COPY: %.4f 4) FAILED.\n", dnow() - ntim); fb_push_wait(0.1, FB_COPY); } } else { batch_dxs[NPP_nreg] = dx; batch_dys[NPP_nreg] = dy; batch_reg[NPP_nreg++] = sraRgnCreateRgn(r3); } } sraRgnDestroy(r0); sraRgnDestroy(r1); sraRgnDestroy(r2); /* 5) copy our temporary stuff from bs to su: */ dx = su_x - bs_x; dy = su_y - bs_y; if (NPP_r_bs_tmp == NULL) { r1 = sraRgnCreateRect(su_x, su_y, su_x + su_w, su_y + su_h); } else { r1 = sraRgnCreateRgn(NPP_r_bs_tmp); sraRgnOffset(r1, dx, dy); sraRgnDestroy(NPP_r_bs_tmp); } if (r3 != NULL) { sraRgnSubtract(r1, r3); sraRgnDestroy(r3); } //fprintf(stderr, "FB_COPY: %.4f 5) move tmp bs to su:\n", dnow() - ntim); if (! use_batch) { do_copyregion(r1, dx, dy, 0); if (!fb_push_wait(0.2, FB_COPY)) { //fprintf(stderr, "FB_COPY: %.4f 5) FAILED.\n", dnow() - ntim); fb_push_wait(0.1, FB_COPY); } } else { batch_dxs[NPP_nreg] = dx; batch_dys[NPP_nreg] = dy; batch_reg[NPP_nreg++] = sraRgnCreateRgn(r1); } if (! sraRgnEmpty(r1)) { some_su = 1; } sraRgnDestroy(r1); /* 6) not really necessary, update bs with current view: */ r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y); r1 = sraRgnCreateRect(x, y, x + w, y + h); sraRgnAnd(r1, r0); dx = bs_x - x; dy = bs_y - y; sraRgnOffset(r1, dx, dy); //fprintf(stderr, "FB_COPY: %.4f 6) snapshot bs:\n", dnow() - ntim); if (! use_batch) { do_copyregion(r1, dx, dy, 0); if (!fb_push_wait(0.2, FB_COPY)) { //fprintf(stderr, "FB_COPY: %.4f 6) FAILED.\n", dnow() - ntim); fb_push_wait(0.1, FB_COPY); } } else { batch_dxs[NPP_nreg] = dx; batch_dys[NPP_nreg] = dy; batch_reg[NPP_nreg++] = sraRgnCreateRgn(r1); } sraRgnDestroy(r0); sraRgnDestroy(r1); if (use_batch) { int k; batch_copyregion(batch_reg, batch_dxs, batch_dys, NPP_nreg, batch_delay); for (k=0; k < NPP_nreg; k++) { sraRgnDestroy(batch_reg[k]); } fprintf(stderr, "FB_COPY: %.4f XX did batch 0x%x %3d su: %dx%d+%d+%d bs: %dx%d+%d+%d\n", dnow() - ntim, (unsigned int) cache_list[nidx].win, nidx, su_w, su_h, su_x, su_y, bs_w, bs_h, bs_x, bs_y); } cache_list[nidx].x = x + np; cache_list[nidx].y = y + np; cache_list[nidx].bs_time = dnow(); if (some_su) { cache_list[nidx].su_time = dnow(); } } else { if (use_batch) { int k; batch_copyregion(batch_reg, batch_dxs, batch_dys, NPP_nreg, batch_delay); for (k=0; k < NPP_nreg; k++) { sraRgnDestroy(batch_reg[k]); } } } if (scaling) { sraRect rects[2]; rects[0].x1 = orig_x; rects[0].y1 = orig_y; rects[0].x2 = orig_w; rects[0].y2 = orig_h; rects[1].x1 = x; rects[1].y1 = y; rects[1].x2 = w; rects[1].y2 = h; push_borders(rects, 2); } } void do_copyrect_drag_move(Window orig_frame, Window frame, int *nidx, int try_batch, int now_x, int now_y, int orig_w, int orig_h, int x, int y, int w, int h, double batch_delay) { int sent_copyrect = 1, obscured = 0; int dx, dy; int use_batch = 0; double ntim = dnow(); sraRegionPtr r0, r1; dx = x - now_x; dy = y - now_y; if (dx == 0 && dy == 0) { return; } ncache_pre_portions(orig_frame, frame, nidx, try_batch, &use_batch, now_x, now_y, orig_w, orig_h, x, y, w, h, ntim); r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y); r1 = sraRgnCreateRect(x, y, x + w, y + h); sraRgnAnd(r1, r0); dx = x - now_x; dy = y - now_y; if (! use_batch) { do_copyregion(r1, dx, dy, 0); if (!fb_push_wait(0.2, FB_COPY)) { fprintf(stderr, "FB_COPY: %.4f 3) FAILED.\n", dnow() - ntim); fb_push_wait(0.1, FB_COPY); } } else { batch_dxs[NPP_nreg] = dx; batch_dys[NPP_nreg] = dy; batch_reg[NPP_nreg++] = sraRgnCreateRgn(r1); } sraRgnDestroy(r0); sraRgnDestroy(r1); if (sent_copyrect) { if (use_batch) { ; } else if (! obscured) { fb_push_wait(0.1, FB_COPY); } else { /* no diff for now... */ fb_push_wait(0.1, FB_COPY); } ncache_post_portions(*nidx, use_batch, now_x, now_y, orig_w, orig_h, x, y, w, h, batch_delay, ntim); } fprintf(stderr, "do_COPY: %.4f -- post_portion done.\n", dnow() - ntim); } void check_macosx_iconify(Window orig_frame, Window frame, int flush) { #ifdef MACOSX static double last = 0.0; double now; int j, m = 5, idx = -1, ok = 0, unmapped = 0; if (! macosx_console) { return; } now = dnow(); if (now < last + 0.3) { return; } last = now; if (ncache > 0 && orig_frame != None) { idx = lookup_win_index(orig_frame); if (idx >= 0) { if (cache_list[idx].map_state == IsUnmapped) { fprintf(stderr, "FAW orig_frame unmapped.\n"); unmapped = 1; m = 3; } } } if (unmapped) { ; } else if (orig_frame && macosxCGS_follow_animation_win(orig_frame, -1, 0)) { fprintf(stderr, "FAW orig_frame %d\n", orig_frame); } else if (0 && frame && macosxCGS_follow_animation_win(frame, -1, 0)) { fprintf(stderr, "FAW frame %d\n", frame); } for (j=0; j 1 && button_mask) fprintf(stderr, "check_wireframe: bm: %d gpi: %d\n", button_mask, got_pointer_input); bdown0 = 0; if (button_mask) { bdown0 = 1; } else if (wireframe_local && display_button_mask) { bdown0 = 2; } if (! bdown0) { return 0; /* no button pressed down */ } gotui = 0; if (got_pointer_input) { gotui = 1; } else if (wireframe_local && display_button_mask) { gotui = 2; } if (!use_threads && !gotui) { return 0; /* need ptr input, e.g. button down, motion */ } if (db > 1) fprintf(stderr, "check_wireframe: %d\n", db); if (db) fprintf(stderr, "\n*** button down!! x: %d y: %d\n", cursor_x, cursor_y); /* * Query where the pointer is and which child of the root * window. We will assume this is the frame the window manager * makes when it reparents the toplevel window. */ X_LOCK; if (! get_wm_frame_pos(&px, &py, &x, &y, &w, &h, &frame, NULL)) { if (db) fprintf(stderr, "NO get_wm_frame_pos-1: 0x%lx\n", frame); X_UNLOCK; #ifdef MACOSX check_macosx_click_frame(); #endif return 0; } X_UNLOCK; if (db) fprintf(stderr, "a: %d wf: %.3f A: %d frm: 0x%lx\n", w*h, wireframe_frac, (dpy_x*dpy_y), frame); /* * apply the percentage size criterion (allow opaque moves for * small windows) */ if ((double) w*h < wireframe_frac * (dpy_x * dpy_y)) { if (db) fprintf(stderr, "small window %.3f\n", ((double) w*h)/(dpy_x * dpy_y)); return 0; } if (db) fprintf(stderr, " frame: x: %d y: %d w: %d h: %d px: %d py: %d fr: 0x%lx\n", x, y, w, h, px, py, frame); /* * see if the pointer is within range of the assumed wm frame * decorations on the edge of the window. */ try_it = near_wm_edge(x, y, w, h, px, py); /* Often Alt+ButtonDown starts a window move: */ if (! try_it && wireframe_mod_state()) { try_it = 1; } if (! try_it) { if (db) fprintf(stderr, "INTERIOR\n"); #ifdef MACOSX check_macosx_click_frame(); #endif return 0; } wireframe_in_progress = 1; if (button_mask_prev) { already_down = 1; } if (! wireframe_str || !strcmp(wireframe_str, WIREFRAME_PARMS)) { int link, latency, netrate; static int didmsg = 0; link = link_rate(&latency, &netrate); if (link == LR_DIALUP || link == LR_BROADBAND) { /* slow link, e.g. dialup, increase timeouts: */ first_event_spin *= 2.0; frame_changed_spin *= 2.0; max_spin *= 2.0; min_draw *= 1.5; if (! didmsg) { rfbLog("increased wireframe timeouts for " "slow network connection.\n"); rfbLog("netrate: %d KB/sec, latency: %d ms\n", netrate, latency); didmsg = 1; } } } /* * pointer() should have snapped the stacking list for us, if * not, do it now (if the XFakeButtonEvent has been flushed by * now the stacking order may be incorrect). */ if (strcmp(wireframe_copyrect, "never")) { if (already_down) { double age = 0.0; /* * see if we can reuse the stack list (pause * with button down) */ if (stack_list_num) { int k, got_me = 0; for (k = stack_list_num -1; k >=0; k--) { if (frame == stack_list[k].win) { got_me = 1; break; } } if (got_me) { age = 1.0; } snapshot_stack_list(0, age); } } if (! stack_list_num) { snapshot_stack_list(0, 0.0); } } /* store initial parameters, we look for changes in them */ orig_frame = frame; orig_px = px; /* pointer position */ orig_py = py; orig_x = x; /* frame position */ orig_y = y; orig_w = w; /* frame size */ orig_h = h; orig_cursor_x = cursor_x; orig_cursor_y = cursor_y; /* this is the box frame we would draw */ box_x = x; box_y = y; box_w = w; box_h = h; dtime0(&tm); last_draw = spin; /* -threads support for check_wireframe() is rough... crash? */ if (use_threads) { /* purge any stored up pointer events: */ pointer(-1, 0, 0, NULL); } if (cursor_noshape_updates_clients(screen)) { try_batch = 0; } if (rotating) { try_batch = 0; } g = got_pointer_input; gd = got_local_pointer_input; while (1) { X_LOCK; XFlush_wr(dpy); X_UNLOCK; /* try do induce/waitfor some more user input */ if (use_threads) { usleep(1000); } else if (drew_box && do_copyrect_drag != 1) { rfbPE(1000); } else { rfbCFD(1000); } if (bdown0 == 2) { /* * This is to just update display_button_mask * which will also update got_local_pointer_input. */ check_x11_pointer(); #if 0 /* what was this for? */ Window frame; int px, py, x, y, w, h; #ifdef MACOSX if (macosx_console) { macosx_get_cursor_pos(&x, &y); } else #endif get_wm_frame_pos(&px, &py, &x, &y, &w, &h, &frame, NULL); #endif } cnt++; spin += dtime(&tm); if (0) fprintf(stderr, "wf-spin: %.3f\n", spin); /* check for any timeouts: */ if (frame_changed) { double delay; /* max time we play this game: */ if (spin > max_spin) { if (db || db2) fprintf(stderr, " SPIN-OUT-MAX: %.3f\n", spin); break_reason = 1; break; } /* watch for pointer events slowing down: */ if (special_t1) { delay = max_spin; } else { delay = 2.0* frame_changed_spin; if (spin > 3.0 * frame_changed_spin) { delay = 1.5 * delay; } } if (spin > last_ptr + delay) { if (db || db2) fprintf(stderr, " SPIN-OUT-NOT-FAST: %.3f\n", spin); break_reason = 2; break; } } else if (got_2nd_pointer) { /* * pointer is moving, max time we wait for wm * move or resize to be detected */ if (spin > frame_changed_spin) { if (db || db2) fprintf(stderr, " SPIN-OUT-NOFRAME-SPIN: %.3f\n", spin); break_reason = 3; break; } } else { /* max time we wait for any pointer input */ if (spin > first_event_spin) { if (db || db2) fprintf(stderr, " SPIN-OUT-NO2ND_PTR: %.3f\n", spin); break_reason = 4; break; } } gpi = 0; /* see if some pointer input occurred: */ if (got_pointer_input > g || (wireframe_local && (got_local_pointer_input > gd))) { if (db) fprintf(stderr, " ++pointer event!! [%02d] dt: %.3f x: %d y: %d mask: %d\n", got_2nd_pointer+1, spin, cursor_x, cursor_y, button_mask); g = got_pointer_input; gd = got_local_pointer_input; gpi = 1; X_LOCK; XFlush_wr(dpy); X_UNLOCK; /* periodically try to let the wm get moving: */ if (!frame_changed && got_2nd_pointer % 4 == 0) { if (got_2nd_pointer == 0) { usleep(50 * 1000); } else { usleep(25 * 1000); } } got_2nd_pointer++; last_ptr = spin; /* * see where the pointer currently is. It may * not be our starting frame (i.e. mouse now * outside of the moving window). */ frame = 0x0; X_LOCK; if (! get_wm_frame_pos(&px, &py, &x, &y, &w, &h, &frame, NULL)) { frame = 0x0; if (db) fprintf(stderr, "NO get_wm_frame_pos-2: 0x%lx\n", frame); } if (frame != orig_frame) { /* see if our original frame is still there */ if (!valid_window(orig_frame, &attr, 1)) { X_UNLOCK; /* our window frame went away! */ win_gone = 1; if (db) fprintf(stderr, "FRAME-GONE: 0x%lx\n", orig_frame); break_reason = 5; break; } if (attr.map_state == IsUnmapped) { X_UNLOCK; /* our window frame is now unmapped! */ win_unmapped = 1; if (db) fprintf(stderr, "FRAME-UNMAPPED: 0x%lx\n", orig_frame); break_reason = 5; break; } if (db) fprintf(stderr, "OUT-OF-FRAME: old: x: %d y: %d px: %d py: %d 0x%lx\n", x, y, px, py, frame); /* new parameters for our frame */ x = attr.x; /* n.b. rootwin is parent */ y = attr.y; w = attr.width; h = attr.height; } X_UNLOCK; if (db) fprintf(stderr, " frame: x: %d y: %d w: %d h: %d px: %d py: %d fr: 0x%lx\n", x, y, w, h, px, py, frame); if (db) fprintf(stderr, " MO,PT,FR: %d/%d %d/%d %d/%d\n", cursor_x - orig_cursor_x, cursor_y - orig_cursor_y, px - orig_px, py - orig_py, x - orig_x, y - orig_y); if (frame_changed && frame != orig_frame) { if (db) fprintf(stderr, "CHANGED and window switch: 0x%lx\n", frame); } if (frame_changed && px - orig_px != x - orig_x) { if (db) fprintf(stderr, "MOVED and diff DX\n"); } if (frame_changed && py - orig_py != y - orig_y) { if (db) fprintf(stderr, "MOVED and diff DY\n"); } /* check and see if our frame has been resized: */ if (!frame_changed && (w != orig_w || h != orig_h)) { int n; if (!already_down) { first_dt_ave += spin; first_dt_cnt++; } n = first_dt_cnt ? first_dt_cnt : 1; frame_changed = 2; if (db) fprintf(stderr, "WIN RESIZE 1st-dt: %.3f\n", first_dt_ave/n); } /* check and see if our frame has been moved: */ if (!frame_changed && (x != orig_x || y != orig_y)) { int n; if (!already_down) { first_dt_ave += spin; first_dt_cnt++; } n = first_dt_cnt ? first_dt_cnt : 1; frame_changed = 1; if (db) fprintf(stderr, "FRAME MOVE 1st-dt: %.3f\n", first_dt_ave/n); } } /* * see if it is time to draw any or a new wireframe box */ if (frame_changed) { int drawit = 0; if (x != box_x || y != box_y) { /* moved since last */ //fprintf(stderr, "DRAW1 %d %d\n", x - box_x, y - box_y); drawit = 1; } else if (w != box_w || h != box_h) { /* resize since last */ drawit = 1; } if (drawit) { int doit = 0; /* * check time (to avoid too much * animations on slow machines * or links). */ if (gpi) { if (spin > last_draw + min_draw || ! drew_box) { doit = 1; } if (macosx_console && doit && !mac_skip) { if (x != box_x && y != box_y && w != box_w && h != box_h) { doit = 0; } else if (!button_mask) { doit = 0; } mac_skip++; } } else { if (drew_box && cnt > last_draw_cnt) { doit = 1; fprintf(stderr, "*** NO GPI DRAW_BOX\n"); } } if (doit) { if (try_copyrect_drag) { if (!ncache_copyrect) { do_copyrect_drag = 0; } else if (w != box_w || h != box_h) { do_copyrect_drag = 0; } else if (do_copyrect_drag < 0) { int idx = lookup_win_index(orig_frame); if (idx < 0) { idx = lookup_win_index(frame); } if (idx >= 0) { if (cache_list[idx].su_time > 0.0) { /* XXX Y */ min_draw *= 0.66; do_copyrect_drag = 1; } nidx = idx; } now_x = orig_x; now_y = orig_y; } if (do_copyrect_drag) { if (orig_w != w || orig_h != h) { do_copyrect_drag = 0; } } } drew_box = 1; if (do_copyrect_drag <= 0) { draw_box(x, y, w, h, 0); fb_push(); /* XXX Y */ rfbPE(1000); } else { #ifndef NO_NCACHE do_copyrect_drag_move(orig_frame, frame, &nidx, try_batch, now_x, now_y, orig_w, orig_h, x, y, w, h, copyrect_drag_delay); now_x = x; now_y = y; if (copyrect_drag_delay == -1.0) { copyrect_drag_delay = 0.04; } #endif } last_draw = spin; last_draw_cnt = cnt; } } box_x = x; box_y = y; box_w = w; box_h = h; } /* * Now (not earlier) check if the button has come back up. * we check here to get a better location and size of * the final window. */ bdown = 0; if (button_mask) { bdown = 1; } else if (wireframe_local && display_button_mask) { bdown = 2; } if (! bdown) { if (db || db2) fprintf(stderr, "NO button_mask\n"); break_reason = 6; break; } } if (! drew_box) { /* nice try, but no move or resize detected. cleanup. */ if (stack_list_num) { stack_list_num = 0; } wireframe_in_progress = 0; if (macosx_console && (break_reason == 6 || break_reason == 5)) { check_macosx_iconify(orig_frame, frame, drew_box); } return 0; } /* remove the wireframe */ if (do_copyrect_drag <= 0) { draw_box(0, 0, 0, 0, 1); fb_push(); /* XXX Y */ } else { do_copyrect_drag_move(orig_frame, frame, &nidx, try_batch, now_x, now_y, orig_w, orig_h, x, y, w, h, -1.0); fb_push_wait(0.15, FB_COPY|FB_MOD); } dx = x - orig_x; dy = y - orig_y; /* * see if we can apply CopyRect or CopyRegion to the change: */ if (!strcmp(wireframe_copyrect, "never")) { ; } else if (win_gone || win_unmapped) { ; } else if (skip_cr_when_scaling("wireframe")) { ; } else if (w != orig_w || h != orig_h) { ; } else if (dx == 0 && dy == 0) { ; } else if (do_copyrect_drag > 0) { clear_win_events(); } else { int spin_ms = (int) (spin * 1000 * 1000); int obscured, sent_copyrect = 0; int nidx = -1; int use_batch = 0; double ntim; /* * set a timescale comparable to the spin time, * but not too short or too long. */ if (spin_ms < 30) { spin_ms = 30; } else if (spin_ms > 400) { spin_ms = 400; } ntim = dnow(); /* try to flush the wireframe removal: */ fprintf(stderr, "\nSEND_COPYRECT %.4f %.4f\n", dnowx(), dnow() - ntim); if (! fb_push_wait(0.15, FB_COPY|FB_MOD)) { fprintf(stderr, "FB_COPY failed, try one more... %.4f", dnow() - ntim); if (! fb_push_wait(0.15, FB_COPY|FB_MOD)) { fprintf(stderr, "FB_COPY failed again! %.4f", dnow() - ntim); } } #ifndef NO_NCACHE ncache_pre_portions(orig_frame, frame, &nidx, try_batch, &use_batch, orig_x, orig_y, orig_w, orig_h, x, y, w, h, ntim); #endif /* 2) try to send a clipped copyrect of translation: */ if (! try_batch) { sent_copyrect = try_copyrect(frame, x, y, w, h, dx, dy, &obscured, NULL, 0.15, NULL); } else { try_copyrect(frame, x, y, w, h, dx, dy, &obscured, NULL, 0.15, &NPP_nreg); /* XXX */ sent_copyrect = 1; use_batch = 1; } if (db) fprintf(stderr, "sent_copyrect: %d - obs: %d frame: 0x%lx\n", sent_copyrect, obscured, frame); if (sent_copyrect) { /* try to push the changes to viewers: */ if (use_batch) { ; } else if (! obscured) { fb_push_wait(0.1, FB_COPY); } else { /* no diff for now... */ fb_push_wait(0.1, FB_COPY); } #ifndef NO_NCACHE ncache_post_portions(nidx, use_batch, orig_x, orig_y, orig_w, orig_h, x, y, w, h, -1.0, ntim); clear_win_events(); #endif if (scaling && !use_batch) { static double last_time = 0.0; double now = dnow(), delay = 0.35; fb_push_wait(0.1, FB_COPY); if (now > last_time + delay) { int xt = x, yt = y; if (clipshift) { xt -= coff_x; yt -= coff_y; } if (subwin) { xt -= off_x; yt -= off_y; } scale_mark(xt, yt, xt+w, yt+h, 1); last_time = now; last_copyrect_fix = now; } } } } if (stack_list_num) { /* clean up stack_list for next time: */ if (break_reason == 1 || break_reason == 2) { /* * save the stack list, perhaps the user has * paused with button down. */ last_save_stacklist = time(NULL); } else { stack_list_num = 0; } } /* final push (for -nowirecopyrect) */ rfbPE(1000); wireframe_in_progress = 0; if (0) { /* No longer needed. see draw_box() */ if (frame_changed && cmap8to24 && multivis_count) { /* handle -8to24 kludge, mark area and check 8bpp... */ int x1, x2, y1, y2, f = 16; x1 = nmin(box_x, orig_x) - f; y1 = nmin(box_y, orig_y) - f; x2 = nmax(box_x + box_w, orig_x + orig_w) + f; y2 = nmax(box_y + box_h, orig_y + orig_h) + f; x1 = nfix(x1, dpy_x); x2 = nfix(x2, dpy_x+1); y1 = nfix(y1, dpy_y); y2 = nfix(y2, dpy_y+1); check_for_multivis(); mark_rect_as_modified(x1, y1, x2, y2, 0); } } urgent_update = 1; if (use_xdamage) { /* DAMAGE can queue ~1000 rectangles for a move */ clear_xdamage_mark_region(NULL, 1); xdamage_scheduled_mark = dnow() + 2.0; } if (macosx_console && (break_reason == 6 || break_reason == 5)) { check_macosx_iconify(orig_frame, frame, drew_box); } return 1; } /* * We need to handle user input, particularly pointer input, carefully. * This function is only called when non-threaded. Note that * rfbProcessEvents() only processes *one* pointer event per call, * so if we interlace it with scan_for_updates(), we can get swamped * with queued up pointer inputs. And if the pointer inputs are inducing * large changes on the screen (e.g. window drags), the whole thing * bogs down miserably and only comes back to life at some time after * one stops moving the mouse. So, to first approximation, we are trying * to eat as much user input here as we can using some hints from the * duration of the previous scan_for_updates() call (in dt). * * note: we do this even under -nofb * * return of 1 means watch_loop should short-circuit and reloop, * return of 0 means watch_loop should proceed to scan_for_updates(). * (this is for pointer_mode == 1 mode, the others do it all internally, * cnt is also only for that mode). */ static void check_user_input2(double dt) { int eaten = 0, miss = 0, max_eat = 50, do_flush = 1; int g, g_in; double spin = 0.0, tm; double quick_spin_fac = 0.40; double grind_spin_time = 0.175; dtime0(&tm); g = g_in = got_pointer_input; if (!got_pointer_input) { return; } /* * Try for some "quick" pointer input processing. * * About as fast as we can, we try to process user input calling * rfbProcessEvents or rfbCheckFds. We do this for a time on * order of the last scan_for_updates() time, dt, but if we stop * getting user input we break out. We will also break out if * we have processed max_eat inputs. * * Note that rfbCheckFds() does not send any framebuffer updates, * so is more what we want here, although it is likely they have * all be sent already. */ while (1) { if (show_multiple_cursors) { rfbPE(1000); } else { rfbCFD(1000); } rfbCFD(0); spin += dtime(&tm); if (spin > quick_spin_fac * dt) { /* get out if spin time comparable to last scan time */ break; } if (got_pointer_input > g) { int i, max_extra = max_eat / 2; g = got_pointer_input; eaten++; for (i=0; i g) { g = got_pointer_input; eaten++; } else if (i > 1) { break; } } X_LOCK; do_flush = 0; if (0) fprintf(stderr, "check_user_input2-A: XFlush %.4f\n", tm); XFlush_wr(dpy); X_UNLOCK; if (eaten < max_eat) { continue; } } else { miss++; } if (miss > 1) { /* 1 means out on 2nd miss */ break; } } if (do_flush) { X_LOCK; if (0) fprintf(stderr, "check_user_input2-B: XFlush %.4f\n", tm); XFlush_wr(dpy); X_UNLOCK; } /* * Probably grinding with a lot of fb I/O if dt is this large. * (need to do this more elegantly) * * Current idea is to spin our wheels here *not* processing any * fb I/O, but still processing the user input. This user input * goes to the X display and changes it, but we don't poll it * while we "rest" here for a time on order of dt, the previous * scan_for_updates() time. We also break out if we miss enough * user input. */ if (dt > grind_spin_time) { int i, ms, split = 30; double shim; /* * Break up our pause into 'split' steps. We get at * most one input per step. */ shim = 0.75 * dt / split; ms = (int) (1000 * shim); /* cutoff how long the pause can be */ if (split * ms > 300) { ms = 300 / split; } spin = 0.0; dtime0(&tm); g = got_pointer_input; miss = 0; for (i=0; i g) { int i, max_extra = max_eat / 2; for (i=0; i g) { g = got_pointer_input; } else if (i > 1) { break; } } X_LOCK; if (0) fprintf(stderr, "check_user_input2-C: XFlush %.4f\n", tm); XFlush_wr(dpy); X_UNLOCK; miss = 0; } else { miss++; } g = got_pointer_input; if (miss > 2) { break; } if (1000 * spin > ms * split) { break; } } } } static void check_user_input3(double dt, double dtr, int tile_diffs) { int allowed_misses, miss_tweak, i, g, g_in; int last_was_miss, consecutive_misses; double spin, spin_max, tm, to, dtm; int rfb_wait_ms = 2; static double dt_cut = 0.075; int gcnt, ginput; static int first = 1; if (dtr || tile_diffs) {} /* unused vars warning: */ if (first) { char *p = getenv("SPIN"); if (p) { double junk; sscanf(p, "%lf,%lf", &dt_cut, &junk); } first = 0; } if (!got_pointer_input) { return; } if (dt < dt_cut) { dt = dt_cut; /* this is to try to avoid early exit */ } spin_max = 0.5; spin = 0.0; /* amount of time spinning */ allowed_misses = 10; /* number of ptr inputs we can miss */ miss_tweak = 8; last_was_miss = 0; consecutive_misses = 1; gcnt = 0; ginput = 0; dtime0(&tm); to = tm; /* last time we did rfbPE() */ g = g_in = got_pointer_input; while (1) { int got_input = 0; gcnt++; if (button_mask) { drag_in_progress = 1; } rfbCFD(rfb_wait_ms * 1000); dtm = dtime(&tm); spin += dtm; if (got_pointer_input == g) { if (last_was_miss) { consecutive_misses++; } last_was_miss = 1; } else { ginput++; if (ginput % miss_tweak == 0) { allowed_misses++; } consecutive_misses = 1; last_was_miss = 0; } if (spin > spin_max) { /* get out if spin time over limit */ break; } else if (got_pointer_input > g) { /* received some input, flush to display. */ got_input = 1; g = got_pointer_input; X_LOCK; XFlush_wr(dpy); X_UNLOCK; } else if (--allowed_misses <= 0) { /* too many misses */ break; } else if (consecutive_misses >=3) { /* too many misses */ break; } else { /* these are misses */ int wms = 0; if (gcnt == 1 && button_mask) { /* * missed our first input, wait * for a defer time. (e.g. on * slow link) hopefully client * will batch them. */ wms = 50; } else if (button_mask) { wms = 10; } else { } if (wms) { usleep(wms * 1000); } } } if (ginput >= 2) { /* try for a couple more quick ones */ for (i=0; i<2; i++) { rfbCFD(rfb_wait_ms * 1000); } } drag_in_progress = 0; } int fb_update_sent(int *count) { static int last_count = 0; int sent = 0, rc = 0; rfbClientIteratorPtr i; rfbClientPtr cl; if (nofb) { return 0; } i = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(i)) ) { #if 0 sent += cl->framebufferUpdateMessagesSent; #else sent += rfbStatGetMessageCountSent(cl, rfbFramebufferUpdate); #endif } rfbReleaseClientIterator(i); if (sent != last_count) { rc = 1; } if (count != NULL) { *count = sent; } last_count = sent; return rc; } static void check_user_input4(double dt, double dtr, int tile_diffs) { int g, g_in, i, ginput, gcnt, tmp; int last_was_miss, consecutive_misses; int min_frame_size = 10; /* 10 tiles */ double spin, tm, to, tc, dtm, rpe_last; int rfb_wait_ms = 2; static double dt_cut = 0.050; static int first = 1; int Btile = tile_x * tile_y * bpp/8; /* Bytes per tile */ double Ttile, dt_use; double screen_rate = 6000000.; /* 5 MB/sec */ double vnccpu_rate = 80 * 100000.; /* 20 KB/sec @ 80X compression */ double net_rate = 50000.; static double Tfac_r = 1.0, Tfac_v = 1.0, Tfac_n = 1.0, Tdelay = 0.001; static double dt_min = -1.0, dt_max = -1.0; double dt_min_fallback = 0.050; static int ssec = 0, total_calls = 0; static int push_frame = 0, update_count = 0; if (first) { char *p = getenv("SPIN"); if (p) { sscanf(p, "%lf,%lf,%lf,%lf", &dt_cut, &Tfac_r, &Tfac_v, &Tfac_n); } first = 0; ssec = time(NULL); if (dtr) {} /* unused vars warning: */ } total_calls++; if (dt_min < 0.0 || dt < dt_min) { if (dt > 0.0) { dt_min = dt; } } if (dt_min < 0.0) { /* sensible value for the very 1st call if dt = 0.0 */ dt_min = dt_min_fallback; } if (dt_max < 0.0 || dt > dt_max) { dt_max = dt; } if (total_calls > 30 && dt_min > 0.0) { static int first = 1; /* * dt_min will soon be the quickest time to do * one scan_for_updates with no tiles copied. * use this (instead of copy_tiles) to estimate * screen read rate. */ screen_rate = (main_bytes_per_line * ntiles_y) / dt_min; if (first) { rfbLog("measured screen read rate: %.2f Bytes/sec\n", screen_rate); } first = 0; } dtime0(&tm); if (dt < dt_cut) { dt_use = dt_cut; } else { dt_use = dt; } if (push_frame) { int cnt, iter = 0; double tp, push_spin = 0.0; dtime0(&tp); while (push_spin < dt_use * 0.5) { fb_update_sent(&cnt); if (cnt != update_count) { break; } /* damn, they didn't push our frame! */ iter++; rfbPE(rfb_wait_ms * 1000); push_spin += dtime(&tp); } if (iter) { X_LOCK; XFlush_wr(dpy); X_UNLOCK; } push_frame = 0; update_count = 0; } /* * when we first enter we require some pointer input */ if (!got_pointer_input) { return; } vnccpu_rate = get_raw_rate(); if ((tmp = get_read_rate()) != 0) { screen_rate = (double) tmp; } if ((tmp = get_net_rate()) != 0) { net_rate = (double) tmp; } net_rate = (vnccpu_rate/get_cmp_rate()) * net_rate; if ((tmp = get_net_latency()) != 0) { Tdelay = 0.5 * ((double) tmp)/1000.; } Ttile = Btile * (Tfac_r/screen_rate + Tfac_v/vnccpu_rate + Tfac_n/net_rate); spin = 0.0; /* amount of time spinning */ last_was_miss = 0; consecutive_misses = 1; gcnt = 0; ginput = 0; rpe_last = to = tc = tm; /* last time we did rfbPE() */ g = g_in = got_pointer_input; tile_diffs = 0; /* reset our knowlegde of tile_diffs to zero */ while (1) { int got_input = 0; gcnt++; if (button_mask) { /* this varible is used by our pointer handler */ drag_in_progress = 1; } /* turn libvncserver crank to process events: */ rfbCFD(rfb_wait_ms * 1000); dtm = dtime(&tm); spin += dtm; if ( (gcnt == 1 && got_pointer_input > g) || tm-tc > 2*dt_min) { tile_diffs = scan_for_updates(1); tc = tm; } if (got_pointer_input == g) { if (last_was_miss) { consecutive_misses++; } last_was_miss = 1; } else { ginput++; consecutive_misses = 1; last_was_miss = 0; } if (tile_diffs > min_frame_size && spin > Ttile * tile_diffs + Tdelay) { /* we think we can push the frame */ push_frame = 1; fb_update_sent(&update_count); break; } else if (got_pointer_input > g) { /* received some input, flush it to display. */ got_input = 1; g = got_pointer_input; X_LOCK; XFlush_wr(dpy); X_UNLOCK; } else if (consecutive_misses >= 2) { /* too many misses in a row */ break; } else { /* these are pointer input misses */ int wms; if (gcnt == 1 && button_mask) { /* * missed our first input, wait for * a defer time. (e.g. on slow link) * hopefully client will batch many * of them for the next read. */ wms = 50; } else if (button_mask) { wms = 10; } else { wms = 0; } if (wms) { usleep(wms * 1000); } } } if (ginput >= 2) { /* try for a couple more quick ones */ for (i=0; i<2; i++) { rfbCFD(rfb_wait_ms * 1000); } } drag_in_progress = 0; } int check_user_input(double dt, double dtr, int tile_diffs, int *cnt) { #ifdef MACOSX if (! macosx_console) { RAWFB_RET(0) } #else RAWFB_RET(0) #endif if (use_xrecord) { int rc = check_xrecord(); /* * 0: nothing found, proceed to other user input schemes. * 1: events found, want to do a screen update now. * 2: events found, want to loop back for some more. * 3: events found, want to loop back for some more, * and not have rfbPE() called. * * For 0, we precede below, otherwise return rc-1. */ if (debug_scroll && rc > 1) fprintf(stderr, " CXR: check_user_input ret %d\n", rc - 1); if (rc == 0) { ; /* proceed below. */ } else { return rc - 1; } } if (wireframe) { if (check_wireframe()) { fprintf(stderr, "check_wireframe: 1\n"); return 0; } //fprintf(stderr, "check_wireframe: 0\n"); } if (pointer_mode == 1) { if ((got_user_input || ui_skip < 0) && *cnt % ui_skip != 0) { /* every ui_skip-th drops thru to scan */ *cnt++; X_LOCK; XFlush_wr(dpy); X_UNLOCK; return 1; /* short circuit watch_loop */ } else { return 0; } } if (pointer_mode >= 2 && pointer_mode <= 4) { if (got_keyboard_input) { /* * for these modes, short circuit watch_loop on * *keyboard* input. */ if (*cnt % ui_skip != 0) { *cnt++; return 1; } } /* otherwise continue below with pointer input method */ } if (pointer_mode == 2) { check_user_input2(dt); } else if (pointer_mode == 3) { check_user_input3(dt, dtr, tile_diffs); } else if (pointer_mode == 4) { check_user_input4(dt, dtr, tile_diffs); } return 0; } #if defined(NO_NCACHE) || (NO_X11 && !defined(MACOSX)) int check_ncache(int a, int b) { if (!a || !b) {} return; } int lookup_win_index(Window win) { if (!win) {} return -1; } int find_rect(int idx, int x, int y, int w, int h) { if (!idx || !x || !y || !w || !h) {} return 0; } #else /* maybe ncache.c it if works */ winattr_t* cache_list = NULL; int cache_list_num = 0; int cache_list_len = 0; void snapshot_cache_list(int free_only, double allowed_age) { static double last_snap = 0.0, last_free = 0.0; double now; int num, rc, i; unsigned int ui; Window r, w; Window *list; int start = 512; if (! cache_list) { cache_list = (winattr_t *) calloc(start*sizeof(winattr_t), 1); cache_list_num = 0; cache_list_len = start; } dtime0(&now); if (free_only) { /* we really don't free it, just reset to zero windows */ cache_list_num = 0; last_free = now; return; } if (cache_list_num && now < last_snap + allowed_age) { return; } cache_list_num = 0; last_free = now; #ifdef MACOSX if (! macosx_console) { RAWFB_RET_VOID } #else RAWFB_RET_VOID #endif #if NO_X11 && !defined(MACOSX) return; #else X_LOCK; /* no need to trap error since rootwin */ rc = XQueryTree_wr(dpy, rootwin, &r, &w, &list, &ui); X_UNLOCK; num = (int) ui; if (! rc) { cache_list_num = 0; last_free = now; last_snap = 0.0; return; } last_snap = now; if (num > cache_list_len) { int n = 2*num; n = num + 3; free(cache_list); cache_list = (winattr_t *) calloc(n*sizeof(winattr_t), 1); cache_list_len = n; } for (i=0; i= 0) { recent[rlast] = win; recidx[rlast++] = idx; rlast = rlast % NRECENT; } } if (idx < 0) { fprintf(stderr, "recentC(fail): %d 0x%x\n", idx, (unsigned int) win); s3++; } if (s1 + s2 + s3 >= 100) { fprintf(stderr, "lookup_win_index recent hit stats: %d/%d/%d\n", s1, s2, s3); s1 = s2 = s3 = 0; } return idx; } int lookup_free_index(void) { int k; if (rfree >= 0) { if (cache_list[rfree].win == None) { fprintf(stderr, "lookup_freeA: %d\n", rfree); return rfree; } } rfree = -1; for(k=0; k= cache_list_len) { int i, n = 2*cache_list_len; winattr_t *cache_new; fprintf(stderr, "lookup_free_index: growing cache_list_len: %d -> %d\n", cache_list_len, n); cache_new = (winattr_t *) calloc(n*sizeof(winattr_t), 1); for (i=0; i= cache_list_num) { fprintf(stderr, "free_rect: bad index: %d\n", idx); clean_up_exit(1); } x = cache_list[idx].bs_x; y = cache_list[idx].bs_y; w = cache_list[idx].bs_w; h = cache_list[idx].bs_h; if (x < 0) { CLEAR(idx); if (dnow() > last_client + 5) fprintf(stderr, "free_rect: already bs_x invalidated: %d bs_x: %d\n", idx, x); return 1; } r2 = sraRgnCreateRect(x, y, x+w, y+h); n = get_bs_n(y); if (n >= 0) { r1 = rect_reg[n]; sraRgnOr(r1, r2); ok = 1; } if (zero_rects) { sraRgnOr(zero_rects, r2); x = cache_list[idx].su_x; y = cache_list[idx].su_y; w = cache_list[idx].su_w; h = cache_list[idx].su_h; if (x >= 0) { sraRgnDestroy(r2); r2 = sraRgnCreateRect(x, y, x+w, y+h); sraRgnOr(zero_rects, r2); } } sraRgnDestroy(r2); CLEAR(idx); if (! ok) fprintf(stderr, "**** free_rect: not-found %d\n", idx); return ok; } int fr_BIG1 = 0; int fr_BIG2 = 0; int fr_REGION = 0; int fr_GRID = 0; int fr_EXPIRE = 0; int fr_FORCE = 0; int fr_FAIL = 0; int fr_BIG1t = 0; int fr_BIG2t = 0; int fr_REGIONt = 0; int fr_GRIDt = 0; int fr_EXPIREt = 0; int fr_FORCEt = 0; int fr_FAILt = 0; void expire_rects1(int idx, int w, int h, int *x_hit, int *y_hit, int big1, int big2, int cram) { sraRegionPtr r1, r2, r3; int x = -1, y = -1, n; if (*x_hit < 0) { int i, k, old[10], N = 4; double dold[10], fa, d, d1, d2, d3; int a0 = w * h, a1; for (k=1; k<=N; k++) { old[k] = -1; dold[k] = -1.0; } for (i=0; i wb || h > hb) { continue; } if (wb == 0 || hb == 0) { continue; } if (a0 == 0) { continue; } if (i == idx) { continue; } a1 = wb * hb; fa = ((double) a1) / a0; k = (int) fa; if (k < 1) k = 1; if (k > N) continue; d1 = cache_list[i].time; d2 = cache_list[i].bs_time; d3 = cache_list[i].su_time; d = d1; if (d2 > d) d = d2; if (d3 > d) d = d3; if (dold[k] == -1.0 || d < dold[k]) { old[k] = i; dold[k] = d; } } for (k=1; k<=N; k++) { if (old[k] >= 0) { int ik = old[k]; int k_x = cache_list[ik].bs_x; int k_y = cache_list[ik].bs_y; int k_w = cache_list[ik].bs_w; int k_h = cache_list[ik].bs_h; fprintf(stderr, ">>**--**>> found rect via EXPIRE: %d 0x%x -- %dx%d+%d+%d %d %d -- %dx%d+%d+%d A: %d/%d\n", ik, (unsigned int) cache_list[ik].win, w, h, x, y, *x_hit, *y_hit, k_w, k_h, k_x, k_y, k_w * k_h, w * h); free_rect(ik); fr_EXPIRE++; fr_EXPIREt++; *x_hit = k_x; *y_hit = k_y; n = get_bs_n(*y_hit); if (n >= 0) { r1 = rect_reg[n]; r2 = sraRgnCreateRect(*x_hit, *y_hit, *x_hit + w, *y_hit + h); sraRgnSubtract(r1, r2); sraRgnDestroy(r2); } else { fprintf(stderr, "failure to find y n in find_rect\n"); clean_up_exit(1); } break; } } } /* next, force ourselves into some corner, expiring many */ if (*x_hit < 0) { int corner_x = (int) (2 * rfac()); int corner_y = (int) (2 * rfac()); int x0 = 0, y0 = 0, i, nrand, nr = ncache/2; if (nr == 1) { nrand = 1; } else { if (! big1) { nrand = 1; } else { if (big2 && nr > 2) { nrand = 1 + (int) ((nr - 2) * rfac()); nrand += 2; } else { nrand = 1 + (int) ((nr - 1) * rfac()); nrand += 1; } } } if (nrand < 0 || nrand > nr) { nrand = nr; } if (cram && big1) { corner_x = 1; } y0 += dpy_y; if (nrand > 1) { y0 += 2 * (nrand - 1) * dpy_y; } if (corner_y) { y0 += dpy_y - h; } if (corner_x) { x0 += dpy_x - w; } r1 = sraRgnCreateRect(x0, y0, x0+w, y0+h); for (i=0; i dpy_y) { continue; } r2 = sraRgnCreateRect(xb, yb, xb+wb, yb+hb); if (sraRgnAnd(r2, r1)) { free_rect(i); } sraRgnDestroy(r2); } *x_hit = x0; *y_hit = y0; r3 = rect_reg[2*nrand-1]; sraRgnSubtract(r3, r1); sraRgnDestroy(r1); fprintf(stderr, ">>**--**>> found rect via FORCE: %dx%d+%d+%d -- %d %d\n", w, h, x, y, *x_hit, *y_hit); fr_FORCE++; fr_FORCEt++; } } void expire_rects2(int idx, int w, int h, int *x_hit, int *y_hit, int big1, int big2, int cram) { sraRegionPtr r1, r2, r3; int x = -1, y = -1, n, i, j, k; int nwgt_max = 128, nwgt = 0; int type[128]; int val[4][128]; double wgt[128], norm; int Expire = 1, Force = 2; int do_expire = 1; int do_force = 1; double now = dnow(), r; if (do_expire) { int old[10], N = 4; double dold[10], fa, d, d1, d2, d3; int a0 = w * h, a1; for (k=1; k<=N; k++) { old[k] = -1; dold[k] = -1.0; } for (i=0; i wb || h > hb) { continue; } if (wb == 0 || hb == 0) { continue; } if (a0 == 0) { continue; } if (i == idx) { continue; } a1 = wb * hb; fa = ((double) a1) / a0; k = (int) fa; if (k < 1) k = 1; if (k > N) continue; d1 = cache_list[i].time; d2 = cache_list[i].bs_time; d3 = cache_list[i].su_time; d = d1; if (d2 > d) d = d2; if (d3 > d) d = d3; if (dold[k] == -1.0 || d < dold[k]) { old[k] = i; dold[k] = d; } } for (k=1; k<=N; k++) { if (old[k] >= 0) { int ik = old[k]; int k_w = cache_list[ik].bs_w; int k_h = cache_list[ik].bs_h; wgt[nwgt] = (now - dold[k]) / (k_w * k_h); type[nwgt] = Expire; val[0][nwgt] = ik; fprintf(stderr, "Expire[%02d] %9.5f age=%9.4f area=%8d need=%8d\n", nwgt, 10000 * wgt[nwgt], now - dold[k], k_w * k_h, w*h); nwgt++; if (nwgt >= nwgt_max) { break; } } } } /* next, force ourselves into some corner, expiring many rect */ if (do_force) { int corner_x, corner_y; int x0, y0; for (n = 1; n < ncache; n += 2) { if (big1 && ncache > 2 && n == 1) { continue; } if (big2 && ncache > 4 && n <= 3) { continue; } for (corner_x = 0; corner_x < 2; corner_x++) { if (cram && big1 && corner_x == 0) { continue; } for (corner_y = 0; corner_y < 2; corner_y++) { double age = 0.0, area = 0.0, a; double d, d1, d2, d3, score; int nc = 0; x0 = 0; y0 = 0; y0 += n * dpy_y; if (corner_y) { y0 += dpy_y - h; } if (corner_x) { x0 += dpy_x - w; } r1 = sraRgnCreateRect(x0, y0, x0+w, y0+h); for (i=0; i dpy_y) { continue; } r2 = sraRgnCreateRect(xb, yb, xb+wb, yb+hb); if (! sraRgnAnd(r2, r1)) { sraRgnDestroy(r2); continue; } sraRgnDestroy(r2); a = wb * hb; d1 = cache_list[i].time; d2 = cache_list[i].bs_time; d3 = cache_list[i].su_time; d = d1; if (d2 > d) d = d2; if (d3 > d) d = d3; area += a; age += (now - d) * a; nc++; } if (nc == 0) { score = 999999.9; } else { age = age / area; score = age / area; } wgt[nwgt] = score; type[nwgt] = Force; val[0][nwgt] = n; val[1][nwgt] = x0; val[2][nwgt] = y0; fprintf(stderr, "Force [%02d] %9.5f age=%9.4f area=%8d need=%8d\n", nwgt, 10000 * wgt[nwgt], age, (int) area, w*h); nwgt++; if (nwgt >= nwgt_max) break; sraRgnDestroy(r1); } if (nwgt >= nwgt_max) break; } if (nwgt >= nwgt_max) break; } } if (nwgt == 0) { fprintf(stderr, "nwgt=0\n"); *x_hit = -1; return; } norm = 0.0; for (i=0; i < nwgt; i++) { norm += wgt[i]; } for (i=0; i < nwgt; i++) { wgt[i] /= norm; } r = rfac(); norm = 0.0; for (j=0; j < nwgt; j++) { norm += wgt[j]; fprintf(stderr, "j=%2d acc=%.6f r=%.6f\n", j, norm, r); if (r < norm) { break; } } if (j >= nwgt) { j = nwgt - 1; } if (type[j] == Expire) { int ik = val[0][j]; int k_x = cache_list[ik].bs_x; int k_y = cache_list[ik].bs_y; int k_w = cache_list[ik].bs_w; int k_h = cache_list[ik].bs_h; fprintf(stderr, ">>**--**>> found rect [%d] via RAN EXPIRE: %d 0x%x -- %dx%d+%d+%d %d %d -- %dx%d+%d+%d A: %d/%d\n", get_bs_n(*y_hit), ik, (unsigned int) cache_list[ik].win, w, h, x, y, *x_hit, *y_hit, k_w, k_h, k_x, k_y, k_w * k_h, w * h); free_rect(ik); fr_EXPIRE++; fr_EXPIREt++; *x_hit = k_x; *y_hit = k_y; n = get_bs_n(*y_hit); if (n >= 0) { r1 = rect_reg[n]; r2 = sraRgnCreateRect(*x_hit, *y_hit, *x_hit + w, *y_hit + h); sraRgnSubtract(r1, r2); sraRgnDestroy(r2); } else { fprintf(stderr, "failure to find y n in find_rect\n"); clean_up_exit(1); } } else if (type[j] == Force) { int x0 = val[1][j]; int y0 = val[2][j]; n = val[0][j]; r1 = sraRgnCreateRect(x0, y0, x0+w, y0+h); for (i=0; i dpy_y) { continue; } r2 = sraRgnCreateRect(xb, yb, xb+wb, yb+hb); if (sraRgnAnd(r2, r1)) { free_rect(i); } sraRgnDestroy(r2); } *x_hit = x0; *y_hit = y0; r3 = rect_reg[2*n-1]; sraRgnSubtract(r3, r1); sraRgnDestroy(r1); fprintf(stderr, ">>**--**>> found rect [%d] via RAN FORCE: %dx%d+%d+%d -- %d %d\n", n, w, h, x, y, *x_hit, *y_hit); fr_FORCE++; fr_FORCEt++; } } void expire_rects(int idx, int w, int h, int *x_hit, int *y_hit, int big1, int big2, int cram) { int method = 2; if (method == 1) { expire_rects1(idx, w, h, x_hit, y_hit, big1, big2, cram); } else if (method == 2) { expire_rects2(idx, w, h, x_hit, y_hit, big1, big2, cram); } } int find_rect(int idx, int x, int y, int w, int h) { sraRegionPtr r1, r2; sraRectangleIterator *iter; sraRect rt; int n, x_hit = -1, y_hit = -1; int big1 = 0, big2 = 0, cram = 0; double fac1 = 0.1, fac2 = 0.25; double last_clean = 0.0; double now = dnow(); if (rect_reg[1] == NULL) { for (n = 1; n <= ncache; n++) { rect_reg[n] = sraRgnCreateRect(0, n * dpy_y, dpy_x, (n+1) * dpy_y); } } else if (now > last_clean + 60) { //fprintf(stderr, "free_rect: cleaning up regions:\n"); last_clean = now; for (n = 1; n < ncache; n += 2) { int i, n2 = n+1; /* n */ sraRgnDestroy(rect_reg[n]); r1 = sraRgnCreateRect(0, n * dpy_y, dpy_x, (n+1) * dpy_y); for (i=0; i= cache_list_num) { fprintf(stderr, "free_rect: bad index: %d\n", idx); clean_up_exit(1); } cache_list[idx].bs_x = -1; cache_list[idx].su_x = -1; cache_list[idx].bs_time = 0.0; cache_list[idx].su_time = 0.0; if (ncache_pad) { x -= ncache_pad; y -= ncache_pad; w += 2 * ncache_pad; h += 2 * ncache_pad; } if (ncache <= 2) { cram = 1; fac2 = 0.45; } else if (ncache <= 4) { fac1 = 0.18; fac2 = 0.35; } if (w * h > fac1 * (dpy_x * dpy_y)) { big1 = 1; } if (w * h > fac2 * (dpy_x * dpy_y)) { big2 = 1; } if (w > dpy_x || h > dpy_y) { fprintf(stderr, ">>**--**>> BIG1 rect: %dx%d+%d+%d -- %d %d\n", w, h, x, y, x_hit, y_hit); fr_BIG1++; fr_BIG1t++; return 0; } if (w == dpy_x && h && dpy_y) { fprintf(stderr, ">>**--**>> BIG1 rect: %dx%d+%d+%d -- %d %d (FULL DISPLAY)\n", w, h, x, y, x_hit, y_hit); fr_BIG1++; fr_BIG1t++; return 0; } if (cram && big2) { fprintf(stderr, ">>**--**>> BIG2 rect: %dx%d+%d+%d -- %d %d\n", w, h, x, y, x_hit, y_hit); fr_BIG2++; fr_BIG2t++; return 0; } /* first try individual rects of unused region */ for (n = 1; n < ncache; n += 2) { r1 = rect_reg[n]; r2 = NULL; if (big1 && n == 1 && ncache > 2) { continue; } if (big2 && n <= 3 && ncache > 4) { continue; } iter = sraRgnGetIterator(r1); while (sraRgnIteratorNext(iter, &rt)) { int rw = rt.x2 - rt.x1; int rh = rt.y2 - rt.y1; if (cram && big1 && rt.x1 < dpy_x/4) { continue; } if (rw >= w && rh >= h) { x_hit = rt.x1; y_hit = rt.y1; if (cram && big1) { x_hit = rt.x2 - w; } r2 = sraRgnCreateRect(x_hit, y_hit, x_hit + w, y_hit + h); break; } } sraRgnReleaseIterator(iter); if (r2 != NULL) { fprintf(stderr, ">>**--**>> found rect via REGION: %dx%d+%d+%d -- %d %d\n", w, h, x, y, x_hit, y_hit); fr_REGION++; fr_REGIONt++; sraRgnSubtract(r1, r2); sraRgnDestroy(r2); break; } } /* next try moving corner to grid points */ if (x_hit < 0) { for (n = 1; n < ncache; n += 2) { int rx, ry, Nx = 48, Ny = 24, ny = n * dpy_y; if (big1 && n == 1 && ncache > 2) { continue; } if (big2 && n == 3 && ncache > 4) { continue; } r1 = sraRgnCreateRect(0, n * dpy_y, dpy_x, (n+1) * dpy_y); sraRgnSubtract(r1, rect_reg[n]); r2 = NULL; rx = 0; while (rx + w <= dpy_x) { ry = 0; if (cram && big1 && rx < dpy_x/4) { rx += dpy_x/Nx; continue; } while (ry + h <= dpy_y) { r2 = sraRgnCreateRect(rx, ry+ny, rx + w, ry+ny + h); if (sraRgnAnd(r2, r1)) { sraRgnDestroy(r2); r2 = NULL; } else { sraRgnDestroy(r2); r2 = sraRgnCreateRect(rx, ry+ny, rx + w, ry+ny + h); x_hit = rx; y_hit = ry+ny; } ry += dpy_y/Ny; if (r2) break; } rx += dpy_x/Nx; if (r2) break; } sraRgnDestroy(r1); if (r2 != NULL) { sraRgnSubtract(rect_reg[n], r2); sraRgnDestroy(r2); fprintf(stderr, ">>**--**>> found rect via GRID: %dx%d+%d+%d -- %d %d\n", w, h, x, y, x_hit, y_hit); fr_GRID++; fr_GRIDt++; break; } } } /* next, try expiring the oldest/smallest used bs/su rectangle we fit in */ if (x_hit < 0) { expire_rects(idx, w, h, &x_hit, &y_hit, big1, big2, cram); } cache_list[idx].bs_x = x_hit; cache_list[idx].bs_y = y_hit; cache_list[idx].bs_w = w; cache_list[idx].bs_h = h; cache_list[idx].su_x = x_hit; cache_list[idx].su_y = y_hit + dpy_y; cache_list[idx].su_w = w; cache_list[idx].su_h = h; if (x_hit < 0) { /* bad news, can it still happen? */ fprintf(stderr, ">>**--**>> *FAIL rect: %dx%d+%d+%d -- %d %d\n", w, h, x, y, x_hit, y_hit); fr_FAIL++; fr_FAILt++; return 0; } else { //fprintf(stderr, ">>**--**>> found rect: %dx%d+%d+%d -- %d %d\n", w, h, x, y, x_hit, y_hit); } if (zero_rects) { r1 = sraRgnCreateRect(x_hit, y_hit, x_hit+w, y_hit+h); sraRgnSubtract(zero_rects, r1); sraRgnDestroy(r1); r1 = sraRgnCreateRect(x_hit, y_hit+dpy_y, x_hit+w, y_hit+dpy_y+h); sraRgnSubtract(zero_rects, r1); sraRgnDestroy(r1); } return 1; } static void cache_cr(sraRegionPtr r, int dx, int dy, double d0, double d1, int *nbatch) { if (sraRgnEmpty(r)) { return; } if (nbatch == NULL) { if (!fb_push_wait(d0, FB_COPY)) { fb_push_wait(d0/2, FB_COPY); } do_copyregion(r, dx, dy, 0); if (!fb_push_wait(d1, FB_COPY)) { fb_push_wait(d1/2, FB_COPY); } } else { batch_dxs[*nbatch] = dx; batch_dys[*nbatch] = dy; batch_reg[*nbatch] = sraRgnCreateRgn(r); (*nbatch)++; } } double save_delay0 = 0.02; double restore_delay0 = 0.02; double save_delay1 = 0.05; double restore_delay1 = 0.05; static double dtA, dtB; int valid_wr(int idx, Window win, XWindowAttributes *attr) { #ifdef MACOSX if (macosx_console) { /* this is all to avoid animation changing WxH+X+Y... */ if (idx >= 0) { int rc = valid_window(win, attr, 1); attr->x = cache_list[idx].x; attr->y = cache_list[idx].y; attr->width = cache_list[idx].width; attr->height = cache_list[idx].height; return rc; } else { return valid_window(win, attr, 1); } } #else if (!idx) {} #endif return valid_window(win, attr, 1); } int bs_save(int idx, int *nbatch) { Window win = cache_list[idx].win; XWindowAttributes attr; int x1, y1, w1, h1; int x2, y2, w2, h2; int x, y, w, h; int dx, dy, rc = 1; sraRegionPtr r, r0; x1 = cache_list[idx].x; y1 = cache_list[idx].y; w1 = cache_list[idx].width; h1 = cache_list[idx].height; fprintf(stderr, "backingstore save: 0x%x %3d \n", (unsigned int) win, idx); X_LOCK; if (! valid_wr(idx, win, &attr)) { fprintf(stderr, "bs_save: not a valid X window: 0x%x\n", (unsigned int) win); /* XXX Y */ // DELETE(idx); X_UNLOCK; cache_list[idx].valid = 0; return 0; } X_UNLOCK; x2 = attr.x; y2 = attr.y; w2 = attr.width; h2 = attr.height; if (cache_list[idx].bs_x < 0) { rc = find_rect(idx, x2, y2, w2, h2); } else if (w2 > cache_list[idx].bs_w || h2 > cache_list[idx].bs_h) { free_rect(idx); rc = find_rect(idx, x2, y2, w2, h2); } x = cache_list[idx].bs_x; y = cache_list[idx].bs_y; w = cache_list[idx].bs_w; h = cache_list[idx].bs_h; if (x < 0 || ! rc) { STORE(idx, win, attr); fprintf(stderr, "BS_save: FAIL FOR: %d\n", idx); return 0; } if (ncache_pad) { x2 -= ncache_pad; y2 -= ncache_pad; w2 += 2 * ncache_pad; h2 += 2 * ncache_pad; } if (clipshift) { x2 -= coff_x; y2 -= coff_y; } dx = x - x2; dy = y - y2; r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y); r = sraRgnCreateRect(x2, y2, x2+w2, y2+h2); sraRgnAnd(r, r0); sraRgnOffset(r, dx, dy); dtA = dnowx(); fprintf(stderr, "BS_save: %.4f %d dx=%d dy=%d\n", dtA, idx, dx, dy); if (w2 > 0 && h2 > 0) { cache_cr(r, dx, dy, save_delay0, save_delay1, nbatch); } dtB = dnowx(); fprintf(stderr, "BS_save: %.4f %.2f %d done. %dx%d+%d+%d %dx%d+%d+%d %.2f %.2f\n", dtB, dtB-dtA, idx, w1, h1, x1, y1, w2, h2, x2, y2, cache_list[idx].bs_time - x11vnc_start, dnowx()); sraRgnDestroy(r0); sraRgnDestroy(r); STORE(idx, win, attr); cache_list[idx].bs_time = dnow(); return 1; } int su_save(int idx, int *nbatch) { Window win = cache_list[idx].win; XWindowAttributes attr; int x1, y1, w1, h1; int x2, y2, w2, h2; int x, y, w, h; int dx, dy, rc = 1; sraRegionPtr r, r0; fprintf(stderr, "save-unders save: 0x%x %3d \n", (unsigned int) win, idx); x1 = cache_list[idx].x; y1 = cache_list[idx].y; w1 = cache_list[idx].width; h1 = cache_list[idx].height; X_LOCK; if (! valid_wr(idx, win, &attr)) { fprintf(stderr, "su_save: not a valid X window: 0x%x\n", (unsigned int) win); /* XXX Y */ // DELETE(idx); X_UNLOCK; cache_list[idx].valid = 0; return 0; } X_UNLOCK; x2 = attr.x; y2 = attr.y; w2 = attr.width; h2 = attr.height; if (cache_list[idx].bs_x < 0) { rc = find_rect(idx, x2, y2, w2, h2); } else if (w2 > cache_list[idx].su_w || h2 > cache_list[idx].su_h) { free_rect(idx); rc = find_rect(idx, x2, y2, w2, h2); } x = cache_list[idx].su_x; y = cache_list[idx].su_y; w = cache_list[idx].su_w; h = cache_list[idx].su_h; if (x < 0 || ! rc) { STORE(idx, win, attr); fprintf(stderr, "SU_save: FAIL FOR: %d\n", idx); return 0; } if (ncache_pad) { x2 -= ncache_pad; y2 -= ncache_pad; w2 += 2 * ncache_pad; h2 += 2 * ncache_pad; } if (clipshift) { x2 -= coff_x; y2 -= coff_y; } dx = x - x2; dy = y - y2; r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y); r = sraRgnCreateRect(x2, y2, x2+w2, y2+h2); sraRgnAnd(r, r0); sraRgnOffset(r, dx, dy); dtA = dnowx(); fprintf(stderr, "SU_save: %.4f %d dx=%d dy=%d\n", dtA, idx, dx, dy); if (w2 > 0 && h2 > 0) { cache_cr(r, dx, dy, save_delay0, save_delay1, nbatch); } dtB = dnowx(); fprintf(stderr, "SU_save: %.4f %.2f %d done. %dx%d+%d+%d %dx%d+%d+%d %.2f %.2f\n", dtB, dtB-dtA, idx, w1, h1, x1, y1, w2, h2, x2, y2, cache_list[idx].su_time - x11vnc_start, dnowx()); sraRgnDestroy(r0); sraRgnDestroy(r); STORE(idx, win, attr); cache_list[idx].su_time = dnow(); return 1; } int bs_restore(int idx, int *nbatch, int nopad) { Window win = cache_list[idx].win; XWindowAttributes attr; int x1, y1, w1, h1; int x2, y2, w2, h2; int x, y, w, h; int dx, dy; sraRegionPtr r, r0; fprintf(stderr, "backingstore restore: 0x%x %3d \n", (unsigned int) win, idx); x1 = cache_list[idx].x; y1 = cache_list[idx].y; w1 = cache_list[idx].width; h1 = cache_list[idx].height; X_LOCK; if (! valid_wr(idx, win, &attr)) { fprintf(stderr, "BS_restore: not a valid X window: 0x%x\n", (unsigned int) win); DELETE(idx); X_UNLOCK; return 0; } X_UNLOCK; x2 = attr.x; y2 = attr.y; w2 = attr.width; h2 = attr.height; x = cache_list[idx].bs_x; y = cache_list[idx].bs_y; w = cache_list[idx].bs_w; h = cache_list[idx].bs_h; if (x < 0 || cache_list[idx].bs_time == 0.0) { STORE(idx, win, attr); return 0; } if (ncache_pad) { if (nopad) { x += ncache_pad; y += ncache_pad; w -= 2 * ncache_pad; h -= 2 * ncache_pad; } else { x2 -= ncache_pad; y2 -= ncache_pad; w2 += 2 * ncache_pad; h2 += 2 * ncache_pad; } } if (clipshift) { x2 -= coff_x; y2 -= coff_y; } dx = x2 - x; dy = y2 - y; if (w2 > w) { w2 = w; } if (h2 > h) { h2 = h; } r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y); r = sraRgnCreateRect(x, y, x+w2, y+h2); sraRgnOffset(r, dx, dy); sraRgnAnd(r, r0); dtA = dnowx(); fprintf(stderr, "BS_rest: %.4f %d dx=%d dy=%d\n", dtA, idx, dx, dy); if (w2 > 0 && h2 > 0) { cache_cr(r, dx, dy, restore_delay0, restore_delay1, nbatch); } dtB = dnowx(); fprintf(stderr, "BS_rest: %.4f %.2f %d done. %dx%d+%d+%d %dx%d+%d+%d %.2f %.2f\n", dtB, dtB-dtA, idx, w1, h1, x1, y1, w2, h2, x2, y2, cache_list[idx].bs_time - x11vnc_start, dnowx()); sraRgnDestroy(r0); sraRgnDestroy(r); STORE(idx, win, attr); return 1; } int su_restore(int idx, int *nbatch, int nopad) { Window win = cache_list[idx].win; XWindowAttributes attr; int x1, y1, w1, h1; int x2, y2, w2, h2; int x, y, w, h; int dx, dy; sraRegionPtr r, r0; int invalid = 0; fprintf(stderr, "save-unders restore: 0x%x %3d \n", (unsigned int) win, idx); x1 = cache_list[idx].x; y1 = cache_list[idx].y; w1 = cache_list[idx].width; h1 = cache_list[idx].height; X_LOCK; if (! valid_wr(idx, win, &attr)) { fprintf(stderr, "SU_restore: not a valid X window: 0x%x\n", (unsigned int) win); invalid = 1; x2 = x1; y2 = y1; w2 = w1; h2 = h1; } else { x2 = attr.x; y2 = attr.y; w2 = attr.width; h2 = attr.height; } X_UNLOCK; x = cache_list[idx].su_x; y = cache_list[idx].su_y; w = cache_list[idx].su_w; h = cache_list[idx].su_h; if (x < 0 || cache_list[idx].bs_x < 0 || cache_list[idx].su_time == 0.0) { fprintf(stderr, "SU_rest: su_x/bs_x/su_time: %d %d %.3f\n", x, cache_list[idx].bs_x, cache_list[idx].su_time); if (invalid) { DELETE(idx); } return 0; } if (ncache_pad) { if (nopad) { x += ncache_pad; y += ncache_pad; w -= 2 * ncache_pad; h -= 2 * ncache_pad; } else { x2 -= ncache_pad; y2 -= ncache_pad; w2 += 2 * ncache_pad; h2 += 2 * ncache_pad; } } if (clipshift) { x2 -= coff_x; y2 -= coff_y; } dx = x2 - x; dy = y2 - y; if (w2 > w) { w2 = w; } if (h2 > h) { h2 = h; } r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y); r = sraRgnCreateRect(x, y, x+w2, y+h2); sraRgnOffset(r, dx, dy); sraRgnAnd(r, r0); dtA = dnowx(); fprintf(stderr, "SU_rest: %.4f %d dx=%d dy=%d\n", dtA, idx, dx, dy); if (w2 > 0 && h2 > 0) { cache_cr(r, dx, dy, restore_delay0, restore_delay1, nbatch); } dtB = dnowx(); fprintf(stderr, "SU_rest: %.4f %.2f %d done. %dx%d+%d+%d %dx%d+%d+%d %.2f %.2f\n", dtB, dtB-dtA, idx, w1, h1, x1, y1, w2, h2, x2, y2, cache_list[idx].su_time - x11vnc_start, dnowx()); sraRgnDestroy(r0); sraRgnDestroy(r); if (invalid) { DELETE(idx); return 0; } else { STORE(idx, win, attr); return 1; } } void check_zero_rects(void) { sraRect rt; sraRectangleIterator *iter; if (! zero_rects) { zero_rects = sraRgnCreate(); } if (sraRgnEmpty(zero_rects)) { return; } iter = sraRgnGetIterator(zero_rects); while (sraRgnIteratorNext(iter, &rt)) { zero_fb(rt.x1, rt.y1, rt.x2, rt.y2); mark_rect_as_modified(rt.x1, rt.y1, rt.x2, rt.y2, 0); } sraRgnReleaseIterator(iter); sraRgnMakeEmpty(zero_rects); } void block_stats(void) { int n, k, s1, s2; static int t = -1; int vcnt, icnt, tcnt, vtot = 0, itot = 0, ttot = 0; t++; for (n = 1; n < ncache+1; n += 2) { double area = 0.0, frac; vcnt = 0; icnt = 0; tcnt = 0; for(k=0; k= 0) { ttot++; } } if (y < n*dpy_y || y > (n+1)*dpy_y) { continue; } if (n != 1) { X_LOCK; rc = valid_window(win, &attr, 1); X_UNLOCK; } if (rc) { vcnt++; } else { icnt++; } if (x >= 0) { tcnt++; } if (x < 0) { continue; } area += cache_list[k].width * cache_list[k].height; if (! rc && ! macosx_console) { char *u = getenv("USER"); if (u && !strcmp(u, "runge")) fprintf(stderr, "\a"); fprintf(stderr, "\n *** UNRECLAIMED WINDOW: 0x%x %dx%d+%d+%d\n\n", (unsigned int) win, w, h, x, y); DELETE(k); } if (t < 3 || (t % 4) == 0 || hack_val || macosx_console) { double t1 = cache_list[k].su_time; double t2 = cache_list[k].bs_time; if (t1 > 0.0) {t1 = dnow() - t1;} else {t1 = -1.0;} if (t2 > 0.0) {t2 = dnow() - t2;} else {t2 = -1.0;} fprintf(stderr, " [%02d] %04d 0x%08x bs: %04dx%04d+%04d+%05d vw: %04dx%04d+%04d+%04d cl: %04dx%04d+%04d+%04d map=%d su=%9.3f bs=%9.3f\n", n, k, (unsigned int) win, w, h, x, y, attr.width, attr.height, attr.x, attr.y, cache_list[k].width, cache_list[k].height, cache_list[k].x, cache_list[k].y, attr.map_state == IsViewable, t1, t2); } } frac = area /(dpy_x * dpy_y); fprintf(stderr, "block[%02d] %.3f %8d trak/val/inval: %d/%d/%d of %d\n", n, frac, (int) area, tcnt, vcnt, icnt, vcnt+icnt); } fprintf(stderr, "\n"); fprintf(stderr, "block: trak/val/inval %d/%d/%d of %d\n", ttot, vtot, itot, vtot+itot); s1 = fr_REGION + fr_GRID + fr_EXPIRE + fr_FORCE + fr_BIG1 + fr_BIG2 + fr_FAIL; s2 = fr_REGIONt + fr_GRIDt + fr_EXPIREt + fr_FORCEt + fr_BIG1t + fr_BIG2t + fr_FAILt; fprintf(stderr, "\n"); fprintf(stderr, "find_rect: REGION/GRID/EXPIRE/FORCE - BIG1/BIG2/FAIL %d/%d/%d/%d - %d/%d/%d of %d\n", fr_REGION, fr_GRID, fr_EXPIRE, fr_FORCE, fr_BIG1, fr_BIG2, fr_FAIL, s1); fprintf(stderr, " totals: %d/%d/%d/%d - %d/%d/%d of %d\n", fr_REGIONt, fr_GRIDt, fr_EXPIREt, fr_FORCEt, fr_BIG1t, fr_BIG2t, fr_FAILt, s2); fr_BIG1 = 0; fr_BIG2 = 0; fr_REGION = 0; fr_GRID = 0; fr_EXPIRE = 0; fr_FORCE = 0; fr_FAIL = 0; fprintf(stderr, "\n"); } #define NSCHED 64 Window sched_bs[NSCHED]; double sched_tm[NSCHED]; double last_sched_bs = 0.0; #define SCHED(w) \ { \ int k, save = -1, empty = 1; \ for (k=0; k < NSCHED; k++) { \ if (sched_bs[k] == None) { \ save = k; \ } \ if (sched_bs[k] == w) { \ save = k; \ empty = 0; \ break; \ } \ } \ if (save >= 0) { \ sched_bs[save] = w; \ if (empty) { \ sched_tm[save] = dnow(); \ fprintf(stderr, "SCHED: %d %f\n", save, dnowx()); \ } \ } \ } void xselectinput(Window w, unsigned long evmask, int sync) { #if NO_X11 trapped_xerror = 0; trapped_xioerror = 0; #else XErrorHandler old_handler1; XIOErrorHandler old_handler2; old_handler1 = XSetErrorHandler(trap_xerror); old_handler2 = XSetIOErrorHandler(trap_xioerror); trapped_xerror = 0; trapped_xioerror = 0; XSelectInput(dpy, w, evmask); /* * We seem to need to synchronize right away since the window * might go away quickly. */ if (sync) { XSync(dpy, False); } else { XFlush_wr(dpy); } XSetErrorHandler(old_handler1); XSetIOErrorHandler(old_handler2); #endif if (trapped_xerror) { fprintf(stderr, "XSELECTINPUT: trapped X Error."); } if (trapped_xioerror) { fprintf(stderr, "XSELECTINPUT: trapped XIO Error."); } if (sync) fprintf(stderr, "XSELECTINPUT: 0x%x sync=%d err=%d/%d\n", (unsigned int) w, sync, trapped_xerror, trapped_xioerror); } Bool xcheckmaskevent(Display *d, long mask, XEvent *ev) { #ifdef MACOSX if (macosx_console) { if (macosx_checkevent(ev)) { return True; } else { return False; } } #endif RAWFB_RET(False); #if NO_X11 return False; #else return XCheckMaskEvent(d, mask, ev); #endif } #include #define EVMAX 2048 XEvent Ev[EVMAX]; int Ev_done[EVMAX]; int Ev_area[EVMAX]; Window Ev_win[EVMAX]; Window Ev_map[EVMAX]; Window Ev_unmap[EVMAX]; sraRect Ev_rects[EVMAX]; int check_ncache(int reset, int mode) { static double last_root = 0.0; static int first = 1; static int last_client_count = -1; int i, k, n; int n_CN = 0, n_RN = 0, n_DN = 0, n_ON = 0, n_MN = 0, n_UN = 0; int n_VN = 0, n_VN_p = 0, n_VN_u = 0; double now, refresh = 60.0; Window win, win2; XWindowAttributes attr; unsigned long all_ev = SubstructureNotifyMask|StructureNotifyMask|VisibilityChangeMask; unsigned long win_ev = StructureNotifyMask|VisibilityChangeMask; int try_batch = 1; /* XXX Y */ int use_batch = 0; int nreg = 0, *nbatch; static int didtopmost = 0; int create_cnt, create_tot; int pixels = 0; int nrects = 0; #ifdef MACOSX if (! macosx_console) { RAWFB_RET(-1) } #else RAWFB_RET(-1) #endif if (! screen) { return -1; } now = dnow(); if (ncache0) { if (reset) { ; } else if (! client_count || !ncache) { static double last_purge = 0.0; double delay = client_count ? 0.5 : 2.0; if (now > last_purge + delay) { int c = 0; XEvent ev; X_LOCK; while (xcheckmaskevent(dpy, all_ev, &ev)) { c++; } X_UNLOCK; last_purge = dnow(); if (c) fprintf(stderr, "check_ncache purged %d events\n", c); } if (!client_count && last_client_count >= 0 && client_count != last_client_count) { /* this should use less RAM when no clients */ do_new_fb(1); } last_client_count = client_count; return -1; } } last_client_count = client_count; if (ncache && ! ncache0) { ncache0 = ncache; } if (! ncache || ! ncache0) { return -1; } if (subwin) { return -1; } if (reset) { rfbLog("check_ncache: resetting cache\n"); for (i=0; i < cache_list_num; i++) { free_rect(i); CLEAR(i); } for (n = 1; n <= ncache; n++) { if (rect_reg[n] != NULL) { sraRgnDestroy(rect_reg[n]); rect_reg[n] = NULL; } } zero_fb(0, dpy_y, dpy_x, (ncache+1)*dpy_y); mark_rect_as_modified(0, dpy_y, dpy_x, (ncache+1)*dpy_y, 0); return -1; } if (first) { int dx = 10, dy = 24, ds = 0; int Dx = dpy_x, Dy = dpy_y; first = 0; for (i=0; i < NRECENT; i++) { recent[i] = None; } for (i=0; i < NSCHED; i++) { sched_bs[i] = None; } rlast = 0; X_LOCK; /* event leak with client_count == 0 */ xselectinput_rootwin |= SubstructureNotifyMask; XSelectInput_wr(dpy, rootwin, xselectinput_rootwin); X_UNLOCK; if (scaling) { Dx = scaled_x; Dy = scaled_y; } if (!rotating_same) { int t = Dx; Dx = Dy; Dy = t; } for (i = 0; i < 3; i++) { rfbDrawString(screen, &default8x16Font, dx, ds + Dy+1*dy, "This is the Pixel buffer cache region. Your VNC Viewer is not hiding it from you.", white_pixel()); rfbDrawString(screen, &default8x16Font, dx, ds + Dy+2*dy, "Try resizing your VNC Viewer so you don't see it !!", white_pixel()); rfbDrawString(screen, &default8x16Font, dx, ds + Dy+3*dy, "To disable run the server with: x11vnc -ncache 0 ...", white_pixel()); rfbDrawString(screen, &default8x16Font, dx, ds + Dy+4*dy, "More info: http://www.karlrunge.com/x11vnc/#faq-client-caching", white_pixel()); ds += 8 * dy; } snapshot_cache_list(0, 100.0); fprintf(stderr, "cache_list_num: %d\n", cache_list_num); for (i=0; i < cache_list_num; i++) { CLEAR(i); } for (n = 1; n <= ncache; n++) { rect_reg[n] = NULL; } } if (now < last_client + 2) { return -1; } if (hack_val == 2) { block_stats(); hack_val = 1; } #ifdef MACOSX if (macosx_console) { static double last_all_windows = 0.0; if (! macosx_checkevent(NULL)) { if (now > last_all_windows + 0.05) { macosxCGS_get_all_windows(); last_all_windows = dnow(); } } /* XXX Y */ rootwin = -1; } #endif if (now > last_root + refresh) { Window topmapped = None; fprintf(stderr, "\n**** checking cache_list[%d]\n\n", cache_list_num); block_stats(); for(k=0; k now - refresh) { ; } else if (valid_window(win, &attr, 1)) { STORE(k, win, attr); //fprintf(stderr, "STORE(%2d) %03dx%03d+%03d+%03d\n", k, cache_list[k].width, cache_list[k].height, cache_list[k].x, cache_list[k].y); if (! cache_list[k].selectinput) { xselectinput(win, win_ev, 0); CLEAR(k); cache_list[k].selectinput = 1; } else { ; } if (attr.map_state == IsViewable) { topmapped = win; } valid = 1; } else { fprintf(stderr, "DELETE(%d) %dx%d+%d+%d\n", k, cache_list[k].width, cache_list[k].height, cache_list[k].x, cache_list[k].y); DELETE(k); } X_UNLOCK; /* XXX Y */ if (valid) { if (cache_list[k].create_cnt && attr.map_state != IsViewable && cache_list[k].map_cnt == 0) { if (cache_list[k].bs_x >= 0) { fprintf(stderr, "Created window never mapped: freeing(%d) 0x%x\n", k, (unsigned int) win); free_rect(k); } } } } last_root = dnow(); if (0 && ! didtopmost && topmapped != None) { int idx = lookup_win_index(topmapped); if (idx >= 0) { if (! macosx_console) { bs_save(idx, NULL); } } } didtopmost = 1; } check_zero_rects(); if (now > last_sched_bs + 0.2) { static double last_sched_vis = 0.0; int nr = 0, *bat = NULL; if (try_batch) { bat = &nr; } for (i=0; i < NSCHED; i++) { if (sched_bs[i] != None) { int idx; win = sched_bs[i]; if (now < sched_tm[i] + 0.65) { continue; } idx = lookup_win_index(win); if (idx >= 0) { int aw = cache_list[idx].width; int ah = cache_list[idx].height; if (cache_list[idx].map_state != IsViewable) { ; } else if (cache_list[idx].vis_state != VisibilityUnobscured) { ; } else if (aw * ah < 64 * 64) { ; } else { fprintf(stderr, "*NEW BS_save: 0x%x %d %d %d\n", (unsigned int) win, aw, ah, cache_list[idx].map_state); bs_save(idx, bat); } } } sched_bs[i] = None; } if (nr) { int k; batch_copyregion(batch_reg, batch_dxs, batch_dys, nr, -1.0); for (k=0; k < nr; k++) { sraRgnDestroy(batch_reg[k]); } } last_sched_bs = dnow(); if (now > last_sched_vis + 3.0) { for (i=0; i < cache_list_num; i++) { if (cache_list[i].map_state == IsViewable) { if (cache_list[i].vis_state == VisibilityUnobscured) { if (cache_list[i].valid) { if (cache_list[i].win != None) { SCHED(cache_list[i].win) } } } } } last_sched_vis = dnow(); } } n = 0; create_tot = 0; X_LOCK; while (xcheckmaskevent(dpy, all_ev, &Ev[n])) { int type = Ev[n].type; win = Ev[n].xany.window; Ev_done[n] = 0; Ev_area[n] = 0; Ev_win[n] = None; Ev_map[n] = None; Ev_unmap[n] = None; if (win == rootwin) { if (type == CreateNotify) { n++; create_tot++; n_CN++; } else if (type == ReparentNotify) { n++; n_RN++; } else { /* skip rest */ #if 0 Window w = None; if (type == DestroyNotify) w = Ev[n].xdestroywindow.window; if (type == UnmapNotify) w = Ev[n].xunmap.window; if (type == MapNotify) w = Ev[n].xmap.window; if (type == Expose) w = Ev[n].xexpose.window; if (type == ConfigureNotify) w = Ev[n].xconfigure.window; if (type != ConfigureNotify) fprintf(stderr, "root: skip %s for 0x%x\n", Etype(type), (unsigned int) w); #endif } } else { if (type == ReparentNotify) { n++; n_RN++; } else if (type == DestroyNotify) { n++; n_DN++; } else if (type == ConfigureNotify) { n++; n_ON++; } else if (type == VisibilityNotify) { if (Ev[n].xvisibility.state == VisibilityUnobscured) { n_VN_u++; } else { n_VN_p++; } n++; n_VN++; } else if (type == MapNotify) { n++; n_MN++; } else if (type == UnmapNotify) { n++; n_UN++; } else { /* skip rest */ fprintf(stderr, "----- skip %s\n", Etype(type)); } } if (n >= EVMAX) { break; } } X_UNLOCK; if (n == 0) { return 0; } fprintf(stderr, "\n"); rfbLog("IN check_ncache() %d events.\n", n); for (i=0; i < n; i++) { XEvent ev = Ev[i]; Ev_win[i] = ev.xany.window; } if (try_batch) { use_batch = 1; } if (rotating) { use_batch = 0; } if (cursor_noshape_updates_clients(screen)) { use_batch = 0; } if (! use_batch) { nbatch = NULL; } else { nreg = 0; nbatch = &nreg; } create_cnt = 0; X_LOCK; for (i=0; i < n; i++) { XEvent ev; int type, idx = -1; if (Ev_done[i]) continue; win = Ev_win[i]; ev = Ev[i]; type = ev.type; Ev_done[i] = 1; if (win == rootwin) { if (type == CreateNotify) { int x=0, y=0, w=0, h=0; int valid = 0; win2 = ev.xcreatewindow.window; idx = lookup_win_index(win2); if (idx < 0) { idx = lookup_free_index(); if (idx < 0) { continue; } CLEAR(idx); } if (valid_window(win2, &attr, 1)) { STORE(idx, win2, attr); CLEAR(idx); x=attr.x; y=attr.y; w=attr.width; h=attr.height; /* XXX Y */ if (create_tot <= 6 && create_cnt++ < 3) { if (w*h > 64 * 64) { X_UNLOCK; su_save(idx, nbatch); X_LOCK; if (cache_list[idx].valid) { SCHED(win2) } create_cnt++; } } if (cache_list[idx].valid) { xselectinput(win2, win_ev, 1); cache_list[idx].selectinput = 1; cache_list[idx].create_cnt = 1; valid = 1; } else { DELETE(idx); } } fprintf(stderr, "root%02d: ** CreateNotify 0x%x %3d -- %dx%d+%d+%d valid=%d\n", i, (unsigned int) win2, idx, w, h, x, y, valid); } else if (type == ReparentNotify) { if (ev.xreparent.parent != rootwin) { win2 = ev.xreparent.window; if (win2 != rootwin) { idx = lookup_win_index(win2); fprintf(stderr, "root%02d: ReparentNotifyRM 0x%x %3d\n", i, (unsigned int) win2, idx); if (idx >= 0) { DELETE(idx); } xselectinput(win2, 0, 1); } } } } else { if (type == ConfigureNotify) { int x_new, y_new, w_new, h_new; int x_old, y_old, w_old, h_old; Window oabove = None; idx = lookup_win_index(win); if (idx >= 0) { oabove = cache_list[idx].above; } fprintf(stderr, "----%02d: ConfigureNotify 0x%x %3d -- above: 0x%x -> 0x%x %dx%d+%d+%d\n", i, (unsigned int) win, idx, (unsigned int) oabove, (unsigned int) ev.xconfigure.above, ev.xconfigure.width, ev.xconfigure.height, ev.xconfigure.x, ev.xconfigure.y); if (idx < 0) { continue; } x_new = ev.xconfigure.x; y_new = ev.xconfigure.y; w_new = ev.xconfigure.width; h_new = ev.xconfigure.height; x_old = cache_list[idx].x; y_old = cache_list[idx].y; w_old = cache_list[idx].width; h_old = cache_list[idx].height; if (x_old != x_new || y_old != y_new) { /* invalidate su */ cache_list[idx].su_time = 0.0; fprintf(stderr, " invalidate su: 0x%x xy: %d/%d %d/%d \n", (unsigned int) win, x_old, y_old, x_new, y_new); } if (w_old != w_new || h_old != h_new) { /* invalidate bs */ cache_list[idx].bs_time = 0.0; fprintf(stderr, " invalidate bs: 0x%x wh: %d/%d %d/%d \n", (unsigned int) win, w_old, h_old, w_new, h_new); } cache_list[idx].x = x_new; cache_list[idx].y = y_new; cache_list[idx].width = w_new; cache_list[idx].height = h_new; cache_list[idx].above = ev.xconfigure.above; cache_list[idx].time = dnow(); } else if (type == VisibilityNotify) { int state = ev.xvisibility.state; idx = lookup_win_index(win); fprintf(stderr, "----%02d: VisibilityNotify 0x%x %3d state: %s U/P %d/%d\n", i, (unsigned int) win, idx, VState(state), n_VN_u, n_VN_p); if (idx < 0) { continue; } if (macosx_console && n_VN_p == 0) { ; /* XXXX not working well yet with UnmapNotify ... */ } else if (state == VisibilityUnobscured) { int i2, ok = 1; for (i2=0; i2 < n; i2++) { if (Ev_map[i2] == win) { ok = 0; break; } } if (ncache <= 2) { ok = 0; } if (ok) { X_UNLOCK; bs_restore(idx, nbatch, 1); X_LOCK; cache_list[idx].time = dnow(); cache_list[idx].vis_cnt++; Ev_map[i] = win; Ev_rects[nrects].x1 = cache_list[idx].x; Ev_rects[nrects].y1 = cache_list[idx].y; Ev_rects[nrects].x2 = cache_list[idx].width; Ev_rects[nrects].y2 = cache_list[idx].height; nrects++; SCHED(win) } } cache_list[idx].vis_state = state; } else if (type == MapNotify) { idx = lookup_win_index(win); fprintf(stderr, "----%02d: MapNotify 0x%x %3d\n", i, (unsigned int) win, idx); if (idx < 0) { continue; } if (cache_list[idx].map_state == IsUnmapped || macosx_console) { X_UNLOCK; su_save(idx, nbatch); bs_restore(idx, nbatch, 0); if (macosx_console) { #ifdef MACOSX macosxCGS_follow_animation_win(win, -1, 1); if (valid_window(win, &attr, 1)) { STORE(idx, win, attr); SCHED(win); } /* XXX Y */ if (cache_list[idx].vis_state == -1) { cache_list[idx].vis_state = VisibilityUnobscured; } #endif } X_LOCK; pixels += cache_list[idx].width * cache_list[idx].height; cache_list[idx].time = dnow(); cache_list[idx].map_cnt++; Ev_map[i] = win; Ev_rects[nrects].x1 = cache_list[idx].x; Ev_rects[nrects].y1 = cache_list[idx].y; Ev_rects[nrects].x2 = cache_list[idx].width; Ev_rects[nrects].y2 = cache_list[idx].height; nrects++; } cache_list[idx].map_state = IsViewable; } else if (type == UnmapNotify) { idx = lookup_win_index(win); fprintf(stderr, "----%02d: UnmapNotify 0x%x %3d\n", i, (unsigned int) win, idx); if (idx < 0) { continue; } if (macosx_console) { if (mode == 2) { cache_list[idx].map_state = IsViewable; } } if (cache_list[idx].map_state == IsViewable || macosx_console) { X_UNLOCK; bs_save(idx, nbatch); su_restore(idx, nbatch, 0); X_LOCK; pixels += cache_list[idx].width * cache_list[idx].height; cache_list[idx].time = dnow(); cache_list[idx].unmap_cnt++; Ev_unmap[i] = win; Ev_rects[nrects].x1 = cache_list[idx].x; Ev_rects[nrects].y1 = cache_list[idx].y; Ev_rects[nrects].x2 = cache_list[idx].width; Ev_rects[nrects].y2 = cache_list[idx].height; nrects++; } cache_list[idx].map_state = IsUnmapped; } else if (type == ReparentNotify) { if (ev.xreparent.parent != rootwin) { win2 = ev.xreparent.window; if (win2 != rootwin) { idx = lookup_win_index(win2); fprintf(stderr, "----%02d: ReparentNotifyRM 0x%x %3d\n", i, (unsigned int) win2, idx); if (idx >= 0) { DELETE(idx); } xselectinput(win2, 0, 1); } } } else if (type == DestroyNotify) { win2 = ev.xdestroywindow.window; idx = lookup_win_index(win2); fprintf(stderr, "----%02d: DestroyNotify 0x%x %3d\n", i, (unsigned int) win2, idx); if (idx >= 0) { DELETE(idx); } } } } X_UNLOCK; if (use_batch && nreg) { int k; batch_copyregion(batch_reg, batch_dxs, batch_dys, nreg, -1.0); for (k=0; k < nreg; k++) { sraRgnDestroy(batch_reg[k]); } } if (nrects) { if (scaling) { push_borders(Ev_rects, nrects); } } rfbLog("OUT check_ncache(): %.6f events: %d pixels: %d\n", dnow() - now, n, pixels); return pixels; } #endif