You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libtdevnc/x11vnc/userinput.c

7786 lines
175 KiB

/* -- 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) {
#if !NO_X11
Window r, c;
XWindowAttributes attr;
Bool ret;
int rootx, rooty, wx, wy;
unsigned int mask;
#endif
#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
if (!px || !py || !x || !y || !w || !h || !frame || !win) {}
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;
}
#else
r = g = b = 0;
cmap = 0;
cdef.pixel = 0;
#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<ks_max; k++) {
if (xrecord_scroll_keysym((rfbKeySym) k)) {
nkeys++;
}
}
}
nkeys++; /* first key, i.e. no commas. */
p = str = strdup(scroll_key_list_str);
while(*p) {
if (*p == ',') {
nkeys++; /* additional key. */
}
p++;
}
nkeys++; /* exclude/include 0 element */
nkeys++; /* trailing NoSymbol */
scroll_key_list = (KeySym *) malloc(nkeys*sizeof(KeySym));
for (i=0; i<nkeys; i++) {
scroll_key_list[i] = NoSymbol;
}
if (*str == '-') {
scroll_key_list[0] = 1;
p = strtok(str+1, ",");
} else {
p = strtok(str, ",");
}
i = 1;
while (p) {
if (!strcmp(p, "builtin")) {
int k;
if (saw_builtin) {
p = strtok(NULL, ",");
continue;
}
saw_builtin = 1;
for (k=1; k<ks_max; k++) {
if (xrecord_scroll_keysym((rfbKeySym) k)) {
scroll_key_list[i++] = (rfbKeySym) k;
}
}
} else {
unsigned int in;
if (sscanf(p, "%u", &in) == 1) {
scroll_key_list[i++] = (rfbKeySym) in;
} else if (sscanf(p, "0x%x", &in) == 1) {
scroll_key_list[i++] = (rfbKeySym) in;
} else if (XStringToKeysym(p) != NoSymbol) {
scroll_key_list[i++] = XStringToKeysym(p);
} else {
rfbLog("initialize_scroll_keys: skip unknown "
"keysym: %s\n", p);
}
}
p = strtok(NULL, ",");
}
free(str);
}
static void destroy_str_list(char **list) {
int i = 0;
if (! list) {
return;
}
while (list[i] != NULL) {
free(list[i++]);
}
free(list);
}
void initialize_scroll_matches(void) {
char *str, *imp = "__IMPOSSIBLE_STR__";
int i, n, nkey, nmouse;
destroy_str_list(scroll_good_all);
scroll_good_all = NULL;
destroy_str_list(scroll_good_key);
scroll_good_key = NULL;
destroy_str_list(scroll_good_mouse);
scroll_good_mouse = NULL;
destroy_str_list(scroll_skip_all);
scroll_skip_all = NULL;
destroy_str_list(scroll_skip_key);
scroll_skip_key = NULL;
destroy_str_list(scroll_skip_mouse);
scroll_skip_mouse = NULL;
/* scroll_good: */
if (scroll_good_str != NULL && *scroll_good_str != '\0') {
str = scroll_good_str;
} else {
str = scroll_good_str0;
}
scroll_good_all = create_str_list(str);
nkey = 0;
nmouse = 0;
n = 0;
while (scroll_good_all[n] != NULL) {
char *s = scroll_good_all[n++];
if (strstr(s, "KEY:") == s) nkey++;
if (strstr(s, "MOUSE:") == s) nmouse++;
}
if (nkey++) {
scroll_good_key = (char **) malloc(nkey*sizeof(char *));
for (i=0; i<nkey; i++) scroll_good_key[i] = NULL;
}
if (nmouse++) {
scroll_good_mouse = (char **) malloc(nmouse*sizeof(char *));
for (i=0; i<nmouse; i++) scroll_good_mouse[i] = NULL;
}
nkey = 0;
nmouse = 0;
for (i=0; i<n; i++) {
char *s = scroll_good_all[i];
if (strstr(s, "KEY:") == s) {
scroll_good_key[nkey++] = strdup(s+strlen("KEY:"));
free(s);
scroll_good_all[i] = strdup(imp);
} else if (strstr(s, "MOUSE:") == s) {
scroll_good_mouse[nmouse++]=strdup(s+strlen("MOUSE:"));
free(s);
scroll_good_all[i] = strdup(imp);
}
}
/* scroll_skip: */
if (scroll_skip_str != NULL && *scroll_skip_str != '\0') {
str = scroll_skip_str;
} else {
str = scroll_skip_str0;
}
scroll_skip_all = create_str_list(str);
nkey = 0;
nmouse = 0;
n = 0;
while (scroll_skip_all[n] != NULL) {
char *s = scroll_skip_all[n++];
if (strstr(s, "KEY:") == s) nkey++;
if (strstr(s, "MOUSE:") == s) nmouse++;
}
if (nkey++) {
scroll_skip_key = (char **) malloc(nkey*sizeof(char *));
for (i=0; i<nkey; i++) scroll_skip_key[i] = NULL;
}
if (nmouse++) {
scroll_skip_mouse = (char **) malloc(nmouse*sizeof(char *));
for (i=0; i<nmouse; i++) scroll_skip_mouse[i] = NULL;
}
nkey = 0;
nmouse = 0;
for (i=0; i<n; i++) {
char *s = scroll_skip_all[i];
if (strstr(s, "KEY:") == s) {
scroll_skip_key[nkey++] = strdup(s+strlen("KEY:"));
free(s);
scroll_skip_all[i] = strdup(imp);
} else if (strstr(s, "MOUSE:") == s) {
scroll_skip_mouse[nmouse++]=strdup(s+strlen("MOUSE:"));
free(s);
scroll_skip_all[i] = strdup(imp);
}
}
}
void initialize_scroll_term(void) {
char *str;
int n;
destroy_str_list(scroll_term);
scroll_term = NULL;
if (scroll_term_str != NULL && *scroll_term_str != '\0') {
str = scroll_term_str;
} else {
str = scroll_term_str0;
}
if (!strcmp(str, "none")) {
return;
}
scroll_term = create_str_list(str);
n = 0;
while (scroll_term[n] != NULL) {
char *s = scroll_good_all[n++];
/* pull parameters out at some point */
s = NULL;
}
}
void initialize_max_keyrepeat(void) {
char *str;
int lo, hi;
if (max_keyrepeat_str != NULL && *max_keyrepeat_str != '\0') {
str = max_keyrepeat_str;
} else {
str = max_keyrepeat_str0;
}
if (sscanf(str, "%d-%d", &lo, &hi) != 2) {
rfbLog("skipping invalid -scr_keyrepeat string: %s\n", str);
sscanf(max_keyrepeat_str0, "%d-%d", &lo, &hi);
}
max_keyrepeat_lo = lo;
max_keyrepeat_hi = hi;
if (max_keyrepeat_lo < 1) {
max_keyrepeat_lo = 1;
}
if (max_keyrepeat_hi > 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();
}
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<y2; j++) {
memmove(dst, src, w);
dst += stride;
src += stride;
}
} else {
dst += (y2 - y1 - 1)*stride;
src += (y2 - y1 - 1)*stride;
for (j=y2-1; 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<SCR_RESULTS_MAX; i++) {
scroll_results[i].win = None;
scroll_results[i].time = 0.0;
scroll_results[i].result = 0;
}
first = 0;
}
if (win == None) {
return 0;
}
if (set == SCR_NONE) {
/* lookup case */
for (i=0; i<SCR_RESULTS_MAX; i++) {
if (win == scroll_results[i].win) {
return scroll_results[i].result;
}
if (scroll_results[i].win == None) {
break;
}
}
return 0;
}
for (i=0; i<SCR_RESULTS_MAX; i++) {
if (oldest == -1.0 || scroll_results[i].time < oldest) {
next_index = i;
oldest = scroll_results[i].time;
}
if (win == scroll_results[i].win) {
index = i;
break;
}
if (next_index >= 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<max_eat; i++) {
int cont = 0;
gp = got_pointer_calls;
gk = got_keyboard_calls;
rfbCFD(0);
if (got_pointer_calls > 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<max_ptr_eat; i++) {
rfbCFD(0);
if (got_pointer_input > 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<blackouts; i++) {
bo_rect = sraRgnCreateRect(blackr[i].x1,
blackr[i].y1, blackr[i].x2, blackr[i].y2);
sraRgnSubtract(rect, bo_rect);
sraRgnDestroy(bo_rect);
}
}
if (!nbatch) {
do_copyregion(rect, dx, dy, 0);
} else {
batch_dxs[*nbatch] = dx;
batch_dys[*nbatch] = dy;
batch_reg[*nbatch] = sraRgnCreateRgn(rect);
(*nbatch)++;
}
sraRgnDestroy(rect);
sent_copyrect = 1;
*obscured = 1; /* set to avoid an aggressive push */
} else if (stack_list_num || dt_bad) {
int k, tx1, tx2, ty1, ty2;
sraRegionPtr moved_win, tmp_win, whole;
sraRectangleIterator *iter;
sraRect rect;
int saw_me = 0;
int orig_x, orig_y;
int boff, bwin;
XWindowAttributes attr;
orig_x = x - dx;
orig_y = y - dy;
tx1 = nfix(orig_x, dpy_x);
ty1 = nfix(orig_y, dpy_y);
tx2 = nfix(orig_x+w, dpy_x+1);
ty2 = nfix(orig_y+h, dpy_y+1);
if (db2) fprintf(stderr, "moved_win: %4d %3d, %4d %3d 0x%lx ---\n",
tx1, ty1, tx2, ty2, frame);
moved_win = sraRgnCreateRect(tx1, ty1, tx2, ty2);
dtime0(&tm);
boff = get_boff();
bwin = get_bwin();
X_LOCK;
/*
* loop over the stack, top to bottom until we
* find our wm frame:
*/
for (k = stack_list_num - 1; k >= 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<nrect; i++) {
int x = rects[i].x1;
int y = rects[i].y1;
int w = rects[i].x2;
int h = rects[i].y2;
if (w > 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", (int) orig_frame);
} else if (0 && frame && macosxCGS_follow_animation_win(frame, -1, 0)) {
fprintf(stderr, "FAW frame %d\n", (int) frame);
}
for (j=0; j<m; j++) {
macosxCGS_get_all_windows();
if (macosx_checkevent(NULL)) {
ok = 1;
fprintf(stderr, "Check Event 1\n");
} else {
fprintf(stderr, "Check Event 0\n");
}
if (ok) {
break;
}
usleep(10 * 1000);
}
if (ok) {
if (flush) {
fb_push_wait(0.1, FB_COPY|FB_MOD);
}
check_ncache(0, 2);
}
#else
if (!orig_frame || !frame || !flush) {}
#endif
}
void check_macosx_click_frame(void) {
#ifdef MACOSX
if (macosx_console) {
//fprintf(stderr, "macosx_click_frame: 0x%x\n", macosx_click_frame);
check_macosx_iconify(macosx_click_frame, None, 0);
macosx_click_frame = None;
if (button_mask && !macosx_checkevent(NULL)) {
check_macosx_iconify(None, None, 0);
}
}
#endif
}
/*
* Applied just before any check_user_input() modes. Look for a
* ButtonPress; find window it happened in; find the wm frame window
* for it; watch for that window moving or resizing. If it does, do the
* wireframe animation. Do this until ButtonRelease or timeouts occur.
* Remove wireframe.
*
* Under -nowirecopyrect, return control to base scheme
* (check_user_input() ...) that will repaint the screen with the window
* in the new postion or size. Under -wirecopyrect, apply rfbDoCopyRect
* or rfbDoCopyRegion: this "pollutes" our framebuffer, but the normal
* polling will quickly repair it. Under happy circumstances, this
* reduces actual XShmGetImage work (i.e. if we correctly predicted how
* the X fb has changed.
*
* -scale doesn't always work under -wirecopyrect, but the wireframe does.
*
* testing of this mode under -threads is incomplete.
*
* returns 1 if it did an animation, 0 if no move/resize triggers
* went off.
*
* TBD: see if we can select StructureNotify ConfigureNotify events for
* the toplevel windows to get better info on moves and resizes.
*/
int check_wireframe(void) {
Window frame = None, orig_frame = None;
XWindowAttributes attr;
int dx, dy;
int orig_px, orig_py, orig_x, orig_y, orig_w, orig_h;
int px, py, x, y, w, h;
int box_x, box_y, box_w, box_h;
int orig_cursor_x, orig_cursor_y, g, gd;
int already_down = 0, win_gone = 0, win_unmapped = 0;
double spin = 0.0, tm, last_ptr = 0.0, last_draw;
int frame_changed = 0, drew_box = 0, got_2nd_pointer = 0;
int try_copyrect_drag = 1, do_copyrect_drag = -1;
int now_x = 0, now_y = 0, nidx = -1;
double copyrect_drag_delay = -1.0;
int try_batch = 1; /* XXX Y */
int mac_skip = 0;
int special_t1 = 0, break_reason = 0, last_draw_cnt = 0, gpi = 0;
static double first_dt_ave = 0.0;
static int first_dt_cnt = 0;
static time_t last_save_stacklist = 0;
int bdown0, bdown, gotui, cnt = 0;
/* heuristics: */
double first_event_spin = wireframe_t1;
double frame_changed_spin = wireframe_t2;
double max_spin = wireframe_t3;
double min_draw = wireframe_t4;
int try_it = 0;
DB_SET
if (unixpw_in_progress) return 0;
#ifdef MACOSX
if (macosx_console) {
;
} else {
RAWFB_RET(0)
}
#else
RAWFB_RET(0)
#endif
if (nofb) {
return 0;
}
if (subwin) {
return 0; /* don't even bother for -id case */
}
if (db > 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<max_extra; i++) {
rfbCFD(0);
if (got_pointer_input > 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<split; i++) {
usleep(ms * 1000);
if (show_multiple_cursors) {
rfbPE(1000);
} else {
rfbCFD(1000);
}
spin += dtime(&tm);
if (got_pointer_input > g) {
int i, max_extra = max_eat / 2;
for (i=0; i<max_extra; i++) {
rfbCFD(0);
if (got_pointer_input > 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<num; i++) {
cache_list[i].win = list[i];
cache_list[i].fetched = 0;
cache_list[i].valid = 0;
cache_list[i].time = now;
cache_list[i].selectinput = 0;
cache_list[i].vis_cnt = 0;
cache_list[i].map_cnt = 0;
cache_list[i].unmap_cnt = 0;
cache_list[i].create_cnt = 0;
cache_list[i].vis_state = -1;
cache_list[i].above = None;
}
if (num == 0) {
cache_list[0].win = None;
cache_list[0].fetched = 0;
cache_list[0].valid = 0;
cache_list[0].time = now;
cache_list[0].selectinput = 0;
cache_list[0].vis_cnt = 0;
cache_list[0].map_cnt = 0;
cache_list[0].unmap_cnt = 0;
cache_list[0].create_cnt = 0;
cache_list[0].vis_state = -1;
cache_list[0].above = None;
num++;
}
cache_list_num = num;
if (num) {
X_LOCK;
XFree_wr(list);
X_UNLOCK;
}
#endif /* NO_X11 */
}
int get_bs_n(int y) {
int n;
for (n = 1; n < ncache; n += 2) {
if (n*dpy_y <= y && y < (n+1)*dpy_y) {
return n;
}
}
return -1;
}
#define NRECENT 32
Window recent[NRECENT];
int recidx[NRECENT];
int rlast, rfree;
int lookup_win_index(Window win) {
int k, idx = -1;
int foundfree = 0;
static int s1 = 0, s2 = 0, s3 = 0;
if (win == rootwin || win == None) {
return -1;
}
for (k = 0; k < NRECENT; k++) {
if (recent[k] == win) {
int k2 = recidx[k];
if (cache_list[k2].win == win) {
idx = k2;
//fprintf(stderr, "recentA(shortcut): %d 0x%x\n", idx, win);
s1++;
break;
}
}
}
if (idx < 0) {
for(k=0; k<cache_list_num; k++) {
if (!foundfree && cache_list[k].win == None) {
rfree = k;
foundfree = 1;
}
if (cache_list[k].win == win) {
idx = k;
//fprintf(stderr, "recentB(normal): %d 0x%x\n", idx, win);
s2++;
break;
}
}
if (idx >= 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_num; k++) {
if (cache_list[k].win == None) {
rfree = k;
break;
}
}
if (rfree < 0) {
fprintf(stderr, "*** LOOKUP_FREE_INDEX: incrementing cache_list_num %d/%d\n", cache_list_num, cache_list_len);
rfree = cache_list_num++;
if (rfree >= 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-1; i++) {
cache_new[i] = cache_list[i];
}
cache_list_len = n;
free(cache_list);
cache_list = cache_new;
}
cache_list[rfree].win = None;
cache_list[rfree].fetched = 0;
cache_list[rfree].valid = 0;
cache_list[rfree].time = 0.0;
cache_list[rfree].selectinput = 0;
cache_list[rfree].vis_cnt = 0;
cache_list[rfree].map_cnt = 0;
cache_list[rfree].unmap_cnt = 0;
cache_list[rfree].create_cnt = 0;
cache_list[rfree].vis_state = -1;
cache_list[rfree].above = None;
}
fprintf(stderr, "lookup_freeB: %d\n", rfree);
return rfree;
}
#define STORE(k, w, attr) \
cache_list[k].win = w; \
cache_list[k].fetched = 1; \
cache_list[k].valid = 1; \
cache_list[k].x = attr.x; \
cache_list[k].y = attr.y; \
cache_list[k].width = attr.width; \
cache_list[k].height = attr.height; \
cache_list[k].map_state = attr.map_state; \
cache_list[k].time = dnow();
#define CLEAR(k) \
cache_list[k].bs_x = -1; \
cache_list[k].bs_y = -1; \
cache_list[k].bs_w = -1; \
cache_list[k].bs_h = -1; \
cache_list[k].su_x = -1; \
cache_list[k].su_y = -1; \
cache_list[k].su_w = -1; \
cache_list[k].su_h = -1; \
cache_list[k].time = 0.0; \
cache_list[k].bs_time = 0.0; \
cache_list[k].su_time = 0.0;
#define DELETE(k) \
cache_list[k].win = None; \
cache_list[k].fetched = 0; \
cache_list[k].valid = 0; \
cache_list[k].selectinput = 0; \
cache_list[k].vis_cnt = 0; \
cache_list[k].map_cnt = 0; \
cache_list[k].unmap_cnt = 0; \
cache_list[k].create_cnt = 0; \
cache_list[k].vis_state = -1; \
cache_list[k].above = None; \
free_rect(k); /* does CLEAR(k) */
static char unk[32];
char *Etype(int type) {
if (type == KeyPress) return "KeyPress";
if (type == KeyRelease) return "KeyRelease";
if (type == ButtonPress) return "ButtonPress";
if (type == ButtonRelease) return "ButtonRelease";
if (type == MotionNotify) return "MotionNotify";
if (type == EnterNotify) return "EnterNotify";
if (type == LeaveNotify) return "LeaveNotify";
if (type == FocusIn) return "FocusIn";
if (type == FocusOut) return "FocusOut";
if (type == KeymapNotify) return "KeymapNotify";
if (type == Expose) return "Expose";
if (type == GraphicsExpose) return "GraphicsExpose";
if (type == NoExpose) return "NoExpose";
if (type == VisibilityNotify) return "VisibilityNotify";
if (type == CreateNotify) return "CreateNotify";
if (type == DestroyNotify) return "DestroyNotify";
if (type == UnmapNotify) return "UnmapNotify";
if (type == MapNotify) return "MapNotify";
if (type == MapRequest) return "MapRequest";
if (type == ReparentNotify) return "ReparentNotify";
if (type == ConfigureNotify) return "ConfigureNotify";
if (type == ConfigureRequest) return "ConfigureRequest";
if (type == GravityNotify) return "GravityNotify";
if (type == ResizeRequest) return "ResizeRequest";
if (type == CirculateNotify) return "CirculateNotify";
if (type == CirculateRequest) return "CirculateRequest";
if (type == PropertyNotify) return "PropertyNotify";
if (type == SelectionClear) return "SelectionClear";
if (type == SelectionRequest) return "SelectionRequest";
if (type == SelectionNotify) return "SelectionNotify";
if (type == ColormapNotify) return "ColormapNotify";
if (type == ClientMessage) return "ClientMessage";
if (type == MappingNotify) return "MappingNotify";
if (type == LASTEvent) return "LASTEvent";
sprintf(unk, "Unknown %d", type);
return unk;
}
char *VState(int state) {
if (state == VisibilityFullyObscured) return "VisibilityFullyObscured";
if (state == VisibilityPartiallyObscured) return "VisibilityPartiallyObscured";
if (state == VisibilityUnobscured) return "VisibilityUnobscured";
sprintf(unk, "Unknown %d", state);
return unk;
}
char *MState(int state) {
if (state == IsViewable) return "IsViewable";
if (state == IsUnmapped) return "IsUnmapped";
sprintf(unk, "Unknown %d", state);
return unk;
}
sraRegionPtr rect_reg[64];
sraRegionPtr zero_rects = NULL;
int free_rect(int idx) {
int n, ok = 0;
sraRegionPtr r1, r2;
int x, y, w, h;
if (idx < 0 || idx >= 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<cache_list_num; i++) {
int wb = cache_list[i].bs_w;
int hb = cache_list[i].bs_h;
if (cache_list[i].bs_x < 0) {
continue;
}
if (w > 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<cache_list_num; i++) {
int xb = cache_list[i].bs_x;
int yb = cache_list[i].bs_y;
int wb = cache_list[i].bs_w;
int hb = cache_list[i].bs_h;
if (xb < 0) {
continue;
}
if (nabs(yb - y0) > 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<cache_list_num; i++) {
int wb = cache_list[i].bs_w;
int hb = cache_list[i].bs_h;
if (cache_list[i].bs_x < 0) {
continue;
}
if (w > 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<cache_list_num; i++) {
int xb = cache_list[i].bs_x;
int yb = cache_list[i].bs_y;
int wb = cache_list[i].bs_w;
int hb = cache_list[i].bs_h;
if (xb < 0) {
continue;
}
if (nabs(yb - y0) > 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<cache_list_num; i++) {
int xb = cache_list[i].bs_x;
int yb = cache_list[i].bs_y;
int wb = cache_list[i].bs_w;
int hb = cache_list[i].bs_h;
if (xb < 0) {
continue;
}
if (nabs(yb - y0) > 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; i++) {
int bs_x = cache_list[i].bs_x;
int bs_y = cache_list[i].bs_y;
int bs_w = cache_list[i].bs_w;
int bs_h = cache_list[i].bs_h;
if (bs_x < 0) {
continue;
}
if (get_bs_n(bs_y) != n) {
continue;
}
r2 = sraRgnCreateRect(bs_x, bs_y, bs_x+bs_w, bs_y+bs_h);
sraRgnSubtract(r1, r2);
}
rect_reg[n] = r1;
/* n+1 */
sraRgnDestroy(rect_reg[n2]);
r1 = sraRgnCreateRect(0, n2 * dpy_y, dpy_x, (n2+1) * dpy_y);
for (i=0; i<cache_list_num; i++) {
int bs_x = cache_list[i].bs_x;
int su_x = cache_list[i].su_x;
int su_y = cache_list[i].su_y;
int su_w = cache_list[i].su_w;
int su_h = cache_list[i].su_h;
if (bs_x < 0) {
continue;
}
if (get_bs_n(su_y) != n2) {
continue;
}
r2 = sraRgnCreateRect(su_x, su_y, su_x+su_w, su_y+su_h);
sraRgnSubtract(r1, r2);
}
rect_reg[n2] = r1;
}
}
if (idx < 0 || idx >= 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<cache_list_num; k++) {
XWindowAttributes attr;
int x = cache_list[k].bs_x;
int y = cache_list[k].bs_y;
int w = cache_list[k].bs_w;
int h = cache_list[k].bs_h;
int rc = 0;
Window win = cache_list[k].win;
if (win == None) {
continue;
}
if (n == 1) {
X_LOCK;
rc = valid_window(win, &attr, 1);
X_UNLOCK;
if (rc) {
vtot++;
} else {
itot++;
}
if (x >= 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;
if (!evmask) {}
#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
if (!d || !mask) {}
return False;
#else
return XCheckMaskEvent(d, mask, ev);
#endif
}
#include <rfb/default8x16.h>
#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<cache_list_num; k++) {
int valid = 0;
win = cache_list[k].win;
X_LOCK;
if (win == None) {
;
} else if (cache_list[k].selectinput && cache_list[k].time > 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