Commit 8aa6fb95 authored by runge's avatar runge

x11vnc: first pass at client-side caching, -ncache option.

parent 399a175f
2006-12-17 Karl Runge <runge@karlrunge.com>
* x11vnc: first pass at client-side caching, -ncache option.
have -http guess ../classes/.. to run out of build area.
2006-12-17 Karl Runge <runge@karlrunge.com>
* x11vnc: make -xwarppointer the default if xinerama is active.
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -2014,6 +2014,32 @@ void print_help(int mode) {
" text selection, and some menu traversals. It overrides\n"
" any -pointer_mode setting.\n"
"\n"
#ifndef NO_NCACHE
"-ncache n Client-side caching scheme. Framebuffer memory \"n\"\n"
" (an integer) times that of the full display is allocated\n"
" below the actual framebuffer to cache screen contents\n"
" for rapid retrieval. So a W x H frambuffer is expanded\n"
" to a W x (n+1)*H one. Use 0 to disable. Default: XXX.\n"
"\n"
" This is an experimental option, currently implemented\n"
" in an awkward way in that in the VNC Viewer you can\n"
" see the cache contents if you scroll down, etc. So you\n"
" will have to set things up so you can't see that region.\n"
" If this method is successful, the changes required for\n"
" clients to do this less awkwardly will be investigated.\n"
"\n"
" Note that this mode consumes a lot of memory, both\n"
" on the x11vnc server side and on the VNC Viewer side.\n"
" If n=2 then the amount of RAM used is roughly tripled\n"
" for both x11vnc and the VNC Viewer. As a rule of\n"
" thumb, note that 1280x1024 at depth 24 is about 5MB of\n"
" pixel data.\n"
"\n"
" Because of the way window backingstore and saveunders\n"
" are implemented, n must be even. It will be incremented\n"
" by 1 if it is not.\n"
"\n"
#endif
"-wireframe [str] Try to detect window moves or resizes when a mouse\n"
"-nowireframe button is held down and show a wireframe instead of\n"
" the full opaque window. This is based completely on\n"
......
......@@ -193,6 +193,9 @@ char *wireframe_copyrect_default = "never";
int wireframe_in_progress = 0;
int wireframe_local = 1;
int ncache = 0;
int ncache0 = 0;
/* T+B+L+R,tkey+presist_key,tmouse+persist_mouse */
char *scroll_copyrect_str = NULL;
#ifndef SCROLL_COPYRECT
......
......@@ -155,6 +155,9 @@ extern char *wireframe_copyrect;
extern char *wireframe_copyrect_default;
extern int wireframe_in_progress;
extern int ncache;
extern int ncache0;
extern char *scroll_copyrect_str;
extern char *scroll_copyrect;
extern char *scroll_copyrect_default;
......
......@@ -25,6 +25,7 @@
#include "selection.h"
#include "unixpw.h"
#include "uinput.h"
#include "userinput.h"
int send_remote_cmd(char *cmd, int query, int wait);
int do_remote_query(char *remote_cmd, char *query_cmd, int remote_sync,
......@@ -335,6 +336,13 @@ int check_httpdir(void) {
} else {
snprintf(httpdir, len, "%s/../share/x11vnc/classes", prog);
}
if (stat(httpdir, &sbuf) != 0) {
if (use_openssl || use_stunnel || http_ssl) {
snprintf(httpdir, len, "%s/../classes/ssl", prog);
} else {
snprintf(httpdir, len, "%s/../classes", prog);
}
}
free(prog);
if (stat(httpdir, &sbuf) == 0) {
......@@ -749,7 +757,35 @@ char *process_remote_cmd(char *cmd, int stringonly) {
/*
* Maybe add: passwdfile logfile bg rfbauth passwd...
*/
if (!strcmp(p, "stop") || !strcmp(p, "quit") ||
if (strstr(p, "CR:") == p) { /* skip-cmd-list */
/* CR:WxH+X+Y,dx,dy */
int w, h, x, y, dx, dy;
NOTAPP
if (sscanf(p+3, "%dx%d+%d+%d,%d,%d", &w, &h, &x, &y, &dx, &dy) == 6) {
sraRegionPtr r;
rfbLog("rfbDoCopyRect(screen, %d, %d, %d, %d, %d, %d)\n", x, y, x+w, y+h, dx, dy);
r = sraRgnCreateRect(x, y, x+w, y+h);
do_copyregion(r, dx, dy);
fb_push();
sraRgnDestroy(r);
rfbLog("did\n");
} else {
rfbLog("remote_cmd: bad CR string: %s\n", p);
}
} else if (strstr(p, "ncache") == p) { /* skip-cmd-list */
COLON_CHECK("ncache:")
if (query) {
snprintf(buf, bufn, "ans=%s%s%d", p, co, ncache);
goto qry;
}
p += strlen("ncache:");
ncache = atoi(p);
if (ncache % 2 != 0) {
ncache++;
}
rfbLog("remote_cmd: set -ncache %d\n", ncache);
} else if (!strcmp(p, "stop") || !strcmp(p, "quit") ||
!strcmp(p, "exit") || !strcmp(p, "shutdown")) {
NOTAPP
if (client_connect_file) {
......
......@@ -2816,6 +2816,31 @@ static int scan_display(int ystart, int rescan) {
/* grab the horizontal scanline from the display: */
X_LOCK;
#ifndef NO_NCACHE
#if !NO_X11
if (ncache > 0 && dpy) {
/* XXX watch for problems. */
XEvent ev;
int gotone = 0;
if (XCheckTypedEvent(dpy, MapNotify, &ev)) {
gotone = 1;
} else if (XCheckTypedEvent(dpy, UnmapNotify, &ev)) {
gotone = 2;
} else if (XCheckTypedEvent(dpy, CreateNotify, &ev)) {
gotone = 3;
}
if (gotone) {
XPutBackEvent(dpy, &ev);
X_UNLOCK;
fprintf(stderr, "*** SCAN_DISPLAY CHECK_NCACHE/%d *** %d\n", gotone, y);
check_ncache(0);
X_LOCK;
}
}
#endif
#endif
XRANDR_SET_TRAP_RET(-1, "scan_display-set");
copy_image(scanline, 0, y, 0, 0);
XRANDR_CHK_TRAP_RET(-1, "scan_display-chk");
......
......@@ -2033,6 +2033,23 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
}
}
#ifndef NO_NCACHE
if (ncache > 0) {
char *new_fb;
int sz = fb->height * fb->bytes_per_line;
new_fb = (char *) calloc((size_t) (sz * (1+ncache)), 1);
if (fb->data) {
memcpy(new_fb, fb->data, sz);
free(fb->data);
}
fb->data = new_fb;
fb->height *= (1+ncache);
height *= (1+ncache);
ncache0 = ncache;
}
#endif
if (cmap8to24 && depth == 8) {
rfb_bytes_per_line *= 4;
rot_bytes_per_line *= 4;
......
......@@ -56,6 +56,7 @@ extern char *crypt(const char*, const char *);
#undef UNIXPW_CRYPT
#endif
int white_pixel(void);
void unixpw_screen(int init);
void unixpw_keystroke(rfbBool down, rfbKeySym keysym, int init);
void unixpw_accept(char *user);
......@@ -65,7 +66,7 @@ int su_verify(char *user, char *pass, char *cmd, char *rbuf, int *rbuf_size);
int crypt_verify(char *user, char *pass);
int cmd_verify(char *user, char *pass);
static int white(void);
static int text_x(void);
static int text_y(void);
static void set_db(void);
......@@ -89,7 +90,7 @@ static int char_x = 0, char_y = 0, char_w = 8, char_h = 16;
static int db = 0;
static int white(void) {
int white_pixel(void) {
static unsigned long black_pix = 0, white_pix = 1, set = 0;
RAWFB_RET(0xffffff)
......@@ -162,7 +163,7 @@ void unixpw_screen(int init) {
pscreen = screen;
}
rfbDrawString(pscreen, &default8x16Font, x, y, log, white());
rfbDrawString(pscreen, &default8x16Font, x, y, log, white_pixel());
char_x = x;
char_y = y;
......@@ -1075,13 +1076,13 @@ if (db) fprintf(stderr, "unixpw_verify: '%s' '%s'\n", user, db > 1 ? pass : "***
x = text_x();
y = text_y();
rfbDrawString(pscreen, &default8x16Font, x, y, li, white());
rfbDrawString(pscreen, &default8x16Font, x, y, li, white_pixel());
char_row += 2;
x = text_x();
y = text_y();
rfbDrawString(pscreen, &default8x16Font, x, y, log, white());
rfbDrawString(pscreen, &default8x16Font, x, y, log, white_pixel());
char_col = strlen(log);
......@@ -1245,7 +1246,7 @@ void unixpw_keystroke(rfbBool down, rfbKeySym keysym, int init) {
x = text_x();
y = text_y();
rfbDrawString(pscreen, &default8x16Font, x, y, pw,
white());
white_pixel());
char_col = strlen(pw);
if (scaling) {
......@@ -1279,7 +1280,7 @@ void unixpw_keystroke(rfbBool down, rfbKeySym keysym, int init) {
x = text_x();
y = text_y();
rfbDrawString(pscreen, &default8x16Font, x, y,
str, white());
str, white_pixel());
mark_rect_as_modified(x, y-char_h, x+char_w,
y, scaling);
char_col++;
......@@ -1323,7 +1324,7 @@ void unixpw_keystroke(rfbBool down, rfbKeySym keysym, int init) {
if (db && db <= 2) fprintf(stderr, "u_cnt: %d %d/%d ks: 0x%x '%s'\n", u_cnt, x, y, keysym, keystr);
rfbDrawString(pscreen, &default8x16Font, x, y, keystr, white());
rfbDrawString(pscreen, &default8x16Font, x, y, keystr, white_pixel());
mark_rect_as_modified(x, y-char_h, x+char_w, y, scaling);
char_col++;
......@@ -1555,7 +1556,7 @@ void unixpw_deny(void) {
x = char_x + char_col * char_w;
y = char_y + char_row * char_h;
rfbDrawString(pscreen, &default8x16Font, x, y, pd, white());
rfbDrawString(pscreen, &default8x16Font, x, y, pd, white_pixel());
if (scaling) {
mark_rect_as_modified(0, 0, scaled_x, scaled_y, 1);
} else {
......@@ -1587,7 +1588,7 @@ void unixpw_msg(char *msg, int delay) {
x = char_x + char_col * char_w;
y = char_y + char_row * char_h;
rfbDrawString(pscreen, &default8x16Font, x, y, msg, white());
rfbDrawString(pscreen, &default8x16Font, x, y, msg, white_pixel());
if (scaling) {
mark_rect_as_modified(0, 0, scaled_x, scaled_y, 1);
} else {
......
......@@ -3,6 +3,7 @@
/* -- unixpw.h -- */
extern int white_pixel(void);
extern void unixpw_screen(int init);
extern void unixpw_keystroke(rfbBool down, rfbKeySym keysym, int init);
extern void unixpw_accept(char *user);
......
......@@ -53,10 +53,9 @@ 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);
static void get_client_regions(int *req, int *mod, int *cpy, int *num) ;
static void do_copyregion(sraRegionPtr region, int dx, int dy) ;
static void parse_scroll_copyrect_str(char *scr);
static void parse_wireframe_str(char *wf);
static void destroy_str_list(char **list);
......@@ -81,6 +80,8 @@ 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
......@@ -1757,7 +1758,7 @@ static void get_client_regions(int *req, int *mod, int *cpy, int *num) {
* is being done. Note that copyrect under the scaling case is often
* only approximate.
*/
static void do_copyregion(sraRegionPtr region, int dx, int dy) {
void do_copyregion(sraRegionPtr region, int dx, int dy) {
sraRectangleIterator *iter;
sraRect rect;
int Bpp0 = bpp/8, Bpp;
......@@ -3364,6 +3365,7 @@ int check_wireframe(void) {
double max_spin = wireframe_t3;
double min_draw = wireframe_t4;
int try_it = 0;
Window desc[6];
DB_SET
if (unixpw_in_progress) return 0;
......@@ -3837,6 +3839,43 @@ if (db) fprintf(stderr, "sent_copyrect: %d - obs: %d frame: 0x%lx\n", sent_copy
/* no diff for now... */
fb_push_wait(0.1, FB_COPY);
}
#ifndef NO_NCACHE
if (ncache > 0) {
int idx = lookup_win_index(frame);
fprintf(stderr, "sent_copyrect: idx=%d 0x%x\n", idx, frame);
if (idx < 0) {
idx = lookup_win_index(orig_frame);
fprintf(stderr, "sent_copyrect: idx=%d 0x%x\n", idx, orig_frame);
}
if (idx >= 0 && cache_list[idx].su_time > 0.0) {
sraRegionPtr r0, r1, r2;
sraRectangleIterator *iter;
sraRect rt;
int su_x = cache_list[idx].su_x;
int su_y = cache_list[idx].su_y;
int dx, dy;
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);
sraRgnSubtract(r1, r2);
dx = orig_x - su_x;
dy = orig_y - su_y;
fprintf(stderr, "sent_copyrect: su_restore: %d %d\n", dx, dy);
do_copyregion(r1, dx, dy);
fb_push_wait(0.1, FB_COPY);
fprintf(stderr, "sent_copyrect: su_restore: done.\n");
sraRgnDestroy(r0);
sraRgnDestroy(r1);
sraRgnDestroy(r2);
cache_list[idx].su_time = 0.0;
}
}
#endif
if (scaling) {
static double last_time = 0.0;
double now = dnow(), delay = 0.35;
......@@ -4504,4 +4543,761 @@ if (debug_scroll && rc > 1) fprintf(stderr, " CXR: check_user_input ret %d\n",
return 0;
}
#if defined(NO_NCACHE) || NO_X11
void check_ncache(void) {
return;
}
int lookup_win_index(Window win) {
return -1;
}
#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, j;
unsigned int ui;
Window r, w;
Window *list;
int start = 2048;
if (! cache_list) {
cache_list = (winattr_t *) malloc(start*sizeof(winattr_t));
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);
num = (int) ui;
if (! rc) {
cache_list_num = 0;
last_free = now;
last_snap = 0.0;
X_UNLOCK;
return;
}
last_snap = now;
if (num > cache_list_len) {
int n = 2*num;
free(cache_list);
cache_list = (winattr_t *) malloc(n*sizeof(winattr_t));
cache_list_len = n;
}
j = 0;
for (i=0; i<num; i++) {
cache_list[j].win = list[i];
cache_list[j].fetched = 0;
cache_list[j].valid = 0;
cache_list[j].time = now;
cache_list[j].selectinput = 0;
j++;
}
cache_list_num = num;
XFree_wr(list);
X_UNLOCK;
#endif /* NO_X11 */
}
#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;
if (win == rootwin || win == None) {
return idx;
}
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: %d 0x%x\n", idx, win);
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: %d 0x%x\n", idx, win);
break;
}
}
if (idx >= 0) {
recent[rlast] = win;
recidx[rlast++] = idx;
rlast = rlast % NRECENT;
}
}
if (idx < 0) {
fprintf(stderr, "recentC: %d 0x%x\n", idx, win);
}
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) {
rfree = cache_list_num++;
if (rfree >= cache_list_len) {
/* bad news... */
fprintf(stderr, "Whoops: %d %d\n", rfree, cache_list_len);
}
}
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].bs_time = 0.0; \
cache_list[k].su_time = 0.0; \
cache_list[k].selectinput = 0;
#define DELETE(k) \
cache_list[k].win = None; \
cache_list[k].fetched = 0; \
cache_list[k].valid = 0; \
free_rect(k);
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";
return "Unknown";
}
sraRegionPtr rect_reg[32];
int find_rect(int idx, int x, int y, int w, int h) {
static int first = 1;
sraRegionPtr r1, r2;
sraRectangleIterator *iter;
sraRect rt;
int n, x_hit = -1, y_hit = -1;
if (first) {
first = 0;
for (n = 1; n <= ncache; n++) {
rect_reg[n] = sraRgnCreateRect(0, n * dpy_y, dpy_x, (n+1) * dpy_y);
}
}
for (n = 1; n < ncache; n += 2) {
r1 = rect_reg[n];
r2 = NULL;
iter = sraRgnGetIterator(r1);
while (sraRgnIteratorNext(iter, &rt)) {
int rw = nabs(rt.x2 - rt.x1);
int rh = nabs(rt.y2 - rt.y1);
if (rw >= w && rh >= h) {
if (rt.x1 < rt.x2) {
x_hit = rt.x1;
} else {
x_hit = rt.x2;
}
if (rt.y1 < rt.y2) {
y_hit = rt.y1;
} else {
y_hit = rt.y2;
}
r2 = sraRgnCreateRect(x_hit, y_hit, x_hit + w, y_hit + h);
break;
}
}
sraRgnReleaseIterator(iter);
if (r2 != NULL) {
sraRgnSubtract(r1, r2);
sraRgnDestroy(r2);
break;
}
}
if (x_hit < 0) {
/* bad news */
fprintf(stderr, "*FAIL rect: %d %d %d %d -- %d %d\n", x, y, w, h, x_hit, y_hit);
} else {
fprintf(stderr, "found rect: %d %d %d %d -- %d %d\n", x, y, w, h, x_hit, y_hit);
}
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;
}
int free_rect(int idx) {
int n, ok = 0;
sraRegionPtr r1, r2;
int x, y, w, h;
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);
fprintf(stderr, "free_rect: invalid: %d\n", idx);
return 0;
}
r2 = sraRgnCreateRect(x, y, x+w, y+h);
for (n = 1; n < ncache; n += 2) {
if (y >= n*dpy_y && y < (n+1)*dpy_y) {
r1 = rect_reg[n];
sraRgnOr(r1, r2);
ok = 1;
fprintf(stderr, "free_rect: found %d region: %d\n", idx, n);
break;
}
}
sraRgnDestroy(r2);
CLEAR(idx);
if (! ok) fprintf(stderr, "free_rect: not-found %d\n", idx);
return ok;
}
int bs_save(int idx) {
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;
x1 = cache_list[idx].x;
y1 = cache_list[idx].y;
w1 = cache_list[idx].width;
h1 = cache_list[idx].height;
if (! valid_window(win, &attr, 1)) {
fprintf(stderr, "bs_save: not valid\n");
// DELETE(idx);
return 0;
}
x2 = attr.x;
y2 = attr.y;
w2 = attr.width;
h2 = attr.height;
if (cache_list[idx].bs_x < 0 || w2 > cache_list[idx].bs_w || h2 > cache_list[idx].bs_h) {
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) {
STORE(idx, win, attr);
return 0;
}
dx = x - x2;
dy = y - y2;
r = sraRgnCreateRect(x2+dx, y2+dy, x2+w2+dx, y2+h2+dy);
fprintf(stderr, "bs_save: dx=%d dy=%d\n", dx, dy);
do_copyregion(r, dx, dy);
fb_push_wait(0.01, FB_COPY);
fprintf(stderr, "bs_save: done. %dx%d+%d+%d %dx%d+%d+%d %.2f %.2f\n", w1, h1, x1, y1, w2, h2, x2, y2, cache_list[idx].bs_time, dnow());
sraRgnDestroy(r);
STORE(idx, win, attr);
cache_list[idx].bs_time = dnow();
return 1;
}
int su_save(int idx) {
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;
x1 = cache_list[idx].x;
y1 = cache_list[idx].y;
w1 = cache_list[idx].width;
h1 = cache_list[idx].height;
if (! valid_window(win, &attr, 1)) {
fprintf(stderr, "su_save: not valid\n");
// DELETE(idx);
return 0;
}
x2 = attr.x;
y2 = attr.y;
w2 = attr.width;
h2 = attr.height;
if (cache_list[idx].su_x < 0 || w2 > cache_list[idx].su_w || h2 > cache_list[idx].su_h) {
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) {
STORE(idx, win, attr);
return 0;
}
dx = x - x2;
dy = y - y2;
r = sraRgnCreateRect(x2+dx, y2+dy, x2+w2+dx, y2+h2+dy);
fprintf(stderr, "su_save: dx=%d dy=%d\n", dx, dy);
do_copyregion(r, dx, dy);
fb_push_wait(0.01, FB_COPY);
fprintf(stderr, "su_save: done. %dx%d+%d+%d %dx%d+%d+%d %.2f %.2f\n", w1, h1, x1, y1, w2, h2, x2, y2, cache_list[idx].su_time, dnow());
sraRgnDestroy(r);
STORE(idx, win, attr);
cache_list[idx].su_time = dnow();
return 1;
}
int bs_restore(int idx) {
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;
if (cache_list[idx].bs_x < 0 || cache_list[idx].bs_time == 0.0) {
return 0;
}
x1 = cache_list[idx].x;
y1 = cache_list[idx].y;
w1 = cache_list[idx].width;
h1 = cache_list[idx].height;
if (! valid_window(win, &attr, 1)) {
fprintf(stderr, "bs_restore: not valid\n");
DELETE(idx);
return 0;
}
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) {
STORE(idx, win, attr);
return 0;
}
dx = x2 - x;
dy = y2 - y;
r = sraRgnCreateRect(x+dx, y+dy, x+w2+dx, y+h2+dy);
fprintf(stderr, "bs_restore: dx=%d dy=%d\n", dx, dy);
do_copyregion(r, dx, dy);
fb_push_wait(0.01, FB_COPY);
fprintf(stderr, "bs_rest: done. %dx%d+%d+%d %dx%d+%d+%d %.2f %.2f\n", w1, h1, x1, y1, w2, h2, x2, y2, cache_list[idx].bs_time, dnow());
sraRgnDestroy(r);
STORE(idx, win, attr);
return 1;
}
int su_restore(int idx) {
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;
int invalid = 0;
if (cache_list[idx].su_x < 0 || cache_list[idx].su_time == 0.0) {
return 0;
}
x1 = cache_list[idx].x;
y1 = cache_list[idx].y;
w1 = cache_list[idx].width;
h1 = cache_list[idx].height;
if (! valid_window(win, &attr, 1)) {
fprintf(stderr, "su_restore: not valid\n");
invalid = 1;
x2 = x1;
y2 = y1;
w2 = w1;
h2 = h1;
} else {
x2 = attr.x;
y2 = attr.y;
w2 = attr.width;
h2 = attr.height;
}
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) {
if (invalid) {
DELETE(idx);
}
return 0;
}
dx = x2 - x;
dy = y2 - y;
r = sraRgnCreateRect(x+dx, y+dy, x+w2+dx, y+h2+dy);
fprintf(stderr, "su_restore: dx=%d dy=%d\n", dx, dy);
do_copyregion(r, dx, dy);
fb_push_wait(0.01, FB_COPY);
fprintf(stderr, "su_rest: done. %dx%d+%d+%d %dx%d+%d+%d %.2f %.2f\n", w1, h1, x1, y1, w2, h2, x2, y2, cache_list[idx].su_time, dnow());
sraRgnDestroy(r);
if (invalid) {
DELETE(idx);
return 0;
} else {
STORE(idx, win, attr);
return 1;
}
}
#include <rfb/default8x16.h>
void check_ncache(void) {
static int last_map = -1;
static double last_root = 0.0;
static int first = 1;
int i, j, k;
double now, refresh = 60.0;
Window win, win2;
XWindowAttributes attr;
XEvent ev;
if (! ncache || ! ncache0) {
return;
}
if (! screen) {
return;
}
if (! dpy) {
return;
}
if (first) {
int dx = 10, dy = 24;
for (i=0; i < NRECENT; i++) {
recent[i] = None;
}
rlast = 0;
X_LOCK;
/* event leak with client_count == 0 */
xselectinput_rootwin |= SubstructureNotifyMask;
XSelectInput(dpy, rootwin, xselectinput_rootwin);
X_UNLOCK;
first = 0;
rfbDrawString(screen, &default8x16Font, dx, dpy_y+1*dy,
"This is the Pixel buffer cache region. Your VNC Viewer is not hiding it from you.",
white_pixel());
rfbDrawString(screen, &default8x16Font, dx, dpy_y+2*dy,
"Try resizing your VNC Viewer so you don't see it !!",
white_pixel());
rfbDrawString(screen, &default8x16Font, dx, dpy_y+3*dy,
"To disable run the server with: x11vnc -ncache 0 ...",
white_pixel());
rfbDrawString(screen, &default8x16Font, dx, dpy_y+4*dy,
"More info: http://www.karlrunge.com/x11vnc/#faq-client-caching",
white_pixel());
snapshot_cache_list(0, 100.0);
}
if (! client_count) {
return;
}
now = dnow();
if (now > last_root + refresh) {
fprintf(stderr, "\n**** checking cache_list[%d]\n\n", cache_list_num);
for(k=0; k<cache_list_num; k++) {
win = cache_list[k].win;
if (win == None) {
fprintf(stderr, " Empty[%d]: 0x%x\n", k, win);
} else if (cache_list[k].selectinput && cache_list[k].time > now - refresh) {
fprintf(stderr, " Young[%d]: 0x%x\n", k, win);
} else if (valid_window(win, &attr, 1)) {
STORE(k, win, attr);
if (! cache_list[k].selectinput) {
X_LOCK;
fprintf(stderr, " XSelectInput[%d]: 0x%x\n", k, win);
XSelectInput(dpy, win, StructureNotifyMask);
X_UNLOCK;
CLEAR(k);
cache_list[k].selectinput = 1;
} else {
//fprintf(stderr, " SKIP XSelectInput[%d]: 0x%x\n", k, win);
}
} else {
fprintf(stderr, " DELETE(%d) 0x%x\n", k, win);
DELETE(k);
}
}
last_root = dnow();
}
X_LOCK;
while (XCheckMaskEvent(dpy, SubstructureNotifyMask|StructureNotifyMask, &ev)) {
int type = ev.type;
int idx;
win = ev.xany.window;
//fprintf(stderr, "evnt: 0x%x %d pending: %d\n", win, type, XPending(dpy));
if (win == rootwin) {
if (type == CreateNotify) {
int x=0,y=0,w=0,h=0;
win2 = ev.xcreatewindow.window;
idx = lookup_win_index(win2);
if (idx < 0) {
idx = lookup_free_index();
}
if (valid_window(win2, &attr, 1)) {
STORE(idx, win2, attr);
CLEAR(idx);
// su_save(idx); // not working
x=attr.x;
y=attr.y;
w=attr.width;
h=attr.height;
XSelectInput(dpy, win2, StructureNotifyMask);
cache_list[idx].selectinput = 1;
}
fprintf(stderr, "root: ** CreateNotify 0x%x %d -- %dx%d+%d+%d\n", win2, idx, w, h, x, y);
} else if (type == ReparentNotify) {
if (ev.xreparent.parent != rootwin) {
win2 = ev.xreparent.window;
if (win2 != rootwin) {
idx = lookup_win_index(win2);
fprintf(stderr, "root: ReparentNotify RM: 0x%x %d\n", win2, idx);
if (idx >= 0) {
DELETE(idx);
}
XSelectInput(dpy, win2, 0);
}
}
} else {
Window ww = None;
/* skip rest */
if (type == DestroyNotify) ww = ev.xdestroywindow.window;
if (type == UnmapNotify) ww = ev.xunmap.window;
if (type == MapNotify) ww = ev.xmap.window;
if (type == Expose) ww = ev.xexpose.window;
if (type == ConfigureNotify) ww = ev.xconfigure.window;
fprintf(stderr, "root: skip %s for 0x%x\n", Etype(type), ww);
}
} else {
if (type == ReparentNotify) {
if (ev.xreparent.parent != rootwin) {
win2 = ev.xreparent.window;
if (win2 != rootwin) {
idx = lookup_win_index(win2);
fprintf(stderr, "----- ReparentNotify RM: 0x%x %d\n", win2, idx);
if (idx >= 0) {
DELETE(idx);
}
XSelectInput(dpy, win2, 0);
}
}
} else if (type == DestroyNotify) {
win2 = ev.xdestroywindow.window;
idx = lookup_win_index(win2);
fprintf(stderr, "----- DestroyNotify 0x%x %d\n", win2, idx);
if (idx >= 0) {
DELETE(idx);
}
} else if (type == ConfigureNotify) {
idx = lookup_win_index(win);
fprintf(stderr, "----- ConfigureNotify 0x%x %d -- %dx%d+%d+%d\n", win, idx, ev.xconfigure.width, ev.xconfigure.height, ev.xconfigure.x, ev.xconfigure.y);
if (idx >= 0) {
/* XXX invalidate su and/or bs */
cache_list[idx].x = ev.xconfigure.x;
cache_list[idx].y = ev.xconfigure.y;
cache_list[idx].width = ev.xconfigure.width;
cache_list[idx].height = ev.xconfigure.height;
}
} else if (type == MapNotify) {
idx = lookup_win_index(win);
fprintf(stderr, "----- MapNotify 0x%x %d\n", win, idx);
if (idx < 0) {
continue;
}
if (cache_list[idx].map_state == IsUnmapped) {
X_UNLOCK;
su_save(idx);
bs_restore(idx);
X_LOCK;
}
} else if (type == UnmapNotify) {
idx = lookup_win_index(win);
fprintf(stderr, "----- UnmapNotify 0x%x %d\n", win, idx);
if (idx < 0) {
continue;
}
if (cache_list[idx].map_state == IsViewable) {
X_UNLOCK;
bs_save(idx);
su_restore(idx);
X_LOCK;
}
} else {
/* skip rest */
fprintf(stderr, "----- skip %s\n", Etype(type));
}
}
}
//fprintf(stderr, "pending2: %d\n", XPending(dpy));
X_UNLOCK;
if (dnow() - now > 0.05) fprintf(stderr, "check_ncache OUT: %f\n", dnow() - now);
}
#endif
......@@ -35,5 +35,6 @@ extern int check_xrecord(void);
extern int check_wireframe(void);
extern int fb_update_sent(int *count);
extern int check_user_input(double dt, double dtr, int tile_diffs, int *cnt);
extern void do_copyregion(sraRegionPtr region, int dx, int dy);
#endif /* _X11VNC_USERINPUT_H */
......@@ -15,6 +15,11 @@ typedef struct winattr {
int map_state;
int rx, ry;
double time;
double bs_time;
double su_time;
int bs_x, bs_y, bs_w, bs_h;
int su_x, su_y, su_w, su_h;
int selectinput;
} winattr_t;
#endif /* _X11VNC_WINATTR_T_H */
......@@ -2,7 +2,7 @@
.TH X11VNC "1" "December 2006" "x11vnc " "User Commands"
.SH NAME
x11vnc - allow VNC connections to real X11 displays
version: 0.8.4, lastmod: 2006-12-14
version: 0.8.4, lastmod: 2006-12-17
.SH SYNOPSIS
.B x11vnc
[OPTION]...
......@@ -2376,6 +2376,32 @@ slow setups, but you lose all visual feedback for drags,
text selection, and some menu traversals. It overrides
any \fB-pointer_mode\fR setting.
.PP
\fB-ncache\fR \fIn\fR
.IP
Client-side caching scheme. Framebuffer memory \fIn\fR
(an integer) times that of the full display is allocated
below the actual framebuffer to cache screen contents
for rapid retrieval. So a W x H frambuffer is expanded
to a W x (n+1)*H one. Use 0 to disable. Default: XXX.
.IP
This is an experimental option, currently implemented
in an awkward way in that in the VNC Viewer you can
see the cache contents if you scroll down, etc. So you
will have to set things up so you can't see that region.
If this method is successful, the changes required for
clients to do this less awkwardly will be investigated.
.IP
Note that this mode consumes a lot of memory, both
on the x11vnc server side and on the VNC Viewer side.
If n=2 then the amount of RAM used is roughly tripled
for both x11vnc and the VNC Viewer. As a rule of
thumb, note that 1280x1024 at depth 24 is about 5MB of
pixel data.
.IP
Because of the way window backingstore and saveunders
are implemented, n must be even. It will be incremented
by 1 if it is not.
.PP
\fB-wireframe\fR \fI[str],\fR \fB-nowireframe\fR
.IP
Try to detect window moves or resizes when a mouse
......
......@@ -554,6 +554,7 @@ static void watch_loop(void) {
}
check_new_clients();
check_ncache();
check_xevents(0);
check_autorepeat();
check_pm();
......@@ -611,8 +612,9 @@ static void watch_loop(void) {
if (button_mask && (!show_dragging || pointer_mode == 0)) {
/*
* if any button is pressed do not update rfb
* screen, but do flush the X11 display.
* if any button is pressed in this mode do
* not update rfb screen, but do flush the
* X11 display.
*/
X_LOCK;
XFlush_wr(dpy);
......@@ -1200,6 +1202,7 @@ static void print_settings(int try_http, int bg, char *gui_str) {
fprintf(stderr, " buttonmap: %s\n", pointer_remap
? pointer_remap : "null");
fprintf(stderr, " dragging: %d\n", show_dragging);
fprintf(stderr, " ncache: %d\n", ncache);
fprintf(stderr, " wireframe: %s\n", wireframe_str ?
wireframe_str : WIREFRAME_PARMS);
fprintf(stderr, " wirecopy: %s\n", wireframe_copyrect ?
......@@ -2105,6 +2108,14 @@ int main(int argc, char* argv[]) {
pointer_remap = strdup(argv[++i]);
} else if (!strcmp(arg, "-nodragging")) {
show_dragging = 0;
#ifndef NO_NCACHE
} else if (!strcmp(arg, "-ncache")) {
CHECK_ARGC
ncache = atoi(argv[++i]);
if (ncache % 2 != 0) {
ncache++;
}
#endif
} else if (!strcmp(arg, "-wireframe")
|| !strcmp(arg, "-wf")) {
wireframe = 1;
......
......@@ -134,7 +134,9 @@
#define PASSWD_UNLESS_NOPW 0
#endif
/* these are for delaying features: */
#define xxNO_SSL_OR_UNIXPW
#define xxNO_NCACHE
/*
* Beginning of support for small binary footprint build for embedded
......@@ -360,6 +362,8 @@ extern int button_mask; /* button state and info */
extern int button_mask_prev;
extern int num_buttons;
extern long xselectinput_rootwin;
extern unsigned int display_button_mask;
extern unsigned int display_mod_mask;
......
......@@ -15,7 +15,7 @@ int xtrap_base_event_type = 0;
int xdamage_base_event_type = 0;
/* date +'lastmod: %Y-%m-%d' */
char lastmod[] = "0.8.4 lastmod: 2006-12-14";
char lastmod[] = "0.8.4 lastmod: 2006-12-17";
/* X display info */
......@@ -36,6 +36,8 @@ int button_mask = 0; /* button state and info */
int button_mask_prev = 0;
int num_buttons = -1;
long xselectinput_rootwin = 0;
unsigned int display_button_mask = 0;
unsigned int display_mod_mask = 0;
......
......@@ -107,7 +107,8 @@ static void initialize_xevents(int reset) {
* XXX: does this cause a flood of other stuff?
*/
X_LOCK;
XSelectInput(dpy, rootwin, PropertyChangeMask);
xselectinput_rootwin |= PropertyChangeMask;
XSelectInput(dpy, rootwin, xselectinput_rootwin);
X_UNLOCK;
did_xselect_input = 1;
}
......
......@@ -382,11 +382,17 @@ void push_sleep(int n) {
* try to forcefully push a black screen to all connected clients
*/
void push_black_screen(int n) {
int Lx = dpy_x, Ly = dpy_y;
if (!screen) {
return;
}
zero_fb(0, 0, dpy_x, dpy_y);
mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0);
#ifndef NO_NCACHE
if (ncache > 0) {
Ly = dpy_y * (1+ncache);
}
#endif
zero_fb(0, 0, Lx, Ly);
mark_rect_as_modified(0, 0, Lx, Ly, 0);
push_sleep(n);
}
......@@ -406,13 +412,19 @@ void refresh_screen(int push) {
*/
void zero_fb(int x1, int y1, int x2, int y2) {
int pixelsize = bpp/8;
int line, fill = 0;
int line, fill = 0, yfac = 1;
char *dst;
#ifndef NO_NCACHE
if (ncache > 0) {
yfac = 1+ncache;
}
#endif
if (x1 < 0 || x2 <= x1 || x2 > dpy_x) {
return;
}
if (y1 < 0 || y2 <= y1 || y2 > dpy_y) {
if (y1 < 0 || y2 <= y1 || y2 > yfac * dpy_y) {
return;
}
if (! main_fb) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment