diff options
author | runge <runge> | 2005-05-03 02:05:51 +0000 |
---|---|---|
committer | runge <runge> | 2005-05-03 02:05:51 +0000 |
commit | b0daa444cc2fdda15b8b82497d17f80e7d75f8ad (patch) | |
tree | 6702f27b14532de86c6c31dc5a95c00e0f95b160 /x11vnc/x11vnc.c | |
parent | 6602a22f2b3e1164bf521868063891ef62b3650f (diff) | |
download | libtdevnc-b0daa444cc2fdda15b8b82497d17f80e7d75f8ad.tar.gz libtdevnc-b0daa444cc2fdda15b8b82497d17f80e7d75f8ad.zip |
x11vnc: -scrollcopyrect/RECORD, etc. configure.ac: customizations for x11vnc pkg
Diffstat (limited to 'x11vnc/x11vnc.c')
-rw-r--r-- | x11vnc/x11vnc.c | 4127 |
1 files changed, 3363 insertions, 764 deletions
diff --git a/x11vnc/x11vnc.c b/x11vnc/x11vnc.c index e09d9ca..092c236 100644 --- a/x11vnc/x11vnc.c +++ b/x11vnc/x11vnc.c @@ -127,60 +127,9 @@ /* -- x11vnc.h -- */ -/* - *************************************************************************** - * if you are inserting this file, x11vnc.c into an old CVS tree you - * may need to set OLD_TREE to 1. Or use -DOLD_TREE=1 in CPPFLAGS. - * See below for LibVNCServer 0.7 tips. - */ - -#ifndef OLD_TREE -#define OLD_TREE 0 -#endif -#if OLD_TREE - -/* BEGIN OLD TREE */ - -/* - * if you have a very old tree (LibVNCServer 0.6) and get errors these may - * be need to be uncommented. LibVNCServer <= 0.5 is no longer supported. - * note the maxRectsPerUpdate below is a hack that may break some usage. -#define oldCursorX cursorX -#define oldCursorY cursorY -#define thisHost rfbThisHost -#define framebufferUpdateMessagesSent rfbFramebufferUpdateMessagesSent -#define bytesSent rfbBytesSent -#define rawBytesEquivalent rfbRawBytesEquivalent -#define progressiveSliceHeight maxRectsPerUpdate - */ - -/* - * If you are building in an older libvncserver tree with this newer - * x11vnc.c file using OLD_TREE=1 you may need to set some of these lines - * since your older libvncserver configure is not setting them. - * - * For the features LIBVNCSERVER_HAVE_LIBXINERAMA and - * LIBVNCSERVER_HAVE_XFIXES you may also need to add - * -lXinerama or -lXfixes, respectively, to the linking line, e.g. - * by setting them in LD_FLAGS before running configure. - */ - -#define LIBVNCSERVER_HAVE_XSHM 1 -#define LIBVNCSERVER_HAVE_XTEST 1 -#define LIBVNCSERVER_HAVE_XTESTGRABCONTROL 1 - -#define LIBVNCSERVER_HAVE_PWD_H 1 -#define LIBVNCSERVER_HAVE_SYS_WAIT_H 1 -#define LIBVNCSERVER_HAVE_UTMPX_H 1 -#define LIBVNCSERVER_HAVE_MMAP 1 - -/* -#define LIBVNCSERVER_HAVE_LIBXINERAMA 1 -#define LIBVNCSERVER_HAVE_XFIXES 1 -#define LIBVNCSERVER_HAVE_LIBXDAMAGE 1 - */ - -/* END OLD TREE */ +/* TODO: autoconf */ +#if 1 +#define LIBVNCSERVER_HAVE_RECORD 1 #endif /****************************************************************************/ @@ -206,63 +155,41 @@ #include <rfb/rfbregion.h> /****************************************************************************/ -#if OLD_TREE -/* - * This is another transient for building in older libvncserver trees, - * due to the API change: - */ -#define dontDisconnect rfbDontDisconnect -#define neverShared rfbNeverShared -#define alwaysShared rfbAlwaysShared -#define clientHead rfbClientHead -#define serverFormat rfbServerFormat -#define port rfbPort -#define listenSock rfbListenSock -#define deferUpdateTime rfbDeferUpdateTime -#define authPasswdData rfbAuthPasswdData -#define rfbEncryptAndStorePasswd vncEncryptAndStorePasswd -#define maxClientWait rfbMaxClientWait -#define rfbHttpInitSockets httpInitSockets - -#define RFBUNDRAWCURSOR(s) if (s) {rfbUndrawCursor(s);} -#else /* OLD_TREE */ -#define RFBUNDRAWCURSOR(s) -#endif /* !OLD_TREE */ + +/* Build-time customization via CPPFLAGS. */ + /* - * To get a clean build in a LibVNCServer 0.7 source tree no need for - * OLD_TREE, you just need to either download the forgotten tkx11vnc.h - * file or run: + * Summary of options to include in CPPFLAGS for custom builds: * - * echo 'char gui_code[] = "";' > tkx11vnc.h + * -DSHARED to have the vnc display shared by default. + * -DFOREVER to have -forever on by default. + * -DNOREPEAT=0 to have -repeat on by default. * - * (this disables the gui) and uncomment this line: -#define rfbSetCursor(a, b) rfbSetCursor((a), (b), FALSE) - */ - -/* - * To get a clean build on LibVNCServer 0.7.1 no need for OLD_TREE, - * just uncomment this line (note the maxRectsPerUpdate below is a hack - * that may break some usage): + * -DREMOTE_DEFAULT=0 to disable remote-control on by default (-yesremote). + * -DREMOTE_CONTROL=0 to disable remote-control mechanism completely. + * -DEXTERNAL_COMMANDS=0 to disable the running of all external commands. * -#define listenInterface maxRectsPerUpdate + * -DHARDWIRE_PASSWD=... hardwired passwords, quoting necessary. + * -DHARDWIRE_VIEWPASSWD=... * - */ -/****************************************************************************/ - - -/* Build-time customization via CPPFLAGS. */ - -/* - * -DX11VNC_SHARED to have the vnc display shared by default. - */ - -/* - * -DX11VNC_FOREVER to have -forever on by default. + * -DWIREFRAME=0 to have -nowireframe as the default. + * -DWIREFRAME_COPYRECT=0 to have -nowirecopyrect as the default. + * -DSCROLL_COPYRECT=0 to have -noscrollcopyrect as the default. + * -DXDAMAGE=0 to have -noxdamage as the default. + * + * -DPOINTER_MODE_DEFAULT={0,1,2,3,4,5} set default -pointer_mode. + * -DBOLDLY_CLOSE_DISPLAY=0 to not close X DISPLAY under -rawfb. + * -DSMALL_FOOTPRINT=1 for smaller binary size (no help, no gui, etc) + * use 2 or 3 for even smaller footprint. + * + * Set these in CPPFLAGS before running configure. E.g.: + * + * % env CPPFLAGS="-DFOREVER -DREMOTE_CONTROL=0" ./configure + * % make */ /* * This can be used to disable the remote control mechanism. - * E.g. -DREMOTE_CONTROL=0 in CPPFLAGS. */ #ifndef REMOTE_CONTROL #define REMOTE_CONTROL 1 @@ -334,6 +261,11 @@ XETC *trap_ctx = NULL; #endif int xtrap_base_event_type = 0; +#if LIBVNCSERVER_HAVE_RECORD +#include <X11/Xproto.h> +#include <X11/extensions/record.h> +#endif + #if LIBVNCSERVER_HAVE_XKEYBOARD #include <X11/XKBlib.h> #endif @@ -423,8 +355,11 @@ int alt_arrow = 1; int xfixes_base_event_type = 0; +#ifndef XDAMAGE +#define XDAMAGE 1 +#endif +int use_xdamage = XDAMAGE; /* use the xdamage rects for scanline hints */ int xdamage_present = 0; -int use_xdamage = 1; /* just use the xdamage rects. for scanline hints */ #if LIBVNCSERVER_HAVE_LIBXDAMAGE #include <X11/extensions/Xdamage.h> Damage xdamage = 0; @@ -436,7 +371,7 @@ int xdamage_tile_count; /* date +'lastmod: %Y-%m-%d' */ -char lastmod[] = "0.7.2 lastmod: 2005-04-19"; +char lastmod[] = "0.7.2 lastmod: 2005-05-02"; int hack_val = 0; /* X display info */ @@ -504,7 +439,6 @@ unsigned short main_red_max, main_green_max, main_blue_max; unsigned short main_red_shift, main_green_shift, main_blue_shift; /* struct with client specific data: */ -#define RATE_SAMPLES 5 #define CILEN 10 typedef struct _ClientData { int uid; @@ -522,11 +456,9 @@ typedef struct _ClientData { double timer; double send_cmp_rate; double send_raw_rate; - int set_cmp_bytes; - int set_raw_bytes; - double cmp_samp[RATE_SAMPLES]; - double raw_samp[RATE_SAMPLES]; - int sample; + double latency; + int cmp_bytes_sent; + int raw_bytes_sent; } ClientData; /* scaling parameters */ @@ -560,6 +492,7 @@ unsigned char *tile_has_xdamage_diff, *tile_row_has_xdamage_diff; /* times of recent events */ time_t last_event, last_input, last_client = 0; +double servertime_diff = 0.0; /* last client to move pointer */ rfbClientPtr last_pointer_client = NULL; @@ -574,7 +507,9 @@ int button_change_x, button_change_y; int got_user_input = 0; int got_pointer_input = 0; int got_keyboard_input = 0; +int urgent_update = 0; int last_keyboard_input = 0; +rfbKeySym last_keysym = 0; int fb_copy_in_progress = 0; int drag_in_progress = 0; int shut_down = 0; @@ -649,6 +584,7 @@ void initialize_watch_bell(void); void initialize_xinerama(void); void initialize_xfixes(void); void initialize_xdamage(void); +int valid_window(Window, XWindowAttributes *); void create_xdamage_if_needed(void); void destroy_xdamage_if_needed(void); void initialize_xrandr(void); @@ -686,12 +622,21 @@ int check_pipeinput(void); void cursor_position(int, int); void parse_wireframe(void); +void parse_scroll_copyrect(void); void set_wirecopyrect_mode(char *); +void set_scrollcopyrect_mode(char *); +int try_copyrect(Window, int, int, int, int, int, int, int *); +void do_copyregion(sraRegionPtr, int, int); +int direct_fb_copy(int, int, int, int, int); +int get_wm_frame_pos(int *, int *, int *, int *, int *, int *, Window *); +int near_wm_edge(int, int, int, int, int, int); +int near_scrollbar_edge(int, int, int, int, int, int); void read_vnc_connect_prop(void); void set_vnc_connect_prop(char *); +void fb_push(void); char *process_remote_cmd(char *, int); -void rfbPE(rfbScreenInfoPtr, long); -void rfbCFD(rfbScreenInfoPtr, long); +void rfbPE(long); +void rfbCFD(long); int scan_for_updates(int); void set_colormap(int); void set_offset(void); @@ -728,8 +673,10 @@ int get_raw_rate(void); int get_read_rate(void); int get_net_rate(void); int get_net_latency(void); +int get_latency(void); void measure_send_rates(int); int fb_update_sent(int *); +void snapshot_stack_list(int, double); int get_remote_port(int sock); int get_local_port(int sock); @@ -771,29 +718,42 @@ char *solid_default = "cyan4"; char *speeds_str = NULL; /* -speeds TBD */ int measure_speeds = 1; int speeds_net_rate = 0; +int speeds_net_rate_measured = 0; int speeds_net_latency = 0; +int speeds_net_latency_measured = 0; int speeds_read_rate = 0; +int speeds_read_rate_measured = 0; char *rc_rcfile = NULL; /* -rc */ int rc_norc = 0; int opts_bg = 0; -#ifndef X11VNC_SHARED +#ifndef SHARED int shared = 0; /* share vnc display. */ #else int shared = 1; #endif -#ifndef X11VNC_FOREVER +#ifndef FOREVER int connect_once = 1; /* disconnect after first connection session. */ #else int connect_once = 0; #endif int deny_all = 0; /* global locking of new clients */ -int accept_remote_cmds = 1; /* -noremote */ +#ifndef REMOTE_DEFAULT +#define REMOTE_DEFAULT 1 +#endif +int accept_remote_cmds = REMOTE_DEFAULT; /* -noremote */ int safe_remote_only = 1; /* -unsafe */ int priv_remote = 0; /* -privremote */ int more_safe = 0; /* -safer */ +#ifndef EXTERNAL_COMMANDS +#define EXTERNAL_COMMANDS 1 +#endif +#if EXTERNAL_COMMANDS int no_external_cmds = 0; /* -nocmds */ +#else +int no_external_cmds = 1; /* cannot be turned back on. */ +#endif int started_as_root = 0; int host_lookup = 1; char *users_list = NULL; /* -users */ @@ -829,6 +789,8 @@ int subwin_wait_mapped = 0; int debug_xevents = 0; /* -R debug_xevents:1 */ int debug_xdamage = 0; /* -R debug_xdamage:1 or 2 ... */ +int debug_wireframe = 0; +int debug_wireframe2 = 0; int xtrap_input = 0; /* -xtrap for user input insertion */ int xinerama = 0; /* -xinerama */ @@ -844,6 +806,10 @@ char *pad_geometry = NULL; time_t pad_geometry_time; int use_snapfb = 0; +Display *rdpy_data = 0; /* Data connection for RECORD */ +Display *rdpy_ctrl = 0; /* Control connection for RECORD */ +int use_xrecord = 0; + char *client_connect = NULL; /* strings for -connect option */ char *client_connect_file = NULL; int vnc_connect = 1; /* -vncconnect option */ @@ -861,15 +827,47 @@ int show_dragging = 1; /* process mouse movement events */ int wireframe = WIREFRAME; /* try to emulate wireframe wm moves */ /* shade,linewidth,percent,T+B+L+R,t1+t2+t3+t4 */ #ifndef WIREFRAME_PARMS -#define WIREFRAME_PARMS "0xff,3,0,32+8+8+8,0.15+0.35+4.0+0.1" +#define WIREFRAME_PARMS "0xff,3,0,32+8+8+8,0.15+0.30+5.0+0.125" #endif char *wireframe_str = NULL; char *wireframe_copyrect = NULL; +#ifndef WIREFRAME_COPYRECT +#define WIREFRAME_COPYRECT 1 +#endif +#if WIREFRAME_COPYRECT char *wireframe_copyrect_default = "always"; +#else +char *wireframe_copyrect_default = "never"; +#endif int wireframe_in_progress = 0; Window *stack_list = NULL; int stack_num = 0; -int no_autorepeat = 1; /* turn off autorepeat with clients */ + +/* T+B+L+R,tkey+presist_key,tmouse+persist_mouse */ +#ifndef SCROLL_COPYRECT_PARMS +#define SCROLL_COPYRECT_PARMS "0+64+32+32,0.02+0.4,0.08+0.4" +#endif +char *scroll_copyrect_str = NULL; +#ifndef SCROLL_COPYRECT +#define SCROLL_COPYRECT 1 +#endif +char *scroll_copyrect = NULL; +#if SCROLL_COPYRECT +#if 1 +char *scroll_copyrect_default = "always"; /* -scrollcopyrect */ +#else +char *scroll_copyrect_default = "keys"; +#endif +#else +char *scroll_copyrect_default = "never"; +#endif +int scrollcopyrect_min_area = 60000; /* minimum rectangle area */ +int debug_scroll = 0; + +#ifndef NOREPEAT +#define NOREPEAT 1 +#endif +int no_autorepeat = NOREPEAT; /* turn off autorepeat with clients */ int no_repeat_countdown = 2; int watch_bell = 1; /* watch for the bell using XKEYBOARD */ int sound_bell = 1; /* actually send it */ @@ -925,6 +923,7 @@ int overlay_cursor = 1; int xshm_present = 0; int xtest_present = 0; int xtrap_present = 0; +int xrecord_present = 0; int xkb_present = 0; int xinerama_present = 0; @@ -947,6 +946,8 @@ int got_nevershared = 0; int got_cursorpos = 0; int got_pointer_mode = -1; int got_noviewonly = 0; +int got_wirecopyrect = 0; +int got_scrollcopyrect = 0; /* threaded vs. non-threaded (default) */ #if LIBVNCSERVER_X11VNC_THREADED && ! defined(X11VNC_THREADED) @@ -1013,6 +1014,14 @@ int nabs(int n) { } } +double dabs(double x) { + if (x < 0.0) { + return -x; + } else { + return x; + } +} + void lowercase(char *str) { char *p; if (str == NULL) { @@ -1136,7 +1145,7 @@ int pick_windowid(unsigned long *num) { * select timedout or error. * note this rfbPE takes about 30ms too: */ - rfbPE(screen, -1); + rfbPE(-1); XFlush(dpy); continue; } @@ -1952,6 +1961,30 @@ char *host2ip(char *host) { return str; } +char *raw2host(char *raw, int len) { + char *str; +#if LIBVNCSERVER_HAVE_NETDB_H && LIBVNCSERVER_HAVE_NETINET_IN_H + struct hostent *hp; + + if (! host_lookup) { + return strdup("unknown"); + } + + hp = gethostbyaddr(raw, len, AF_INET); + if (!hp) { + return strdup(inet_ntoa(*((struct in_addr *)raw))); + } + str = strdup(hp->h_name); +#else + str = strdup("unknown"); +#endif + return str; +} + +char *raw2ip(char *raw) { + return strdup(inet_ntoa(*((struct in_addr *)raw))); +} + char *ip2host(char *ip) { char *str; #if LIBVNCSERVER_HAVE_NETDB_H && LIBVNCSERVER_HAVE_NETINET_IN_H @@ -2558,15 +2591,6 @@ Bool XTestCompareCursorWithWindow_wr(Display* dpy, Window w, Cursor cursor) { #endif } -/* how to handle old tree for this w/o OLD_TREE? */ -#if 0 -#if LIBVNCSERVER_HAVE_XTEST -#ifndef LIBVNCSERVER_HAVE_XTESTGRABCONTROL -#define LIBVNCSERVER_HAVE_XTESTGRABCONTROL 1 -#endif -#endif -#endif - Bool XTestQueryExtension_wr(Display *dpy, int *ev, int *er, int *maj, int *min) { #if LIBVNCSERVER_HAVE_XTEST @@ -2642,40 +2666,1010 @@ int XTRAP_GrabControl_wr(Display *dpy, Bool impervious) { return 0; } -void disable_grabserver(void) { +void disable_grabserver(Display *dpy) { int ok = 0; + static int didmsg = 0; if (! xtrap_input) { if (XTestGrabControl_wr(dpy, True)) { XTRAP_GrabControl_wr(dpy, False); - rfbLog("GrabServer control via XTEST.\n"); + if (! didmsg) { + rfbLog("GrabServer control via XTEST.\n"); + didmsg = 1; + } ok = 1; } else { if (XTRAP_GrabControl_wr(dpy, True)) { ok = 1; - rfbLog("Using DEC-XTRAP for protection from " - "XGrabServer.\n"); + if (! didmsg) { + rfbLog("Using DEC-XTRAP for protection" + " from XGrabServer.\n"); + didmsg = 1; + } } } } else { if (XTRAP_GrabControl_wr(dpy, True)) { XTestGrabControl_wr(dpy, False); - rfbLog("GrabServer control via DEC-XTRAP.\n"); + if (! didmsg) { + rfbLog("GrabServer control via DEC-XTRAP.\n"); + didmsg = 1; + } ok = 1; } else { if (XTestGrabControl_wr(dpy, True)) { ok = 1; - rfbLog("DEC-XTRAP XGrabServer protection not " - "available, using XTEST.\n"); + if (! didmsg) { + rfbLog("DEC-XTRAP XGrabServer " + "protection not available, " + "using XTEST.\n"); + didmsg = 1; + } } } } - if (! ok) { + if (! ok && ! didmsg) { rfbLog("No XTEST or DEC-XTRAP protection from XGrabServer.\n"); rfbLog("Deadlock if your window manager calls XGrabServer!!\n"); } } +Bool XRecordQueryVersion_wr(Display *dpy, int *maj, int *min) { +#if LIBVNCSERVER_HAVE_RECORD + return XRecordQueryVersion(dpy, maj, min); +#else + return False; +#endif +} + +#if LIBVNCSERVER_HAVE_RECORD +XRecordRange *rr_CA; +XRecordRange *rr_CW; +XRecordRange *rr_scroll[10]; +XRecordContext rc_scroll; +XRecordClientSpec rcs_scroll; +#endif + +int xrecording = 0; +int xrecord_set_by_keys = 0; +int xrecord_set_by_mouse = 0; +Window xrecord_focus_window = None; +Window xrecord_wm_window = None; + +void initialize_xrecord(void) { + use_xrecord = 0; + if (! xrecord_present) { + return; + } +#if LIBVNCSERVER_HAVE_RECORD + rr_CA = XRecordAllocRange(); + rr_CW = XRecordAllocRange(); + if (! rr_CA || ! rr_CW) { + return; + } + /* protocol request ranges: */ + rr_CA->core_requests.first = X_CopyArea; + rr_CA->core_requests.last = X_CopyArea; + + rr_CW->core_requests.first = X_ConfigureWindow; + rr_CW->core_requests.last = X_ConfigureWindow; + + /* open a 2nd control connection to DISPLAY: */ + rdpy_ctrl = XOpenDisplay(DisplayString(dpy)); + XSync(dpy, True); + XSync(rdpy_ctrl, True); + /* open datalink connection to DISPLAY: */ + rdpy_data = XOpenDisplay(DisplayString(dpy)); + if (!rdpy_ctrl || ! rdpy_data) { + return; + } + disable_grabserver(rdpy_ctrl); + disable_grabserver(rdpy_data); + use_xrecord = 1; +#endif +} + +int xrecord_skip_keysym(rfbKeySym keysym) { + KeySym sym = (KeySym) keysym; + + if (IsModifierKey(sym)) { + return 1; + } + return 0; +} + +int xrecord_skip_button(int new, int old) { + return 0; +} + +int xrecord_scroll_keysym(rfbKeySym keysym) { + KeySym sym = (KeySym) keysym; + /* X11/keysymdef.h */ + + if (sym == XK_Return || sym == XK_KP_Enter || sym == XK_Linefeed) { + return 1; /* Enter */ + } + if (sym==XK_Up || sym==XK_KP_Up || sym==XK_Down || sym==XK_KP_Down) { + return 1; /* U/D arrows */ + } + if (sym == XK_Left || sym == XK_KP_Left || sym == XK_Right || + sym == XK_KP_Right) { + return 1; /* L/R arrows */ + } + if (sym == XK_J || sym == XK_j || sym == XK_K || sym == XK_k) { + return 1; /* vi */ + } + if (sym == XK_N || sym == XK_n || sym == XK_P || sym == XK_p) { + return 1; /* emacs */ + } + return 0; +} + +typedef struct scroll_event { + Window win, frame; + int dx, dy; + int x, y, w, h, t; + int win_x, win_y, win_w, win_h; + int new_x, new_y, new_w, new_h; +} scroll_event_t; + +#define SCR_EV_MAX 128 +scroll_event_t scr_ev[SCR_EV_MAX]; +int scr_ev_cnt = 0; + +XID xrecord_seq = 0; +double xrecord_start = 0.0; + +#if LIBVNCSERVER_HAVE_RECORD +void record_CA(XPointer ptr, XRecordInterceptData *rec_data) { + xCopyAreaReq *req; + Window src = None, dst = None, c; + XWindowAttributes attr; + int src_x, src_y, dst_x, dst_y, rx, ry; + int good = 1, dx, dy, k=0, i; + unsigned int w, h; + int dba = 0, db = debug_scroll; + +//dba = 1; + + if (dba || db) { + if (rec_data->category == XRecordFromClient) { + req = (xCopyAreaReq *) rec_data->data; + if (req->reqType == X_CopyArea) { + src = req->srcDrawable; + dst = req->dstDrawable; + } + } + } + +if (dba || db) fprintf(stderr, "record_CA-%d id_base: 0x%lx ptr: 0x%lx " + "seq: 0x%lx rc: 0x%lx cat: %d swapped: %d 0x%lx/0x%lx\n", k++, + rec_data->id_base, (unsigned long) ptr, xrecord_seq, rc_scroll, + rec_data->category, rec_data->client_swapped, src, dst); + + if (! xrecording) { + return; + } +if (db) fprintf(stderr, "record_CA-%d\n", k++); + + if (rec_data->id_base == 0) { + return; + } +if (db) fprintf(stderr, "record_CA-%d\n", k++); + + if ((XID) ptr != xrecord_seq) { + return; + } +if (db) fprintf(stderr, "record_CA-%d\n", k++); + + if (rec_data->category != XRecordFromClient) { + return; + } +if (db) fprintf(stderr, "record_CA-%d\n", k++); + + req = (xCopyAreaReq *) rec_data->data; + + if (req->reqType != X_CopyArea) { + return; + } +if (db) fprintf(stderr, "record_CA-%d\n", k++); + +/* + +xterm, gnome-terminal, others. + +Note we miss the X_ImageText8 that clears the block cursor. So there is a +short period of time with a painting error: two cursors, one above the other. + + X_ImageText8 + draw: 0x8c00017 nChars: 1, gc: 0x8c00013, x: 101, y: 585, chars=' ' + X_ClearArea + window: 0x8c00018, x: 2, y: 217, w: 10, h: 5 + X_FillPoly + draw: 0x8c00018 gc: 0x8c0000a, shape: 0, coordMode: 0, + X_FillPoly + draw: 0x8c00018 gc: 0x8c0000b, shape: 0, coordMode: 0, + X_CopyArea + src: 0x8c00017, dst: 0x8c00017, gc: 0x8c00013, srcX: 17, srcY: 15, dstX: 17, dstY: 2, w: 480, h: 572 + X_ChangeWindowAttributes + X_ClearArea + window: 0x8c00017, x: 17, y: 574, w: 480, h: 13 + X_ChangeWindowAttributes + + */ + + src = req->srcDrawable; + dst = req->dstDrawable; + src_x = req->srcX; + src_y = req->srcY; + dst_x = req->dstX; + dst_y = req->dstY; + w = req->width; + h = req->height; + + if (w*h < scrollcopyrect_min_area) { + good = 0; + } else if (!src || !dst) { + good = 0; + } else if (src != dst) { + good = 0; + } + + dx = dst_x - src_x; + dy = dst_y - src_y; + + if (dx != 0 && dy != 0) { + good = 0; + } + + if (! good) { + return; + } +if (db) fprintf(stderr, "record_CA-%d\n", k++); + + if (! valid_window(src, &attr)) { + return; + } +if (db) fprintf(stderr, "record_CA-%d\n", k++); + + if (attr.map_state != IsViewable) { + return; + } + + XTranslateCoordinates(dpy, src, rootwin, 0, 0, &rx, &ry, &c); + +if (dba || db) fprintf(stderr, "record_CA-%d *FOUND: src: 0x%lx dx: %d dy: %d " + "x: %d y: %d w: %d h: %d st: %.4f\n", k++, src, dx, dy, src_x, + src_y, w, h, (double) rec_data->server_time/1000.0); + + if (scr_ev_cnt >= SCR_EV_MAX) { + return; + } + + i = scr_ev_cnt; + + scr_ev[i].win = src; + scr_ev[i].frame = None; + scr_ev[i].dx = dx; + scr_ev[i].dy = dy; + scr_ev[i].x = rx + dst_x; + scr_ev[i].y = ry + dst_y; + scr_ev[i].w = w; + scr_ev[i].h = h; + scr_ev[i].t = (int) rec_data->server_time; + scr_ev[i].win_x = rx; + scr_ev[i].win_y = ry; + scr_ev[i].win_w = attr.width; + scr_ev[i].win_h = attr.height; + scr_ev[i].new_x = 0; + scr_ev[i].new_y = 0; + scr_ev[i].new_w = 0; + scr_ev[i].new_h = 0; + + if (dx == 0) { + if (dy > 0) { + scr_ev[i].new_x = rx + src_x; + scr_ev[i].new_y = ry + src_y; + scr_ev[i].new_w = w; + scr_ev[i].new_h = dy; + } else { + scr_ev[i].new_x = rx + src_x; + scr_ev[i].new_y = ry + dst_y + h; + scr_ev[i].new_w = w; + scr_ev[i].new_h = -dy; + } + } else if (dy == 0) { + if (dx > 0) { + scr_ev[i].new_x = rx + src_x; + scr_ev[i].new_y = rx + src_y; + scr_ev[i].new_w = dx; + scr_ev[i].new_h = h; + } else { + scr_ev[i].new_x = rx + dst_x + w; + scr_ev[i].new_y = ry + src_y; + scr_ev[i].new_w = -dx; + scr_ev[i].new_h = h; + } + } + + scr_ev_cnt++; +} + +typedef struct cw_event { + Window win; + int x, y, w, h; +} cw_event_t; + +#define MAX_CW 128 +cw_event_t cw_events[MAX_CW]; + +void record_CW(XPointer ptr, XRecordInterceptData *rec_data) { + xConfigureWindowReq *req; + Window win = None, c; + Window src = None, dst = None; + XWindowAttributes attr; + int absent = 0x100000; + int src_x, src_y, dst_x, dst_y, rx, ry; + int good = 1, dx, dy, k=0, i, j, match, list[3]; + int f_x, f_y, f_w, f_h; + int x, y, w, h; + int x0, y0, w0, h0, x1, y1, w1, h1, x2, y2, w2, h2; + static int index = 0; + unsigned long vals[4]; + unsigned tmask; + char *data; + int dba = 0, db = debug_scroll; + +//dba = 1; +//db = 1; + + if (1 || db) { + if (rec_data->category == XRecordFromClient) { + req = (xConfigureWindowReq *) rec_data->data; + if (req->reqType == X_ConfigureWindow) { + src = req->window; + } + } + } + +if (dba || db) fprintf(stderr, "record_CW-%d id_base: 0x%lx ptr: 0x%lx " + "seq: 0x%lx rc: 0x%lx cat: %d swapped: %d 0x%lx/0x%lx\n", k++, + rec_data->id_base, (unsigned long) ptr, xrecord_seq, rc_scroll, + rec_data->category, rec_data->client_swapped, src, dst); + + + if (! xrecording) { + return; + } +if (db) fprintf(stderr, "record_CW-%d\n", k++); + + if ((XID) ptr != xrecord_seq) { + return; + } +if (db) fprintf(stderr, "record_CW-%d\n", k++); + + if (rec_data->id_base == 0) { + return; + } +if (db) fprintf(stderr, "record_CW-%d\n", k++); + + if (rec_data->category == XRecordStartOfData) { + index = 0; + return; + } +if (db) fprintf(stderr, "record_CW-%d\n", k++); + + if (rec_data->category != XRecordFromClient) { + return; + } +if (db) fprintf(stderr, "record_CW-%d\n", k++); + + if (rec_data->client_swapped) { + return; + } +if (db) fprintf(stderr, "record_CW-%d\n", k++); + + req = (xConfigureWindowReq *) rec_data->data; + + if (req->reqType != X_ConfigureWindow) { + return; + } +if (db) fprintf(stderr, "record_CW-%d\n", k++); + + tmask = req->mask; + + tmask &= ~CWX; + tmask &= ~CWY; + tmask &= ~CWWidth; + tmask &= ~CWHeight; + + if (tmask) { + /* require no more than these 4 flags */ + return; + } +if (db) fprintf(stderr, "record_CW-%d\n", k++); + + f_x = req->mask & CWX; + f_y = req->mask & CWY; + f_w = req->mask & CWWidth; + f_h = req->mask & CWHeight; + + if (! f_x || ! f_y) { + if (f_w && f_h) { + ; /* netscape 4.x style */ + } else { + return; + } + } +if (db) fprintf(stderr, "record_CW-%d\n", k++); + + if ( (f_w && !f_h) || (!f_w && f_h) ) { + return; + } +if (db) fprintf(stderr, "record_CW-%d\n", k++); + + for (i=0; i<4; i++) { + vals[i] = 0; + } + + data = (char *)req; + data += sz_xConfigureWindowReq; + + for (i=0; i<req->length; i++) { + unsigned long v; + v = *( (unsigned long *) data); + vals[i] = v; + data += 4; + } + + + if (index >= MAX_CW) { + int i, j; + + /* FIXME, circular, etc. */ + for (i=0; i<2; i++) { + j = MAX_CW - 2 + i; + cw_events[i].win = cw_events[j].win; + cw_events[i].x = cw_events[j].x; + cw_events[i].y = cw_events[j].y; + cw_events[i].w = cw_events[j].w; + cw_events[i].h = cw_events[j].h; + index = 2; + } + } + + if (! f_x && ! f_y) { + /* netscape 4.x style CWWidth,CWHeight */ + vals[2] = vals[0]; + vals[3] = vals[1]; + vals[0] = 0; + vals[1] = 0; + } + + cw_events[index].win = req->window; + + if (! f_x) { + cw_events[index].x = absent; + } else { + cw_events[index].x = (int) vals[0]; + } + if (! f_y) { + cw_events[index].y = absent; + } else { + cw_events[index].y = (int) vals[1]; + } + + if (! f_w) { + cw_events[index].w = absent; + } else { + cw_events[index].w = (int) vals[2]; + } + if (! f_h) { + cw_events[index].h = absent; + } else { + cw_events[index].h = (int) vals[3]; + } + + x = cw_events[index].x; + y = cw_events[index].y; + w = cw_events[index].w; + h = cw_events[index].h; + win = cw_events[index].win; + +if (dba || db) fprintf(stderr, " record_CW ind: %d win: 0x%lx x: %d y: %d w: %d h: %d\n", + index, win, x, y, w, h); + + index++; + + if (index < 3) { + good = 0; + } else if (w != absent && h != absent && + w*h < scrollcopyrect_min_area) { + good = 0; + } + + if (! good) { + return; + } +if (db) fprintf(stderr, "record_CW-%d\n", k++); + + match = 0; + for (j=index - 1; j >= 0; j--) { + if (cw_events[j].win == win) { + list[match++] = j; + } + if (match >= 3) { + break; + } + } + + if (match != 3) { + return; + } +if (db) fprintf(stderr, "record_CW-%d\n", k++); + +/* + +Mozilla: + +Up arrow: window moves down a bit (dy > 0): + + X_ConfigureWindow + length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 -18, v2 760, v3 906, v4 327692, v5 48234701, v6 3, + CW-mask: CWX,CWY,CWWidth,CWHeight, + X_ConfigureWindow + length: 5, window: 0x2e000cd, mask: 0x3, v0 0, v1 0, v2 506636, v3 48234701, v4 48234511, + CW-mask: CWX,CWY, + X_ConfigureWindow + length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 0, v2 760, v3 888, v4 65579, v5 0, v6 108009, + CW-mask: CWX,CWY,CWWidth,CWHeight, + +Down arrow: window moves up a bit (dy < 0): + + X_ConfigureWindow + length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 0, v2 760, v3 906, v4 327692, v5 48234701, v6 262147, + CW-mask: CWX,CWY,CWWidth,CWHeight, + X_ConfigureWindow + length: 5, window: 0x2e000cd, mask: 0x3, v0 0, v1 -18, v2 506636, v3 48234701, v4 48234511, + CW-mask: CWX,CWY, + X_ConfigureWindow + length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 0, v2 760, v3 888, v4 96555, v5 48265642, v6 48265262, + CW-mask: CWX,CWY,CWWidth,CWHeight, + + +Netscape 4.x + +Up arrow: +71.76142 0.01984 X_ConfigureWindow + length: 7, window: 0x9800488, mask: 0xf, v0 0, v1 -15, v2 785, v3 882, v4 327692, v5 159384712, v6 1769484, + CW-mask: CWX,CWY,CWWidth,CWHeight, +71.76153 0.00011 X_ConfigureWindow + length: 5, window: 0x9800488, mask: 0xc, v0 785, v1 867, v2 329228, v3 159384712, v4 159383555, + CW-mask: CWWidth,CWHeight, + XXX,XXX +71.76157 0.00003 X_ConfigureWindow + length: 5, window: 0x9800488, mask: 0x3, v0 0, v1 0, v2 131132, v3 159385313, v4 328759, + CW-mask: CWX,CWY, + XXX,XXX + +Down arrow: +72.93147 0.01990 X_ConfigureWindow + length: 5, window: 0x9800488, mask: 0xc, v0 785, v1 882, v2 328972, v3 159384712, v4 159383555, + CW-mask: CWWidth,CWHeight, + XXX,XXX +72.93156 0.00009 X_ConfigureWindow + length: 5, window: 0x9800488, mask: 0x3, v0 0, v1 -15, v2 458764, v3 159384712, v4 159383567, + CW-mask: CWX,CWY, +72.93160 0.00004 X_ConfigureWindow + length: 7, window: 0x9800488, mask: 0xf, v0 0, v1 0, v2 785, v3 867, v4 131132, v5 159385335, v6 328759, + CW-mask: CWX,CWY,CWWidth,CWHeight, + + +sadly, probably need to handle some more... + + */ + x0 = cw_events[list[2]].x; + y0 = cw_events[list[2]].y; + w0 = cw_events[list[2]].w; + h0 = cw_events[list[2]].h; + + x1 = cw_events[list[1]].x; + y1 = cw_events[list[1]].y; + w1 = cw_events[list[1]].w; + h1 = cw_events[list[1]].h; + + x2 = cw_events[list[0]].x; + y2 = cw_events[list[0]].y; + w2 = cw_events[list[0]].w; + h2 = cw_events[list[0]].h; + + /* see NS4 XXX's above: */ + if (w2 == absent || h2 == absent) { + /* up arrow */ + if (w2 == absent) { + w2 = w1; + } + if (h2 == absent) { + h2 = h1; + } + } + if (x1 == absent || y1 == absent) { + /* up arrow */ + if (x1 == absent) { + x1 = x2; + } + if (y1 == absent) { + y1 = y2; + } + } + if (x0 == absent || y0 == absent) { + /* down arrow */ + if (x0 == absent) { + /* hmmm... what to do */ + x0 = x2; + } + if (y0 == absent) { + y0 = y2; + } + } + +if (dba) fprintf(stderr, "%d/%d/%d/%d %d/%d/%d/%d %d/%d/%d/%d\n", x0, y0, w0, h0, x1, y1, w1, h1, x2, y2, w2, h2); + + dy = y1 - y0; + dx = x1 - x0; + + src_x = x2; + src_y = y2; + w = w2; + h = h2; + + /* check w and h before we modify them */ + if (w <= 0 || h <= 0) { + good = 0; + } else if (w == absent || h == absent) { + good = 0; + } + if (! good) { + return; + } +if (db) fprintf(stderr, "record_CW-%d\n", k++); + + if (dy > 0) { + h -= dy; + } else { + h += dy; + src_y -= dy; + } + if (dx > 0) { + w -= dx; + } else { + w += dx; + src_x -= dx; + } + + dst_x = src_x + dx; + dst_y = src_y + dy; + + if (x0 == absent || x1 == absent || x2 == absent) { + good = 0; + } else if (y0 == absent || y1 == absent || y2 == absent) { + good = 0; + } else if (dx != 0 && dy != 0) { + good = 0; + } else if (w0 - w2 != nabs(dx)) { + good = 0; + } else if (h0 - h2 != nabs(dy)) { + good = 0; + } + + if (! good) { + return; + } +if (db) fprintf(stderr, "record_CW-%d\n", k++); + + /* geometry OK, now check with the X server: */ + if (! valid_window(win, &attr)) { + return; + } +if (db) fprintf(stderr, "record_CW-%d\n", k++); + + if (attr.map_state != IsViewable) { + return; + } +if (db) fprintf(stderr, "record_CW-%d\n", k++); + + XTranslateCoordinates(dpy, win, rootwin, 0, 0, &rx, &ry, &c); + +if (dba || db) fprintf(stderr, "record_CW-%d *FOUND: win: 0x%lx dx: %d dy: %d " + "x: %d y: %d w: %d h: %d st: %.4f\n", k++, win, dx, dy, src_x, src_y, + w, h, (double) rec_data->server_time/1000.0); + + if (scr_ev_cnt >= SCR_EV_MAX) { + return; + } + + i = scr_ev_cnt; + + scr_ev[i].win = win; + scr_ev[i].frame = None; + scr_ev[i].dx = dx; + scr_ev[i].dy = dy; + scr_ev[i].x = rx + dst_x; + scr_ev[i].y = ry + dst_y; + scr_ev[i].w = w; + scr_ev[i].h = h; + scr_ev[i].t = (int) rec_data->server_time; + scr_ev[i].win_x = rx; + scr_ev[i].win_y = ry; + scr_ev[i].win_w = attr.width; + scr_ev[i].win_h = attr.height; + scr_ev[i].new_x = 0; + scr_ev[i].new_y = 0; + scr_ev[i].new_w = 0; + scr_ev[i].new_h = 0; + + if (dx == 0) { + if (dy > 0) { + scr_ev[i].new_x = rx + src_x; + scr_ev[i].new_y = ry + src_y; + scr_ev[i].new_w = w; + scr_ev[i].new_h = dy; + } else { + scr_ev[i].new_x = rx + src_x; + scr_ev[i].new_y = ry + dst_y + h; + scr_ev[i].new_w = w; + scr_ev[i].new_h = -dy; + } + } else if (dy == 0) { + if (dx > 0) { + scr_ev[i].new_x = rx + src_x; + scr_ev[i].new_y = rx + src_y; + scr_ev[i].new_w = dx; + scr_ev[i].new_h = h; + } else { + scr_ev[i].new_x = rx + dst_x + w; + scr_ev[i].new_y = ry + src_y; + scr_ev[i].new_w = -dx; + scr_ev[i].new_h = h; + } + } + + /* indicate we have a new one */ + scr_ev_cnt++; + + index = 0; +} + +void record_switch(XPointer ptr, XRecordInterceptData *rec_data) { + xReq *req; + +//fprintf(stderr, "XRecordFreeData-0: %p\n", rec_data); + /* should handle control msgs, start/stop/etc */ + if (rec_data->category == XRecordStartOfData) { + record_CW(ptr, rec_data); + } else if (rec_data->category == XRecordEndOfData) { + ; + } else if (rec_data->category == XRecordClientStarted) { + ; + } else if (rec_data->category == XRecordClientDied) { + ; + } else if (rec_data->category == XRecordFromServer) { + ; + } + + if (rec_data->category != XRecordFromClient) { +//fprintf(stderr, "XRecordFreeData-1: %p\n", rec_data); + XRecordFreeData(rec_data); + return; + } + + req = (xReq *) rec_data->data; + + if (req->reqType == X_CopyArea) { + record_CA(ptr, rec_data); + } else if (req->reqType == X_ConfigureWindow) { + record_CW(ptr, rec_data); + } else { + ; + } +//fprintf(stderr, "XRecordFreeData-2: %p\n", rec_data); + XRecordFreeData(rec_data); +} +#endif + +void xrecord_watch(int start) { + Window focus, wm, r, c, clast; + int rx, ry, wx, wy; + int depth = 0, i; + unsigned int m; + int db = debug_scroll; + int do_shutdown = 0; + +//db = 1; + +#if LIBVNCSERVER_HAVE_RECORD + if (! start) { + xrecording = 0; + if (! rc_scroll) { + xrecord_focus_window = None; + xrecord_wm_window = None; + rcs_scroll = 0; + return; + } + if (do_shutdown) { +if (db) fprintf(stderr, "=== shutdown-scroll 0x%lx\n", rc_scroll); + X_LOCK; + if (! XRecordDisableContext(rdpy_ctrl, rc_scroll)) { + rfbLog("XRecordDisableContext(rc_scroll)" + " failed.\n"); + } + if (! XRecordFreeContext(rdpy_ctrl, rc_scroll)) { + rfbLog("XRecordFreeContext(rc_scroll)" + " failed.\n"); + } + XRecordProcessReplies(rdpy_data); + X_UNLOCK; + rc_scroll = 0; + } else { + if (rcs_scroll) { +if (db) fprintf(stderr, "=== disab-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll); + X_LOCK; + rcs_scroll = XRecordCurrentClients; + XRecordUnregisterClients(rdpy_ctrl, rc_scroll, + &rcs_scroll, 1); + XRecordDisableContext(rdpy_ctrl, rc_scroll); + XFlush(rdpy_ctrl); + XRecordProcessReplies(rdpy_data); + X_UNLOCK; + } + } + /* + * XXX if we do a XFlush(rdpy_ctrl) here we get: + * + + X Error of failed request: XRecordBadContext + Major opcode of failed request: 145 (RECORD) + Minor opcode of failed request: 5 (XRecordEnableContext) + Context in failed request: 0x2200013 + Serial number of failed request: 29 + Current serial number in output stream: 29 + + * + * need to figure out what is going on... since it may lead + * infrequent failures. + */ + xrecord_focus_window = None; + xrecord_wm_window = None; + rcs_scroll = 0; + return; + } + + if (xrecording) { + return; + } + + if (do_shutdown && rc_scroll) { + static int didmsg = 0; + /* should not happen... */ + if (!didmsg) { + rfbLog("warning: do_shutdown && rc_scroll\n"); + didmsg = 1; + } + xrecord_watch(0); + } + + xrecording = 0; + xrecord_focus_window = None; + xrecord_wm_window = None; + xrecord_set_by_keys = 0; + xrecord_set_by_mouse = 0; + + X_LOCK; + + /* get the window with focus and mouse pointer: */ + clast = None; + focus = None; + wm = None; + + XGetInputFocus(dpy, &focus, &i); + + XQueryPointer(dpy, rootwin, &r, &wm, &rx, &ry, &wx, &wy, &m); + if (wm) { + c = wm; + } else { + c = rootwin; + } + for (i=0; i<3; i++) { + /* descend a bit to avoid wm frames: */ + clast = c; + if (! XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &m)) { + break; + } + if (! c) { + break; + } + depth++; + } + if (!clast || clast == rootwin) { +if (db) fprintf(stderr, "--- xrecord_watch: skip.\n"); + X_UNLOCK; + return; + } + + /* set protocol request ranges: */ + rr_scroll[0] = rr_CA; + rr_scroll[1] = rr_CW; + + if (! rc_scroll) { + /* do_shutdown case or first time in */ + rcs_scroll = (XRecordClientSpec) clast; + rc_scroll = XRecordCreateContext(rdpy_ctrl, 0, &rcs_scroll, 1, + rr_scroll, 2); + + } else if (! do_shutdown) { + if (rcs_scroll) { +if (db) fprintf(stderr, "=2= unreg-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll); + rcs_scroll = XRecordCurrentClients; + XRecordUnregisterClients(rdpy_ctrl, rc_scroll, + &rcs_scroll, 1); + } + + rcs_scroll = (XRecordClientSpec) clast; +if (db) fprintf(stderr, "=-= reg-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll); + + if (!XRecordRegisterClients(rdpy_ctrl, rc_scroll, 0, + &rcs_scroll, 1, rr_scroll, 2)) { + static time_t last_err = 0; + time_t now = time(0); + if (now > last_err + 60) { + rfbLog("failed to register client 0x%lx with" + " X RECORD context rc_scroll.\n", clast); + last_err = now; + } + /* continue on for now... */ + } + } + XFlush(rdpy_ctrl); + +if (db) fprintf(stderr, "rc_scroll: 0x%lx\n", rc_scroll); + + if (! rc_scroll) { + X_UNLOCK; + use_xrecord = 0; + rfbLog("failed to create X RECORD context rc_scroll.\n"); + rfbLog(" switching to -noscrollcopyrect mode.\n"); + return; + } + + xrecord_focus_window = focus; + if (! xrecord_focus_window) { + xrecord_focus_window = clast; + } + xrecord_wm_window = wm; + if (! xrecord_wm_window) { + xrecord_wm_window = clast; + } + + xrecording = 1; + xrecord_seq++; + xrecord_start = 0.0; + dtime(&xrecord_start); + + if (!XRecordEnableContextAsync(rdpy_data, rc_scroll, record_switch, + (XPointer) xrecord_seq)) { + static time_t last_err = 0; + time_t now = time(0); + if (now > last_err + 60) { + rfbLog("failed to enable RECORD context " + "rc_scroll.\n"); + last_err = now; + } + /* continue on for now... */ + } + XFlush(rdpy_data); + X_UNLOCK; +#endif +} /* -- cleanup.c -- */ /* @@ -2766,6 +3760,14 @@ void clean_up_exit (int ret) { XEFreeTC(trap_ctx); } #endif +#if LIBVNCSERVER_HAVE_RECORD + /* XXX currently blocks: */ +#if 0 + if (rdpy_ctrl && rc_scroll) XRecordDisableContext(rdpy_ctrl, rc_scroll); + if (rdpy_data) XCloseDisplay(rdpy_data); + if (rdpy_ctrl) XCloseDisplay(rdpy_ctrl); +#endif +#endif XCloseDisplay(dpy); X_UNLOCK; @@ -4229,7 +5231,7 @@ static void reverse_connect(char *str) { p = strtok(tmp, ", \t\r\n"); while (p) { if ((n = do_reverse_connect(p)) != 0) { - rfbPE(screen, -1); + rfbPE(-1); } cnt += n; @@ -4238,7 +5240,7 @@ static void reverse_connect(char *str) { t = 0; while (t < sleep_between_host) { usleep(dt * 1000); - rfbPE(screen, -1); + rfbPE(-1); t += dt; } } @@ -4264,7 +5266,7 @@ static void reverse_connect(char *str) { t = 0; while (t < tot) { - rfbPE(screen, -1); + rfbPE(-1); usleep(dt * 1000); t += dt; } @@ -4374,7 +5376,8 @@ void check_connect_inputs(void) { */ enum rfbNewClientAction new_client(rfbClientPtr client) { ClientData *cd; - int i; + double tmr = 0.0; + last_event = last_input = time(0); if (inetd) { @@ -4441,11 +5444,13 @@ enum rfbNewClientAction new_client(rfbClientPtr client) { install_padded_fb(pad_geometry); } - for (i=0; i<RATE_SAMPLES; i++) { - cd->cmp_samp[i] = 5000; /* 56k modem */ - cd->raw_samp[i] = 50000; - } - cd->sample = 0; + dtime(&tmr); + cd->timer = tmr; + cd->send_cmp_rate = 0.0; + cd->send_raw_rate = 0.0; + cd->latency = 0.0; + cd->cmp_bytes_sent = 0; + cd->raw_bytes_sent = 0; accepted_client = 1; last_client = time(0); @@ -6103,6 +7108,7 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { if (! view_only || raw_fb) { /* raw_fb hack */ last_keyboard_client = client; last_event = last_input = time(0); + last_keysym = keysym; got_user_input++; got_keyboard_input++; } @@ -6120,6 +7126,7 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { last_keyboard_client = client; last_event = last_input = time(0); + last_keysym = keysym; got_user_input++; got_keyboard_input++; @@ -6170,6 +7177,20 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { return; } + if (use_xrecord && ! xrecording && down) { + if (scaling && ! got_scrollcopyrect) { + ; + } else if (!strcmp(scroll_copyrect, "never")) { + ; + } else if (!strcmp(scroll_copyrect, "mouse")) { + ; + } else if (! xrecord_skip_keysym(keysym)) { + snapshot_stack_list(0, 0.25); + xrecord_watch(1); + xrecord_set_by_keys = 1; + } + } + if (use_modifier_tweak) { modifier_tweak_keyboard(down, keysym, client); X_LOCK; @@ -6457,18 +7478,50 @@ void initialize_pointer_map(char *pointer_remap) { * children of the root window. Ideally done before we send ButtonPress * to the X server. */ -void snapshot_stack_list(void) { +void snapshot_stack_list(int free_only, double allowed_age) { + static double last_snap = 0.0, last_sync = 0.0, last_free = 0.0; + double now = 0.0, xsync_max = 0.25; Window r, w; + + dtime(&now); + if (free_only) { + if (stack_list) { + X_LOCK; + XFree(stack_list); + X_UNLOCK; + stack_list = NULL; + stack_num = 0; + last_free = now; + } + return; + } + + if (stack_list && now < last_snap + allowed_age) { + return; + } + if (stack_list) { XFree(stack_list); stack_list = NULL; stack_num = 0; + last_free = now; } - XSync(dpy, False); + + X_LOCK; + if (now > last_sync + xsync_max) { + XSync(dpy, False); + last_sync = now; + } + if (! XQueryTree(dpy, rootwin, &r, &w, &stack_list, &stack_num)) { stack_list = NULL; stack_num = 0; + last_free = now; + last_snap = 0.0; + } else { + last_snap = now; } + X_UNLOCK; } /* @@ -6509,20 +7562,69 @@ static void update_x11_pointer_position(int x, int y) { */ static void update_x11_pointer_mask(int mask) { int i, mb; + int xr_mouse = 1; + int snapped = 0; last_event = last_input = time(0); if (raw_fb && ! dpy) return; /* raw_fb hack */ - X_LOCK; + if (scaling && ! got_scrollcopyrect) { + xr_mouse = 0; + } else if (!strcmp(scroll_copyrect, "never")) { + xr_mouse = 0; + } else if (!strcmp(scroll_copyrect, "keys")) { + xr_mouse = 0; + } else if (xrecord_skip_button(mask, button_mask)) { + xr_mouse = 0; + } + + if (mask && use_xrecord && ! xrecording && xr_mouse) { + static int px, py, x, y, w, h, ok; + Window frame; + int skip = 0; + + if (!button_mask) { + if (get_wm_frame_pos(&px, &py, &x, &y, &w, &h, + &frame)) { + ok = 1; + } else { + ok = 0; + } + } + if (ok) { + if (! near_scrollbar_edge(x, y, w, h, px, py)) { + skip = 1; + } + if (near_wm_edge(x, y, w, h, px, py)) { + /* step out of wireframe's way */ + skip = 1; + } + } + + if (! skip) { + snapshot_stack_list(0, 0.25); + snapped = 1; + xrecord_watch(1); + if (button_mask) { + xrecord_set_by_mouse = 1; + } else { + xrecord_set_by_mouse = 2; + } + } + } + if (mask && !button_mask) { /* button down, snapshot the stacking list before flushing */ if (wireframe && !wireframe_in_progress && strcmp(wireframe_copyrect, "never")) { - snapshot_stack_list(); + if (! snapped) { + snapshot_stack_list(0, 0.0); + } } } + X_LOCK; /* look for buttons that have be clicked or released: */ for (i=0; i < MAX_BUTTONS; i++) { if ( (button_mask & (1<<i)) != (mask & (1<<i)) ) { @@ -7714,6 +8816,55 @@ void print_xevent_bases(void) { fprintf(stderr, " SelClear=%d, Expose=%d\n", SelectionClear, Expose); } +void sync_tod_with_servertime() { + static Atom servertime = None; + XEvent xev; + char diff[64]; + static int seq = 0; + int i; + + if (! servertime) { + servertime = XInternAtom(dpy, "X11VNC_SERVERTIME_DIFF", False); + } + if (! servertime) { + return; + } + + XSync(dpy, False); + while (XCheckTypedEvent(dpy, PropertyNotify, &xev)) { + ; + } + + snprintf(diff, 64, "%.6f/%08d", servertime_diff, seq++); + XChangeProperty(dpy, rootwin, servertime, XA_STRING, 8, + PropModeReplace, (unsigned char *) diff, strlen(diff)); + XSync(dpy, False); + + for (i=0; i<10; i++) { + int k, got = 0; + + for (k = 0; k<5; k++) { + while (XCheckTypedEvent(dpy, PropertyNotify, &xev)) { + if (xev.xproperty.atom == servertime) { + double now = 0.0, stime; + + dtime(&now); + stime = (double) xev.xproperty.time; + stime = stime/1000.0; + servertime_diff = now - stime; + if (0) rfbLog("set servertime_diff: " + "%.6f\n", servertime_diff); + got = 1; + } + } + } + if (got) { + break; + } + usleep(1000); + } +} + /* * This routine is periodically called to check for selection related * and other X11 events and respond to them as needed. @@ -7727,6 +8878,7 @@ void check_xevents(void) { static time_t last_bell = 0; static time_t last_init_check = 0; static time_t last_sync = 0; + static time_t last_time_sync = 0; time_t now = time(0); if (raw_fb && ! dpy) return; /* raw_fb hack */ @@ -7739,6 +8891,7 @@ void check_xevents(void) { if (screen && screen->clientHead) { have_clients = 1; } + X_LOCK; /* * There is a bug where we have to wait before sending text to @@ -7848,6 +9001,11 @@ void check_xevents(void) { } } + if (now > last_time_sync + 30) { + sync_tod_with_servertime(); + last_time_sync = now; + } + #if LIBVNCSERVER_HAVE_LIBXRANDR if (xrandr) { check_xrandr_event("check_xevents"); @@ -8460,6 +9618,9 @@ int remote_control_access_ok(void) { char *home, *xauth; char *dpy_str = DisplayString(dpy); Display *dpy2; + XHostAddress *xha; + Bool enabled; + int n; home = get_home_dir(); if (getenv("XAUTHORITY") != NULL) { @@ -8499,6 +9660,32 @@ int remote_control_access_ok(void) { } } + xha = XListHosts(dpy, &n, &enabled); + if (! enabled) { + rfbLog("X access control is disabled, X clients can\n"); + rfbLog(" connect from any host. Run 'xhost -'\n"); + return 0; + } + if (xha) { + int i; + rfbLog("The following hosts can connect w/o X11 " + "auth:\n"); + for (i=0; i<n; i++) { + if (xha[i].family == FamilyInternet) { + char *str = raw2host(xha[i].address, + xha[i].length); + char *ip = raw2ip(xha[i].address); + rfbLog(" %s/%s\n", str, ip); + free(str); + free(ip); + } else { + rfbLog(" unknown-%d\n", i+1); + } + } + XFree(xha); + return 0; + } + if (getenv("XAUTHORITY")) { xauth = strdup(getenv("XAUTHORITY")); } else { @@ -8508,8 +9695,11 @@ int remote_control_access_ok(void) { fprintf(stderr, "\nChecking if display %s requires " "XAUTHORITY\n", dpy_str); - fprintf(stderr, " (ignore any Xlib: errors that follow)\n"); + fprintf(stderr, " -- (ignore any Xlib: errors that" + " follow) --\n"); dpy2 = XOpenDisplay(dpy_str); + fflush(stderr); + fprintf(stderr, " -- (done checking) --\n\n"); if (xauth) { set_env("XAUTHORITY", xauth); @@ -8526,6 +9716,7 @@ int remote_control_access_ok(void) { XCloseDisplay(dpy2); return 0; } + } return 1; } @@ -8542,22 +9733,20 @@ char *process_remote_cmd(char *cmd, int stringonly) { int bufn = VNC_CONNECT_MAX; int query = 0; static char *prev_cursors_mode = NULL; - static int first = 1; if (! accept_remote_cmds) { rfbLog("remote commands disabled: %s\n", cmd); return NULL; } - if (first && priv_remote) { + if (priv_remote) { if (! remote_control_access_ok()) { - rfbLog("disabling remote commands in -privremote " + rfbLog("** Disabling remote commands in -privremote " "mode.\n"); accept_remote_cmds = 0; return NULL; } } - first = 0; strcpy(buf, ""); if (strstr(cmd, "cmd=") == cmd) { @@ -9542,7 +10731,7 @@ char *process_remote_cmd(char *cmd, int stringonly) { "(if applicable).\n"); if (! xtrap_input) { xtrap_input = 1; - disable_grabserver(); + disable_grabserver(dpy); } } else if (!strcmp(p, "noxtrap")) { @@ -9554,7 +10743,7 @@ char *process_remote_cmd(char *cmd, int stringonly) { "(if applicable).\n"); if (xtrap_input) { xtrap_input = 0; - disable_grabserver(); + disable_grabserver(dpy); } } else if (!strcmp(p, "xrandr")) { @@ -9964,7 +11153,7 @@ char *process_remote_cmd(char *cmd, int stringonly) { set_no_cursor(); for (i=0; i<max; i++) { /* XXX: try to force empty cursor back to client */ - rfbPE(screen, -1); + rfbPE(-1); } cursor_shape_updates = 0; disable_cursor_shape_updates(screen); @@ -10315,14 +11504,30 @@ char *process_remote_cmd(char *cmd, int stringonly) { } rfbLog("remote_cmd: enabling -wireframe mode.\n"); wireframe = 1; - } else if (!strcmp(p, "wireframe")) { + } else if (strstr(p, "wf:") == p) { /* skip-cmd-list */ + COLON_CHECK("wf:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, wireframe); + goto qry; + } + p += strlen("wf:"); + if (*p) { + if (wireframe_str) { + free(wireframe_str); + } + wireframe_str = strdup(p); + parse_wireframe(); + } + rfbLog("remote_cmd: enabling -wireframe mode.\n"); + wireframe = 1; + } else if (!strcmp(p, "wireframe") || !strcmp(p, "wf")) { if (query) { snprintf(buf, bufn, "ans=%s:%d", p, wireframe); goto qry; } rfbLog("remote_cmd: enabling -wireframe mode.\n"); wireframe = 1; - } else if (!strcmp(p, "nowireframe")) { + } else if (!strcmp(p, "nowireframe") || !strcmp(p, "nowf")) { if (query) { snprintf(buf, bufn, "ans=%s:%d", p, !wireframe); goto qry; @@ -10342,7 +11547,21 @@ char *process_remote_cmd(char *cmd, int stringonly) { set_wirecopyrect_mode(p); rfbLog("remote_cmd: changed -wirecopyrect mode " "to: %s\n", NONUL(wireframe_copyrect)); - } else if (!strcmp(p, "nowirecopyrect")) { + got_wirecopyrect = 1; + } else if (strstr(p, "wcr") == p) { + COLON_CHECK("wcr:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(wireframe_copyrect)); + goto qry; + } + p += strlen("wcr:"); + + set_wirecopyrect_mode(p); + rfbLog("remote_cmd: changed -wirecopyrect mode " + "to: %s\n", NONUL(wireframe_copyrect)); + got_wirecopyrect = 1; + } else if (!strcmp(p, "nowirecopyrect") || !strcmp(p, "nowcr")) { if (query) { snprintf(buf, bufn, "ans=%s:%s", p, NONUL(wireframe_copyrect)); @@ -10353,6 +11572,56 @@ char *process_remote_cmd(char *cmd, int stringonly) { rfbLog("remote_cmd: changed -wirecopyrect mode " "to: %s\n", NONUL(wireframe_copyrect)); + } else if (strstr(p, "scr_area") == p) { + COLON_CHECK("scr_area:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, + scrollcopyrect_min_area); + goto qry; + } + p += strlen("scr_area:"); + + scrollcopyrect_min_area = atoi(p); + rfbLog("remote_cmd: changed -scr_area to: %d\n", + scrollcopyrect_min_area); + + } else if (strstr(p, "scrollcopyrect") == p) { + COLON_CHECK("scrollcopyrect:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(scroll_copyrect)); + goto qry; + } + p += strlen("scrollcopyrect:"); + + set_scrollcopyrect_mode(p); + rfbLog("remote_cmd: changed -scrollcopyrect mode " + "to: %s\n", NONUL(scroll_copyrect)); + got_scrollcopyrect = 1; + } else if (strstr(p, "scr") == p) { + COLON_CHECK("scr:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(scroll_copyrect)); + goto qry; + } + p += strlen("scr:"); + + set_scrollcopyrect_mode(p); + rfbLog("remote_cmd: changed -scrollcopyrect mode " + "to: %s\n", NONUL(scroll_copyrect)); + got_scrollcopyrect = 1; + } else if (!strcmp(p, "noscrollcopyrect") || !strcmp(p, "noscr")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%s", p, + NONUL(scroll_copyrect)); + goto qry; + } + + set_scrollcopyrect_mode("never"); + rfbLog("remote_cmd: changed -scrollcopyrect mode " + "to: %s\n", NONUL(scroll_copyrect)); + } else if (strstr(p, "pointer_mode") == p) { int pm; COLON_CHECK("pointer_mode:") @@ -10854,6 +12123,20 @@ char *process_remote_cmd(char *cmd, int stringonly) { rfbLog("remote_cmd: disabling remote commands.\n"); accept_remote_cmds = 0; /* cannot be turned back on. */ + } else if (!strcmp(p, "debug_xevents")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, debug_xevents); + goto qry; + } + debug_xevents = 1; + rfbLog("set debug_xevents to: %d\n", debug_xevents); + } else if (!strcmp(p, "nodebug_xevents")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !debug_xevents); + goto qry; + } + debug_xevents = 0; + rfbLog("set debug_xevents to: %d\n", debug_xevents); } else if (strstr(p, "debug_xevents") == p) { COLON_CHECK("debug_xevents:") if (query) { @@ -10864,6 +12147,20 @@ char *process_remote_cmd(char *cmd, int stringonly) { debug_xevents = atoi(p); rfbLog("set debug_xevents to: %d\n", debug_xevents); + } else if (!strcmp(p, "debug_xdamage")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, debug_xdamage); + goto qry; + } + debug_xdamage = 1; + rfbLog("set debug_xdamage to: %d\n", debug_xdamage); + } else if (!strcmp(p, "nodebug_xdamage")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !debug_xdamage); + goto qry; + } + debug_xdamage = 0; + rfbLog("set debug_xdamage to: %d\n", debug_xdamage); } else if (strstr(p, "debug_xdamage") == p) { COLON_CHECK("debug_xdamage:") if (query) { @@ -10874,6 +12171,56 @@ char *process_remote_cmd(char *cmd, int stringonly) { debug_xdamage = atoi(p); rfbLog("set debug_xdamage to: %d\n", debug_xdamage); + } else if (!strcmp(p, "debug_wireframe")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, debug_wireframe); + goto qry; + } + debug_wireframe = 1; + rfbLog("set debug_wireframe to: %d\n", debug_wireframe); + } else if (!strcmp(p, "nodebug_wireframe")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !debug_wireframe); + goto qry; + } + debug_wireframe = 0; + rfbLog("set debug_wireframe to: %d\n", debug_wireframe); + } else if (strstr(p, "debug_wireframe") == p) { + COLON_CHECK("debug_wireframe:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, + debug_wireframe); + goto qry; + } + p += strlen("debug_wireframe:"); + debug_wireframe = atoi(p); + rfbLog("set debug_wireframe to: %d\n", debug_wireframe); + + } else if (!strcmp(p, "debug_scroll")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, debug_scroll); + goto qry; + } + debug_scroll = 1; + rfbLog("set debug_scroll to: %d\n", debug_scroll); + } else if (!strcmp(p, "nodebug_scroll")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !debug_scroll); + goto qry; + } + debug_scroll = 0; + rfbLog("set debug_scroll to: %d\n", debug_scroll); + } else if (strstr(p, "debug_scroll") == p) { + COLON_CHECK("debug_scroll:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, + debug_scroll); + goto qry; + } + p += strlen("debug_scroll:"); + debug_scroll = atoi(p); + rfbLog("set debug_scroll to: %d\n", debug_scroll); + } else if (strstr(p, "hack") == p) { /* skip-cmd-list */ COLON_CHECK("hack:") if (query) { @@ -10980,6 +12327,12 @@ char *process_remote_cmd(char *cmd, int stringonly) { snprintf(buf, bufn, "aro=%s:%s", p, NONUL(sigpipe)); } else if (!strcmp(p, "threads")) { snprintf(buf, bufn, "aro=%s:%d", p, use_threads); + } else if (!strcmp(p, "readrate")) { + snprintf(buf, bufn, "aro=%s:%d", p, get_read_rate()); + } else if (!strcmp(p, "netrate")) { + snprintf(buf, bufn, "aro=%s:%d", p, get_net_rate()); + } else if (!strcmp(p, "netlatency")) { + snprintf(buf, bufn, "aro=%s:%d", p, get_net_latency()); } else if (!strcmp(p, "pipeinput")) { snprintf(buf, bufn, "aro=%s:%s", p, NONUL(pipeinput_str)); @@ -10995,6 +12348,8 @@ char *process_remote_cmd(char *cmd, int stringonly) { snprintf(buf, bufn, "aro=%s:%d", p, xtest_present); } else if (!strcmp(p, "ext_xtrap")) { snprintf(buf, bufn, "aro=%s:%d", p, xtrap_present); + } else if (!strcmp(p, "ext_xrecord")) { + snprintf(buf, bufn, "aro=%s:%d", p, xrecord_present); } else if (!strcmp(p, "ext_xkb")) { snprintf(buf, bufn, "aro=%s:%d", p, xkb_present); } else if (!strcmp(p, "ext_xshm")) { @@ -11114,6 +12469,7 @@ void record_desired_xdamage_rect(int x, int y, int w, int h) { int dt_x, dt_y, nt_x1, nt_y1, nt_x2, nt_y2, nt; int ix, iy, cnt = 0; int area = w*h, always_accept = 0; + int use_direct_fb_copy = 0; /* TBD: not working yet */ if (xdamage_max_area <= 0) { always_accept = 1; @@ -11133,24 +12489,33 @@ void record_desired_xdamage_rect(int x, int y, int w, int h) { */ return; } + + if (use_direct_fb_copy) { + X_UNLOCK; + direct_fb_copy(x, y, x + w, y + h, 1); + X_LOCK; + } else { + nt_x1 = nfix( (x)/tile_x, ntiles_x); + nt_x2 = nfix((x+w)/tile_x, ntiles_x); + nt_y1 = nfix( (y)/tile_y, ntiles_y); + nt_y2 = nfix((y+h)/tile_y, ntiles_y); - nt_x1 = nfix( (x)/tile_x, ntiles_x); - nt_x2 = nfix((x+w)/tile_x, ntiles_x); - nt_y1 = nfix( (y)/tile_y, ntiles_y); - nt_y2 = nfix((y+h)/tile_y, ntiles_y); - - - /* loop over the rectangle of tiles (1 tile for a small input rect */ - for (ix = nt_x1; ix <= nt_x2; ix++) { - for (iy = nt_y1; iy <= nt_y2; iy++) { - nt = ix + iy * ntiles_x; - cnt++; - if (! tile_has_xdamage_diff[nt]) { - XD_des++; + /* + * loop over the rectangle of tiles (1 tile for a small + * input rect). + */ + for (ix = nt_x1; ix <= nt_x2; ix++) { + for (iy = nt_y1; iy <= nt_y2; iy++) { + nt = ix + iy * ntiles_x; + cnt++; + if (! tile_has_xdamage_diff[nt]) { + XD_des++; + } + tile_has_xdamage_diff[nt] = 1; + /* not used: */ + tile_row_has_xdamage_diff[iy] = 1; + xdamage_tile_count++; } - tile_has_xdamage_diff[nt] = 1; - tile_row_has_xdamage_diff[iy] = 1; - xdamage_tile_count++; } } if (debug_xdamage > 1) { @@ -11940,7 +13305,6 @@ void setup_cursors(void) { first = 0; if (screen) { - RFBUNDRAWCURSOR(screen); screen->cursor = NULL; LOCK(screen->cursorMutex); } @@ -12515,7 +13879,6 @@ rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h, c->cleanupRichSource = FALSE; c->richSource = rich; -#if !OLD_TREE if (alpha_blend && !indexed_color) { c->alphaSource = alpha; c->alphaPreMultiplied = TRUE; @@ -12523,8 +13886,6 @@ rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h, free(alpha); c->alphaSource = NULL; } -#endif - return c; } @@ -12618,8 +13979,6 @@ int get_xfixes_cursor(int init) { } } - RFBUNDRAWCURSOR(screen); - /* we need to create the cursor and overwrite oldest */ use = oldest; if (cursors[use]->rfb) { @@ -12627,11 +13986,9 @@ int get_xfixes_cursor(int init) { if (cursors[use]->rfb->richSource) { free(cursors[use]->rfb->richSource); } -#if !OLD_TREE if (cursors[use]->rfb->alphaSource) { free(cursors[use]->rfb->alphaSource); } -#endif if (cursors[use]->rfb->source) { free(cursors[use]->rfb->source); } @@ -12831,47 +14188,6 @@ int get_which_cursor(void) { return which; } -#if OLD_TREE -/* - * Some utilities for marking the little cursor patch region as - * modified, etc. - */ -void mark_cursor_patch_modified(rfbScreenInfoPtr s, int old) { - int curx, cury, xhot, yhot, w, h; - int x1, x2, y1, y2; - - if (! s || ! s->cursor) { - return; - } - - if (old) { - /* use oldCursor pos */ - curx = s->oldCursorX; - cury = s->oldCursorY; - } else { - curx = s->cursorX; - cury = s->cursorY; - } - - xhot = s->cursor->xhot; - yhot = s->cursor->yhot; - w = s->cursor->width; - h = s->cursor->height; - - x1 = curx - xhot; - x2 = x1 + w; - x1 = nfix(x1, s->width); - x2 = nfix(x2, s->width); - - y1 = cury - yhot; - y2 = y1 + h; - y1 = nfix(y1, s->height); - y2 = nfix(y2, s->height); - - rfbMarkRectAsModified(s, x1, y1, x1+x2, y1+y2); -} -#endif - void set_cursor_was_changed(rfbScreenInfoPtr s) { rfbClientIteratorPtr iter; rfbClientPtr cl; @@ -13006,9 +14322,6 @@ void cursor_position(int x, int y) { rfbClientPtr cl; int cnt = 0, nonCursorPosUpdates_clients = 0; int x_in = x, y_in = y; -#if OLD_TREE - int x_old, y_old; -#endif /* x and y are current positions of X11 pointer on the X11 display */ if (!screen) { @@ -13026,26 +14339,10 @@ void cursor_position(int x, int y) { return; } -#if OLD_TREE - x_old = screen->oldCursorX; - y_old = screen->oldCursorY; - - if (screen->cursorIsDrawn) { - rfbUndrawCursor(screen); - } - - LOCK(screen->cursorMutex); - if (! screen->cursorIsDrawn) { - screen->cursorX = x; - screen->cursorY = y; - } - UNLOCK(screen->cursorMutex); -#else LOCK(screen->cursorMutex); screen->cursorX = x; screen->cursorY = y; UNLOCK(screen->cursorMutex); -#endif iter = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(iter)) ) { @@ -13080,21 +14377,12 @@ void cursor_position(int x, int y) { } rfbReleaseClientIterator(iter); -#if OLD_TREE - if (nonCursorPosUpdates_clients && show_cursor) { - if (x_old != x || y_old != y) { - mark_cursor_patch_modified(screen, 0); - } - } -#endif - if (debug_pointer && cnt) { rfbLog("cursor_position: sent position x=%3d y=%3d to %d" " clients\n", x, y, cnt); } } -#if !OLD_TREE void set_rfb_cursor(int which) { if (! show_cursor) { @@ -13112,57 +14400,7 @@ void set_rfb_cursor(int which) { } } -#else - -void set_rfb_cursor(int which) { - - if (! show_cursor) { - return; - } - if (! screen) { - return; - } - - if (screen->cursor) { - int all_are_cursor_pos = 1; - rfbClientIteratorPtr iter; - rfbClientPtr cl; - - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - if (! cl->enableCursorPosUpdates) { - all_are_cursor_pos = 0; - } - if (! cl->enableCursorShapeUpdates) { - all_are_cursor_pos = 0; - } - } - rfbReleaseClientIterator(iter); - - if (! all_are_cursor_pos) { - mark_cursor_patch_modified(screen, 1); - } - } - - if (!cursors[which] || !cursors[which]->rfb) { - rfbLog("non-existent cursor: which=%d\n", which); - return; - } else { - rfbSetCursor(screen, cursors[which]->rfb, FALSE); - } - - if (screen->underCursorBuffer == NULL && - screen->underCursorBufferLen != 0) { - LOCK(screen->cursorMutex); - screen->underCursorBufferLen = 0; - UNLOCK(screen->cursorMutex); - } - set_cursor_was_changed(screen); -} -#endif - void set_no_cursor(void) { - RFBUNDRAWCURSOR(screen); set_rfb_cursor(CURS_EMPTY); } @@ -14607,38 +15845,28 @@ void initialize_screen(int *argc, char **argv, XImage *fb) { clean_up_exit(1); } -/* - * This ifdef is a transient for source compatibility for people who download - * the x11vnc.c file by itself and plop it down into their libvncserver tree. - * Remove at some point. BTW, this assumes no usage of earlier "0.7pre". - */ -#if OLD_TREE && defined(LIBVNCSERVER_VERSION) - if (strcmp(LIBVNCSERVER_VERSION, "0.6")) -#endif - { - if (create_screen && *argc != 1) { - int i; - rfbLog("*** unrecognized option(s) ***\n"); - for (i=1; i< *argc; i++) { - rfbLog("\t[%d] %s\n", i, argv[i]); - } - rfbLog("For a list of options run: x11vnc -help\n"); - rfbLog("\n"); - rfbLog("Here is a list of removed or obsolete" - " options:\n"); - rfbLog("\n"); - rfbLog("removed: -hints, -nohints\n"); - rfbLog("removed: -cursorposall\n"); - rfbLog("\n"); - rfbLog("renamed: -old_copytile, use -onetile\n"); - rfbLog("renamed: -mouse, use -cursor\n"); - rfbLog("renamed: -mouseX, use -cursor X\n"); - rfbLog("renamed: -X, use -cursor X\n"); - rfbLog("renamed: -nomouse, use -nocursor\n"); - rfbLog("renamed: -old_pointer, use -pointer_mode 1\n"); - - clean_up_exit(1); + if (create_screen && *argc != 1) { + int i; + rfbLog("*** unrecognized option(s) ***\n"); + for (i=1; i< *argc; i++) { + rfbLog("\t[%d] %s\n", i, argv[i]); } + rfbLog("For a list of options run: x11vnc -help\n"); + rfbLog("\n"); + rfbLog("Here is a list of removed or obsolete" + " options:\n"); + rfbLog("\n"); + rfbLog("removed: -hints, -nohints\n"); + rfbLog("removed: -cursorposall\n"); + rfbLog("\n"); + rfbLog("renamed: -old_copytile, use -onetile\n"); + rfbLog("renamed: -mouse, use -cursor\n"); + rfbLog("renamed: -mouseX, use -cursor X\n"); + rfbLog("renamed: -X, use -cursor X\n"); + rfbLog("renamed: -nomouse, use -nocursor\n"); + rfbLog("renamed: -old_pointer, use -pointer_mode 1\n"); + + clean_up_exit(1); } /* set up format from scratch: */ @@ -14840,6 +16068,7 @@ void initialize_screen(int *argc, char **argv, XImage *fb) { /* may need, bpp, main_red_max, etc. */ parse_wireframe(); + parse_scroll_copyrect(); setup_cursors_and_push(); @@ -15936,7 +17165,7 @@ void initialize_blackouts_and_xinerama(void) { void push_sleep(n) { int i; for (i=0; i<n; i++) { - rfbPE(screen, -1); + rfbPE(-1); if (i != n-1 && defer_update) { usleep(defer_update * 1000); } @@ -15960,7 +17189,7 @@ void refresh_screen(void) { return; } mark_rect_as_modified(0, 0, dpy_x, dpy_y, 1); - rfbPE(screen, -1); + rfbPE(-1); } /* @@ -17928,7 +19157,7 @@ static void nap_sleep(int ms, int split) { for (i=0; i<split; i++) { usleep(ms * 1000 / split); if (! use_threads && i != split - 1) { - rfbPE(screen, -1); + rfbPE(-1); } if (input != got_user_input) { break; @@ -18451,7 +19680,7 @@ int scan_for_updates(int count_only) { } /* -- gui.c -- */ -#if OLD_TREE || SMALL_FOOTPRINT +#if SMALL_FOOTPRINT char gui_code[] = ""; #else #include "tkx11vnc.h" @@ -18800,7 +20029,77 @@ int get_wm_frame_pos(int *px, int *py, int *x, int *y, int *w, int *h, Window *w } static int defer_update_nofb = 6; /* defer a shorter time under -nofb */ -static Window maybe_scrolling = 0; + +int scrollcopyrect_top, scrollcopyrect_bot; +int scrollcopyrect_left, scrollcopyrect_right; +double scr_key_time, scr_key_persist, scr_mouse_time, scr_mouse_persist; + +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; + if (sscanf(str, "%lf+%lf", &t1, &t2) == 2) { + scr_key_time = t1; + scr_key_persist = t2; + } + free(str); + } + + /* mouse scrolling timing heuristics. */ + if ((str = part[2]) != NULL) { + double t1, t2; + if (sscanf(str, "%lf+%lf", &t1, &t2) == 2) { + scr_mouse_time = t1; + scr_mouse_persist = t2; + } + 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); +} /* WIREFRAME_PARMS "0xff,2,0,30+6+6+6,0.05+0.3+2.0,8" @@ -18976,6 +20275,34 @@ void set_wirecopyrect_mode(char *str) { } } +/* + * 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); + } + rfbLog("unknown -scrollcopyrect mode: %s, using: %s\n", str, + scroll_copyrect); + } + if (orig) { + free(orig); + } +} + typedef struct saveline { int x0, y0, x1, y1; int shift; @@ -19233,6 +20560,519 @@ if (0) fprintf(stderr, " DrawBox: %dx%d+%d+%d\n", w, h, x, y); } } +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 = 0; + + x1 = nfix(x1, dpy_x); + y1 = nfix(y1, dpy_y); + x2 = nfix(x2, dpy_x); + y2 = nfix(y2, dpy_y); + + 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, 1); + } + return 1; +} + +#define PUSH_TEST(n) \ +if (n) { \ + double dt = 0.0, tm = 0.0; dtime(&tm); \ + fprintf(stderr, "PUSH---\n"); \ + while (dt < 2.0) { rfbPE(50000); dt += dtime(&tm); } \ + fprintf(stderr, "---PUSH\n"); \ +} + +int push_scr_ev(double bdpush) { + 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, obscured; + int ev, ev_tot = scr_ev_cnt; + double st, dnow = 0.0; + int db = 0, rrate = get_read_rate(); + sraRegionPtr backfill, whole, tmpregion; + XWindowAttributes attr; + +//db = 1; + + dtime(&dnow); + + if (ev_tot == 0) { + return dret; + } + backfill = sraRgnCreate(); + whole = sraRgnCreateRect(0, 0, dpy_x, dpy_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; + + for (ev=0; ev < ev_tot; ev++) { + + 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 = (double) scr_ev[ev].t/1000.0; + + if (dabs((dnow - servertime_diff) - st) > 0.15) { +if (db) fprintf(stderr, "push_scr_ev: TOO OLD: %.4f\n", (dnow - servertime_diff) - st); + dret = 0; + break; + } else { +if (db) fprintf(stderr, "push_scr_ev: AGE: %.4f\n", (dnow - servertime_diff) - st); + } + 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) 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) 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) 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) { + Window r; + int rx, ry, wx, wy; + unsigned int m; + if (! XQueryPointer(dpy, rootwin, &r, &frame, + &rx, &ry, &wx, &wy, &m)) { + frame = None; + } + } + if (! frame) { + frame = win; + } + + if (try_copyrect(frame, x, y, w, h, dx, dy, &obscured)) { + fb_push(); + urgent_update = 1; +PUSH_TEST(0); + + } else { + dret = 0; + break; + } + + if (ev > 0) { + sraRgnOffset(backfill, dx, dy); + sraRgnAnd(backfill, whole); + } + + tmpregion = sraRgnCreateRect(nx, ny, nx + nw, ny + nh); + sraRgnAnd(tmpregion, whole); + sraRgnOr(backfill, tmpregion); + sraRgnDestroy(tmpregion); + } + + if (dret != 0) { + double est, win_area = 0.0, area = 0.0; + sraRectangleIterator *iter; + sraRect rect; + int tx1, ty1, tx2, ty2; + double tm = 0.0, dt = 0.0; + + 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.85 * 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 { + dtime(&tm); + iter = sraRgnGetIterator(backfill); + while (sraRgnIteratorNext(iter, &rect)) { + tx1 = rect.x1; + ty1 = rect.y1; + tx2 = rect.x2; + ty2 = rect.y2; + tx1 = nfix(tx1, dpy_x); + ty1 = nfix(ty1, dpy_y); + tx2 = nfix(tx2, dpy_x); + ty2 = nfix(ty2, dpy_y); + + dtime(&tm); +if (db) fprintf(stderr, " DFC(%d,%d-%d,%d)", tx1, ty1, tx2, ty2); + direct_fb_copy(tx1, ty1, tx2, ty2, 1); + dt = dtime(&tm); + fb_push(); +PUSH_TEST(0); + } + dt = dtime(&tm); + sraRgnReleaseIterator(iter); +if (db) fprintf(stderr, " 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 > 0.0) fprintf(stderr, "BDPUSH-TIME: %.3f 0x%lx\n", bdpush, xrecord_wm_window); + + if (bdpush > 0.0 && xrecord_wm_window != None && + valid_window(xrecord_wm_window, &attr)) { + + double wm_area = 0.0, win_area = 0.0, d_area; + sraRectangleIterator *iter; + sraRect rect; + sraRegionPtr frame; + int tx1, ty1, tx2, ty2; + + /* wm frame: */ + tx1 = attr.x; + ty1 = attr.y; + tx2 = attr.x + attr.width; + ty2 = attr.y + attr.height; + + frame = sraRgnCreateRect(tx1, ty1, tx2, ty2); + sraRgnAnd(frame, whole); + + iter = sraRgnGetIterator(frame); + while (sraRgnIteratorNext(iter, &rect)) { + tx1 = rect.x1; + ty1 = rect.y1; + tx2 = rect.x2; + ty2 = rect.y2; + + wm_area += (tx2 - tx1)*(ty2 - ty1); + } + sraRgnReleaseIterator(iter); + + /* scrolling window: */ + tmpregion = sraRgnCreateRect(x0, y0, x0 + w0, y0 + h0); + sraRgnAnd(tmpregion, whole); + + 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); + + d_area = wm_area - win_area; + sraRgnSubtract(frame, tmpregion); + sraRgnDestroy(tmpregion); + +if (db) fprintf(stderr, "d_area: %.4f wm_area: %.4f\n", d_area, wm_area); + + if (d_area >= 0.0 && d_area < bdpush * wm_area && + !sraRgnEmpty(frame)) { + double dt = 0.0, dm = 0.0; + dtime(&dm); + iter = sraRgnGetIterator(frame); + while (sraRgnIteratorNext(iter, &rect)) { + tx1 = rect.x1; + ty1 = rect.y1; + tx2 = rect.x2; + ty2 = rect.y2; + direct_fb_copy(tx1, ty1, tx2, ty2, 1); + fb_push(); + dt += dtime(&dm); +if (db) fprintf(stderr, " BDP(%d,%d-%d,%d) dt: %.4f\n", tx1, ty1, tx2, ty2, dt); + } + sraRgnReleaseIterator(iter); + } + sraRgnDestroy(frame); + } + + sraRgnDestroy(backfill); + sraRgnDestroy(whole); + return dret; +} +/* + * Wrapper to apply the rfbDoCopyRegion taking into account if scaling + * is being done. Note that copyrect under the scaling case is often + * only approximate. + */ +void do_copyregion(sraRegionPtr region, int dx, int dy) { + sraRectangleIterator *iter; + sraRect rect; + int Bpp = bpp/8; + int x1, y1, x2, y2, w, stride; + int sx1, sy1, sx2, sy2, sdx, sdy; + char *dst, *src; + + if (!scaling || rfb_fb == main_fb) { + rfbDoCopyRegion(screen, region, dx, dy); + return; + } + + stride = dpy_x * Bpp; + + iter = sraRgnGetIterator(region); + while(sraRgnIteratorNext(iter, &rect)) { + int j; + + x1 = rect.x1; + y1 = rect.y1; + x2 = rect.x2; + y2 = rect.y2; + + w = (x2 - x1)*Bpp; + dst = main_fb + y1*stride + x1*Bpp; + src = main_fb + (y1-dy)*stride + (x1-dx)*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; + } + } + + 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; + + rfbDoCopyRect(screen, sx1, sy1, sx2, sy2, sdx, sdy); + } + sraRgnReleaseIterator(iter); +} + +void fb_push0(int first_ms, int loop_ms, int loop_max) { + int t; + fb_update_sent(NULL); + rfbPE(1000 * first_ms); /* long select */ + for (t=0; t<loop_max; t++) { + if (fb_update_sent(NULL)) { +if (1 || debug_wireframe) fprintf(stderr, "FB_UPDATE_SENT: t=%d\n", t); + break; + } + rfbPE(1000 * loop_ms); /* short selects. */ + } +} + +void get_client_regions(int *req, int *mod, int *cpy) { + + rfbClientIteratorPtr i; + rfbClientPtr cl; + + *req = 0; + *mod = 0; + *cpy = 0; + + i = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(i)) ) { + *req += sraRgnCountRects(cl->requestedRegion); + *mod += sraRgnCountRects(cl->modifiedRegion); + *cpy += sraRgnCountRects(cl->copyRegion); + } + rfbReleaseClientIterator(i); +} + +void fb_push(void) { + char *httpdir = screen->httpDir; + int defer = screen->deferUpdateTime; + int i, req0, mod0, cpy0, req1, mod1, cpy1; + int db = 0; +//db = 1; + + screen->httpDir = NULL; + screen->deferUpdateTime = 0; + + get_client_regions(&req0, &mod0, &cpy0); + + rfbPE(0); + + screen->httpDir = httpdir; + screen->deferUpdateTime = defer; + + get_client_regions(&req1, &mod1, &cpy1); +if (db) fprintf(stderr, "\nFB_push: req: %d/%d mod: %d/%d cpy: %d/%d\n", + req0, req1, mod0, mod1, cpy0, cpy1); + + for (i = 0; i < 0; i++) { + get_client_regions(&req0, &mod0, &cpy0); + rfbCFD(1000); + get_client_regions(&req1, &mod1, &cpy1); +if (db) fprintf(stderr, "-------: req: %d/%d mod: %d/%d cpy: %d/%d\n", + req0, req1, mod0, mod1, cpy0, cpy1); + } +} + /* * utility routine for CopyRect of the window (but not CopyRegion) */ @@ -19252,6 +21092,541 @@ int crfix(int x, int dx, int Lx) { return x; } +int check_xrecord_keys(void) { + double spin = 0.0, tm = 0.0; + int gk, gk0, extra_keys = 0, ret = 0; + int db = debug_scroll; + int get_out = 1, got_one = 0, flush1 = 0, flush2 = 0; + static double last_key_scroll = 0.0; + static double persist_start = 0.0; + double this_scroll, scroll_persist = scr_key_persist; + double spin_fac = 1.0, scroll_fac = 2.0; + double max_spin, tnow = 0.0; + +//db = 1; + + if (got_keyboard_input) { + get_out = 0; + } + + dtime(&tnow); + if (tnow < last_key_scroll + scroll_persist) { + get_out = 0; + } + + if (get_out) { + persist_start = 0.0; + xrecord_watch(0); + return 0; + } + +if (db) fprintf(stderr, "xrecord_set_by_mouse: %d\n", xrecord_set_by_mouse); +if (db) fprintf(stderr, "xrecord_set_by_keys: %d\n", xrecord_set_by_keys); + + max_spin = scr_key_time; + + if (tnow < last_key_scroll + scroll_persist) { + max_spin = 1.25*(tnow - last_key_scroll); + } else if (xrecord_scroll_keysym(last_keysym)) { + spin_fac = scroll_fac; + } + + gk = gk0 = got_keyboard_input; + dtime(&tm); + +if (db) fprintf(stderr, "check_xrecord: LOOP: scr_ev_cnt: %d max: %.3f\n", + scr_ev_cnt, max_spin); + + while (1) { + + if (scr_ev_cnt) { + got_one = 1; + this_scroll = 0.0; + dtime(&this_scroll); + break; + } + + X_LOCK; + XFlush(dpy); + flush1 = 1; + X_UNLOCK; + + if (use_threads) { + usleep(1000); + } else { + rfbCFD(1000); + } + spin += dtime(&tm); + + if (spin >= max_spin * spin_fac) { +if (db) fprintf(stderr, "check_xrecord: SPIN-OUT: %.3f/%.3f\n", spin, + max_spin * spin_fac); + break; + } + + if (got_keyboard_input > gk) { + gk = got_keyboard_input; + if (xrecord_scroll_keysym(last_keysym)) { + spin_fac = scroll_fac; + } + extra_keys++; +if (db) fprintf(stderr, "check_xrecord: more keys: %d %.3f\n", extra_keys, + spin); + flush2 = 1; + } + + X_LOCK; + if (flush2) { + XFlush(dpy); + } + XRecordProcessReplies(rdpy_data); + X_UNLOCK; + } + +if (db) fprintf(stderr, " f1: %d f2: %d spin: %.4f\n", flush1, flush2, spin); + /* since we've flushed it, we might as well avoid -input_skip */ + if (flush1 || flush2) { + got_keyboard_input = 0; + got_pointer_input = 0; + } + + if (scr_ev_cnt) { + int dret; + double bdpush = 0.0; + static double last_border_push = 0.0; + + if (persist_start > 0.0 && + this_scroll > last_border_push + 1.00) { + bdpush = 0.0; + last_border_push = this_scroll; + } + dret = push_scr_ev(bdpush); + ret = 1 + dret; + scr_ev_cnt = 0; + } + + if (xrecording) { + if (ret < 2) { + xrecord_watch(0); + } + } + + if (this_scroll > 0.0) { + last_key_scroll = this_scroll; + } + + if (ret == 2) { + if (persist_start == 0.0) { + dtime(&persist_start); + } + } else { + persist_start = 0.0; + } + + return ret; +} + +int check_xrecord_mouse(void) { + double spin = 0.0, tm = 0.0; + int gp, gp0, ret = 0; + int db = debug_scroll; + int flush1 = 0, flush2 = 0; + int get_out = 1, got_one = 0; + static double last_mouse_scroll = 0.0; + static double persist_start = 0.0; + double this_scroll, scroll_persist = scr_mouse_persist; + double spin_fac = 1.0; + double max_spin, tnow = 0.0; + +//db = 1; + + if (button_mask) { + get_out = 0; + } + dtime(&tnow); + if (tnow < last_mouse_scroll + scroll_persist) { + get_out = 0; + } + + if (get_out) { + persist_start = 0.0; + xrecord_watch(0); + return 0; + } + +if (db) fprintf(stderr, "xrecord_set_by_mouse: %d\n", xrecord_set_by_mouse); +if (db) fprintf(stderr, "xrecord_set_by_keys: %d\n", xrecord_set_by_keys); + + max_spin = scr_mouse_time; + + if (tnow < last_mouse_scroll + scroll_persist) { + max_spin = 1.25*(tnow - last_mouse_scroll); + } + + gp = gp0 = got_pointer_input; + dtime(&tm); + +if (db) fprintf(stderr, "check_xrecord: LOOP: scr_ev_cnt: %d max: %.3f\n", + scr_ev_cnt, max_spin); + + while (1) { + + if (scr_ev_cnt) { + got_one = 1; + this_scroll = 0.0; + dtime(&this_scroll); + break; + } + + X_LOCK; + XFlush(dpy); + flush1 = 1; + X_UNLOCK; + + if (use_threads) { + usleep(1000); + } else { + rfbCFD(1000); + } + spin += dtime(&tm); + + if (spin >= max_spin * spin_fac) { +if (db) fprintf(stderr, "check_xrecord: SPIN-OUT: %.3f/%.3f\n", spin, + max_spin * spin_fac); + break; + } + if (got_pointer_input > gp) { + gp = got_pointer_input; + flush2 = 1; + } + + X_LOCK; + if (flush2) { + XFlush(dpy); + } + XRecordProcessReplies(rdpy_data); + X_UNLOCK; + if (! button_mask) { + break; + } + } + +if (db) fprintf(stderr, " f1: %d f2: %d\n", flush1, flush2); + /* since we've flushed it, we might as well avoid -input_skip */ + if (flush1 || flush2) { + got_keyboard_input = 0; + got_pointer_input = 0; + } + + if (scr_ev_cnt) { + int dret; + double bdpush = 0.0; + static double last_border_push = 0.0; + + if (persist_start > 0.0 && + this_scroll > last_border_push + 1.00) { + bdpush = 0.0; + last_border_push = this_scroll; + } + dret = push_scr_ev(bdpush); + if (! button_mask) { + dret = 0; + } + ret = 1 + dret; + scr_ev_cnt = 0; + } + + if (xrecording) { + if (ret < 2) { + xrecord_watch(0); + } + } + + if (this_scroll > 0.0) { + last_mouse_scroll = this_scroll; + } + + if (ret == 2) { + if (persist_start == 0.0) { + dtime(&persist_start); + } + } else { + persist_start = 0.0; + } + + return ret; +} + +int check_xrecord(void) { + int watch_keys = 0, watch_mouse = 0; + + if (! use_xrecord) { + return 0; + } + if (scaling && ! got_scrollcopyrect) { + return 0; + } + + 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 (watch_mouse && button_mask && xrecord_set_by_mouse) { + return check_xrecord_mouse(); + } else if (watch_keys && xrecord_set_by_keys) { + return check_xrecord_keys(); + } else { + 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; \ + } + +int try_copyrect(Window frame, int x, int y, int w, int h, int dx, int dy, + int *obscured) { + + static int dt_bad = 0; + static time_t dt_bad_check = 0; + int x1, y1, x2, y2, sent_copyrect = 0; + DB_SET + + *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(0) > 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(0); + } + + if (dt_bad) { + 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), dx, dpy_x); + y2 = crfix(nfix(y+h, dpy_y), dy, dpy_y); + + rect = sraRgnCreateRect(x1, y1, x2, y2); + do_copyregion(rect, dx, dy); + sraRgnDestroy(rect); + + sent_copyrect = 1; + *obscured = 1; /* set to avoid an aggressive push */ + + } else if (stack_list) { + int k, tx1, tx2, ty1, ty2; + sraRegionPtr moved_win, tmp_win; + int saw_me = 0; + int orig_x, orig_y; + 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); + ty2 = nfix(orig_y+h, dpy_y); + +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); + + X_LOCK; + + /* + * loop over the stack, top to bottom until we + * find our wm frame: + */ + for (k = stack_num - 1; k >= 0; k--) { + Window swin = stack_list[k]; + if (swin == frame) { +if (db2) { +saw_me = 1; +fprintf(stderr, " ----------\n"); +} else { + break; +} + } + + /* skip some unwanted cases: */ + if (swin == None) { + continue; + } + if (!valid_window(swin, &attr)) { + continue; + } + if (attr.map_state != IsViewable) { + continue; + } + + /* + * 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); + ty2 = nfix(attr.y + attr.height, dpy_y); + +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 woul + * window rectangle + */ + + /* 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); + ty2 = nfix(attr.y - dy + attr.height, dpy_y); + +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 (*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)) { +if (db2) fprintf(stderr, "do_copyregion: %d %d %d %d dx: %d dy: %d\n", tx1, ty1, tx2, ty2, dx, dy); + do_copyregion(shifted_region, dx, dy); + 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; +} + /* * Applied just before any check_user_input() modes. Look for a * ButtonPress; find window it happened in; find the wm frame window @@ -19267,7 +21642,7 @@ int crfix(int x, int dx, int Lx) { * reduces actual XShmGetImage work (i.e. if we correctly predicted how * the X fb has changed. * - * -scale doesn't work under -wirecopyrect, but the wireframe does. + * -scale doesn't always work under -wirecopyrect, but the wireframe does. * * testing of this mode under -threads is incomplete. * @@ -19285,27 +21660,22 @@ int check_wireframe(void) { 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, g_in; + int orig_cursor_x, orig_cursor_y, g; int already_down = 0, win_gone = 0, win_unmapped = 0; double spin = 0.0, tm = 0.0, last_ptr, last_draw; int frame_changed = 0, drew_box = 0, got_2nd_pointer = 0; - int break_reason = 0; + int special_t1 = 0, break_reason = 0; static double first_dt_ave = 0.0; static int first_dt_cnt = 0; static time_t last_save_stacklist = 0; /* heuristics: */ - int wf_t = wireframe_top; - int wf_b = wireframe_bot; - int wf_l = wireframe_left; - int wf_r = wireframe_right; double first_event_spin = wireframe_t1; double frame_changed_spin = wireframe_t2; double max_spin = wireframe_t3; double min_draw = wireframe_t4; - - int db = 0; - int db2 = 0; + int near_edge; + DB_SET if (subwin) { return 0; /* don't even bother for -id case */ @@ -19317,7 +21687,7 @@ int check_wireframe(void) { return 0; /* need ptr input, e.g. button down, motion */ } -if (db || db2) fprintf(stderr, "\n*** button down!! x: %d y: %d\n", cursor_x, cursor_y); +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 @@ -19333,10 +21703,6 @@ if (db) fprintf(stderr, "NO get_wm_frame_pos: 0x%lx\n", frame); X_UNLOCK; if (db) fprintf(stderr, "a: %d wf: %.3f A: %d\n", w*h, wireframe_frac, (dpy_x*dpy_y)); - if (nabs(x + w - px) < 35) { - maybe_scrolling = frame; /* not yet used... */ - } - /* * apply the percentage size criterion (allow opaque moves for * small windows) @@ -19351,24 +21717,11 @@ if (db) fprintf(stderr, " frame: x: %d y: %d w: %d h: %d px: %d py: %d fr * see if the pointer is within range of the assumed wm frame * decorations on the edge of the window. */ - if (wf_t || wf_b || wf_l || wf_r) { - int near_edge = 0; - 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; - } - if (! near_edge) { -if (db) fprintf(stderr, "INTERIOR %d %d %d %d\n", wf_t, wf_b, wf_l, wf_r); - return 0; - } + + near_edge = near_wm_edge(x, y, w, h, px, py); + if (! near_edge) { +if (db) fprintf(stderr, "INTERIOR\n"); + return 0; } wireframe_in_progress = 1; @@ -19376,6 +21729,27 @@ if (db) fprintf(stderr, "INTERIOR %d %d %d %d\n", wf_t, wf_b, wf_l, wf_r); if (button_mask_prev) { already_down = 1; } + + if (! wireframe_str || !strcmp(wireframe_str, WIREFRAME_PARMS)) { + int latency = get_net_latency(); + int netrate = get_net_rate(); + static int didmsg = 0; + + if (latency > 100 || netrate < 10) { /* 100ms, 10KB/sec */ + /* 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 @@ -19384,6 +21758,7 @@ if (db) fprintf(stderr, "INTERIOR %d %d %d %d\n", wf_t, wf_b, wf_l, wf_r); */ if (strcmp(wireframe_copyrect, "never")) { if (already_down) { + double age = 0.0; /* * see if we can reuse the stack list (pause * with button down) @@ -19396,26 +21771,18 @@ if (db) fprintf(stderr, "INTERIOR %d %d %d %d\n", wf_t, wf_b, wf_l, wf_r); break; } } - if (! got_me) { - last_save_stacklist = 0; + if (got_me) { + age = 1.0; } + snapshot_stack_list(0, age); } - if (stack_list && time(0) > last_save_stacklist + 1) { - /* stack_list too old */ - X_LOCK; - XFree(stack_list); - stack_list = NULL; - stack_num = 0; - X_UNLOCK; - } - } else if (! stack_list) { - /* ! already_down, might as well get a copy */ - X_LOCK; - snapshot_stack_list(); - X_UNLOCK; + } + if (! stack_list) { + snapshot_stack_list(0, 0.0); } } + /* store initial parameters, we look for changes in them */ orig_frame = frame; orig_px = px; /* pointer position */ @@ -19444,7 +21811,7 @@ if (db) fprintf(stderr, "INTERIOR %d %d %d %d\n", wf_t, wf_b, wf_l, wf_r); pointer(-1, 0, 0, NULL); } - g = g_in = got_pointer_input; + g = got_pointer_input; while (1) { @@ -19456,28 +21823,37 @@ if (db) fprintf(stderr, "INTERIOR %d %d %d %d\n", wf_t, wf_b, wf_l, wf_r); if (use_threads) { usleep(1000); } else if (drew_box) { - rfbPE(screen, 1000); + rfbPE(1000); } else { - rfbCFD(screen, 1000); + rfbCFD(1000); } spin += dtime(&tm); /* 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; } - /* pointer events slowing down: */ - if (spin > last_ptr + frame_changed_spin) { + /* 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) { + } else if (got_2nd_pointer) { /* * pointer is moving, max time we wait for wm * move or resize to be detected @@ -19502,6 +21878,10 @@ if (db) fprintf(stderr, " ++pointer event!! [%02d] dt: %.3f x: %d y: %d mas g = got_pointer_input; + X_LOCK; + XFlush(dpy); + X_UNLOCK; + /* periodically try to let the wm get moving: */ if (!frame_changed && got_2nd_pointer % 4 == 0) { #if 0 @@ -19622,7 +22002,7 @@ if (db) fprintf(stderr, "FRAME MOVE 1st-dt: %.3f\n", first_dt_ave/n); ! drew_box) { draw_box(x, y, w, h, 0); drew_box = 1; - rfbPE(screen, 1000); + rfbPE(1000); last_draw = spin; } } @@ -19667,15 +22047,19 @@ if (db || db2) fprintf(stderr, "NO button_mask\n"); /* * see if we can apply CopyRect or CopyRegion to the change: */ - if (!scaling && w == orig_w && h == orig_h && (dx != 0 || dy != 0) && - !win_gone && !win_unmapped && strcmp(wireframe_copyrect, "never")) { - - int x1, y1, x2, y2, t, tmax = 50; + if (!strcmp(wireframe_copyrect, "never")) { + ; + } else if (win_gone || win_unmapped) { + ; + } else if (scaling && ! got_wirecopyrect) { + ; + } else if (w != orig_w || h != orig_h) { + ; + } else if (dx == 0 && dy == 0) { + ; + } else { int spin_ms = (int) (spin * 1000 * 1000); - int sent_copyrect = 0; - int obscured = 0; - static int dt_bad = 0; - static time_t dt_bad_check = 0; + int obscured, sent_copyrect = 0; /* * set a timescale comparable to the spin time, @@ -19688,176 +22072,18 @@ if (db || db2) fprintf(stderr, "NO button_mask\n"); } /* try to flush the wireframe removal: */ - fb_update_sent(NULL); - rfbPE(screen, spin_ms/3); /* long select */ - for (t=0; t<tmax; t++) { - if (fb_update_sent(NULL)) { -if (db2) fprintf(stderr, "A-FB_UPDATE_SENT: t=%d\n", t); - break; - } - rfbPE(screen, 1000); /* short selects. */ - } - - /* - * 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(0) > dt_bad_check + 5) { - if (!strcmp(guess_desktop(), "kde")) { - dt_bad = 1; - } else if (!strcmp(guess_desktop(), "xfce")) { - dt_bad = 1; - } else { - dt_bad = 0; - } - dt_bad_check = time(0); - } - - if (dt_bad) { - /* 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), dx, dpy_x); - y2 = crfix(nfix(y+h, dpy_y), dy, dpy_y); - - rfbDoCopyRect(screen, x1, y1, x2, y2, dx, dy); - - sent_copyrect = 1; - obscured = 1; /* set to avoid an aggressive push */ - -if (db2) fprintf(stderr, "CopyRect: x1: %d y1: %d x2: %d y2: %d dx: %d dy: %d spin: %.3f\n", x1, y1, x2, y2, dx, dy, spin); - - } else if (stack_list) { - int k, tx1, tx2, ty1, ty2; - sraRegionPtr moved_win, tmp_win; - int saw_me = 0; - - tx1 = nfix(orig_x, dpy_x); - ty1 = nfix(orig_y, dpy_y); - tx2 = nfix(orig_x+w, dpy_x); - ty2 = nfix(orig_y+h, dpy_y); - -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); - - X_LOCK; - - /* - * loop over the stack, top to bottom until we - * find our wm frame: - */ - for (k = stack_num - 1; k >= 0; k--) { - Window win = stack_list[k]; - if (win == frame) { -if (db2) { - saw_me = 1; - fprintf(stderr, " ----------\n"); -} else { - break; -} - } - - /* skip some unwanted cases: */ - if (win == None) { - continue; - } - if (!valid_window(win, &attr)) { - continue; - } - if (attr.map_state != IsViewable) { - continue; - } - - /* clip the window to the visable screen: */ - tx1 = nfix(attr.x, dpy_x); - ty1 = nfix(attr.y, dpy_y); - tx2 = nfix(attr.x + attr.width, dpy_x); - ty2 = nfix(attr.y + attr.height, dpy_y); - -if (db2) fprintf(stderr, " tmp_win: %4d %3d, %4d %3d 0x%lx\n", tx1, ty1, tx2, ty2, win); -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); - } - X_UNLOCK; + fb_push(); - if (obscured && !strcmp(wireframe_copyrect, "top")) { - ; /* cannot send CopyRegion */ - } else if (! sraRgnEmpty(moved_win)) { - sraRectangleIterator *iter; - sraRegionPtr whole, shifted_region; - sraRect rect; - - /* - * prepare for CopyRegion, apply dx and - * dy to each rectangle in the region. - * keep only the part on the screen. - */ - whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y); - shifted_region = sraRgnCreate(); - -if (db2) fprintf(stderr, "\n"); - - iter = sraRgnGetIterator(moved_win); - while (sraRgnIteratorNext(iter, &rect)) { - tx1 = rect.x1 + dx; - ty1 = rect.y1 + dy; - tx2 = rect.x2 + dx; - ty2 = rect.y2 + dy; - -if (db2) fprintf(stderr, " shf_win: %4d %3d, %4d %3d\n", tx1, ty1, tx2, ty2); - - tmp_win = sraRgnCreateRect(tx1, ty1, - tx2, ty2); - sraRgnAnd(tmp_win, whole); - if (! sraRgnEmpty(tmp_win)) { - sraRgnOr(shifted_region, - tmp_win); - } - sraRgnDestroy(tmp_win); - } - sraRgnReleaseIterator(iter); - sraRgnDestroy(whole); - - /* now send the CopyRegion: */ - if (! sraRgnEmpty(shifted_region)) { - rfbDoCopyRegion(screen, shifted_region, - dx, dy); - sent_copyrect = 1; -if (db2) fprintf(stderr, " rfbDoCopyRegion\n"); - } - sraRgnDestroy(shifted_region); - } - sraRgnDestroy(moved_win); - } + /* try to send a clipped copyrect of translation: */ + sent_copyrect = try_copyrect(frame, x, y, w, h, dx, dy, + &obscured); if (sent_copyrect) { - /* try to push the changes to viewers: */ - fb_update_sent(NULL); - rfbPE(screen, spin_ms/3); if (! obscured) { - for (t=0; t<tmax; t++) { - if (fb_update_sent(NULL)) { -if (db2) fprintf(stderr, "B-FB_UPDATE_SENT: t=%d\n", t); - break; - } - rfbPE(screen, 1000); - } + fb_push(); + } else { + fb_push(); } } } @@ -19867,8 +22093,7 @@ if (db2) fprintf(stderr, "B-FB_UPDATE_SENT: t=%d\n", t); if (break_reason == 1 || break_reason == 2) { /* * save the stack list, perhaps the user has - * paused with button down. XXX unstable if - * windows in the list go away? + * paused with button down. */ last_save_stacklist = time(0); } else { @@ -19881,7 +22106,7 @@ if (db2) fprintf(stderr, "B-FB_UPDATE_SENT: t=%d\n", t); } /* final push (for -nowirecopyrect) */ - rfbPE(screen, 1000); + rfbPE(1000); wireframe_in_progress = 0; return 1; } @@ -19934,9 +22159,9 @@ static void check_user_input2(double dt) { */ while (1) { if (show_multiple_cursors) { - rfbPE(screen, 1000); + rfbPE(1000); } else { - rfbCFD(screen, 1000); + rfbCFD(1000); } X_LOCK; XFlush(dpy); @@ -19999,9 +22224,9 @@ static void check_user_input2(double dt) { for (i=0; i<split; i++) { usleep(ms * 1000); if (show_multiple_cursors) { - rfbPE(screen, 1000); + rfbPE(1000); } else { - rfbCFD(screen, 1000); + rfbCFD(1000); } spin += dtime(&tm); if (got_pointer_input > g) { @@ -20055,9 +22280,9 @@ static void check_user_input3(double dt) { int mcut = miss_max; if (show_multiple_cursors) { - rfbPE(screen, 1000); + rfbPE(1000); } else { - rfbCFD(screen, 1000); + rfbCFD(1000); } X_LOCK; XFlush(dpy); @@ -20136,9 +22361,9 @@ static void check_user_input3(double dt) { for (i=0; i<split; i++) { usleep(ms * 1000); if (show_multiple_cursors) { - rfbPE(screen, 1000); + rfbPE(1000); } else { - rfbCFD(screen, 1000); + rfbCFD(1000); } spin += dtime(&tm); if (got_pointer_input > g) { @@ -20212,7 +22437,7 @@ static void check_user_input4(double dt, double dtr, int tile_diffs) { drag_in_progress = 1; } - rfbCFD(screen, rfb_wait_ms * 1000); + rfbCFD(rfb_wait_ms * 1000); dtm = dtime(&tm); spin += dtm; @@ -20272,7 +22497,7 @@ static void check_user_input4(double dt, double dtr, int tile_diffs) { if (ginput >= 2) { /* try for a couple more quick ones */ for (i=0; i<2; i++) { - rfbCFD(screen, rfb_wait_ms * 1000); + rfbCFD(rfb_wait_ms * 1000); } } @@ -20385,7 +22610,7 @@ static void check_user_input5(double dt, double dtr, int tile_diffs) { } /* damn, they didn't push our frame! */ iter++; - rfbPE(screen, rfb_wait_ms * 1000); + rfbPE(rfb_wait_ms * 1000); push_spin += dtime(&tp); } @@ -20443,7 +22668,7 @@ static void check_user_input5(double dt, double dtr, int tile_diffs) { } /* turn libvncserver crank to process events: */ - rfbCFD(screen, rfb_wait_ms * 1000); + rfbCFD(rfb_wait_ms * 1000); dtm = dtime(&tm); spin += dtm; @@ -20507,7 +22732,7 @@ static void check_user_input5(double dt, double dtr, int tile_diffs) { if (ginput >= 2) { /* try for a couple more quick ones */ for (i=0; i<2; i++) { - rfbCFD(screen, rfb_wait_ms * 1000); + rfbCFD(rfb_wait_ms * 1000); } } drag_in_progress = 0; @@ -20517,7 +22742,15 @@ static int check_user_input(double dt, double dtr, int tile_diffs, int *cnt) { if (raw_fb && ! dpy) return 0; /* raw_fb hack */ - maybe_scrolling = None; + if (use_xrecord) { + int rc = check_xrecord(); + if (rc == 1) { + return 0; + } else if (rc == 2) { +if (0) fprintf(stderr, " CXR: check_user_input ret 1\n"); + return 1; + } + } if (wireframe) { if (check_wireframe()) { @@ -20593,67 +22826,90 @@ double dtime(double *t_old) { void measure_display_hook(rfbClientPtr cl) { ClientData *cd = (ClientData *) cl->clientData; +#if 0 +double tm = 0.0; +dtime(&tm); +fprintf(stderr, " MDH: %.4f\n", tm); +#endif cd->timer = 0.0; dtime(&cd->timer); } -void measure_send_rates_init(void) { - int i, bs, rbs; +int get_rate(int which) { rfbClientIteratorPtr iter; rfbClientPtr cl; - - screen->displayHook = measure_display_hook; - + int irate, irate_min = 1; /* 1 KB/sec */ + int irate_max = 100000; /* 100 MB/sec */ + double slowest = -1.0, rate; + static double save_rate = 10000.0; /* 10000.0 B/sec */ + iter = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(iter)) ) { ClientData *cd = (ClientData *) cl->clientData; - bs = 0; - for (i=0; i<MAX_ENCODINGS; i++) { - bs += cl->bytesSent[i]; + + if (which == 0) { + rate = cd->send_cmp_rate; + } else { + rate = cd->send_raw_rate; + } + if (slowest == -1.0 || rate < slowest) { + slowest = rate; } - rbs = cl->rawBytesEquivalent; - cd->set_cmp_bytes = bs; - cd->set_raw_bytes = rbs; - cd->timer = -1.0; } rfbReleaseClientIterator(iter); + + if (slowest == -1.0) { + slowest = save_rate; + } else { + save_rate = slowest; + } + + irate = (int) (slowest/1000.0); + if (irate < irate_min) { + irate = irate_min; + } + if (irate > irate_max) { + irate = irate_max; + } + + return irate; } -int get_rate(int which) { +int get_latency(void) { rfbClientIteratorPtr iter; rfbClientPtr cl; - int i, samples = RATE_SAMPLES; - double dslowest = -1.0, dsum; + int ilat, ilat_min = 1; /* 1 ms */ + int ilat_max = 2000; /* 2 sec */ + double slowest = -1.0, lat; + static double save_lat = 0.020; /* 20 ms */ iter = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(iter)) ) { ClientData *cd = (ClientData *) cl->clientData; - - dsum = 0.0; - for (i=0; i<samples; i++) { - if (which == 0) { - dsum += cd->cmp_samp[i]; - } else { - dsum += cd->raw_samp[i]; - } - } - dsum = dsum / samples; - if (dsum > dslowest) { - dslowest = dsum; - } + lat = cd->latency; + if (slowest == -1.0 || lat > slowest) { + slowest = lat; + } } rfbReleaseClientIterator(iter); - if (dslowest < 0.0) { - if (which == 0) { - dslowest = 5000.0; - } else { - dslowest = 50000.0; - } + if (slowest == -1.0) { + slowest = save_lat; + } else { + save_lat = slowest; + } + + ilat = (int) (slowest * 1000.0); + if (ilat < ilat_min) { + ilat = ilat_min; + } + if (ilat > ilat_max) { + ilat = ilat_max; } - return (int) dslowest; + + return ilat; } int get_cmp_rate(void) { @@ -20665,24 +22921,26 @@ int get_raw_rate(void) { } void initialize_speeds(void) { - char *s, *p; + char *s, *s_in, *p; int i; speeds_read_rate = 0; speeds_net_rate = 0; speeds_net_latency = 0; if (! speeds_str || *speeds_str == '\0') { - return; + s_in = strdup(""); + } else { + s_in = strdup(speeds_str); } - if (!strcmp(speeds_str, "modem")) { + if (!strcmp(s_in, "modem")) { s = strdup("6,4,200"); - } else if (!strcmp(speeds_str, "dsl")) { + } else if (!strcmp(s_in, "dsl")) { s = strdup("6,100,50"); - } else if (!strcmp(speeds_str, "lan")) { + } else if (!strcmp(s_in, "lan")) { s = strdup("6,5000,1"); } else { - s = strdup(speeds_str); + s = strdup(s_in); } p = strtok(s, ","); @@ -20703,12 +22961,40 @@ void initialize_speeds(void) { p = strtok(NULL, ","); } free(s); + free(s_in); + + if (! speeds_read_rate) { + int n = 0; + double dt, timer = 0.0; + dtime(&timer); + if (fullscreen) { + copy_image(fullscreen, 0, 0, 0, 0); + n = fullscreen->bytes_per_line * fullscreen->height; + } else if (scanline) { + copy_image(scanline, 0, 0, 0, 0); + n = scanline->bytes_per_line * scanline->height; + } + dt = dtime(&timer); + if (n && dt > 0.0) { + double rate = ((double) n) / dt; + speeds_read_rate_measured = (int) (rate/1000000.0); + if (speeds_read_rate_measured < 1) { + speeds_read_rate_measured = 1; + } else { + rfbLog("fb read rate: %d MB/sec\n", + speeds_read_rate_measured); + } + } + } } int get_read_rate(void) { if (speeds_read_rate) { return speeds_read_rate; } + if (speeds_read_rate_measured) { + return speeds_read_rate_measured; + } return 0; } @@ -20716,6 +23002,12 @@ int get_net_rate(void) { if (speeds_net_rate) { return speeds_net_rate; } + if (! speeds_net_rate_measured) { + speeds_net_rate_measured = get_cmp_rate(); + } + if (speeds_net_rate_measured) { + return speeds_net_rate_measured; + } return 0; } @@ -20723,143 +23015,319 @@ int get_net_latency(void) { if (speeds_net_latency) { return speeds_net_latency; } + if (! speeds_net_latency_measured) { + speeds_net_latency_measured = get_latency(); + } + if (speeds_net_latency_measured) { + return speeds_net_latency_measured; + } return 0; } void measure_send_rates(int init) { - int i, j, nclient = 0; - int min_width = 200; - double dt, cmp_rate, raw_rate; - rfbClientPtr id[100]; - double dts[100], dts_sorted[100], dtmp; - int sorted[100], did[100], best; + double cmp_rate, raw_rate; + static double now, start = 0.0; + static rfbDisplayHookPtr orig_display_hook = NULL; + double cmp_max = 1.0e+08; /* 100 MB/sec */ + double cmp_min = 1000.0; /* 9600baud */ + double lat_max = 5.0; /* 5 sec */ + double lat_min = .0005; /* 0.5 ms */ + int min_cmp = 10000, nclients; rfbClientIteratorPtr iter; rfbClientPtr cl; + int db = 0; if (! measure_speeds) { return; } - if (init) { - measure_send_rates_init(); + if (speeds_net_rate && speeds_net_latency) { return; } + if (! orig_display_hook) { + orig_display_hook = screen->displayHook; + } + + if (start == 0.0) { + dtime(&start); + } + now = 0.0; + dtime(&now); + now = now - start; + + nclients = 0; + iter = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(iter)) ) { - double tmp2; + int defer, i, cbs, rbs; + char *httpdir; + double dt, dt1 = 0.0, dt2, dt3; + double tm, spin_max = 15.0, spin_lat_max = 1.5; + int got_t2 = 0, got_t3 = 0; ClientData *cd = (ClientData *) cl->clientData; - tmp2 = 0.0; - dtime(&tmp2); -if (init) { - continue; -} - if (cd->timer <= 0.0) { + + if (cd->send_cmp_rate > 0.0) { continue; } - dt = dtime(&cd->timer); - cd->timer = dt; - if (nclient < 100) { - id[nclient] = cl; - dts[nclient] = dt; - nclient++; + nclients++; + + cbs = 0; + for (i=0; i<MAX_ENCODINGS; i++) { + cbs += cl->bytesSent[i]; } - } - rfbReleaseClientIterator(iter); -if (init) { - return; -} + rbs = cl->rawBytesEquivalent; - for (i=0; i<nclient; i++) { - did[i] = 0; - } - for (i=0; i<nclient; i++) { - dtmp = -1.0; - best = -1; - for (j=0; j<nclient; j++) { - if (did[j]) { - continue; - } - if (dts[j] > dtmp) { - best = j; - dtmp = dts[j]; - } - } - did[best] = 1; - sorted[i] = best; - dts_sorted[i] = dts[best]; - } - + if (init) { + double tmr = 0.0; - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - int db, dbr, cbs, rbs; - ClientData *cd = (ClientData *) cl->clientData; +if (db) fprintf(stderr, "%d client num rects req: %d mod: %d cbs: %d " + "rbs: %d dt1: %.3f t: %.3f\n", init, + (int) sraRgnCountRects(cl->requestedRegion), + (int) sraRgnCountRects(cl->modifiedRegion), cbs, rbs, dt1, now); - dt = cd->timer; - if (dt <= 0.0) { + dtime(&tmr); + cd->timer = tmr; + cd->cmp_bytes_sent = cbs; + cd->raw_bytes_sent = rbs; continue; } - if (nclient > 1) { - for (i=0; i<nclient; i++) { - if (cl != id[i]) { - continue; + + /* first part of the bulk transfer of initial screen */ + dt1 = dtime(&cd->timer); + +if (db) fprintf(stderr, "%d client num rects req: %d mod: %d cbs: %d " + "rbs: %d dt1: %.3f t: %.3f\n", init, + (int) sraRgnCountRects(cl->requestedRegion), + (int) sraRgnCountRects(cl->modifiedRegion), cbs, rbs, dt1, now); + + if (dt1 <= 0.0) { + continue; + } + + cbs = cbs - cd->cmp_bytes_sent; + rbs = rbs - cd->raw_bytes_sent; + + if (cbs < min_cmp) { + continue; + } + + rfbPE(1000); + if (sraRgnCountRects(cl->modifiedRegion)) { + rfbPE(1000); + } + + defer = screen->deferUpdateTime; + httpdir = screen->httpDir; + screen->deferUpdateTime = 0; + screen->httpDir = NULL; + + /* mark a small rectangle: */ + mark_rect_as_modified(0, 0, 16, 16, 1); + + tm = 0.0; + dtime(&tm); + + dt2 = 0.0; + dt3 = 0.0; + + if (dt1 < 0.25) { + /* try to cut it down to avoid long pauses. */ + spin_max = 5.0; + } + + /* when req1 = 1 mod1 == 0, end of 2nd part of bulk transfer */ + while (1) { + int req0, req1, mod0, mod1; + req0 = sraRgnCountRects(cl->requestedRegion); + mod0 = sraRgnCountRects(cl->modifiedRegion); + if (use_threads) { + usleep(1000); + } else { + if (mod0) { + rfbPE(1000); + } else { + rfbCFD(1000); + } + } + dt = dtime(&tm); + dt2 += dt; + if (dt2 > spin_max) { + break; + } + req1 = sraRgnCountRects(cl->requestedRegion); + mod1 = sraRgnCountRects(cl->modifiedRegion); + +if (db) fprintf(stderr, "dt2 calc: num rects req: %d/%d mod: %d/%d " + "fbu-sent: %d dt: %.4f dt2: %.4f tm: %.4f\n", + req0, req1, mod0, mod1, cl->framebufferUpdateMessagesSent, dt, dt2, tm); + if (req1 != 0 && mod1 == 0) { + got_t2 = 1; + break; + } + } + + if (! got_t2) { + dt2 = 0.0; + } else { + int tr, trm = 3; + double dts[10]; + + /* + * Note: since often select(2) cannot sleep + * less than 1/HZ (e.g. 10ms), the resolution + * of the latency may be messed up by something + * of this order. Effect may occur on both ends, + * i.e. the viewer may not respond immediately. + */ + + for (tr = 0; tr < trm; tr++) { + usleep(5000); + + /* mark a 2nd small rectangle: */ + mark_rect_as_modified(0, 0, 16, 16, 1); + i = 0; + tm = 0.0; + dtime(&tm); + dt3 = 0.0; + + /* + * when req1 > 0 and mod1 == 0, we say + * that is the "ping" time. + */ + while (1) { + int req0, req1, mod0, mod1; + + req0 = sraRgnCountRects( + cl->requestedRegion); + mod0 = sraRgnCountRects( + cl->modifiedRegion); + + if (i == 0) { + rfbPE(0); + } else { + if (use_threads) { + usleep(1000); + } else { + /* try to get it all */ + rfbCFD(1000*1000); + } + } + dt = dtime(&tm); + i++; + + dt3 += dt; + if (dt3 > spin_lat_max) { + break; + } + req1 = sraRgnCountRects( + cl->requestedRegion); + + mod1 = sraRgnCountRects( + cl->modifiedRegion); + +if (db) fprintf(stderr, "dt3 calc: num rects req: %d/%d mod: %d/%d " + "fbu-sent: %d dt: %.4f dt3: %.4f tm: %.4f\n", + req0, req1, mod0, mod1, cl->framebufferUpdateMessagesSent, dt, dt3, tm); + + if (req1 != 0 && mod1 == 0) { + dts[got_t3++] = dt3; + break; + } } - for (j=0; j<nclient; j++) { - if (sorted[j] == i) { - if (j < nclient - 1) { - dt -= dts_sorted[j+1]; + } + + if (! got_t3) { + dt3 = 0.0; + } else { + if (got_t3 == 1) { + dt3 = dts[0]; + } else if (got_t3 == 2) { + dt3 = dts[1]; + } else { + if (dts[2] >= 0.0) { + double rat = dts[1]/dts[2]; + if (rat > 0.5 && rat < 2.0) { + dt3 = dts[1]+dts[2]; + dt3 *= 0.5; + } else { + dt3 = dts[1]; } + } else { + dt3 = dts[1]; } } - break; } } - if (dt <= 0.0) { - continue; + + screen->deferUpdateTime = defer; + screen->httpDir = httpdir; + + dt = dt1 + dt2; + + + if (dt3 <= dt2/2.0) { + /* guess only 1/2 a ping for reply... */ + dt = dt - dt3/2.0; } - cbs = 0; - for (i=0; i<MAX_ENCODINGS; i++) { - cbs += cl->bytesSent[i]; + cmp_rate = cbs/dt; + raw_rate = rbs/dt; + + if (cmp_rate > cmp_max) { + cmp_rate = cmp_max; + } + if (cmp_rate <= cmp_min) { + cmp_rate = cmp_min; } - rbs = cl->rawBytesEquivalent; - db = cbs - cd->set_cmp_bytes; - dbr = rbs - cd->set_raw_bytes; - cmp_rate = db/dt; - raw_rate = dbr/dt; - if (dbr > min_width * min_width * bpp/8) { - cd->sample++; - if (cd->sample >= RATE_SAMPLES) { - cd->sample = 0; - } - i = cd->sample; - cd->cmp_samp[i] = cmp_rate; - cd->raw_samp[i] = raw_rate; + cd->send_cmp_rate = cmp_rate; + cd->send_raw_rate = raw_rate; + + if (dt3 > lat_max) { + dt3 = lat_max; + } + if (dt3 <= lat_min) { + dt3 = lat_min; } + + cd->latency = dt3; + + rfbLog("client %d network rate %.1f KB/sec (%.1f eff KB/sec)\n", + cd->uid, cmp_rate/1000.0, raw_rate/1000.0); + rfbLog("client %d latency: %.1f ms\n", cd->uid, 1000.0*dt3); + rfbLog("dt1: %.4f, dt2: %.4f dt3: %.4f bytes: %d\n", + dt1, dt2, dt3, cbs); } rfbReleaseClientIterator(iter); + + if (init) { + if (nclients) { + screen->displayHook = measure_display_hook; + } + } else { + screen->displayHook = orig_display_hook; + } } /* * utility wrapper to call rfbProcessEvents * checks that we are not in threaded mode. */ -void rfbPE(rfbScreenInfoPtr scr, long usec) { - if (! scr) { +void rfbPE(long usec) { + if (! screen) { return; } if (! use_threads) { - rfbProcessEvents(scr, usec); + rfbProcessEvents(screen, usec); } } -void rfbCFD(rfbScreenInfoPtr scr, long usec) { - if (! scr) { +void rfbCFD(long usec) { + if (! screen) { return; } if (! use_threads) { - rfbCheckFds(scr, usec); + rfbCheckFds(screen, usec); } } @@ -20880,11 +23348,14 @@ static void watch_loop(void) { got_user_input = 0; got_pointer_input = 0; got_keyboard_input = 0; + urgent_update = 0; if (! use_threads) { double tm = 0.0; dtime(&tm); - rfbPE(screen, -1); + measure_send_rates(1); + rfbPE(-1); + measure_send_rates(0); dtr = dtime(&tm); fb_update_sent(NULL); @@ -20895,6 +23366,7 @@ static void watch_loop(void) { if (screen && screen->clientHead && check_user_input(dt, dtr, tile_diffs, &cnt)) { /* true means loop back for more input */ +if (0) fprintf(stderr, "watch_loop: LOOP-BACK\n"); continue; } } else if (use_threads && wireframe && button_mask) { @@ -20905,23 +23377,25 @@ static void watch_loop(void) { clean_up_exit(0); } - if (do_copy_screen) { - do_copy_screen = 0; - copy_screen(); - } + if (! urgent_update) { + if (do_copy_screen) { + do_copy_screen = 0; + copy_screen(); + } - check_new_clients(); - check_xevents(); - check_connect_inputs(); - check_padded_fb(); - check_xdamage_state(); - if (started_as_root) { - check_switched_user(); - } + check_new_clients(); + check_xevents(); + check_connect_inputs(); + check_padded_fb(); + check_xdamage_state(); + if (started_as_root) { + check_switched_user(); + } - if (first_conn_timeout < 0) { - start = time(0); - first_conn_timeout = -first_conn_timeout; + if (first_conn_timeout < 0) { + start = time(0); + first_conn_timeout = -first_conn_timeout; + } } if (! screen || ! screen->clientHead) { @@ -20962,7 +23436,6 @@ static void watch_loop(void) { double tm = 0.0; dtime(&tm); - RFBUNDRAWCURSOR(screen); if (use_snapfb) { int t, tries = 5; copy_snap(); @@ -20973,6 +23446,9 @@ static void watch_loop(void) { tile_diffs = scan_for_updates(0); } dt = dtime(&tm); +if (0 && tile_diffs > 4) { +fprintf(stderr, "TILES: %d dt: %.4f t: %.4f\n", tile_diffs, dt, tm); +} check_x11_pointer(); } @@ -21701,7 +24177,10 @@ static void print_help(int mode) { " heuristics and may not always work: it depends on your\n" " window manager and even how you move things around.\n" " See -pointer_mode below for discussion of the \"bogging\n" -" down\" problem this tries to avoid. Default: %s\n" +" down\" problem this tries to avoid.\n" +" Default: %s\n" +"\n" +" Shorter aliases: -wf [str] and -nowf\n" "\n" " The value \"str\" is optional and, of course, is\n" " packed with many tunable parameters for this scheme:\n" @@ -21739,16 +24218,67 @@ static void print_help(int mode) { " link this might be a better choice: 0.25+0.6+6.0+0.15\n" "\n" "-wirecopyrect mode Since the -wireframe mechanism evidently tracks moving\n" -"-nowirecopyrect windows, a speedup can be obtained by telling the VNC\n" -" viewers to locally copy the translated window region.\n" -" This is the VNC CopyRect encoding: the framebuffer\n" -" update doesn't need to send the actual new image data.\n" +"-nowirecopyrect windows accurately, a speedup can be obtained by\n" +" telling the VNC viewers to locally copy the translated\n" +" window region. This is the VNC CopyRect encoding:\n" +" the framebuffer update doesn't need to send the actual\n" +" new image data.\n" +"\n" +" Shorter aliases: -wcr [mode] and -nowcr\n" +"\n" " \"mode\" can be \"never\" (same as -nowirecopyrect)\n" " to never try the copyrect, \"top\" means only do it if\n" " the window was not covered by any other windows, and\n" " \"always\" means to translate the orginally unobscured\n" " region (this may look odd as the remaining pieces come\n" -" in, but helps on a slow link) Default: %s\n" +" in, but helps on a slow link). Default: \"%s\"\n" +"\n" +" Note: there can be painting errors when using -scale\n" +" so CopyRect is skipped when scaling unless you specify\n" +" -wirecopyrect on the command line or by remote-control.\n" +"\n" +"-scrollcopyrect mode Like -wirecopyrect, but use heuristics to try to guess\n" +"-noscrollcopyrect if a window has scrolled its contents (either vertically\n" +" or horizontally). This requires the RECORD X extension\n" +" to \"snoop\" on X applications (currently for certain\n" +" XCopyArea and XConfigureWindow X protocol requests).\n" +" Examples: Hitting <Return> in a terminal window when the\n" +" cursor was at the bottom, the text scrolls up one line.\n" +" Hitting <Down> arrow in a web browser window, the web\n" +" page scrolls up a small amount.\n" +"\n" +" Shorter aliases: -scr [mode] and -noscr\n" +"\n" +" This scheme will not always detect scrolls, but when\n" +" it does there is a nice speedup from using the VNC\n" +" CopyRect encoding (see -wirecopyrect). The speedup\n" +" is both in reduced network traffic and reduced X\n" +" framebuffer polling/copying. On the other hand,\n" +" it may induce undesired transients (e.g. a terminal\n" +" cursor being scrolled up when it should not be) or other\n" +" painting errors. These are automatically repaired in a\n" +" short period of time. If this is unacceptable disable\n" +" the feature with -noscrollcopyrect.\n" +"\n" +" \"mode\" can be \"never\" (same as -noscrollcopyrect)\n" +" to never try the copyrect, \"keys\" means to try it\n" +" in response to keystrokes only, \"mouse\" means to\n" +" try it in response to mouse events only, \"always\"\n" +" means to do both. Default: \"%s\"\n" +"\n" +" Note: there can be painting errors when using\n" +" -scale so CopyRect is skipped when scaling unless\n" +" you specify -scrollcopyrect on the command line or\n" +" by remote-control.\n" +"\n" +"-scr_area n Set the minimum area in pixels for a rectangle\n" +" to be considered for the -scrollcopyrect detection\n" +" scheme. This is to avoid wasting the effort on small\n" +" rectangles that would be quickly updated the normal way.\n" +" E.g. suppose an app updated the position of its skinny\n" +" scrollbar first and then shifted the large panel\n" +" it controlled. We want to be sure to skip the small\n" +" scrollbar and get the large panel. Default: %d\n" "\n" "-pointer_mode n Various pointer motion update schemes. \"-pm\" is\n" " an alias. The problem is pointer motion can cause\n" @@ -22204,11 +24734,14 @@ static void print_help(int mode) { " buttonmap:str set -buttonmap \"str\", empty to disable\n" " dragging disable -nodragging mode.\n" " nodragging enable -nodragging mode.\n" -" wireframe enable -wireframe mode.\n" -" nowireframe disable -wireframe mode.\n" +" wireframe enable -wireframe mode. same as \"wf\"\n" +" nowireframe disable -wireframe mode. same as \"nowf\"\n" " wireframe:str enable -wireframe mode string.\n" " wireframe_mode:str enable -wireframe mode string.\n" -" wirecopyrect:str set -wirecopyrect string.\n" +" wirecopyrect:str set -wirecopyrect string. same as \"wcr:\"\n" +" scrollcopyrect:str set -scrollcopyrect string. same \"scr\"\n" +" noscrollcopyrect disable -scrollcopyrect mode. \"noscr\"\n" +" scr_area:n set -scr_area to n\n" " pointer_mode:n set -pointer_mode to n. same as \"pm\"\n" " input_skip:n set -input_skip to n.\n" " speeds:str set -speeds to str.\n" @@ -22251,6 +24784,15 @@ static void print_help(int mode) { " dontdisconnect enable -dontdisconnect mode.\n" " nodontdisconnect disable -dontdisconnect mode.\n" " (may interfere with other options)\n" +" debug_xevents enable debugging X events.\n" +" nodebug_xevents disable debugging X events.\n" +" debug_xdamage enable debugging X DAMAGE mechanism.\n" +" nodebug_xdamage disable debugging X DAMAGE mechanism.\n" +" debug_wireframe enable debugging wireframe mechanism.\n" +" nodebug_wireframe disable debugging wireframe mechanism.\n" +" debug_scroll enable debugging scrollcopy mechanism.\n" +" nodebug_scroll disable debugging scrollcopy mechanism.\n" +"\n" " noremote disable the -remote command processing,\n" " it cannot be turned back on.\n" "\n" @@ -22307,13 +24849,14 @@ static void print_help(int mode) { " add_keysyms noadd_keysyms clear_mods noclear_mods\n" " clear_keys noclear_keys remap repeat norepeat\n" " fb nofb bell nobell sel nosel primary noprimary\n" -" cursorshape nocursorshape cursorpos nocursorpos\n" -" cursor show_cursor noshow_cursor nocursor arrow\n" -" xfixes noxfixes xdamage noxdamage xd_area xd_mem\n" -" alphacut alphafrac alpharemove noalpharemove alphablend\n" -" noalphablend xwarp xwarppointer noxwarp noxwarppointer\n" -" buttonmap dragging nodragging wireframe_mode\n" -" wireframe nowireframe wirecopyrect nowirecopyrect\n" +" cursorshape nocursorshape cursorpos nocursorpos cursor\n" +" show_cursor noshow_cursor nocursor arrow xfixes noxfixes\n" +" xdamage noxdamage xd_area xd_mem alphacut alphafrac\n" +" alpharemove noalpharemove alphablend noalphablend\n" +" xwarp xwarppointer noxwarp noxwarppointer buttonmap\n" +" dragging nodragging wireframe_mode wireframe wf\n" +" nowireframe nowf wirecopyrect wcr nowirecopyrect nowcr\n" +" scr_area scrollcopyrect scr noscrollcopyrect noscr\n" " pointer_mode pm input_skip input client_input speeds\n" " debug_pointer dp nodebug_pointer nodp debug_keyboard dk\n" " nodebug_keyboard nodk deferupdate defer wait rfbwait\n" @@ -22323,19 +24866,23 @@ static void print_help(int mode) { " noalwaysshared nevershared noalwaysshared dontdisconnect\n" " nodontdisconnect desktop noremote\n" "\n" -" aro= debug_xevents debug_xdamage display vncdisplay\n" -" desktopname http_url auth users rootshift clipshift\n" -" scale_str scaled_x scaled_y scale_numer scale_denom\n" -" scale_fac scaling_blend scaling_nomult4 scaling_pad\n" -" scaling_interpolate inetd privremote unsafe safer\n" -" nocmds passwdfile using_shm logfile o flag rc norc h\n" -" help V version lastmod bg sigpipe threads pipeinput\n" -" clients client_count pid ext_xtest ext_xtrap ext_xkb\n" -" ext_xshm ext_xinerama ext_overlay ext_xfixes ext_xdamage\n" -" ext_xrandr rootwin num_buttons button_mask mouse_x\n" -" mouse_y bpp depth indexed_color dpy_x dpy_y wdpy_x\n" -" wdpy_y off_x off_y cdpy_x cdpy_y coff_x coff_y rfbauth\n" -" passwd\n" +" aro= debug_xevents nodebug_xevents debug_xevents\n" +" debug_xdamage nodebug_xdamage debug_xdamage\n" +" debug_wireframe nodebug_wireframe debug_wireframe\n" +" debug_scroll nodebug_scroll debug_scroll display\n" +" vncdisplay desktopname http_url auth users rootshift\n" +" clipshift scale_str scaled_x scaled_y scale_numer\n" +" scale_denom scale_fac scaling_blend scaling_nomult4\n" +" scaling_pad scaling_interpolate inetd privremote\n" +" unsafe safer nocmds passwdfile using_shm logfile\n" +" o flag rc norc h help V version lastmod bg sigpipe\n" +" threads readrate netrate netlatency pipeinput clients\n" +" client_count pid ext_xtest ext_xtrap ext_xrecord\n" +" ext_xkb ext_xshm ext_xinerama ext_overlay ext_xfixes\n" +" ext_xdamage ext_xrandr rootwin num_buttons button_mask\n" +" mouse_x mouse_y bpp depth indexed_color dpy_x dpy_y\n" +" wdpy_x wdpy_y off_x off_y cdpy_x cdpy_y coff_x coff_y\n" +" rfbauth passwd\n" "\n" "-sync By default -remote commands are run asynchronously, that\n" " is, the request is posted and the program immediately\n" @@ -22357,6 +24904,8 @@ static void print_help(int mode) { " taken place.\n" "\n" "-noremote Do not process any remote control commands or queries.\n" +"-yesremote Do process remote control commands or queries.\n" +" Default: %s\n" "\n" " A note about security wrt remote control commands.\n" " If someone can connect to the X display and change\n" @@ -22386,10 +24935,10 @@ static void print_help(int mode) { "-safer Equivalent to: -novncconnect -noremote and prohibiting\n" " -gui and the -connect file. Shuts off communcation\n" " channels.\n" -"-privremote Perform some sanity checks and only allow remote-control\n" +"-privremote Perform some sanity checks and disable remote-control\n" " commands if it appears that the X DISPLAY and/or\n" -" connectfile cannot be accessed by other users. (not\n" -" complete, does not check for empty access control list)\n" +" connectfile can be accessed by other users. Once\n" +" remote-control is disabled it cannot be turned back on.\n" "-nocmds No external commands (e.g. system(3), popen(3), exec(3))\n" " will be run.\n" "\n" @@ -22441,6 +24990,8 @@ static void print_help(int mode) { wireframe ? "-wireframe":"-nowireframe", WIREFRAME_PARMS, wireframe_copyrect_default, + scroll_copyrect_default, + scrollcopyrect_min_area, pointer_mode_max, pointer_mode, ui_skip, defer_update, @@ -22453,6 +25004,7 @@ static void print_help(int mode) { gaps_fill, grow_fill, tile_fuzz, + accept_remote_cmds ? "-yesremote":"-noremote", "" ); @@ -23134,6 +25686,8 @@ int main(int argc, char* argv[]) { } else if (!strcmp(arg, "-arrow")) { CHECK_ARGC alt_arrow = atoi(argv[++i]); + } else if (!strcmp(arg, "-xfixes")) { + use_xfixes = 1; } else if (!strcmp(arg, "-noxfixes")) { use_xfixes = 0; } else if (!strcmp(arg, "-alphacut")) { @@ -23160,7 +25714,8 @@ int main(int argc, char* argv[]) { pointer_remap = strdup(argv[++i]); } else if (!strcmp(arg, "-nodragging")) { show_dragging = 0; - } else if (!strcmp(arg, "-wireframe")) { + } else if (!strcmp(arg, "-wireframe") + || !strcmp(arg, "-wf")) { wireframe = 1; if (i < argc-1) { char *s = argv[i+1]; @@ -23168,13 +25723,32 @@ int main(int argc, char* argv[]) { wireframe_str = strdup(argv[++i]); } } - } else if (!strcmp(arg, "-nowireframe")) { + } else if (!strcmp(arg, "-nowireframe") + || !strcmp(arg, "-nowf")) { wireframe = 0; - } else if (!strcmp(arg, "-wirecopyrect")) { + } else if (!strcmp(arg, "-wirecopyrect") + || !strcmp(arg, "-wcr")) { CHECK_ARGC set_wirecopyrect_mode(argv[++i]); - } else if (!strcmp(arg, "-nowirecopyrect")) { + got_wirecopyrect = 1; + } else if (!strcmp(arg, "-nowirecopyrect") + || !strcmp(arg, "-nowf")) { set_wirecopyrect_mode("never"); + } else if (!strcmp(arg, "-scrollcopyrect") + || !strcmp(arg, "-scr")) { + CHECK_ARGC + set_scrollcopyrect_mode(argv[++i]); + got_scrollcopyrect = 1; + } else if (!strcmp(arg, "-noscrollcopyrect") + || !strcmp(arg, "-noscr")) { + set_scrollcopyrect_mode("never"); + } else if (!strcmp(arg, "-scr_area")) { + int tn; + CHECK_ARGC + tn = atoi(argv[++i]); + if (tn >= 0) { + scrollcopyrect_min_area = tn; + } } else if (!strcmp(arg, "-pointer_mode") || !strcmp(arg, "-pm")) { char *p, *s; @@ -23219,6 +25793,8 @@ int main(int argc, char* argv[]) { } else if (!strcmp(arg, "-sb")) { CHECK_ARGC screen_blank = atoi(argv[++i]); + } else if (!strcmp(arg, "-xdamage")) { + use_xdamage = 1; } else if (!strcmp(arg, "-noxdamage")) { use_xdamage = 0; } else if (!strcmp(arg, "-xd_area")) { @@ -23280,7 +25856,7 @@ int main(int argc, char* argv[]) { } } } else if (!strcmp(arg, "-remote") || !strcmp(arg, "-R") - || !strcmp(arg, "-r")) { + || !strcmp(arg, "-r") || !strcmp(arg, "-remote-control")) { CHECK_ARGC i++; if (!strcmp(argv[i], "ping")) { @@ -23299,6 +25875,8 @@ int main(int argc, char* argv[]) { remote_sync = 1; } else if (!strcmp(arg, "-noremote")) { accept_remote_cmds = 0; + } else if (!strcmp(arg, "-yesremote")) { + accept_remote_cmds = 1; } else if (!strcmp(arg, "-unsafe")) { safe_remote_only = 0; } else if (!strcmp(arg, "-privremote")) { @@ -23562,6 +26140,9 @@ int main(int argc, char* argv[]) { if (! wireframe_copyrect) { set_wirecopyrect_mode(NULL); } + if (! scroll_copyrect) { + set_scrollcopyrect_mode(NULL); + } /* increase rfbwait if threaded */ if (use_threads && ! got_rfbwait) { @@ -23852,6 +26433,14 @@ int main(int argc, char* argv[]) { exit(rc); } + if (priv_remote) { + if (! remote_control_access_ok()) { + rfbLog("** Disabling remote commands in -privremote " + "mode.\n"); + accept_remote_cmds = 0; + } + } + #if LIBVNCSERVER_HAVE_LIBXFIXES if (! XFixesQueryExtension(dpy, &xfixes_base_event_type, &er)) { if (! quiet) { @@ -24016,7 +26605,16 @@ int main(int argc, char* argv[]) { * input is not processed) we tell the server to process our * requests during all grabs: */ - disable_grabserver(); + disable_grabserver(dpy); + + /* check for RECORD */ + if (! XRecordQueryVersion_wr(dpy, &maj, &min)) { + xrecord_present = 0; + } else { + xrecord_present = 1; + } + + initialize_xrecord(); /* check for OS with small shm limits */ if (using_shm && ! single_copytile) { @@ -24175,3 +26773,4 @@ int main(int argc, char* argv[]) { #undef argv } + |