summaryrefslogtreecommitdiffstats
path: root/x11vnc/x11vnc.c
diff options
context:
space:
mode:
authorrunge <runge>2005-05-14 04:01:37 +0000
committerrunge <runge>2005-05-14 04:01:37 +0000
commit3a7e70de49823259558694a3f92401a9f966d9da (patch)
tree66e006d617f50b3c6eddffe228ad87be34ba92d8 /x11vnc/x11vnc.c
parent34f714bcdc3e2d7a21a9179357dc93f2561301c8 (diff)
downloadlibtdevnc-3a7e70de49823259558694a3f92401a9f966d9da.tar.gz
libtdevnc-3a7e70de49823259558694a3f92401a9f966d9da.zip
x11vnc: more improvements to -scrollcopyrect and -xkb modes.
Diffstat (limited to 'x11vnc/x11vnc.c')
-rw-r--r--x11vnc/x11vnc.c5339
1 files changed, 4206 insertions, 1133 deletions
diff --git a/x11vnc/x11vnc.c b/x11vnc/x11vnc.c
index 80eef99..c991dda 100644
--- a/x11vnc/x11vnc.c
+++ b/x11vnc/x11vnc.c
@@ -127,11 +127,6 @@
/* -- x11vnc.h -- */
-/* TODO: autoconf */
-#if 1
-#define LIBVNCSERVER_HAVE_RECORD 1
-#endif
-
/****************************************************************************/
/* Standard includes and libvncserver */
@@ -164,6 +159,7 @@
* -DSHARED to have the vnc display shared by default.
* -DFOREVER to have -forever on by default.
* -DNOREPEAT=0 to have -repeat on by default.
+ * -DADDKEYSYMS=0 to have -noadd_keysyms the default.
*
* -DREMOTE_DEFAULT=0 to disable remote-control on by default (-yesremote).
* -DREMOTE_CONTROL=0 to disable remote-control mechanism completely.
@@ -177,7 +173,7 @@
* -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.
+ * -DPOINTER_MODE_DEFAULT={0,1,2,3,4} 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.
@@ -367,11 +363,11 @@ Damage xdamage = 0;
int xdamage_base_event_type = 0;
int xdamage_max_area = 20000; /* pixels */
double xdamage_memory = 1.0; /* in units of NSCAN */
-int xdamage_tile_count;
+int xdamage_tile_count, xdamage_direct_count;
/* date +'lastmod: %Y-%m-%d' */
-char lastmod[] = "0.7.2 lastmod: 2005-05-02";
+char lastmod[] = "0.7.2 lastmod: 2005-05-14";
int hack_val = 0;
/* X display info */
@@ -480,7 +476,6 @@ int scaling_cursor_blend = 1;
int scaling_cursor_interpolate = 0;
int scale_cursor_numer = 0, scale_cursor_denom = 0;
-
/* size of the basic tile unit that is polled for changes: */
int tile_x = 32;
int tile_y = 32;
@@ -491,8 +486,11 @@ unsigned char *tile_has_diff, *tile_tried, *tile_copied;
unsigned char *tile_has_xdamage_diff, *tile_row_has_xdamage_diff;
/* times of recent events */
-time_t last_event, last_input, last_client = 0;
+time_t last_event, last_input = 0, last_client = 0;
+time_t last_keyboard_input = 0, last_pointer_input = 0;
+double last_keyboard_time = 0.0;
double servertime_diff = 0.0;
+double x11vnc_start = 0.0;
/* last client to move pointer */
rfbClientPtr last_pointer_client = NULL;
@@ -508,7 +506,7 @@ int got_user_input = 0;
int got_pointer_input = 0;
int got_keyboard_input = 0;
int urgent_update = 0;
-int last_keyboard_input = 0;
+int last_keyboard_keycode = 0;
rfbKeySym last_keysym = 0;
int fb_copy_in_progress = 0;
int drag_in_progress = 0;
@@ -517,6 +515,7 @@ int do_copy_screen = 0;
time_t damage_time = 0;
int damage_delay = 0;
+int program_pid = 0;
char *program_name = NULL;
char *program_cmdline = NULL;
@@ -542,7 +541,8 @@ int all_clients_initialized(void);
void close_all_clients(void);
void close_clients(char *);
int get_autorepeat_state(void);
-void autorepeat(int restore);
+int get_initial_autorepeat_state(void);
+void autorepeat(int restore, int quiet);
char *bitprint(unsigned int, int);
void blackout_tiles(void);
void solid_bg(int);
@@ -559,10 +559,13 @@ void install_fake_fb(int, int, int);
void remove_fake_fb(void);
int add_keysym(KeySym);
-void delete_keycode(KeyCode);
-void delete_added_keycodes(void);
+void delete_keycode(KeyCode, int);
+void delete_added_keycodes(int);
+int count_added_keycodes(void);
double dtime(double *);
+double dtime0(double *);
+double dnow(void);
void initialize_blackouts(char *);
void initialize_blackouts_and_xinerama(void);
@@ -584,7 +587,7 @@ void initialize_watch_bell(void);
void initialize_xinerama(void);
void initialize_xfixes(void);
void initialize_xdamage(void);
-int valid_window(Window, XWindowAttributes *);
+int valid_window(Window, XWindowAttributes *, int);
void create_xdamage_if_needed(void);
void destroy_xdamage_if_needed(void);
void initialize_xrandr(void);
@@ -620,20 +623,27 @@ void pointer(int mask, int x, int y, rfbClientPtr client);
void pipe_pointer(int mask, int x, int y, rfbClientPtr client);
int check_pipeinput(void);
void cursor_position(int, int);
+void do_button_mask_change(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 initialize_scroll_matches(void);
+void initialize_scroll_keys(void);
+int try_copyrect(Window, int, int, int, int, int, int, int *, sraRegionPtr,
+ double);
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 get_wm_frame_pos(int *, int *, int *, int *, int *, int *,
+ Window *, Window *);
+Window descend_pointer(int, Window, char *, int);
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);
+void fb_push_wait(double);
char *process_remote_cmd(char *, int);
void rfbPE(long);
void rfbCFD(long);
@@ -656,6 +666,7 @@ int get_xfixes_cursor(int);
void disable_cursor_shape_updates(rfbScreenInfoPtr);
void restore_cursor_shape_updates(rfbScreenInfoPtr);
int new_fb_size_clients(rfbScreenInfoPtr);
+void get_client_regions(int *, int *, int *, int *);
void shm_clean(XShmSegmentInfo *, XImage *);
void shm_delete(XShmSegmentInfo *);
@@ -668,6 +679,10 @@ void set_vnc_desktop_name(void);
char *short_kmb(char *);
+char **create_str_list(char *);
+int match_str_list(char *, char **);
+
+int link_rate(int *, int *);
int get_cmp_rate(void);
int get_raw_rate(void);
int get_read_rate(void);
@@ -715,6 +730,8 @@ int use_solid_bg = 0; /* -solid */
char *solid_str = NULL;
char *solid_default = "cyan4";
+#define LATENCY0 20 /* 20ms */
+#define NETRATE0 20 /* 20KB/sec */
char *speeds_str = NULL; /* -speeds TBD */
int measure_speeds = 1;
int speeds_net_rate = 0;
@@ -723,6 +740,14 @@ int speeds_net_latency = 0;
int speeds_net_latency_measured = 0;
int speeds_read_rate = 0;
int speeds_read_rate_measured = 0;
+enum {
+ LR_UNSET = 0,
+ LR_UNKNOWN,
+ LR_DIALUP,
+ LR_BROADBAND,
+ LR_LAN
+};
+
char *rc_rcfile = NULL; /* -rc */
int rc_norc = 0;
@@ -790,7 +815,7 @@ 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 debug_tiles = 0;
int xtrap_input = 0; /* -xtrap for user input insertion */
int xinerama = 0; /* -xinerama */
@@ -840,12 +865,30 @@ char *wireframe_copyrect_default = "always";
char *wireframe_copyrect_default = "never";
#endif
int wireframe_in_progress = 0;
-Window *stack_list = NULL;
-int stack_num = 0;
+
+typedef struct winattr {
+ Window win;
+ int fetched;
+ int valid;
+ int x, y;
+ int width, height;
+ int depth;
+ int class;
+ int backing_store;
+ int map_state;
+ int rx, ry;
+ double time;
+} winattr_t;
+winattr_t *stack_list = NULL;
+int stack_list_len = 0;
+int stack_list_num = 0;
+winattr_t *stack_clip = NULL;
+int stack_clip_len = 0;
+int stack_clip_num = 0;
/* 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"
+#define SCROLL_COPYRECT_PARMS "0+64+32+32,0.02+0.10+0.9,0.03+0.06+0.5+0.1+5.0"
#endif
char *scroll_copyrect_str = NULL;
#ifndef SCROLL_COPYRECT
@@ -861,8 +904,43 @@ char *scroll_copyrect_default = "keys";
#else
char *scroll_copyrect_default = "never";
#endif
+char *scroll_key_list_str = NULL;
+KeySym *scroll_key_list = NULL;
+int pointer_queued_sent = 0;
+
int scrollcopyrect_min_area = 60000; /* minimum rectangle area */
int debug_scroll = 0;
+double pointer_flush_delay = 0.0;
+double last_scroll_event = 0.0;
+int max_scroll_keyrate = 0;
+enum scroll_types {
+ SCR_NONE = 0,
+ SCR_MOUSE,
+ SCR_KEY,
+ SCR_FAIL,
+ SCR_SUCCESS
+};
+int last_scroll_type = SCR_NONE;
+
+char **scroll_good_all = NULL;
+char **scroll_good_key = NULL;
+char **scroll_good_mouse = NULL;
+char *scroll_good_str = NULL;
+char *scroll_good_str0 =
+/* "##Firefox-bin," */
+/* "##Gnome-terminal," */
+/* "##XTerm", */
+ "##Nomatch"
+;
+
+char **scroll_skip_all = NULL;
+char **scroll_skip_key = NULL;
+char **scroll_skip_mouse = NULL;
+char *scroll_skip_str = NULL;
+char *scroll_skip_str0 =
+/* "##Konsole," * no problems, known heuristics do not work */
+ "##Soffice.bin" /* big problems, no clips, scrolls outside area */
+;
#ifndef NOREPEAT
#define NOREPEAT 1
@@ -873,8 +951,15 @@ int watch_bell = 1; /* watch for the bell using XKEYBOARD */
int sound_bell = 1; /* actually send it */
int xkbcompat = 0; /* ignore XKEYBOARD extension */
int use_xkb_modtweak = 0; /* -xkb */
+#ifndef SKIPDUPS
+#define SKIPDUPS 1
+#endif
+int skip_duplicate_key_events = SKIPDUPS;
char *skip_keycodes = NULL;
-int add_keysyms = 0; /* automatically add keysyms to X server */
+#ifndef ADDKEYSYMS
+#define ADDKEYSYMS 1
+#endif
+int add_keysyms = ADDKEYSYMS; /* automatically add keysyms to X server */
char *remap_file = NULL; /* -remap */
char *pointer_remap = NULL;
@@ -884,7 +969,7 @@ char *pointer_remap = NULL;
#endif
#define POINTER_MODE_NOFB 2
int pointer_mode = POINTER_MODE_DEFAULT;
-int pointer_mode_max = 5;
+int pointer_mode_max = 4;
int single_copytile = 0; /* use the old way copy_tiles() */
int single_copytile_orig = 0;
int single_copytile_count = 0;
@@ -948,6 +1033,8 @@ int got_pointer_mode = -1;
int got_noviewonly = 0;
int got_wirecopyrect = 0;
int got_scrollcopyrect = 0;
+int got_noxkb = 0;
+int got_nomodtweak = 0;
/* threaded vs. non-threaded (default) */
#if LIBVNCSERVER_X11VNC_THREADED && ! defined(X11VNC_THREADED)
@@ -2494,9 +2581,9 @@ void XTestFakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down,
down ? "down":"up");
}
if (down) {
- last_keyboard_input = -key;
+ last_keyboard_keycode = -key;
} else {
- last_keyboard_input = key;
+ last_keyboard_keycode = key;
}
if (xtrap_input) {
@@ -2507,6 +2594,10 @@ void XTestFakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down,
DEBUG_SKIPPED_INPUT(debug_keyboard, "keyboard: no-XTEST");
return;
}
+ if (debug_keyboard) {
+ rfbLog("calling XTestFakeKeyEvent(%d, %d) %.4f\n",
+ key, down, dnow() - x11vnc_start);
+ }
#if LIBVNCSERVER_HAVE_XTEST
XTestFakeKeyEvent(dpy, key, down, delay);
#endif
@@ -2538,6 +2629,10 @@ void XTestFakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press,
DEBUG_SKIPPED_INPUT(debug_keyboard, "button: no-XTEST");
return;
}
+ if (debug_pointer) {
+ rfbLog("calling XTestFakeButtonEvent(%d, %d) %.4f\n",
+ button, is_press, dnow() - x11vnc_start);
+ }
#if LIBVNCSERVER_HAVE_XTEST
XTestFakeButtonEvent(dpy, button, is_press, delay);
#endif
@@ -2564,6 +2659,10 @@ void XTestFakeMotionEvent_wr(Display* dpy, int screen, int x, int y,
return XTRAP_FakeMotionEvent_wr(dpy, screen, x, y, delay);
}
+ if (debug_pointer) {
+ rfbLog("calling XTestFakeMotionEvent(%d, %d) %.4f\n",
+ x, y, dnow() - x11vnc_start);
+ }
#if LIBVNCSERVER_HAVE_XTEST
XTestFakeMotionEvent(dpy, screen, x, y, delay);
#endif
@@ -2666,20 +2765,20 @@ int XTRAP_GrabControl_wr(Display *dpy, Bool impervious) {
return 0;
}
-void disable_grabserver(Display *dpy) {
+void disable_grabserver(Display *in_dpy) {
int ok = 0;
static int didmsg = 0;
if (! xtrap_input) {
- if (XTestGrabControl_wr(dpy, True)) {
- XTRAP_GrabControl_wr(dpy, False);
+ if (XTestGrabControl_wr(in_dpy, True)) {
+ XTRAP_GrabControl_wr(in_dpy, False);
if (! didmsg) {
rfbLog("GrabServer control via XTEST.\n");
didmsg = 1;
}
ok = 1;
} else {
- if (XTRAP_GrabControl_wr(dpy, True)) {
+ if (XTRAP_GrabControl_wr(in_dpy, True)) {
ok = 1;
if (! didmsg) {
rfbLog("Using DEC-XTRAP for protection"
@@ -2689,15 +2788,15 @@ void disable_grabserver(Display *dpy) {
}
}
} else {
- if (XTRAP_GrabControl_wr(dpy, True)) {
- XTestGrabControl_wr(dpy, False);
+ if (XTRAP_GrabControl_wr(in_dpy, True)) {
+ XTestGrabControl_wr(in_dpy, False);
if (! didmsg) {
rfbLog("GrabServer control via DEC-XTRAP.\n");
didmsg = 1;
}
ok = 1;
} else {
- if (XTestGrabControl_wr(dpy, True)) {
+ if (XTestGrabControl_wr(in_dpy, True)) {
ok = 1;
if (! didmsg) {
rfbLog("DEC-XTRAP XGrabServer "
@@ -2735,6 +2834,7 @@ int xrecord_set_by_keys = 0;
int xrecord_set_by_mouse = 0;
Window xrecord_focus_window = None;
Window xrecord_wm_window = None;
+Window xrecord_ptr_window = None;
void initialize_xrecord(void) {
use_xrecord = 0;
@@ -2771,8 +2871,39 @@ void initialize_xrecord(void) {
int xrecord_skip_keysym(rfbKeySym keysym) {
KeySym sym = (KeySym) keysym;
+ int ok = -1, matched = 0;
- if (IsModifierKey(sym)) {
+ if (scroll_key_list) {
+ int k, exclude = 0;
+ if (scroll_key_list[0]) {
+ exclude = 1;
+ }
+ k = 1;
+ while (scroll_key_list[k] != NoSymbol) {
+ if (scroll_key_list[k++] == sym) {
+ matched = 1;
+ break;
+ }
+ }
+ if (exclude) {
+ if (matched) {
+ return 1;
+ } else {
+ ok = 1;
+ }
+ } else {
+ if (matched) {
+ ok = 1;
+ } else {
+ ok = 0;
+ }
+ }
+ }
+ if (ok == 1) {
+ return 0;
+ } else if (ok == 0) {
+ return 1;
+ } else if (IsModifierKey(sym)) {
return 1;
}
return 0;
@@ -2782,6 +2913,29 @@ int xrecord_skip_button(int new, int old) {
return 0;
}
+int xrecord_vi_scroll_keysym(rfbKeySym keysym) {
+ KeySym sym = (KeySym) keysym;
+ if (sym == XK_J || sym == XK_j || sym == XK_K || sym == XK_k) {
+ return 1; /* vi */
+ }
+ if (sym == XK_D || sym == XK_d || sym == XK_U || sym == XK_u) {
+ return 1; /* Ctrl-d/u */
+ }
+ if (sym == XK_Z || sym == XK_z) {
+ return 1; /* zz, zt, zb .. */
+ }
+ return 0;
+}
+
+int xrecord_emacs_scroll_keysym(rfbKeySym keysym) {
+ KeySym sym = (KeySym) keysym;
+ if (sym == XK_N || sym == XK_n || sym == XK_P || sym == XK_p) {
+ return 1; /* emacs */
+ }
+ /* Must be some more ... */
+ return 0;
+}
+
int xrecord_scroll_keysym(rfbKeySym keysym) {
KeySym sym = (KeySym) keysym;
/* X11/keysymdef.h */
@@ -2796,19 +2950,88 @@ int xrecord_scroll_keysym(rfbKeySym keysym) {
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 (xrecord_vi_scroll_keysym(keysym)) {
+ return 1;
}
- if (sym == XK_N || sym == XK_n || sym == XK_P || sym == XK_p) {
- return 1; /* emacs */
+ if (xrecord_emacs_scroll_keysym(keysym)) {
+ return 1;
}
return 0;
}
+#define SCR_ATTR_CACHE 8
+winattr_t scr_attr_cache[SCR_ATTR_CACHE];
+double attr_cache_max_age = 1.5;
+
+int lookup_attr_cache(Window win, int *cache_index, int *next_index) {
+ double dnow, t, oldest;
+ int i, old_index = -1, count = 0;
+ Window cwin;
+
+ *cache_index = -1;
+ *next_index = -1;
+
+ if (win == None) {
+ return 0;
+ }
+ if (attr_cache_max_age == 0.0) {
+ return 0;
+ }
+
+ dtime0(&dnow);
+ for (i=0; i < SCR_ATTR_CACHE; i++) {
+
+ cwin = scr_attr_cache[i].win;
+ t = scr_attr_cache[i].time;
+
+ if (dnow > t + attr_cache_max_age) {
+ /* expire it even if it is the one we want */
+ scr_attr_cache[i].win = cwin = None;
+ scr_attr_cache[i].fetched = 0;
+ scr_attr_cache[i].valid = 0;
+ }
+
+ if (*next_index == -1 && cwin == None) {
+ *next_index = i;
+ }
+ if (*next_index == -1) {
+ /* record oldest */
+ if (old_index == -1 || t < oldest) {
+ oldest = t;
+ old_index = i;
+ }
+ }
+ if (cwin != None) {
+ count++;
+ }
+ if (cwin == win) {
+ if (*cache_index == -1) {
+ *cache_index = i;
+ } else {
+ /* remove dups */
+ scr_attr_cache[i].win = None;
+ scr_attr_cache[i].fetched = 0;
+ scr_attr_cache[i].valid = 0;
+ }
+ }
+ }
+ if (*next_index == -1) {
+ *next_index = old_index;
+ }
+
+if (0) fprintf(stderr, "lookup_attr_cache count: %d\n", count);
+ if (*cache_index != -1) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
typedef struct scroll_event {
Window win, frame;
int dx, dy;
- int x, y, w, h, t;
+ int x, y, w, h;
+ double t;
int win_x, win_y, win_w, win_h;
int new_x, new_y, new_w, new_h;
} scroll_event_t;
@@ -2829,8 +3052,7 @@ void record_CA(XPointer ptr, XRecordInterceptData *rec_data) {
int good = 1, dx, dy, k=0, i;
unsigned int w, h;
int dba = 0, db = debug_scroll;
-
-//dba = 1;
+ int cache_index, next_index, valid;
if (dba || db) {
if (rec_data->category == XRecordFromClient) {
@@ -2842,7 +3064,7 @@ void record_CA(XPointer ptr, XRecordInterceptData *rec_data) {
}
}
-if (dba || db) fprintf(stderr, "record_CA-%d id_base: 0x%lx ptr: 0x%lx "
+if (dba || db > 1) 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);
@@ -2850,29 +3072,29 @@ if (dba || db) fprintf(stderr, "record_CA-%d id_base: 0x%lx ptr: 0x%lx "
if (! xrecording) {
return;
}
-if (db) fprintf(stderr, "record_CA-%d\n", k++);
+if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
if (rec_data->id_base == 0) {
return;
}
-if (db) fprintf(stderr, "record_CA-%d\n", k++);
+if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
if ((XID) ptr != xrecord_seq) {
return;
}
-if (db) fprintf(stderr, "record_CA-%d\n", k++);
+if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
if (rec_data->category != XRecordFromClient) {
return;
}
-if (db) fprintf(stderr, "record_CA-%d\n", k++);
+if (db > 1) 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++);
+if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
/*
@@ -2913,6 +3135,8 @@ short period of time with a painting error: two cursors, one above the other.
good = 0;
} else if (src != dst) {
good = 0;
+ } else if (scr_ev_cnt >= SCR_EV_MAX) {
+ good = 0;
}
dx = dst_x - src_x;
@@ -2925,26 +3149,71 @@ short period of time with a painting error: two cursors, one above the other.
if (! good) {
return;
}
-if (db) fprintf(stderr, "record_CA-%d\n", k++);
+if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
- if (! valid_window(src, &attr)) {
+ /*
+ * after all of the above succeeds, now contact X server.
+ * we try to get away with some caching here.
+ */
+ if (lookup_attr_cache(src, &cache_index, &next_index)) {
+ i = cache_index;
+ attr.x = scr_attr_cache[i].x;
+ attr.y = scr_attr_cache[i].y;
+ attr.width = scr_attr_cache[i].width;
+ attr.height = scr_attr_cache[i].height;
+ attr.map_state = scr_attr_cache[i].map_state;
+ rx = scr_attr_cache[i].rx;
+ ry = scr_attr_cache[i].ry;
+ valid = scr_attr_cache[i].valid;
+
+ } else {
+ valid = valid_window(src, &attr, 1);
+
+ if (valid) {
+ XTranslateCoordinates(dpy, src, rootwin, 0, 0,
+ &rx, &ry, &c);
+ }
+ if (next_index >= 0) {
+ i = next_index;
+ scr_attr_cache[i].win = src;
+ scr_attr_cache[i].fetched = 1;
+ scr_attr_cache[i].valid = valid;
+ scr_attr_cache[i].time = dnow();
+ if (valid) {
+ scr_attr_cache[i].x = attr.x;
+ scr_attr_cache[i].y = attr.y;
+ scr_attr_cache[i].width = attr.width;
+ scr_attr_cache[i].height = attr.height;
+ scr_attr_cache[i].depth = attr.depth;
+ scr_attr_cache[i].class = attr.class;
+ scr_attr_cache[i].backing_store =
+ attr.backing_store;
+ scr_attr_cache[i].map_state = attr.map_state;
+
+ scr_attr_cache[i].rx = rx;
+ scr_attr_cache[i].ry = ry;
+ }
+ }
+ }
+
+ if (! valid) {
return;
}
-if (db) fprintf(stderr, "record_CA-%d\n", k++);
+if (db > 1) 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;
- }
+if (dba || db) {
+ double st, dt;
+ st = (double) rec_data->server_time/1000.0;
+ dt = (dnow() - servertime_diff) - st;
+ fprintf(stderr, "record_CA-%d *FOUND_SCROLL: src: 0x%lx dx: %d dy: %d "
+ "x: %d y: %d w: %d h: %d st: %.4f %.4f %.4f\n", k++, src, dx, dy,
+ src_x, src_y, w, h, st, dt, dnow() - x11vnc_start);
+}
i = scr_ev_cnt;
@@ -2956,7 +3225,7 @@ if (dba || db) fprintf(stderr, "record_CA-%d *FOUND: src: 0x%lx dx: %d dy: %d "
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].t = ((double) rec_data->server_time)/1000.0;
scr_ev[i].win_x = rx;
scr_ev[i].win_y = ry;
scr_ev[i].win_w = attr.width;
@@ -3019,11 +3288,9 @@ void record_CW(XPointer ptr, XRecordInterceptData *rec_data) {
unsigned tmask;
char *data;
int dba = 0, db = debug_scroll;
+ int cache_index, next_index, valid;
-//dba = 1;
-//db = 1;
-
- if (1 || db) {
+ if (db) {
if (rec_data->category == XRecordFromClient) {
req = (xConfigureWindowReq *) rec_data->data;
if (req->reqType == X_ConfigureWindow) {
@@ -3032,7 +3299,7 @@ void record_CW(XPointer ptr, XRecordInterceptData *rec_data) {
}
}
-if (dba || db) fprintf(stderr, "record_CW-%d id_base: 0x%lx ptr: 0x%lx "
+if (dba || db > 1) 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);
@@ -3041,40 +3308,40 @@ if (dba || db) fprintf(stderr, "record_CW-%d id_base: 0x%lx ptr: 0x%lx "
if (! xrecording) {
return;
}
-if (db) fprintf(stderr, "record_CW-%d\n", k++);
+if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
if ((XID) ptr != xrecord_seq) {
return;
}
-if (db) fprintf(stderr, "record_CW-%d\n", k++);
+if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
if (rec_data->id_base == 0) {
return;
}
-if (db) fprintf(stderr, "record_CW-%d\n", k++);
+if (db > 1) 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 (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
if (rec_data->category != XRecordFromClient) {
return;
}
-if (db) fprintf(stderr, "record_CW-%d\n", k++);
+if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
if (rec_data->client_swapped) {
return;
}
-if (db) fprintf(stderr, "record_CW-%d\n", k++);
+if (db > 1) 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++);
+if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
tmask = req->mask;
@@ -3087,7 +3354,7 @@ if (db) fprintf(stderr, "record_CW-%d\n", k++);
/* require no more than these 4 flags */
return;
}
-if (db) fprintf(stderr, "record_CW-%d\n", k++);
+if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
f_x = req->mask & CWX;
f_y = req->mask & CWY;
@@ -3101,12 +3368,12 @@ if (db) fprintf(stderr, "record_CW-%d\n", k++);
return;
}
}
-if (db) fprintf(stderr, "record_CW-%d\n", k++);
+if (db > 1) 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++);
+if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
for (i=0; i<4; i++) {
vals[i] = 0;
@@ -3134,8 +3401,8 @@ if (db) fprintf(stderr, "record_CW-%d\n", k++);
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;
}
+ index = 2;
}
if (! f_x && ! f_y) {
@@ -3191,7 +3458,7 @@ if (dba || db) fprintf(stderr, " record_CW ind: %d win: 0x%lx x: %d y: %d w: %d
if (! good) {
return;
}
-if (db) fprintf(stderr, "record_CW-%d\n", k++);
+if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
match = 0;
for (j=index - 1; j >= 0; j--) {
@@ -3206,7 +3473,7 @@ if (db) fprintf(stderr, "record_CW-%d\n", k++);
if (match != 3) {
return;
}
-if (db) fprintf(stderr, "record_CW-%d\n", k++);
+if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
/*
@@ -3332,7 +3599,7 @@ if (dba) fprintf(stderr, "%d/%d/%d/%d %d/%d/%d/%d %d/%d/%d/%d\n", x0, y0, w0,
if (! good) {
return;
}
-if (db) fprintf(stderr, "record_CW-%d\n", k++);
+if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
if (dy > 0) {
h -= dy;
@@ -3360,33 +3627,84 @@ if (db) fprintf(stderr, "record_CW-%d\n", k++);
good = 0;
} else if (h0 - h2 != nabs(dy)) {
good = 0;
+ } else if (scr_ev_cnt >= SCR_EV_MAX) {
+ good = 0;
}
if (! good) {
return;
}
-if (db) fprintf(stderr, "record_CW-%d\n", k++);
+if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
- /* geometry OK, now check with the X server: */
- if (! valid_window(win, &attr)) {
- return;
+ /*
+ * geometry OK.
+ * after all of the above succeeds, now contact X server.
+ */
+ if (lookup_attr_cache(win, &cache_index, &next_index)) {
+ i = cache_index;
+ attr.x = scr_attr_cache[i].x;
+ attr.y = scr_attr_cache[i].y;
+ attr.width = scr_attr_cache[i].width;
+ attr.height = scr_attr_cache[i].height;
+ attr.map_state = scr_attr_cache[i].map_state;
+ rx = scr_attr_cache[i].rx;
+ ry = scr_attr_cache[i].ry;
+ valid = scr_attr_cache[i].valid;
+
+if (0) fprintf(stderr, "lookup_attr_cache hit: %2d %2d 0x%lx %d\n",
+ cache_index, next_index, win, valid);
+
+ } else {
+ valid = valid_window(win, &attr, 1);
+
+if (0) fprintf(stderr, "lookup_attr_cache MISS: %2d %2d 0x%lx %d\n",
+ cache_index, next_index, win, valid);
+
+ if (valid) {
+ XTranslateCoordinates(dpy, win, rootwin, 0, 0,
+ &rx, &ry, &c);
+ }
+ if (next_index >= 0) {
+ i = next_index;
+ scr_attr_cache[i].win = win;
+ scr_attr_cache[i].fetched = 1;
+ scr_attr_cache[i].valid = valid;
+ scr_attr_cache[i].time = dnow();
+ if (valid) {
+ scr_attr_cache[i].x = attr.x;
+ scr_attr_cache[i].y = attr.y;
+ scr_attr_cache[i].width = attr.width;
+ scr_attr_cache[i].height = attr.height;
+ scr_attr_cache[i].depth = attr.depth;
+ scr_attr_cache[i].class = attr.class;
+ scr_attr_cache[i].backing_store =
+ attr.backing_store;
+ scr_attr_cache[i].map_state = attr.map_state;
+
+ scr_attr_cache[i].rx = rx;
+ scr_attr_cache[i].ry = ry;
+ }
+ }
}
-if (db) fprintf(stderr, "record_CW-%d\n", k++);
- if (attr.map_state != IsViewable) {
+ if (! valid) {
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 (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
- if (scr_ev_cnt >= SCR_EV_MAX) {
+ if (attr.map_state != IsViewable) {
return;
}
+if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
+
+if (dba || db) {
+ double st, dt;
+ st = (double) rec_data->server_time/1000.0;
+ dt = (dnow() - servertime_diff) - st;
+ fprintf(stderr, "record_CW-%d *FOUND_SCROLL: win: 0x%lx dx: %d dy: %d "
+ "x: %d y: %d w: %d h: %d st: %.4f dt: %.4f %.4f\n", k++, win,
+ dx, dy, src_x, src_y, w, h, st, dt, dnow() - x11vnc_start);
+}
i = scr_ev_cnt;
@@ -3398,7 +3716,7 @@ if (dba || db) fprintf(stderr, "record_CW-%d *FOUND: win: 0x%lx dx: %d dy: %d "
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].t = ((double) rec_data->server_time)/1000.0;
scr_ev[i].win_x = rx;
scr_ev[i].win_y = ry;
scr_ev[i].win_w = attr.width;
@@ -3441,9 +3759,20 @@ if (dba || db) fprintf(stderr, "record_CW-%d *FOUND: win: 0x%lx dx: %d dy: %d "
}
void record_switch(XPointer ptr, XRecordInterceptData *rec_data) {
+ static int first = 1;
xReq *req;
-//fprintf(stderr, "XRecordFreeData-0: %p\n", rec_data);
+ if (first) {
+ int i;
+ for (i=0; i<SCR_ATTR_CACHE; i++) {
+ scr_attr_cache[i].win = None;
+ scr_attr_cache[i].fetched = 0;
+ scr_attr_cache[i].valid = 0;
+ scr_attr_cache[i].time = 0.0;
+ }
+ first = 0;
+ }
+
/* should handle control msgs, start/stop/etc */
if (rec_data->category == XRecordStartOfData) {
record_CW(ptr, rec_data);
@@ -3458,7 +3787,6 @@ void record_switch(XPointer ptr, XRecordInterceptData *rec_data) {
}
if (rec_data->category != XRecordFromClient) {
-//fprintf(stderr, "XRecordFreeData-1: %p\n", rec_data);
XRecordFreeData(rec_data);
return;
}
@@ -3472,54 +3800,160 @@ void record_switch(XPointer ptr, XRecordInterceptData *rec_data) {
} else {
;
}
-//fprintf(stderr, "XRecordFreeData-2: %p\n", rec_data);
XRecordFreeData(rec_data);
}
#endif
-void xrecord_watch(int start) {
+#if LIBVNCSERVER_HAVE_RECORD
+void shutdown_record_context(XRecordContext rc, int bequiet, int reopen) {
+ int ret1, ret2;
+
+ ret1 = XRecordDisableContext(rdpy_ctrl, rc);
+ if (!ret1 && !bequiet && !quiet) {
+ rfbLog("XRecordDisableContext(0x%lx) failed.\n", rc);
+ }
+ ret2 = XRecordFreeContext(rdpy_ctrl, rc_scroll);
+ if (!ret2 && ! bequiet && !quiet) {
+ rfbLog("XRecordFreeContext(0x%lx) failed.\n", rc);
+ }
+ XFlush(rdpy_ctrl);
+
+ if (reopen == 2 && ret1 && ret2) {
+ reopen = 0; /* 2 means reopen only on failure */
+ }
+ if (reopen) {
+ char *dpystr = DisplayString(dpy);
+
+ XCloseDisplay(rdpy_data);
+ rdpy_data = XOpenDisplay(dpystr);
+
+ if (! rdpy_data) {
+ rfbLog("Failed to reopen RECORD data connection:"
+ "%s\n", dpystr);
+ rfbLog(" disabling RECORD scroll detection.\n");
+ use_xrecord = 0;
+ return;
+ }
+
+ XCloseDisplay(rdpy_ctrl);
+ rdpy_ctrl = XOpenDisplay(dpystr);
+
+ if (! rdpy_ctrl) {
+ rfbLog("Failed to reopen RECORD control connection:"
+ "%s\n", dpystr);
+ rfbLog(" disabling RECORD scroll detection.\n");
+ use_xrecord = 0;
+ return;
+ }
+ if (! bequiet && reopen == 2) {
+ rfbLog("reopened RECORD data and control display"
+ " connections: %s\n", dpystr);
+ }
+ disable_grabserver(rdpy_ctrl);
+ disable_grabserver(rdpy_data);
+ }
+}
+#endif
+
+char *xerror_string(XErrorEvent *error);
+int trap_record_xerror(Display *, XErrorEvent *);
+int trapped_record_xerror;
+XErrorEvent *trapped_record_xerror_event;
+
+#define RECORD_ERROR_MSG \
+ if (! quiet) { \
+ rfbLog("trapped RECORD XError: %s %d/%d/%d (0x%lx)\n", \
+ xerror_string(trapped_record_xerror_event), \
+ (int) trapped_record_xerror_event->error_code, \
+ (int) trapped_record_xerror_event->request_code, \
+ (int) trapped_record_xerror_event->minor_code, \
+ (int) trapped_record_xerror_event->resourceid); \
+ }
+
+void xrecord_watch(int start, int setby) {
Window focus, wm, r, c, clast;
+ static double create_time = 0.0;
+ double dnow;
+ static double last_error = 0.0;
int rx, ry, wx, wy;
- int depth = 0, i;
unsigned int m;
- int db = debug_scroll;
+ int rc, db = debug_scroll;
int do_shutdown = 0;
+ int reopen_dpys = 1;
+ char name_info[2048];
+ XErrorHandler old_handler = NULL;
+ static Window last_win = None, last_result = None;
-//db = 1;
+ dtime0(&dnow);
+ if (dnow < last_error + 0.5) {
+ return;
+ }
#if LIBVNCSERVER_HAVE_RECORD
if (! start) {
+ int shut_reopen = 2;
+if (db) fprintf(stderr, "XRECORD OFF: %d/%d %.4f\n", xrecording, setby, dnow - x11vnc_start);
xrecording = 0;
if (! rc_scroll) {
xrecord_focus_window = None;
xrecord_wm_window = None;
+ xrecord_ptr_window = None;
rcs_scroll = 0;
return;
}
+
+ if (! do_shutdown && dnow > create_time + 45) {
+ /* XXX unstable if we keep a RECORD going forever */
+ do_shutdown = 1;
+ shut_reopen = 1;
+ }
+
if (do_shutdown) {
-if (db) fprintf(stderr, "=== shutdown-scroll 0x%lx\n", rc_scroll);
+if (db > 1) 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");
- }
+ trapped_record_xerror = 0;
+ old_handler = XSetErrorHandler(trap_record_xerror);
+
+ shutdown_record_context(rc_scroll, 0, shut_reopen);
+ if (! use_xrecord) return;
+
XRecordProcessReplies(rdpy_data);
+
+ if (trapped_record_xerror) {
+ RECORD_ERROR_MSG;
+ last_error = dnow;
+ }
+ XSetErrorHandler(old_handler);
X_UNLOCK;
+
rc_scroll = 0;
+
} else {
if (rcs_scroll) {
-if (db) fprintf(stderr, "=== disab-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll);
+if (db > 1) fprintf(stderr, "=== disab-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll);
X_LOCK;
+ trapped_record_xerror = 0;
+ old_handler =
+ XSetErrorHandler(trap_record_xerror);
+
rcs_scroll = XRecordCurrentClients;
XRecordUnregisterClients(rdpy_ctrl, rc_scroll,
&rcs_scroll, 1);
XRecordDisableContext(rdpy_ctrl, rc_scroll);
XFlush(rdpy_ctrl);
XRecordProcessReplies(rdpy_data);
+
+ if (trapped_record_xerror) {
+ RECORD_ERROR_MSG;
+
+ shutdown_record_context(rc_scroll,
+ 0, reopen_dpys);
+ if (! use_xrecord) return;
+
+ last_error = dnow;
+ rc_scroll = 0;
+ }
+ XSetErrorHandler(old_handler);
X_UNLOCK;
}
}
@@ -3540,9 +3974,11 @@ if (db) fprintf(stderr, "=== disab-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll)
*/
xrecord_focus_window = None;
xrecord_wm_window = None;
+ xrecord_ptr_window = None;
rcs_scroll = 0;
return;
}
+if (db) fprintf(stderr, "XRECORD ON: %d/%d %.4f\n", xrecording, setby, dnow - x11vnc_start);
if (xrecording) {
return;
@@ -3551,27 +3987,38 @@ if (db) fprintf(stderr, "=== disab-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll)
if (do_shutdown && rc_scroll) {
static int didmsg = 0;
/* should not happen... */
- if (!didmsg) {
+ if (1 || !didmsg) {
rfbLog("warning: do_shutdown && rc_scroll\n");
didmsg = 1;
}
- xrecord_watch(0);
+ xrecord_watch(0, SCR_NONE);
}
xrecording = 0;
xrecord_focus_window = None;
xrecord_wm_window = None;
+ xrecord_ptr_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;
+ X_LOCK;
+#if 0
+ /*
+ * xrecord_focus_window / focus not currently used... save a
+ * round trip to the X server for now.
+ * N.B. our heuristic is inaccurate: if he is scrolling and
+ * drifts off of the scrollbar onto another application we
+ * will catch that application, not the starting ones.
+ * check_xrecord_{keys,mouse} mitigates this somewhat by
+ * delaying calls to xrecord_watch as much as possible.
+ */
XGetInputFocus(dpy, &focus, &i);
+#endif
XQueryPointer(dpy, rootwin, &r, &wm, &rx, &ry, &wx, &wy, &m);
if (wm) {
@@ -3579,19 +4026,54 @@ if (db) fprintf(stderr, "=== disab-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll)
} 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;
+
+ /* descend a bit to avoid wm frames: */
+ if (c != rootwin && c == last_win) {
+ /* use cached results to avoid roundtrips: */
+ clast = last_result;
+ } else if (scroll_good_all == NULL && scroll_skip_all == NULL) {
+ /* more efficient if name info not needed. */
+ clast = descend_pointer(6, c, NULL, 0);
+ } else {
+ char *nm;
+ int matched_good = 0, matched_skip = 0;
+
+ clast = descend_pointer(6, c, name_info, 2048);
+if (db) fprintf(stderr, "name_info: %s\n", name_info);
+
+ nm = name_info;
+
+ if (scroll_good_all) {
+ matched_good += match_str_list(nm, scroll_good_all);
}
- if (! c) {
- break;
+ if (setby == SCR_KEY && scroll_good_key) {
+ matched_good += match_str_list(nm, scroll_good_key);
+ }
+ if (setby == SCR_MOUSE && scroll_good_mouse) {
+ matched_good += match_str_list(nm, scroll_good_mouse);
+ }
+ if (scroll_skip_all) {
+ matched_skip += match_str_list(nm, scroll_skip_all);
+ }
+ if (setby == SCR_KEY && scroll_skip_key) {
+ matched_skip += match_str_list(nm, scroll_skip_key);
+ }
+ if (setby == SCR_MOUSE && scroll_skip_mouse) {
+ matched_skip += match_str_list(nm, scroll_skip_mouse);
+ }
+
+ if (!matched_good && matched_skip) {
+ clast = None;
}
- depth++;
}
+ if (c != rootwin) {
+ /* cache results for possible use next call */
+ last_win = c;
+ last_result = clast;
+ }
+
if (!clast || clast == rootwin) {
-if (db) fprintf(stderr, "--- xrecord_watch: skip.\n");
+if (db) fprintf(stderr, "--- xrecord_watch: SKIP.\n");
X_UNLOCK;
return;
}
@@ -3600,73 +4082,123 @@ if (db) fprintf(stderr, "--- xrecord_watch: skip.\n");
rr_scroll[0] = rr_CA;
rr_scroll[1] = rr_CW;
+ /*
+ * start trapping... there still are some occasional failures
+ * not yet understood, likely some race condition WRT the
+ * context being setup.
+ */
+ trapped_record_xerror = 0;
+ old_handler = XSetErrorHandler(trap_record_xerror);
+
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);
+ if (rc_scroll) {
+ dtime0(&create_time);
+ } else {
+ rcs_scroll = 0;
+ }
+
} else if (! do_shutdown) {
if (rcs_scroll) {
-if (db) fprintf(stderr, "=2= unreg-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll);
+ /*
+ * should have been unregistered in xrecord_watch(0)...
+ */
rcs_scroll = XRecordCurrentClients;
XRecordUnregisterClients(rdpy_ctrl, rc_scroll,
&rcs_scroll, 1);
+
+if (db > 1) fprintf(stderr, "=2= unreg-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll);
+
}
rcs_scroll = (XRecordClientSpec) clast;
-if (db) fprintf(stderr, "=-= reg-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll);
+
+if (db > 1) 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) {
+ if (1 || dnow > last_error + 60) {
rfbLog("failed to register client 0x%lx with"
" X RECORD context rc_scroll.\n", clast);
- last_err = now;
}
+ last_error = dnow;
+ rcs_scroll = 0;
/* continue on for now... */
}
}
+
XFlush(rdpy_ctrl);
if (db) fprintf(stderr, "rc_scroll: 0x%lx\n", rc_scroll);
+ if (trapped_record_xerror) {
+ RECORD_ERROR_MSG;
+ }
if (! rc_scroll) {
+ XSetErrorHandler(old_handler);
X_UNLOCK;
use_xrecord = 0;
rfbLog("failed to create X RECORD context rc_scroll.\n");
rfbLog(" switching to -noscrollcopyrect mode.\n");
return;
+ } else if (! rcs_scroll || trapped_record_xerror) {
+ /* try again later */
+ shutdown_record_context(rc_scroll, 0, reopen_dpys);
+ if (! use_xrecord) return;
+ rc_scroll = 0;
+ last_error = dnow;
+
+ XSetErrorHandler(old_handler);
+ X_UNLOCK;
+
+ return;
}
xrecord_focus_window = focus;
+#if 0
+ /* xrecord_focus_window currently unused. */
if (! xrecord_focus_window) {
xrecord_focus_window = clast;
}
+#endif
xrecord_wm_window = wm;
if (! xrecord_wm_window) {
xrecord_wm_window = clast;
}
+ xrecord_ptr_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) {
+ dtime0(&xrecord_start);
+
+ rc = XRecordEnableContextAsync(rdpy_data, rc_scroll, record_switch,
+ (XPointer) xrecord_seq);
+
+ if (!rc || trapped_record_xerror) {
+ if (1 || dnow > last_error + 60) {
rfbLog("failed to enable RECORD context "
- "rc_scroll.\n");
- last_err = now;
+ "rc_scroll: 0x%lx\n", rc_scroll);
+ if (trapped_record_xerror) {
+ RECORD_ERROR_MSG;
+ }
}
+ shutdown_record_context(rc_scroll, 0, reopen_dpys);
+ if (! use_xrecord) return;
+ rc_scroll = 0;
+ last_error = dnow;
+ xrecording = 0;
/* continue on for now... */
}
+ XSetErrorHandler(old_handler);
+
+ /* XXX this may cause more problems than it solves... */
XFlush(rdpy_data);
+
X_UNLOCK;
#endif
}
@@ -3734,7 +4266,7 @@ void clean_up_exit (int ret) {
if (! dpy) exit(ret); /* raw_rb hack */
/* X keyboard cleanups */
- delete_added_keycodes();
+ delete_added_keycodes(0);
if (clear_mods == 1) {
clear_modifiers(0);
@@ -3743,7 +4275,7 @@ void clean_up_exit (int ret) {
}
if (no_autorepeat) {
- autorepeat(1);
+ autorepeat(1, 0);
}
if (use_solid_bg) {
solid_bg(1);
@@ -3783,6 +4315,7 @@ XErrorEvent *trapped_xerror_event;
int trapped_xerror = 0;
int trapped_xioerror = 0;
int trapped_getimage_xerror = 0;
+int trapped_record_xerror = 0;
int trap_xerror(Display *d, XErrorEvent *error) {
trapped_xerror = 1;
@@ -3801,6 +4334,12 @@ int trap_getimage_xerror(Display *d, XErrorEvent *error) {
return 0;
}
+int trap_record_xerror(Display *d, XErrorEvent *error) {
+ trapped_record_xerror = 1;
+ trapped_record_xerror_event = error;
+ return 0;
+}
+
void interrupted(int);
static int Xerror(Display *d, XErrorEvent *error) {
@@ -3850,6 +4389,93 @@ char *xerror_string(XErrorEvent *error) {
}
}
+char *crash_stack_command1 = NULL;
+char *crash_stack_command2 = NULL;
+char *crash_debug_command = NULL;
+int crash_debug = 1;
+
+void initialize_crash_handler(void) {
+ int pid = program_pid;
+ crash_stack_command1 = malloc(1000);
+ crash_stack_command2 = malloc(1000);
+ crash_debug_command = malloc(1000);
+
+ snprintf(crash_stack_command1, 500, "echo where > /tmp/gdb.%d;"
+ " env PATH=$PATH:/usr/local/bin:/usr/sfw/bin:/usr/bin"
+ " gdb -x /tmp/gdb.%d -batch -n %s %d;"
+ " rm -f /tmp/gdb.%d", pid, pid, program_name, pid, pid);
+ snprintf(crash_stack_command2, 500, "pstack %d", program_pid);
+
+ snprintf(crash_debug_command, 500, "gdb %s %d", program_name, pid);
+}
+
+void crash_shell_help(void) {
+ int pid = program_pid;
+ fprintf(stderr, "\n");
+ fprintf(stderr, " *** Welcome to the x11vnc crash shell! ***\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "PROGRAM: %s PID: %d\n", program_name, pid);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "POSSIBLE DEBUGGER COMMAND:\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " %s\n", crash_debug_command);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Press \"q\" to quit.\n");
+ fprintf(stderr, "Press \"h\" or \"?\" for this help.\n");
+ fprintf(stderr, "Press \"s\" to try to run some commands to"
+ " show a stack trace (gdb/pstack).\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Anything else is passed to -Q query function.\n");
+ fprintf(stderr, "\n");
+}
+
+void crash_shell(void) {
+ char qry[1000], cmd[1000], line[1000];
+ char *str, *p;
+
+ crash_shell_help();
+ fprintf(stderr, "\ncrash> ");
+ while (fgets(line, 1000, stdin) != NULL) {
+ str = lblanks(line);
+
+ p = str;
+ while(*p) {
+ if (*p == '\n') {
+ *p = '\0';
+ }
+ p++;
+ }
+
+ if (*str == 'q' && *(str+1) == '\0') {
+ fprintf(stderr, "quiting.\n");
+ return;
+ } else if (*str == 'h' && *(str+1) == '\0') {
+ crash_shell_help();
+ } else if (*str == '?' && *(str+1) == '\0') {
+ crash_shell_help();
+ } else if (*str == 's' && *(str+1) == '\0') {
+ sprintf(cmd, "sh -c '(%s) &'", crash_stack_command1);
+ fprintf(stderr, "\nrunning:\n\t%s\n\n",
+ crash_stack_command1);
+ system(cmd);
+ usleep(1000*1000);
+
+ sprintf(cmd, "sh -c '(%s) &'", crash_stack_command2);
+ fprintf(stderr, "\nrunning:\n\t%s\n\n",
+ crash_stack_command2);
+ system(cmd);
+ usleep(1000*1000);
+ } else {
+ snprintf(qry, 1000, "qry=%s", str);
+ p = process_remote_cmd(qry, 1);
+ fprintf(stderr, "\n\nresult:\n%s\n", p);
+ free(p);
+ }
+
+ fprintf(stderr, "crash> ");
+ }
+}
+
/*
* General problem handler
*/
@@ -3888,7 +4514,7 @@ void interrupted (int sig) {
}
/* X keyboard cleanups */
- delete_added_keycodes();
+ delete_added_keycodes(0);
if (clear_mods == 1) {
clear_modifiers(0);
@@ -3896,19 +4522,23 @@ void interrupted (int sig) {
clear_keys();
}
if (no_autorepeat) {
- autorepeat(1);
+ autorepeat(1, 0);
}
if (use_solid_bg) {
solid_bg(1);
}
+ if (crash_debug) {
+ crash_shell();
+ }
+
if (sig) {
exit(2);
}
}
/* trapping utility to check for a valid window: */
-int valid_window(Window win, XWindowAttributes *attr_ret) {
+int valid_window(Window win, XWindowAttributes *attr_ret, int bequiet) {
XErrorHandler old_handler;
XWindowAttributes attr, *pattr;
int ok = 0;
@@ -3924,9 +4554,12 @@ int valid_window(Window win, XWindowAttributes *attr_ret) {
if (XGetWindowAttributes(dpy, win, pattr)) {
ok = 1;
}
- if (trapped_xerror && trapped_xerror_event && ! quiet) {
- rfbLog("trapped XError: %s (0x%lx)\n",
- xerror_string(trapped_xerror_event), win);
+ if (trapped_xerror && trapped_xerror_event) {
+ if (! quiet && ! bequiet) {
+ rfbLog("valid_window: trapped XError: %s (0x%lx)\n",
+ xerror_string(trapped_xerror_event), win);
+ }
+ ok = 0;
}
XSetErrorHandler(old_handler);
trapped_xerror = 0;
@@ -3940,7 +4573,7 @@ int wait_until_mapped(Window win) {
XWindowAttributes attr;
while (1) {
- if (! valid_window(win, NULL)) {
+ if (! valid_window(win, NULL, 0)) {
if (time(0) > start + waittime) {
return 0;
}
@@ -4370,7 +5003,7 @@ static void client_gone(rfbClientPtr client) {
rfbLog("client_count: %d\n", client_count);
if (no_autorepeat && client_count == 0) {
- autorepeat(1);
+ autorepeat(1, 0);
}
if (use_solid_bg && client_count == 0) {
solid_bg(1);
@@ -5376,7 +6009,6 @@ void check_connect_inputs(void) {
*/
enum rfbNewClientAction new_client(rfbClientPtr client) {
ClientData *cd;
- double tmr = 0.0;
last_event = last_input = time(0);
@@ -5429,12 +6061,14 @@ enum rfbNewClientAction new_client(rfbClientPtr client) {
client->clientGoneHook = client_gone;
client_count++;
+ last_keyboard_input = last_pointer_input = time(0);
+
if (no_autorepeat && client_count == 1 && ! view_only) {
/*
* first client, turn off X server autorepeat
* XXX handle dynamic change of view_only and per-client.
*/
- autorepeat(0);
+ autorepeat(0, 0);
}
if (use_solid_bg && client_count == 1) {
solid_bg(0);
@@ -5444,8 +6078,7 @@ enum rfbNewClientAction new_client(rfbClientPtr client) {
install_padded_fb(pad_geometry);
}
- dtime(&tmr);
- cd->timer = tmr;
+ cd->timer = dnow();
cd->send_cmp_rate = 0.0;
cd->send_raw_rate = 0.0;
cd->latency = 0.0;
@@ -5625,6 +6258,8 @@ void clear_keys(void) {
* keystroke autorepeating as well, it kind of makes sense to shut it
* off if no one is at the physical display...
*/
+static int save_auto_repeat = -1;
+
int get_autorepeat_state(void) {
XKeyboardState kstate;
X_LOCK;
@@ -5633,10 +6268,16 @@ int get_autorepeat_state(void) {
return kstate.global_auto_repeat;
}
-void autorepeat(int restore) {
+int get_initial_autorepeat_state(void) {
+ if (save_auto_repeat < 0) {
+ save_auto_repeat = get_autorepeat_state();
+ }
+ return save_auto_repeat;
+}
+
+void autorepeat(int restore, int bequiet) {
int global_auto_repeat;
XKeyboardControl kctrl;
- static int save_auto_repeat = -1;
if (raw_fb && ! dpy) return; /* raw_fb hack */
@@ -5657,11 +6298,19 @@ void autorepeat(int restore) {
XFlush(dpy);
X_UNLOCK;
- rfbLog("Restored X server key autorepeat to: %d\n",
- save_auto_repeat);
+ if (! bequiet && ! quiet) {
+ rfbLog("Restored X server key autorepeat to: %d\n",
+ save_auto_repeat);
+ }
} else {
global_auto_repeat = get_autorepeat_state();
- save_auto_repeat = global_auto_repeat;
+ if (save_auto_repeat < 0) {
+ /*
+ * we only remember the state at startup
+ * to avoid confusing ourselves later on.
+ */
+ save_auto_repeat = global_auto_repeat;
+ }
X_LOCK;
kctrl.auto_repeat_mode = AutoRepeatModeOff;
@@ -5669,24 +6318,54 @@ void autorepeat(int restore) {
XFlush(dpy);
X_UNLOCK;
- rfbLog("Disabled X server key autorepeat.\n");
- if (no_repeat_countdown >= 0) {
- rfbLog(" you can run the command: 'xset r on' (%d "
- "times)\n", no_repeat_countdown+1);
- rfbLog(" to force it back on.\n");
+ if (! bequiet && ! quiet) {
+ rfbLog("Disabled X server key autorepeat.\n");
+ if (no_repeat_countdown >= 0) {
+ rfbLog(" to force back on run: 'xset r on' (%d "
+ "times)\n", no_repeat_countdown+1);
+ }
+ }
+ }
+}
+
+/*
+ * We periodically delete any keysyms we have added, this is to
+ * lessen our effect on the X server state if we are terminated abruptly
+ * and cannot clear them and also to clear out any strange little used
+ * ones that would just fill up the keymapping.
+ */
+void check_add_keysyms(void) {
+ static time_t last_check = 0;
+ int clear_freq = 300, quiet = 1, count;
+ time_t now = time(0);
+ if (now > last_check + clear_freq) {
+ count = count_added_keycodes();
+ /*
+ * only really delete if they have not typed recently
+ * and we have added 8 or more.
+ */
+ if (now > last_keyboard_input + 5 && count >= 8) {
+ X_LOCK;
+ delete_added_keycodes(quiet);
+ X_UNLOCK;
}
+ last_check = now;
}
}
static KeySym added_keysyms[0x100];
+/* these are just for rfbLog messages: */
+static KeySym alltime_added_keysyms[1024];
+static int alltime_len = 1024;
+static int alltime_num = 0;
+
int add_keysym(KeySym keysym) {
int minkey, maxkey, syms_per_keycode;
int kc, n, ret = 0;
static int first = 1;
KeySym *keymap;
- if (raw_fb && ! dpy) return 0; /* raw_fb hack */
if (first) {
for (n=0; n < 0x100; n++) {
@@ -5694,6 +6373,9 @@ int add_keysym(KeySym keysym) {
}
first = 0;
}
+
+ if (raw_fb && ! dpy) return 0; /* raw_fb hack */
+
if (keysym == NoSymbol) {
return 0;
}
@@ -5709,7 +6391,7 @@ int add_keysym(KeySym keysym) {
&syms_per_keycode);
for (kc = minkey+1; kc <= maxkey; kc++) {
- int i, is_empty = 1;
+ int i, j, didmsg = 0, is_empty = 1;
char *str;
KeySym new[8];
@@ -5728,7 +6410,7 @@ int add_keysym(KeySym keysym) {
new[i] = NoSymbol;
}
if (add_keysyms == 2) {
- new[0] = keysym;
+ new[0] = keysym; /* XXX remove me */
} else {
for(i=0; i < syms_per_keycode; i++) {
new[i] = keysym;
@@ -5739,9 +6421,25 @@ int add_keysym(KeySym keysym) {
XChangeKeyboardMapping(dpy, kc, syms_per_keycode,
new, 1);
- str = XKeysymToString(keysym);
- rfbLog("added missing keysym to X display: %03d 0x%x \"%s\"\n",
- kc, keysym, str ? str : "null");
+ if (alltime_num >= alltime_len) {
+ didmsg = 1; /* something weird */
+ } else {
+ for (j=0; j<alltime_num; j++) {
+ if (alltime_added_keysyms[j] == keysym) {
+ didmsg = 1;
+ break;
+ }
+ }
+ }
+ if (! didmsg) {
+ str = XKeysymToString(keysym);
+ rfbLog("added missing keysym to X display: %03d "
+ "0x%x \"%s\"\n", kc, keysym, str ? str : "null");
+
+ if (alltime_num < alltime_len) {
+ alltime_added_keysyms[alltime_num++] = keysym;
+ }
+ }
XFlush(dpy);
added_keysyms[kc] = keysym;
@@ -5752,7 +6450,7 @@ int add_keysym(KeySym keysym) {
return ret;
}
-void delete_keycode(KeyCode kc) {
+void delete_keycode(KeyCode kc, int bequiet) {
int minkey, maxkey, syms_per_keycode, i;
KeySym *keymap;
KeySym ksym, new[8];
@@ -5767,22 +6465,35 @@ void delete_keycode(KeyCode kc) {
for (i=0; i<8; i++) {
new[i] = NoSymbol;
}
+
XChangeKeyboardMapping(dpy, kc, syms_per_keycode, new, 1);
- ksym = XKeycodeToKeysym(dpy, kc, 0);
- str = XKeysymToString(ksym);
- rfbLog("deleted keycode from X display: %03d 0x%x \"%s\"\n",
- kc, ksym, str ? str : "null");
+ if (! bequiet && ! quiet) {
+ ksym = XKeycodeToKeysym(dpy, kc, 0);
+ str = XKeysymToString(ksym);
+ rfbLog("deleted keycode from X display: %03d 0x%x \"%s\"\n",
+ kc, ksym, str ? str : "null");
+ }
XFree(keymap);
XFlush(dpy);
}
-void delete_added_keycodes(void) {
+int count_added_keycodes(void) {
+ int kc, count = 0;
+ for (kc = 0; kc < 0x100; kc++) {
+ if (added_keysyms[kc] != NoSymbol) {
+ count++;
+ }
+ }
+ return count;
+}
+
+void delete_added_keycodes(int bequiet) {
int kc;
for (kc = 0; kc < 0x100; kc++) {
if (added_keysyms[kc] != NoSymbol) {
- delete_keycode(kc);
+ delete_keycode(kc, bequiet);
added_keysyms[kc] = NoSymbol;
}
}
@@ -5801,15 +6512,159 @@ typedef struct keyremap {
static keyremap_t *keyremaps = NULL;
+void add_remap(char *line) {
+ char str1[256], str2[256];
+ KeySym ksym1, ksym2;
+ int isbtn = 0, i;
+ static keyremap_t *current = NULL;
+ keyremap_t *remap;
+
+ if (sscanf(line, "%s %s", str1, str2) != 2) {
+ rfbLog("remap: bad line: %s\n", line);
+ clean_up_exit(1);
+ }
+ if (sscanf(str1, "0x%x", &i) == 1) {
+ ksym1 = (KeySym) i;
+ } else {
+ ksym1 = XStringToKeysym(str1);
+ }
+ if (sscanf(str2, "0x%x", &i) == 1) {
+ ksym2 = (KeySym) i;
+ } else {
+ ksym2 = XStringToKeysym(str2);
+ }
+ if (ksym2 == NoSymbol) {
+ int i;
+ if (sscanf(str2, "Button%d", &i) == 1) {
+ ksym2 = (KeySym) i;
+ isbtn = 1;
+ }
+ }
+ if (ksym1 == NoSymbol || ksym2 == NoSymbol) {
+ rfbLog("warning: skipping bad remap line: %s", line);
+ return;
+ }
+ remap = (keyremap_t *) malloc((size_t) sizeof(keyremap_t));
+ remap->before = ksym1;
+ remap->after = ksym2;
+ remap->isbutton = isbtn;
+ remap->next = NULL;
+
+ rfbLog("remapping: (%s, 0x%x) -> (%s, 0x%x) isbtn=%d\n", str1,
+ ksym1, str2, ksym2, isbtn);
+
+ if (keyremaps == NULL) {
+ keyremaps = remap;
+ } else {
+ current->next = remap;
+ }
+ current = remap;
+}
+
+void add_dead_keysyms(char *str) {
+ char *p, *q;
+ int i;
+ char *list[] = {
+ "g grave dead_grave",
+ "a acute dead_acute",
+ "c asciicircum dead_circumflex",
+ "t asciitilde dead_tilde",
+ "m macron dead_macron",
+ "b breve dead_breve",
+ "D abovedot dead_abovedot",
+ "d diaeresis dead_diaeresis",
+ "o degree dead_abovering",
+ "A doubleacute dead_doubleacute",
+ "r caron dead_caron",
+ "e cedilla dead_cedilla",
+/* "x XXX-ogonek dead_ogonek", */
+/* "x XXX-belowdot dead_belowdot", */
+/* "x XXX-hook dead_hook", */
+/* "x XXX-horn dead_horn", */
+ NULL
+ };
+
+ p = str;
+
+ while (*p != '\0') {
+ if (isspace(*p)) {
+ *p = '\0';
+ }
+ p++;
+ }
+
+ if (!strcmp(str, "DEAD")) {
+ for (i = 0; list[i] != NULL; i++) {
+ p = list[i] + 2;
+ add_remap(p);
+ }
+ } else if (!strcmp(str, "DEAD=missing")) {
+ for (i = 0; list[i] != NULL; i++) {
+ KeySym ksym, ksym2;
+ int inmap = 0;
+
+ p = strdup(list[i] + 2);
+ q = strchr(p, ' ');
+ if (q == NULL) {
+ free(p);
+ continue;
+ }
+ *q = '\0';
+ ksym = XStringToKeysym(p);
+ *q = ' ';
+ if (ksym == NoSymbol) {
+ free(p);
+ continue;
+ }
+ if (XKeysymToKeycode(dpy, ksym)) {
+ inmap = 1;
+ }
+#if LIBVNCSERVER_HAVE_XKEYBOARD
+ if (! inmap && xkb_present) {
+ int kc, grp, lvl;
+ for (kc = 0; kc < 0x100; kc++) {
+ for (grp = 0; grp < 4; grp++) {
+ for (lvl = 0; lvl < 8; lvl++) {
+ ksym2 = XkbKeycodeToKeysym(dpy,
+ kc, grp, lvl);
+ if (ksym2 == NoSymbol) {
+ continue;
+ }
+ if (ksym2 == ksym) {
+ inmap = 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+#endif
+ if (! inmap) {
+ add_remap(p);
+ }
+ free(p);
+ }
+ } else if ((p = strchr(str, '=')) != NULL) {
+ while (*p != '\0') {
+ for (i = 0; list[i] != NULL; i++) {
+ q = list[i];
+ if (*p == *q) {
+ q += 2;
+ add_remap(q);
+ break;
+ }
+ }
+ p++;
+ }
+ }
+}
+
/*
* process the -remap string (file or mapping string)
*/
void initialize_remap(char *infile) {
FILE *in;
- char *p, *q, line[256], str1[256], str2[256];
- int i;
- KeySym ksym1, ksym2;
- keyremap_t *remap, *current;
+ char *p, *q, line[256];
if (keyremaps != NULL) {
/* free last remapping */
@@ -5829,11 +6684,20 @@ void initialize_remap(char *infile) {
in = fopen(infile, "r");
if (in == NULL) {
/* assume cmd line key1-key2,key3-key4 */
- if (! strchr(infile, '-') || (in = tmpfile()) == NULL) {
+ if (strstr(infile, "DEAD") == infile) {
+ ;
+ } else if (!strchr(infile, '-')) {
rfbLog("remap: cannot open: %s\n", infile);
rfbLogPerror("fopen");
clean_up_exit(1);
}
+ if ((in = tmpfile()) == NULL) {
+ rfbLog("remap: cannot open tmpfile for %s\n", infile);
+ rfbLogPerror("tmpfile");
+ clean_up_exit(1);
+ }
+
+ /* copy in the string to file format */
p = infile;
while (*p) {
if (*p == '-') {
@@ -5849,8 +6713,8 @@ void initialize_remap(char *infile) {
fflush(in);
rewind(in);
}
+
while (fgets(line, 256, in) != NULL) {
- int isbtn = 0;
p = lblanks(line);
if (*p == '\0') {
continue;
@@ -5858,50 +6722,16 @@ void initialize_remap(char *infile) {
if (strchr(line, '#')) {
continue;
}
- if ( (q = strchr(line, '-')) != NULL) {
- /* allow Keysym1-Keysym2 notation */
- *q = ' ';
- }
-
- if (sscanf(line, "%s %s", str1, str2) != 2) {
- rfbLog("remap: bad line: %s\n", line);
- fclose(in);
- clean_up_exit(1);
- }
- if (sscanf(str1, "0x%x", &i) == 1) {
- ksym1 = (KeySym) i;
- } else {
- ksym1 = XStringToKeysym(str1);
- }
- if (sscanf(str2, "0x%x", &i) == 1) {
- ksym2 = (KeySym) i;
- } else {
- ksym2 = XStringToKeysym(str2);
- }
- if (ksym2 == NoSymbol) {
- int i;
- if (sscanf(str2, "Button%d", &i) == 1) {
- ksym2 = (KeySym) i;
- isbtn = 1;
- }
- }
- if (ksym1 == NoSymbol || ksym2 == NoSymbol) {
- rfbLog("warning: skipping bad remap line: %s", line);
+
+ if (strstr(p, "DEAD") == p) {
+ add_dead_keysyms(p);
continue;
}
- remap = (keyremap_t *) malloc((size_t) sizeof(keyremap_t));
- remap->before = ksym1;
- remap->after = ksym2;
- remap->isbutton = isbtn;
- remap->next = NULL;
- rfbLog("remapping: (%s, 0x%x) -> (%s, 0x%x) isbtn=%d\n", str1,
- ksym1, str2, ksym2, isbtn);
- if (keyremaps == NULL) {
- keyremaps = remap;
- } else {
- current->next = remap;
+ if ((q = strchr(line, '-')) != NULL) {
+ /* allow Keysym1-Keysym2 notation */
+ *q = ' ';
}
- current = remap;
+ add_remap(p);
}
fclose(in);
}
@@ -5928,6 +6758,16 @@ static unsigned int xkbmodifiers[0x100][GRP][LVL];
static int multi_key[0x100], mode_switch[0x100], skipkeycode[0x100];
static int shift_keys[0x100];
+/*
+ * for trying to order the keycodes to avoid problems, note the
+ * *first* keycode bound to it. kc_vec will be a permutation
+ * of 1...256 to get them in the preferred order.
+ */
+static int kc_vec[0x100];
+static int kc1_shift, kc1_control, kc1_caplock, kc1_alt;
+static int kc1_meta, kc1_numlock, kc1_super, kc1_hyper;
+static int kc1_mode_switch, kc1_iso_level3_shift, kc1_multi_key;
+
#if !LIBVNCSERVER_HAVE_XKEYBOARD
/* empty functions for no xkb */
@@ -5935,9 +6775,250 @@ static void initialize_xkb_modtweak(void) {}
static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym,
rfbClientPtr client) {
}
+static void switch_to_xkb_if_better(void) {}
#else
+static void switch_to_xkb_if_better(void) {
+ KeySym keysym, *keymap;
+ int miss_noxkb[256], miss_xkb[256], missing_noxkb = 0, missing_xkb = 0;
+ int i, j, k, n, minkey, maxkey, syms_per_keycode;
+ int syms_gt_4 = 0;
+ int kc, grp, lvl;
+
+ /* non-alphanumeric on us keyboard */
+ KeySym must_have[] = {
+ XK_exclam,
+ XK_at,
+ XK_numbersign,
+ XK_dollar,
+ XK_percent,
+/* XK_asciicircum, */
+ XK_ampersand,
+ XK_asterisk,
+ XK_parenleft,
+ XK_parenright,
+ XK_underscore,
+ XK_plus,
+ XK_minus,
+ XK_equal,
+ XK_bracketleft,
+ XK_bracketright,
+ XK_braceleft,
+ XK_braceright,
+ XK_bar,
+ XK_backslash,
+ XK_semicolon,
+/* XK_apostrophe, */
+ XK_colon,
+ XK_quotedbl,
+ XK_comma,
+ XK_period,
+ XK_less,
+ XK_greater,
+ XK_slash,
+ XK_question,
+/* XK_asciitilde, */
+/* XK_grave, */
+ NoSymbol
+ };
+
+ if (! use_modifier_tweak || got_noxkb) {
+ return;
+ }
+ if (use_xkb_modtweak) {
+ /* already using it */
+ return;
+ }
+
+ XDisplayKeycodes(dpy, &minkey, &maxkey);
+
+ keymap = XGetKeyboardMapping(dpy, minkey, (maxkey - minkey + 1),
+ &syms_per_keycode);
+
+ /* handle alphabetic char with only one keysym (no upper + lower) */
+ for (i = minkey; i <= maxkey; i++) {
+ KeySym lower, upper;
+ /* 2nd one */
+ keysym = keymap[(i - minkey) * syms_per_keycode + 1];
+ if (keysym != NoSymbol) {
+ continue;
+ }
+ /* 1st one */
+ keysym = keymap[(i - minkey) * syms_per_keycode + 0];
+ if (keysym == NoSymbol) {
+ continue;
+ }
+ XConvertCase(keysym, &lower, &upper);
+ if (lower != upper) {
+ keymap[(i - minkey) * syms_per_keycode + 0] = lower;
+ keymap[(i - minkey) * syms_per_keycode + 1] = upper;
+ }
+ }
+
+ k = 0;
+ while (must_have[k] != NoSymbol) {
+ int gotit = 0;
+ KeySym must = must_have[k];
+ for (i = minkey; i <= maxkey; i++) {
+ for (j = 0; j < syms_per_keycode; j++) {
+ keysym = keymap[(i-minkey) * syms_per_keycode + j];
+ if (j >= 4) {
+ if (k == 0 && keysym != NoSymbol) {
+ /* for k=0 count the high keysyms */
+ syms_gt_4++;
+ if (debug_keyboard > 1) {
+ char *str = XKeysymToString(keysym);
+ fprintf(stderr, "- high keysym mapping"
+ ": at %3d j=%d "
+ "'%s'\n", i, j, str ? str:"null");
+ }
+ }
+ continue;
+ }
+ if (keysym == must) {
+ if (debug_keyboard > 1) {
+ char *str = XKeysymToString(must);
+ fprintf(stderr, "- at %3d j=%d found "
+ "'%s'\n", i, j, str ? str:"null");
+ }
+ /* n.b. do not break, see syms_gt_4 above. */
+ gotit = 1;
+ }
+ }
+ }
+ if (! gotit) {
+ if (debug_keyboard > 1) {
+ char *str = XKeysymToString(must);
+ KeyCode kc = XKeysymToKeycode(dpy, must);
+ fprintf(stderr, "- did not find 0x%lx '%s'\t"
+ "Ks2Kc: %d\n", must, str ? str:"null", kc);
+ if (kc != None) {
+ int j2;
+ for(j2=0; j2<syms_per_keycode; j2++) {
+ keysym = keymap[(kc-minkey) *
+ syms_per_keycode + j2];
+ fprintf(stderr, " %d=0x%lx",
+ j2, keysym);
+ }
+ fprintf(stderr, "\n");
+ }
+ }
+ missing_noxkb++;
+ miss_noxkb[k] = 1;
+ } else {
+ miss_noxkb[k] = 0;
+ }
+ k++;
+ }
+ n = k;
+
+ XFree(keymap);
+ if (missing_noxkb == 0 && syms_gt_4 >= 8) {
+ rfbLog("XKEYBOARD: number of keysyms per keycode %d "
+ "is greater\n", syms_per_keycode);
+ rfbLog(" than 4 and %d keysyms are mapped above 4.\n",
+ syms_gt_4);
+ rfbLog(" Automatically switching to -xkb mode.\n");
+ rfbLog(" If this makes the key mapping worse you can\n");
+ rfbLog(" disable it with the \"-noxkb\" option.\n");
+ rfbLog(" Also, remember \"-remap DEAD\" for accenting"
+ " characters.\n");
+
+ use_xkb_modtweak = 1;
+ return;
+
+ } else if (missing_noxkb == 0) {
+ rfbLog("XKEYBOARD: all %d \"must have\" keysyms accounted"
+ " for.\n", n);
+ rfbLog(" Not automatically switching to -xkb mode.\n");
+ rfbLog(" If some keys still cannot be typed, try using"
+ " -xkb.\n");
+ rfbLog(" Also, remember \"-remap DEAD\" for accenting"
+ " characters.\n");
+ return;
+ }
+
+ for (k=0; k<n; k++) {
+ miss_xkb[k] = 1;
+ }
+
+ for (kc = 0; kc < 0x100; kc++) {
+ for (grp = 0; grp < GRP; grp++) {
+ for (lvl = 0; lvl < LVL; lvl++) {
+ /* look up the Keysym, if any */
+ keysym = XkbKeycodeToKeysym(dpy, kc, grp, lvl);
+ if (keysym == NoSymbol) {
+ continue;
+ }
+ for (k=0; k<n; k++) {
+ if (keysym == must_have[k]) {
+ miss_xkb[k] = 0;
+ }
+ }
+ }
+ }
+ }
+
+ for (k=0; k<n; k++) {
+ if (miss_xkb[k]) {
+ missing_xkb++;
+ }
+ }
+
+ rfbLog("\n");
+ if (missing_xkb < missing_noxkb) {
+ rfbLog("XKEYBOARD:\n");
+ rfbLog("Switching to -xkb mode to recover these keysyms:\n");
+ } else {
+ rfbLog("XKEYBOARD: \"must have\" keysyms better accounted"
+ " for\n");
+ rfbLog("under -noxkb mode: not switching to -xkb mode:\n");
+ }
+
+ rfbLog(" xkb noxkb Keysym (\"X\" means present)\n");
+ rfbLog(" --- ----- -----------------------------\n");
+ for (k=0; k<n; k++) {
+ char *xx, *xn, *name;
+
+ keysym = must_have[k];
+ if (keysym == NoSymbol) {
+ continue;
+ }
+ if (!miss_xkb[k] && !miss_noxkb[k]) {
+ continue;
+ }
+ if (miss_xkb[k]) {
+ xx = " ";
+ } else {
+ xx = " X ";
+ }
+ if (miss_noxkb[k]) {
+ xn = " ";
+ } else {
+ xn = " X ";
+ }
+ name = XKeysymToString(keysym);
+ rfbLog(" %s %s 0x%lx %s\n", xx, xn, keysym,
+ name ? name : "null");
+ }
+ rfbLog("\n");
+
+ if (missing_xkb < missing_noxkb) {
+ rfbLog(" If this makes the key mapping worse you can\n");
+ rfbLog(" disable it with the \"-noxkb\" option.\n");
+ rfbLog("\n");
+
+ use_xkb_modtweak = 1;
+
+ } else {
+ rfbLog(" If some keys still cannot be typed, try using"
+ " -xkb.\n");
+ rfbLog(" Also, remember \"-remap DEAD\" for accenting"
+ " characters.\n");
+ }
+}
+
/* sets up all the keymapping info via Xkb API */
static void initialize_xkb_modtweak(void) {
@@ -6021,6 +7102,19 @@ xkbmodifiers[] For the KeySym bound to this (keycode,group,level) store
kc_max = 0;
kc_min = 0x100;
+ /* first keycode for a modifier type (multi_key too) */
+ kc1_shift = -1;
+ kc1_control = -1;
+ kc1_caplock = -1;
+ kc1_alt = -1;
+ kc1_meta = -1;
+ kc1_numlock = -1;
+ kc1_super = -1;
+ kc1_hyper = -1;
+ kc1_mode_switch = -1;
+ kc1_iso_level3_shift = -1;
+ kc1_multi_key = -1;
+
/*
* loop over all possible (keycode, group, level) triples
* and record what we find for it:
@@ -6058,6 +7152,62 @@ xkbmodifiers[] For the KeySym bound to this (keycode,group,level) store
shift_keys[kc] = lvl+1;
}
+ if (ks == XK_Shift_L || ks == XK_Shift_R) {
+ if (kc1_shift == -1) {
+ kc1_shift = kc;
+ }
+ }
+ if (ks == XK_Control_L || ks == XK_Control_R) {
+ if (kc1_control == -1) {
+ kc1_control = kc;
+ }
+ }
+ if (ks == XK_Caps_Lock || ks == XK_Caps_Lock) {
+ if (kc1_caplock == -1) {
+ kc1_caplock = kc;
+ }
+ }
+ if (ks == XK_Alt_L || ks == XK_Alt_R) {
+ if (kc1_alt == -1) {
+ kc1_alt = kc;
+ }
+ }
+ if (ks == XK_Meta_L || ks == XK_Meta_R) {
+ if (kc1_meta == -1) {
+ kc1_meta = kc;
+ }
+ }
+ if (ks == XK_Num_Lock) {
+ if (kc1_numlock == -1) {
+ kc1_numlock = kc;
+ }
+ }
+ if (ks == XK_Super_L || ks == XK_Super_R) {
+ if (kc1_super == -1) {
+ kc1_super = kc;
+ }
+ }
+ if (ks == XK_Hyper_L || ks == XK_Hyper_R) {
+ if (kc1_hyper == -1) {
+ kc1_hyper = kc;
+ }
+ }
+ if (ks == XK_Mode_switch) {
+ if (kc1_mode_switch == -1) {
+ kc1_mode_switch = kc;
+ }
+ }
+ if (ks == XK_ISO_Level3_Shift) {
+ if (kc1_iso_level3_shift == -1) {
+ kc1_iso_level3_shift = kc;
+ }
+ }
+ if (ks == XK_Multi_key) { /* not a modifier.. */
+ if (kc1_multi_key == -1) {
+ kc1_multi_key = kc;
+ }
+ }
+
/*
* record maximum extent for group/level indices
* and keycode range:
@@ -6160,6 +7310,23 @@ xkbmodifiers[] For the KeySym bound to this (keycode,group,level) store
}
/*
+ * kc_vec will be used in some places to find modifiers, etc
+ * we apply some permutations to it as workarounds.
+ */
+ for (kc = 0; kc < 0x100; kc++) {
+ kc_vec[kc] = kc;
+ }
+
+ if (kc1_mode_switch != -1 && kc1_iso_level3_shift != -1) {
+ if (kc1_mode_switch < kc1_iso_level3_shift) {
+ /* we prefer XK_ISO_Level3_Shift: */
+ kc_vec[kc1_mode_switch] = kc1_iso_level3_shift;
+ kc_vec[kc1_iso_level3_shift] = kc1_mode_switch;
+ }
+ }
+ /* any more? need to watch for undoing the above. */
+
+ /*
* process the user supplied -skip_keycodes string.
* This is presumably a list if "ghost" keycodes, the X server
* thinks they exist, but they do not. ghosts can lead to
@@ -6197,21 +7364,28 @@ xkbmodifiers[] For the KeySym bound to this (keycode,group,level) store
static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym,
rfbClientPtr client) {
- int kc, grp, lvl, i;
+ int kc, grp, lvl, i, kci;
int kc_f[0x100], grp_f[0x100], lvl_f[0x100], state_f[0x100], found;
+ int ignore_f[0x100];
unsigned int state;
+
/* these are used for finding modifiers, etc */
XkbStateRec kbstate;
int got_kbstate = 0;
int Kc_f, Grp_f, Lvl_f;
+ static int Kc_last_down = -1;
+ static KeySym Ks_last_down = NoSymbol;
X_LOCK;
if (debug_keyboard) {
char *str = XKeysymToString(keysym);
- if (debug_keyboard > 1) fprintf(stderr, "\n");
+ if (debug_keyboard > 1) {
+ rfbLog("----------start-xkb_tweak_keyboard (%s) "
+ "--------\n", down ? "DOWN" : "UP");
+ }
rfbLog("xkb_tweak_keyboard: %s keysym=0x%x \"%s\"\n",
down ? "down" : "up", (int) keysym, str ? str : "null");
@@ -6219,7 +7393,7 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym,
/*
* set everything to not-yet-found.
- * these "found" arrays (*_f) let us dyanamically consider the
+ * these "found" arrays (*_f) let us dynamically consider the
* one-to-many Keysym -> Keycode issue. we set the size at 256,
* but of course only very few will be found.
*/
@@ -6228,6 +7402,7 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym,
grp_f[i] = -1;
lvl_f[i] = -1;
state_f[i] = -1;
+ ignore_f[i] = -1;
}
found = 0;
@@ -6278,6 +7453,7 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym,
grp_f[found] = grp;
lvl_f[found] = lvl;
state_f[found] = state;
+ ignore_f[found] = xkbignore[kc][grp][lvl];
found++;
}
}
@@ -6327,15 +7503,86 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym,
}
/*
- * we could optimize here if found > 1
+ * we try to optimize here if found > 1
* e.g. minimize lvl or grp, or other things to give
* "safest" scenario to simulate the keystrokes.
- * but for now we just take the first one we found.
*/
- Kc_f = kc_f[0];
- Grp_f = grp_f[0];
- Lvl_f = lvl_f[0];
- state = state_f[0];
+
+ if (found > 1) {
+ if (down) {
+ int l, score[0x100];
+ int best, best_score = -1;
+ /* need to break the tie... */
+ if (! got_kbstate) {
+ XkbGetState(dpy, XkbUseCoreKbd, &kbstate);
+ got_kbstate = 1;
+ }
+ for (l=0; l < found; l++) {
+ int myscore = 0, b = 0x1, i;
+ int curr, curr_state = kbstate.mods;
+ int need, need_state = state_f[l];
+ int ignore_state = ignore_f[l];
+
+ /* see how many modifiers need to be changed */
+ for (i=0; i<8; i++) {
+ curr = b & curr_state;
+ need = b & need_state;
+ if (! (b & ignore_state)) {
+ ;
+ } else if (curr == need) {
+ ;
+ } else {
+ myscore++;
+ }
+ b = b << 1;
+ }
+ myscore *= 100;
+
+ /* throw in some minimization of lvl too: */
+ myscore += 2*lvl_f[l] + grp_f[l];
+
+ score[l] = myscore;
+ if (debug_keyboard > 1) {
+ fprintf(stderr, " *** score for "
+ "keycode %03d: %4d\n",
+ kc_f[l], myscore);
+ }
+ }
+ for (l=0; l < found; l++) {
+ int myscore = score[l];
+ if (best_score == -1 || myscore < best_score) {
+ best = l;
+ best_score = myscore;
+ }
+ }
+ Kc_f = kc_f[best];
+ Grp_f = grp_f[best];
+ Lvl_f = lvl_f[best];
+ state = state_f[best];
+
+ } else {
+ Kc_f = -1;
+ if (keysym == Ks_last_down) {
+ int l;
+ for (l=0; l < found; l++) {
+ if (Kc_last_down == kc_f[l]) {
+ Kc_f = Kc_last_down;
+ break;
+ }
+ }
+ }
+
+ if (Kc_f == -1) {
+ /* hope for the best... XXX check mods */
+ Kc_f = kc_f[0];
+ }
+ }
+ } else {
+ Kc_f = kc_f[0];
+ Grp_f = grp_f[0];
+ Lvl_f = lvl_f[0];
+ state = state_f[0];
+ }
if (debug_keyboard && found > 1) {
int l;
@@ -6348,7 +7595,8 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym,
str = XKeysymToString(XKeycodeToKeysym(dpy,kc_f[l],0));
fprintf(stderr, " \"%s\"", str ? str : "null");
}
- fprintf(stderr, ", using first one: %03d\n", Kc_f);
+ fprintf(stderr, ", picked this one: %03d (last down: %03d)\n",
+ Kc_f, Kc_last_down);
}
if (down) {
@@ -6362,9 +7610,14 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym,
KeySym ks;
Bool dn;
+ /* remember these to aid the subsequent up case: */
+ Ks_last_down = keysym;
+ Kc_last_down = Kc_f;
+
if (! got_kbstate) {
/* get the current modifier state if we haven't yet */
XkbGetState(dpy, XkbUseCoreKbd, &kbstate);
+ got_kbstate = 1;
}
/*
@@ -6475,77 +7728,80 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym,
/*
* Again, an inefficient loop, this time just
* looking for modifiers...
+ *
+ * note the use of kc_vec to prefer XK_ISO_Level3_Shift
+ * over XK_Mode_switch.
*/
- for (kc = kc_min; kc <= kc_max; kc++) {
- for (grp = 0; grp < grp_max+1; grp++) {
- for (lvl = 0; lvl < lvl_max+1; lvl++) {
- int skip = 1, dbmsg = 0;
+ for (kci = kc_min; kci <= kc_max; kci++) {
+ for (grp = 0; grp < grp_max+1; grp++) {
+ for (lvl = 0; lvl < lvl_max+1; lvl++) {
+ int skip = 1, dbmsg = 0;
- ms = xkbmodifiers[kc][grp][lvl];
- if (! ms || ms != b) {
- continue;
- }
+ kc = kc_vec[kci];
- if (skipkeycode[kc] && debug_keyboard) {
- fprintf(stderr, " xxx skipping "
- "keycode: %d G%d/L%d\n",
- kc, grp+1, lvl+1);
- }
- if (skipkeycode[kc]) {
- continue;
- }
+ ms = xkbmodifiers[kc][grp][lvl];
+ if (! ms || ms != b) {
+ continue;
+ }
- ks = xkbkeysyms[kc][grp][lvl];
- if (! ks) {
- continue;
- }
+ if (skipkeycode[kc] && debug_keyboard) {
+ fprintf(stderr, " xxx skipping keycode:"
+ " %d G%d/L%d\n", kc, grp+1, lvl+1);
+ }
+ if (skipkeycode[kc]) {
+ continue;
+ }
- if (ks == XK_Shift_L) {
- skip = 0;
- } else if (ks == XK_Shift_R) {
- skip = 0;
- } else if (ks == XK_Mode_switch) {
- skip = 0;
- } else if (ks == XK_ISO_Level3_Shift) {
- skip = 0;
- }
- /*
- * Alt, Meta, Control, Super,
- * Hyper, Num, Caps are skipped.
- *
- * XXX need more work on Locks,
- * and non-standard modifiers.
- * (e.g. XF86_Next_VMode using
- * Ctrl+Alt)
- */
- if (debug_keyboard > 1) {
- char *str = XKeysymToString(ks);
- int kt = keystate[kc];
- fprintf(stderr, " === for "
- "mod=%s found kc=%03d/G%d"
- "/L%d it is %d %s skip=%d "
- "(%s)\n", bitprint(b,8), kc,
- grp+1, lvl+1, kt, kt ?
- "down" : "up ", skip,
- str ? str : "null");
- }
+ ks = xkbkeysyms[kc][grp][lvl];
+ if (! ks) {
+ continue;
+ }
- if (! skip && needmods[i] !=
- keystate[kc] && sentmods[i] == 0) {
- sentmods[i] = kc;
- dbmsg = 1;
- }
+ if (ks == XK_Shift_L) {
+ skip = 0;
+ } else if (ks == XK_Shift_R) {
+ skip = 0;
+ } else if (ks == XK_Mode_switch) {
+ skip = 0;
+ } else if (ks == XK_ISO_Level3_Shift) {
+ skip = 0;
+ }
+ /*
+ * Alt, Meta, Control, Super,
+ * Hyper, Num, Caps are skipped.
+ *
+ * XXX need more work on Locks,
+ * and non-standard modifiers.
+ * (e.g. XF86_Next_VMode using
+ * Ctrl+Alt)
+ */
+ if (debug_keyboard > 1) {
+ char *str = XKeysymToString(ks);
+ int kt = keystate[kc];
+ fprintf(stderr, " === for mod=%s "
+ "found kc=%03d/G%d/L%d it is %d "
+ "%s skip=%d (%s)\n", bitprint(b,8),
+ kc, grp+1, lvl+1, kt, kt ?
+ "down" : "up ", skip, str ?
+ str : "null");
+ }
- if (debug_keyboard > 1 && dbmsg) {
- int nm = needmods[i];
- fprintf(stderr, " >>> we "
- "choose kc=%03d=0x%02x to "
- "change it to: %d %s\n", kc,
- kc, nm, nm ? "down" : "up");
- }
-
+ if (! skip && needmods[i] !=
+ keystate[kc] && sentmods[i] == 0) {
+ sentmods[i] = kc;
+ dbmsg = 1;
+ }
+
+ if (debug_keyboard > 1 && dbmsg) {
+ int nm = needmods[i];
+ fprintf(stderr, " >>> we choose "
+ "kc=%03d=0x%02x to change it to: "
+ "%d %s\n", kc, kc, nm, nm ?
+ "down" : "up");
}
+
}
+ }
}
}
for (i=0; i<8; i++) {
@@ -6852,8 +8108,12 @@ void initialize_modtweak(void) {
for (j = 0; j < syms_per_keycode; j++) {
if (debug_keyboard) {
char *sym;
- sym = XKeysymToString(XKeycodeToKeysym(dpy,
- i, j));
+#if 0
+ sym =XKeysymToString(XKeycodeToKeysym(dpy,i,j));
+#else
+ keysym = keymap[(i-minkey)*syms_per_keycode+j];
+ sym = XKeysymToString(keysym);
+#endif
fprintf(stderr, "%-18s ", sym ? sym : "null");
if (j == syms_per_keycode - 1) {
fprintf(stderr, "\n");
@@ -6863,7 +8123,8 @@ void initialize_modtweak(void) {
/*
* Something wacky in the keymapping.
* Ignore these non Shift/AltGr chords
- * for now...
+ * for now... n.b. we try to automatically
+ * switch to -xkb for this case.
*/
continue;
}
@@ -7082,6 +8343,11 @@ void pipe_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
check_pipeinput();
}
+typedef struct keyevent {
+ rfbKeySym sym;
+ rfbBool down;
+ double time;
+} keyevent_t;
/*
* key event handler. See the above functions for contortions for
* running under -modtweak.
@@ -7092,22 +8358,110 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
KeyCode k;
int isbutton = 0;
allowed_input_t input;
+ time_t now = time(0);
+ double dnow;
+ static rfbBool last_down;
+ static rfbKeySym last_keysym;
+
+ dtime0(&dnow);
if (debug_keyboard) {
char *str;
X_LOCK;
str = XKeysymToString(keysym);
- rfbLog("keyboard(%s, 0x%x \"%s\")\n", down ? "down":"up",
- (int) keysym, str ? str : "null");
+ rfbLog("keyboard(%s, 0x%x \"%s\") %.4f\n", down ? "down":"up",
+ (int) keysym, str ? str : "null", dnow - x11vnc_start);
X_UNLOCK;
}
+ if (skip_duplicate_key_events) {
+ if (keysym == last_keysym && down == last_down) {
+ if (debug_keyboard) {
+ rfbLog("skipping dup key event: %d 0x%x\n",
+ down, keysym);
+ }
+ return;
+ }
+ }
+
+ last_down = down;
+ last_keysym = keysym;
+ last_keyboard_time = dnow;
+
+ if (0 && max_scroll_keyrate) {
+ /* XXX not working... */
+ static int hlen = 256, hidx = 0;
+ static keyevent_t history[256];
+ static rfbKeySym last_down_skip_keysym = None;
+ double key_dt, keytimes[256];
+ int idx, i, nrep = 0, skip = 0;
+
+ if (!down) {
+ if (last_down_skip_keysym != None) {
+ if (keysym == last_down_skip_keysym) {
+ skip = 1;
+ }
+ }
+ } else {
+ if (last_scroll_type == SCR_KEY &&
+ dnow < last_scroll_event + 1.0) {
+ key_dt = 1.0/max_scroll_keyrate;
+if (0) fprintf(stderr, "key_dt: %.4f\n", key_dt);
+ for (i=0; i<hlen; i++) {
+ idx = hidx - i - 1;
+ if (idx < 0) idx += hlen;
+
+ if (history[idx].sym != keysym) {
+ break;
+ }
+ if (dnow > history[idx].time + 1.5) {
+ break;
+ }
+ if (history[idx].down == down) {
+
+if (0) fprintf(stderr, "key_dt: %.4f %d %d %.4f\n", key_dt, history[idx].sym,
+ history[idx].down, history[idx].time - x11vnc_start);
+
+ keytimes[nrep++] =
+ history[idx].time;
+ }
+ }
+ if (nrep > 0) {
+ idx = hidx - 1;
+ if (idx < 0) idx += hlen;
+ if (dnow < keytimes[0] + key_dt) {
+ skip = 1;
+ }
+ }
+ }
+ }
+ if (skip) {
+ rfbLog("--- scroll keyrate skipping 0x%lx %s rep:%d "
+ "%.4f\n", keysym, down ? "down":"up", nrep,
+ down ? dnow - keytimes[0] : dnow - x11vnc_start);
+ if (down) {
+ last_down_skip_keysym = keysym;
+ }
+ return;
+ }
+ last_down_skip_keysym = None;
+
+ history[hidx].sym = keysym;
+ history[hidx].time = dnow;
+ history[hidx].down = down;
+ if (++hidx >= hlen) {
+ hidx = 0;
+ }
+ }
+
if (pipeinput_fh != NULL) {
pipe_keyboard(down, keysym, client);
if (! pipeinput_tee) {
if (! view_only || raw_fb) { /* raw_fb hack */
last_keyboard_client = client;
- last_event = last_input = time(0);
+ last_event = last_input = now;
+ last_keyboard_input = now;
+
last_keysym = keysym;
got_user_input++;
got_keyboard_input++;
@@ -7125,7 +8479,9 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
}
last_keyboard_client = client;
- last_event = last_input = time(0);
+ last_event = last_input = now;
+ last_keyboard_input = now;
+
last_keysym = keysym;
got_user_input++;
got_keyboard_input++;
@@ -7155,8 +8511,28 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
}
}
+ 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, SCR_KEY);
+ xrecord_set_by_keys = 1;
+ } else {
+ if (debug_scroll) {
+ char *str = XKeysymToString(keysym);
+ rfbLog("xrecord_skip_keysym: %s\n",
+ str ? str : "NoSymbol");
+ }
+ }
+ }
+
if (isbutton) {
- int button = (int) keysym;
+ int mask, button = (int) keysym;
if (! down) {
return; /* nothing to send */
}
@@ -7164,33 +8540,22 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
rfbLog("keyboard(): remapping keystroke to button %d"
" click\n", button);
}
- if (button < 1 || button > num_buttons) {
- rfbLog("keyboard(): ignoring mouse button out of "
- "bounds: %d\n", button);
- return;
- }
+
X_LOCK;
- XTestFakeButtonEvent_wr(dpy, button, True, CurrentTime);
- XTestFakeButtonEvent_wr(dpy, button, False, CurrentTime);
+ /*
+ * This in principle can be a little dicey... i.e. even
+ * remap the button click to keystroke sequences!
+ * Usually just will simulate the button click.
+ */
+ mask = 1<<(button-1);
+ do_button_mask_change(mask, button); /* down */
+ mask = 0;
+ do_button_mask_change(mask, button); /* up */
XFlush(dpy);
X_UNLOCK;
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;
@@ -7480,50 +8845,105 @@ void initialize_pointer_map(char *pointer_remap) {
*/
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;
+ double now, xsync_max = 0.25;
+ int num, rc, i;
Window r, w;
+ Window *list;
- dtime(&now);
+ if (! stack_list) {
+ stack_list = (winattr_t *) malloc(256*sizeof(winattr_t));
+ stack_list_num = 0;
+ stack_list_len = 256;
+ }
+
+ dtime0(&now);
if (free_only) {
- if (stack_list) {
- X_LOCK;
- XFree(stack_list);
- X_UNLOCK;
- stack_list = NULL;
- stack_num = 0;
- last_free = now;
- }
+ stack_list_num = 0;
+ last_free = now;
return;
}
- if (stack_list && now < last_snap + allowed_age) {
+ if (stack_list_num && now < last_snap + allowed_age) {
return;
}
- if (stack_list) {
- XFree(stack_list);
- stack_list = NULL;
- stack_num = 0;
- last_free = now;
- }
+ stack_list_num = 0;
+ last_free = now;
X_LOCK;
- if (now > last_sync + xsync_max) {
+ if (0 && now > last_sync + xsync_max) {
XSync(dpy, False);
last_sync = now;
}
+ rc = XQueryTree(dpy, rootwin, &r, &w, &list, &num);
+ X_UNLOCK;
- if (! XQueryTree(dpy, rootwin, &r, &w, &stack_list, &stack_num)) {
- stack_list = NULL;
- stack_num = 0;
+ if (! rc) {
+ stack_list_num = 0;
last_free = now;
last_snap = 0.0;
- } else {
- last_snap = now;
+ return;
}
+
+ last_snap = now;
+ if (num > stack_list_len) {
+ int n = 2*num;
+ free(stack_list);
+ stack_list = (winattr_t *) malloc(n*sizeof(winattr_t));
+ stack_list_len = n;
+ }
+ for (i=0; i<num; i++) {
+ stack_list[i].win = list[i];
+ stack_list[i].fetched = 0;
+ stack_list[i].valid = 0;
+ stack_list[i].time = now;
+ }
+ stack_list_num = num;
+
+ X_LOCK;
+ XFree(list);
X_UNLOCK;
}
+void update_stack_list(void) {
+ int k;
+ double now;
+ XWindowAttributes attr;
+
+ if (! stack_list) {
+ return;
+ }
+ if (! stack_list_num) {
+ return;
+ }
+
+ dtime0(&now);
+
+ for (k=0; k < stack_list_num; k++) {
+ Window win = stack_list[k].win;
+ if (!valid_window(win, &attr, 1)) {
+ stack_list[k].valid = 0;
+ } else {
+ stack_list[k].valid = 1;
+ stack_list[k].x = attr.x;
+ stack_list[k].y = attr.y;
+ stack_list[k].width = attr.width;
+ stack_list[k].height = attr.height;
+ stack_list[k].depth = attr.depth;
+ stack_list[k].class = attr.class;
+ stack_list[k].backing_store = attr.backing_store;
+ stack_list[k].map_state = attr.map_state;
+
+ /* root_x, root_y not used for stack_list usage: */
+ stack_list[k].rx = -1;
+ stack_list[k].ry = -1;
+ }
+ stack_list[k].fetched = 1;
+ stack_list[k].time = now;
+ }
+if (0) fprintf(stderr, "update_stack_list[%d]: %.4f %.4f\n", stack_list_num, now - x11vnc_start, dtime(&now));
+}
+
/*
* Send a pointer position event to the X server.
*/
@@ -7554,18 +8974,78 @@ static void update_x11_pointer_position(int x, int y) {
/* change the cursor shape if necessary */
set_cursor(x, y, get_which_cursor());
- last_event = last_input = time(0);
+ last_event = last_input = last_pointer_input = time(0);
+}
+
+void do_button_mask_change(int mask, int button) {
+ int mb, k, i = button-1;
+
+ /*
+ * this expands to any pointer_map button -> keystrokes
+ * remappings. Usually just k=0 and we send one button event.
+ */
+ for (k=0; k < MAX_BUTTON_EVENTS; k++) {
+ int bmask = (mask & (1<<i));
+
+ if (pointer_map[i+1][k].end) {
+ break;
+ }
+
+ if (pointer_map[i+1][k].button) {
+ /* send button up or down */
+
+ mb = pointer_map[i+1][k].button;
+ if ((num_buttons && mb > num_buttons) || mb < 1) {
+ rfbLog("ignoring mouse button out of "
+ "bounds: %d>%d mask: 0x%x -> 0x%x\n",
+ mb, num_buttons, button_mask, mask);
+ continue;
+ }
+ if (debug_pointer) {
+ rfbLog("pointer(): sending button %d"
+ " %s (event %d)\n", mb, bmask
+ ? "down" : "up", k+1);
+ }
+ XTestFakeButtonEvent_wr(dpy, mb, (mask & (1<<i))
+ ? True : False, CurrentTime);
+ } else {
+ /* send keysym up or down */
+ KeyCode key = pointer_map[i+1][k].keycode;
+ int up = pointer_map[i+1][k].up;
+ int down = pointer_map[i+1][k].down;
+
+ if (! bmask) {
+ /* do not send keysym on button up */
+ continue;
+ }
+ if (debug_pointer) {
+ rfbLog("pointer(): sending button %d "
+ "down as keycode 0x%x (event %d)\n",
+ i+1, key, k+1);
+ rfbLog(" down=%d up=%d "
+ "keysym: %s\n", down, up,
+ XKeysymToString(XKeycodeToKeysym(
+ dpy, key, 0)));
+ }
+ if (down) {
+ XTestFakeKeyEvent_wr(dpy, key, True,
+ CurrentTime);
+ }
+ if (up) {
+ XTestFakeKeyEvent_wr(dpy, key, False,
+ CurrentTime);
+ }
+ }
+ }
}
/*
* Send a pointer button event to the X server.
*/
static void update_x11_pointer_mask(int mask) {
- int i, mb;
- int xr_mouse = 1;
- int snapped = 0;
+ int snapped, xr_mouse = 1, i;
- last_event = last_input = time(0);
+ last_event = last_input = last_pointer_input = time(0);
if (raw_fb && ! dpy) return; /* raw_fb hack */
@@ -7580,35 +9060,56 @@ static void update_x11_pointer_mask(int mask) {
}
if (mask && use_xrecord && ! xrecording && xr_mouse) {
- static int px, py, x, y, w, h, ok;
- Window frame;
+ static int px, py, x, y, w, h, got_wm_frame;
+ static XWindowAttributes attr;
+ Window frame = None, mwin = None;
int skip = 0;
if (!button_mask) {
if (get_wm_frame_pos(&px, &py, &x, &y, &w, &h,
- &frame)) {
- ok = 1;
+ &frame, &mwin)) {
+ got_wm_frame = 1;
+if (debug_scroll > 1) fprintf(stderr, "wm_win: 0x%lx\n", mwin);
+ if (mwin != None) {
+ if (!valid_window(mwin, &attr, 1)) {
+ mwin = None;
+ }
+ }
} else {
- ok = 0;
+ got_wm_frame = 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)) {
+ if (got_wm_frame) {
+ if (wireframe && near_wm_edge(x, y, w, h, px, py)) {
/* step out of wireframe's way */
skip = 1;
+ } else {
+ int ok = 0;
+ if (near_scrollbar_edge(x, y, w, h, px, py)) {
+ ok = 1;
+ }
+ if (! ok && mwin != None) {
+ int w = attr.width;
+ int h = attr.height;
+ if (h > 10 * w || w > 10 * h) {
+if (debug_scroll > 1) fprintf(stderr, "internal scrollbar: %dx%d\n", w, h);
+ ok = 1;
+ }
+ }
+ if (! ok) {
+ skip = 1;
+ }
}
}
if (! skip) {
- snapshot_stack_list(0, 0.25);
+ xrecord_watch(1, SCR_MOUSE);
+ snapshot_stack_list(0, 0.50);
snapped = 1;
- xrecord_watch(1);
if (button_mask) {
xrecord_set_by_mouse = 1;
} else {
+ update_stack_list();
xrecord_set_by_mouse = 2;
}
}
@@ -7625,71 +9126,18 @@ static void update_x11_pointer_mask(int mask) {
}
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)) ) {
- int k;
if (debug_pointer) {
rfbLog("pointer(): mask change: mask: 0x%x -> "
"0x%x button: %d\n", button_mask, mask,i+1);
}
- for (k=0; k < MAX_BUTTON_EVENTS; k++) {
- int bmask = (mask & (1<<i));
-
- if (pointer_map[i+1][k].end) {
- break;
- }
-
- if (pointer_map[i+1][k].button) {
- /* sent button up or down */
- mb = pointer_map[i+1][k].button;
- if ((num_buttons && mb > num_buttons)
- || mb < 1) {
- rfbLog("ignoring mouse button out of "
- "bounds: %d>%d mask: 0x%x -> 0x%x\n",
- mb, num_buttons, button_mask, mask);
- continue;
- }
- if (debug_pointer) {
- rfbLog("pointer(): sending button %d"
- " %s (event %d)\n", mb, bmask
- ? "down" : "up", k+1);
- }
- XTestFakeButtonEvent_wr(dpy, mb, (mask & (1<<i))
- ? True : False, CurrentTime);
- } else {
- /* sent keysym up or down */
- KeyCode key = pointer_map[i+1][k].keycode;
- int up = pointer_map[i+1][k].up;
- int down = pointer_map[i+1][k].down;
-
- if (! bmask) {
- /* do not send keysym on button up */
- continue;
- }
- if (debug_pointer) {
- rfbLog("pointer(): sending button %d "
- "down as keycode 0x%x (event %d)\n",
- i+1, key, k+1);
- rfbLog(" down=%d up=%d "
- "keysym: %s\n", down, up,
- XKeysymToString(XKeycodeToKeysym(
- dpy, key, 0)));
- }
- if (down) {
- XTestFakeKeyEvent_wr(dpy, key, True,
- CurrentTime);
- }
- if (up) {
- XTestFakeKeyEvent_wr(dpy, key, False,
- CurrentTime);
- }
- }
- }
+ do_button_mask_change(mask, i+1); /* button # is i+1 */
}
}
-
X_UNLOCK;
/*
@@ -7769,10 +9217,13 @@ void pipe_pointer(int mask, int x, int y, rfbClientPtr client) {
*/
void pointer(int mask, int x, int y, rfbClientPtr client) {
allowed_input_t input;
- int sent = 0;
+ int sent = 0, buffer_it = 0;
if (debug_pointer && mask >= 0) {
static int show_motion = -1;
+ static double last_pointer = 0.0;
+ double tnow, dt;
+ static int last_x, last_y;
if (show_motion == -1) {
if (getenv("X11VNC_DB_NOMOTION")) {
show_motion = 0;
@@ -7780,10 +9231,17 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
show_motion = 1;
}
}
+ dtime0(&tnow);
+ tnow -= x11vnc_start;
+ dt = tnow - last_pointer;
+ last_pointer = tnow;
if (show_motion) {
- rfbLog("pointer(mask: 0x%x, x:%4d, y:%4d)\n",
- mask, x, y);
+ rfbLog("pointer(mask: 0x%x, x:%4d, y:%4d) "
+ "dx: %3d dy: %3d dt: %.4f t: %.4f\n", mask, x, y,
+ x - last_x, y - last_y, dt, tnow);
}
+ last_x = x;
+ last_y = y;
}
if (scaling) {
@@ -7814,15 +9272,16 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
if (view_only) {
return;
}
- get_allowed_input(client, &input);
- if (! input.motion && ! input.button) {
- return;
- }
if (mask >= 0) {
/*
* mask = -1 is a special case call from scan_for_updates()
* to flush the event queue; there is no real pointer event.
*/
+ get_allowed_input(client, &input);
+ if (! input.motion && ! input.button) {
+ return;
+ }
+
got_user_input++;
got_pointer_input++;
last_pointer_client = client;
@@ -7834,7 +9293,7 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
* See check_user_input() for the more complicated things we do
* in the non-threaded case.
*/
- if (use_threads && pointer_mode != 1) {
+ if ((use_threads && pointer_mode != 1) || pointer_flush_delay > 0.0) {
# define NEV 32
/* storage for the event queue */
static int mutex_init = 0;
@@ -7849,6 +9308,15 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
mutex_init = 1;
}
+ if (pointer_flush_delay > 0.0) {
+ maxwait = pointer_flush_delay;
+ }
+ if (mask >= 0) {
+ if (fb_copy_in_progress || pointer_flush_delay > 0.0) {
+ buffer_it = 1;
+ }
+ }
+
LOCK(pointerMutex);
/*
@@ -7863,7 +9331,7 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
* not suspend work in the other libvncserver threads.
* Maybe that is a possibility with a mutex...
*/
- if (fb_copy_in_progress && mask >= 0) {
+ if (buffer_it) {
/*
* mask = -1 is an all-clear signal from
* scan_for_updates().
@@ -7886,8 +9354,8 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
}
UNLOCK(pointerMutex);
if (debug_pointer) {
- rfbLog("pointer(): deferring event "
- "%d\n", i);
+ rfbLog("pointer(): deferring event %d"
+ " %.4f\n", i, tmr - x11vnc_start);
}
return;
}
@@ -7895,33 +9363,57 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
/* time to send the queue */
for (i=0; i<nevents; i++) {
+ int sent = 0;
+ if (mask < 0 && client != NULL) {
+ /* hack to only push the latest event */
+ if (i < nevents - 1) {
+ if (debug_pointer) {
+ rfbLog("- skip deferred event:"
+ " %d\n", i);
+ }
+ continue;
+ }
+ }
if (debug_pointer) {
- rfbLog("pointer(): sending event %d\n", i+1);
+ rfbLog("pointer(): sending event %d %.4f\n",
+ i+1, dnow() - x11vnc_start);
}
if (ev[i][1] >= 0) {
update_x11_pointer_position(ev[i][1], ev[i][2]);
+ sent = 1;
}
if (ev[i][0] >= 0) {
update_x11_pointer_mask(ev[i][0]);
+ sent = 1;
+ }
+
+ if (sent) {
+ pointer_queued_sent++;
}
}
if (nevents && dt > maxwait) {
- X_LOCK;
- if (dpy) { /* raw_fb hack */
+ if (dpy) { /* raw_fb hack */
+ if (mask < 0) {
+ if (debug_pointer) {
+ rfbLog("pointer(): calling XFlush "
+ "%.4f\n", dnow() - x11vnc_start);
+ }
+ X_LOCK;
XFlush(dpy);
+ X_UNLOCK;
}
- X_UNLOCK;
+ }
}
nevents = 0; /* reset everything */
- tmr = 0.0;
dt = 0.0;
- dtime(&tmr);
+ dtime0(&tmr);
UNLOCK(pointerMutex);
}
if (mask < 0) { /* -1 just means flush the event queue */
- if (debug_pointer > 1) {
- rfbLog("pointer(): flush only.\n");
+ if (debug_pointer) {
+ rfbLog("pointer(): flush only. %.4f\n",
+ dnow() - x11vnc_start);
}
return;
}
@@ -7948,6 +9440,14 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
X_LOCK;
XFlush(dpy);
X_UNLOCK;
+ } else if (buffer_it) {
+ if (debug_pointer) {
+ rfbLog("pointer(): calling XFlush+"
+ "%.4f\n", dnow() - x11vnc_start);
+ }
+ X_LOCK;
+ XFlush(dpy);
+ X_UNLOCK;
}
}
@@ -8329,7 +9829,7 @@ int handle_subwin_resize(char *msg) {
if (! subwin) {
return 0; /* hmmm... */
}
- if (! valid_window(subwin, NULL)) {
+ if (! valid_window(subwin, NULL, 0)) {
rfbLog("subwin 0x%lx went away!\n", subwin);
X_UNLOCK;
clean_up_exit(1);
@@ -8821,7 +10321,7 @@ void sync_tod_with_servertime() {
XEvent xev;
char diff[64];
static int seq = 0;
- int i;
+ int i, db = 0;
if (! servertime) {
servertime = XInternAtom(dpy, "X11VNC_SERVERTIME_DIFF", False);
@@ -8840,19 +10340,18 @@ void sync_tod_with_servertime() {
PropModeReplace, (unsigned char *) diff, strlen(diff));
XSync(dpy, False);
- for (i=0; i<10; i++) {
+ for (i=0; i < 10; i++) {
int k, got = 0;
- for (k = 0; k<5; k++) {
+ for (k=0; k < 5; k++) {
while (XCheckTypedEvent(dpy, PropertyNotify, &xev)) {
if (xev.xproperty.atom == servertime) {
- double now = 0.0, stime;
+ double stime;
- dtime(&now);
stime = (double) xev.xproperty.time;
stime = stime/1000.0;
- servertime_diff = now - stime;
- if (0) rfbLog("set servertime_diff: "
+ servertime_diff = dnow() - stime;
+ if (db) rfbLog("set servertime_diff: "
"%.6f\n", servertime_diff);
got = 1;
}
@@ -8865,6 +10364,73 @@ void sync_tod_with_servertime() {
}
}
+void check_autorepeat() {
+ static time_t last_check = 0;
+ time_t now = time(0);
+ int autorepeat_is_on, autorepeat_initially_on, idle_timeout = 300;
+ static int idle_reset = 0;
+
+ if (! no_autorepeat || ! client_count) {
+ return;
+ }
+ if (now <= last_check + 1) {
+ return;
+ }
+ last_check = now;
+
+ autorepeat_is_on = get_autorepeat_state();
+ autorepeat_initially_on = get_initial_autorepeat_state();
+
+ if (view_only) {
+ if (! autorepeat_is_on) {
+ autorepeat(1, 1);
+ }
+ return;
+ }
+
+ if (now > last_keyboard_input + idle_timeout) {
+ /* autorepeat should be on when idle */
+ if (! autorepeat_is_on && autorepeat_initially_on) {
+ static time_t last_msg = 0;
+ static int cnt = 0;
+ if (now > last_msg + idle_timeout && cnt++ < 5) {
+ rfbLog("idle keyboard: turning X autorepeat"
+ " back on.\n");
+ last_msg = now;
+ }
+ autorepeat(1, 1);
+ idle_reset = 1;
+ }
+ } else {
+ if (idle_reset) {
+ static time_t last_msg = 0;
+ static int cnt = 0;
+ if (now > last_msg + idle_timeout && cnt++ < 5) {
+ rfbLog("active keyboard: turning X autorepeat"
+ " off.\n");
+ last_msg = now;
+ }
+ autorepeat(0, 1);
+ idle_reset = 0;
+
+ } else if (no_repeat_countdown && autorepeat_is_on) {
+ int n = no_repeat_countdown - 1;
+ if (n >= 0) {
+ rfbLog("Battling with something for "
+ "-norepeat!! (%d resets left)\n", n);
+ } else {
+ rfbLog("Battling with something for "
+ "-norepeat!!\n");
+ }
+ if (no_repeat_countdown > 0) {
+ no_repeat_countdown--;
+ }
+ autorepeat(1, 0);
+ autorepeat(0, 0);
+ }
+ }
+}
+
/*
* This routine is periodically called to check for selection related
* and other X11 events and respond to them as needed.
@@ -8917,30 +10483,6 @@ void check_xevents(void) {
}
}
- if (no_autorepeat && have_clients && no_repeat_countdown) {
- static time_t last_check = 0;
- if (now > last_check + 1 && ! view_only) {
- last_check = now;
- X_UNLOCK;
- if (get_autorepeat_state() != 0) {
- int n = no_repeat_countdown - 1;
- if (n >= 0) {
- rfbLog("Battling with something for "
- "-norepeat!! (%d resets left)\n",n);
- } else {
- rfbLog("Battling with something for "
- "-norepeat!!\n");
- }
- if (no_repeat_countdown > 0) {
- no_repeat_countdown--;
- }
- autorepeat(1);
- autorepeat(0);
- }
- X_LOCK;
- }
- }
-
if (now > last_call+1) {
/* we only check these once a second or so. */
int n = 0;
@@ -9920,7 +11462,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
ok = 1;
}
if (ok) {
- if (twin && ! valid_window(twin, NULL)) {
+ if (twin && ! valid_window(twin, NULL, 0)) {
rfbLog("skipping invalid sub-window: 0x%lx\n",
twin);
} else {
@@ -9957,7 +11499,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
ok = 1;
}
if (ok) {
- if (twin && ! valid_window(twin, NULL)) {
+ if (twin && ! valid_window(twin, NULL, 0)) {
rfbLog("skipping invalid sub-window: 0x%lx\n",
twin);
} else {
@@ -9982,7 +11524,8 @@ char *process_remote_cmd(char *cmd, int stringonly) {
}
subwin_wait_mapped = 0;
- } else if (strstr(p, "clip") == p) {
+ } else if (!strcmp(p, "clip") ||
+ strstr(p, "clip:") == p) { /* skip-cmd-list */
COLON_CHECK("clip:")
if (query) {
snprintf(buf, bufn, "ans=%s%s%s", p, co,
@@ -10865,6 +12408,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
goto qry;
}
rfbLog("remote_cmd: enabling -nomodtweak mode.\n");
+ got_nomodtweak = 1;
use_modifier_tweak = 0;
} else if (!strcmp(p, "xkb")) {
@@ -10899,6 +12443,8 @@ char *process_remote_cmd(char *cmd, int stringonly) {
}
rfbLog("remote_cmd: disabling -xkb modtweak mode.\n");
use_xkb_modtweak = 0;
+ got_noxkb = 1;
+ initialize_modtweak();
} else if (strstr(p, "skip_keycodes") == p) {
COLON_CHECK("skip_keycodes:")
@@ -10924,6 +12470,23 @@ char *process_remote_cmd(char *cmd, int stringonly) {
skip_keycodes = strdup(p);
initialize_modtweak();
+ } else if (!strcmp(p, "skip_dups")) {
+ if (query) {
+ snprintf(buf, bufn, "ans=%s:%d", p,
+ skip_duplicate_key_events);
+ goto qry;
+ }
+ rfbLog("remote_cmd: enabling -skip_dups mode\n");
+ skip_duplicate_key_events = 1;
+ } else if (!strcmp(p, "noskip_dups")) {
+ if (query) {
+ snprintf(buf, bufn, "ans=%s:%d", p,
+ !skip_duplicate_key_events);
+ goto qry;
+ }
+ rfbLog("remote_cmd: disabling -skip_dups mode\n");
+ skip_duplicate_key_events = 0;
+
} else if (!strcmp(p, "add_keysyms")) {
if (query) {
snprintf(buf, bufn, "ans=%s:%d", p, add_keysyms);
@@ -11025,7 +12588,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
goto qry;
}
rfbLog("remote_cmd: enabling -repeat mode.\n");
- autorepeat(1); /* restore initial setting */
+ autorepeat(1, 0); /* restore initial setting */
no_autorepeat = 0;
} else if (!strcmp(p, "norepeat")) {
@@ -11039,7 +12602,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
no_repeat_countdown = 2;
}
if (client_count && ! view_only) {
- autorepeat(0); /* disable if any clients */
+ autorepeat(0, 0); /* disable if any clients */
}
} else if (!strcmp(p, "fb")) {
@@ -11426,15 +12989,15 @@ char *process_remote_cmd(char *cmd, int stringonly) {
setup_cursors_and_push();
}
- } else if (strstr(p, "xwarp") == p || strstr(p, "xwarppointer") == p) {
+ } else if (strstr(p, "xwarppointer") == p || strstr(p, "xwarp") == p) {
if (query) {
snprintf(buf, bufn, "ans=%s:%d", p, use_xwarppointer);
goto qry;
}
rfbLog("remote_cmd: turning on xwarppointer mode.\n");
use_xwarppointer = 1;
- } else if (strstr(p, "noxwarp") == p ||
- strstr(p, "noxwarppointer") == p) {
+ } else if (strstr(p, "noxwarppointer") == p ||
+ strstr(p, "noxwarp") == p) {
if (query) {
snprintf(buf, bufn, "ans=%s:%d", p, !use_xwarppointer);
goto qry;
@@ -11585,6 +13148,78 @@ char *process_remote_cmd(char *cmd, int stringonly) {
rfbLog("remote_cmd: changed -scr_area to: %d\n",
scrollcopyrect_min_area);
+ } else if (strstr(p, "scr_skip") == p) {
+ char *s = scroll_skip_str;
+ COLON_CHECK("scr_skip:")
+ if (!s || *s == '\0') s = scroll_skip_str0;
+ if (query) {
+ snprintf(buf, bufn, "ans=%s%s%s", p, co, NONUL(s));
+ goto qry;
+ }
+ p += strlen("scr_skip:");
+ if (scroll_skip_str) {
+ free(scroll_skip_str);
+ }
+
+ scroll_skip_str = strdup(p);
+ rfbLog("remote_cmd: changed -scr_skip to: %s\n",
+ scroll_skip_str);
+ initialize_scroll_matches();
+ } else if (strstr(p, "scr_inc") == p) {
+ char *s = scroll_good_str;
+ if (!s || *s == '\0') s = scroll_good_str0;
+ COLON_CHECK("scr_inc:")
+ if (query) {
+ snprintf(buf, bufn, "ans=%s%s%s", p, co, NONUL(s));
+ goto qry;
+ }
+ p += strlen("scr_inc:");
+ if (scroll_good_str) {
+ free(scroll_good_str);
+ }
+
+ scroll_good_str = strdup(p);
+ rfbLog("remote_cmd: changed -scr_inc to: %s\n",
+ scroll_good_str);
+ initialize_scroll_matches();
+ } else if (strstr(p, "scr_keys") == p) {
+ COLON_CHECK("scr_keys:")
+ if (query) {
+ snprintf(buf, bufn, "ans=%s%s%s", p, co,
+ NONUL(scroll_key_list_str));
+ goto qry;
+ }
+ p += strlen("scr_keys:");
+ if (scroll_key_list_str) {
+ free(scroll_key_list_str);
+ }
+
+ scroll_key_list_str = strdup(p);
+ rfbLog("remote_cmd: changed -scr_keys to: %s\n",
+ scroll_key_list_str);
+ initialize_scroll_keys();
+
+ } else if (strstr(p, "scr_parms") == p) {
+ COLON_CHECK("scr_parms:")
+ if (query) {
+ snprintf(buf, bufn, "ans=%s%s%s", p, co,
+ scroll_copyrect_str ? scroll_copyrect_str
+ : SCROLL_COPYRECT_PARMS);
+ goto qry;
+ }
+ p += strlen("scr_parms:");
+ if (*p) {
+ if (scroll_copyrect_str) {
+ free(scroll_copyrect_str);
+ }
+ set_scrollcopyrect_mode("always");
+ scroll_copyrect_str = strdup(p);
+ parse_scroll_copyrect();
+ }
+ rfbLog("remote_cmd: set -scr_parms %s.\n",
+ NONUL(scroll_copyrect_str));
+ got_scrollcopyrect = 1;
+
} else if (strstr(p, "scrollcopyrect") == p) {
COLON_CHECK("scrollcopyrect:")
if (query) {
@@ -11598,7 +13233,8 @@ char *process_remote_cmd(char *cmd, int stringonly) {
rfbLog("remote_cmd: changed -scrollcopyrect mode "
"to: %s\n", NONUL(scroll_copyrect));
got_scrollcopyrect = 1;
- } else if (strstr(p, "scr") == p) {
+ } else if (!strcmp(p, "scr") ||
+ strstr(p, "scr:") == p) { /* skip-cmd-list */
COLON_CHECK("scr:")
if (query) {
snprintf(buf, bufn, "ans=%s%s%s", p, co,
@@ -11781,24 +13417,20 @@ char *process_remote_cmd(char *cmd, int stringonly) {
rfbLog("remote_cmd: setting wait %d -> %d ms.\n", waitms, w);
waitms = w;
- } else if (strstr(p, "rfbwait") == p) {
+ } else if (strstr(p, "readtimeout") == p) {
int w, orig = rfbMaxClientWait;
- COLON_CHECK("rfbwait:")
+ COLON_CHECK("readtimeout:")
if (query) {
snprintf(buf, bufn, "ans=%s%s%d", p, co,
- rfbMaxClientWait);
+ rfbMaxClientWait/1000);
goto qry;
}
- p += strlen("rfbwait:");
- w = atoi(p);
- if (w < 0) w = 0;
+ p += strlen("readtimeout:");
+ w = atoi(p) * 1000;
+ if (w <= 0) w = 0;
rfbLog("remote_cmd: setting rfbMaxClientWait %d -> "
- "%d ms.\n", orig, w);
+ "%d msec.\n", orig, w);
rfbMaxClientWait = w;
- if (screen) {
- /* current unused by libvncserver: */
- screen->maxClientWait = w;
- }
} else if (!strcmp(p, "nap")) {
if (query) {
@@ -12098,7 +13730,8 @@ char *process_remote_cmd(char *cmd, int stringonly) {
rfbLog("turning off dontdisconnect.\n");
screen->dontDisconnect = 0;
- } else if (strstr(p, "desktop") == p) {
+ } else if (!strcmp(p, "desktop") ||
+ strstr(p, "desktop:") == p) { /* skip-cmd-list */
COLON_CHECK("desktop:")
if (query) {
snprintf(buf, bufn, "ans=%s%s%s", p, co,
@@ -12114,15 +13747,6 @@ char *process_remote_cmd(char *cmd, int stringonly) {
rfbLog("remote_cmd: setting desktop name to %s\n",
rfb_desktop_name);
- } else if (!strcmp(p, "noremote")) {
- if (query) {
- snprintf(buf, bufn, "ans=%s:%d", p,
- !accept_remote_cmds);
- goto qry;
- }
- 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);
@@ -12221,6 +13845,46 @@ char *process_remote_cmd(char *cmd, int stringonly) {
debug_scroll = atoi(p);
rfbLog("set debug_scroll to: %d\n", debug_scroll);
+ } else if (!strcmp(p, "debug_tiles") || !strcmp(p, "dbt")) {
+ if (query) {
+ snprintf(buf, bufn, "ans=%s:%d", p, debug_tiles);
+ goto qry;
+ }
+ debug_tiles = 1;
+ rfbLog("set debug_tiles to: %d\n", debug_tiles);
+ } else if (!strcmp(p, "nodebug_tiles") || !strcmp(p, "nodbt")) {
+ if (query) {
+ snprintf(buf, bufn, "ans=%s:%d", p, !debug_tiles);
+ goto qry;
+ }
+ debug_tiles = 0;
+ rfbLog("set debug_tiles to: %d\n", debug_tiles);
+ } else if (strstr(p, "debug_tiles") == p) {
+ COLON_CHECK("debug_tiles:")
+ if (query) {
+ snprintf(buf, bufn, "ans=%s%s%d", p, co,
+ debug_tiles);
+ goto qry;
+ }
+ p += strlen("debug_tiles:");
+ debug_tiles = atoi(p);
+ rfbLog("set debug_tiles to: %d\n", debug_tiles);
+
+ } else if (!strcmp(p, "dbg")) {
+ if (query) {
+ snprintf(buf, bufn, "ans=%s:%d", p, crash_debug);
+ goto qry;
+ }
+ crash_debug = 1;
+ rfbLog("set crash_debug to: %d\n", crash_debug);
+ } else if (!strcmp(p, "nodbg")) {
+ if (query) {
+ snprintf(buf, bufn, "ans=%s:%d", p, !crash_debug);
+ goto qry;
+ }
+ crash_debug = 0;
+ rfbLog("set crash_debug to: %d\n", crash_debug);
+
} else if (strstr(p, "hack") == p) { /* skip-cmd-list */
COLON_CHECK("hack:")
if (query) {
@@ -12231,6 +13895,15 @@ char *process_remote_cmd(char *cmd, int stringonly) {
hack_val = atoi(p);
rfbLog("set hack_val to: %d\n", hack_val);
+ } else if (!strcmp(p, "noremote")) {
+ if (query) {
+ snprintf(buf, bufn, "ans=%s:%d", p,
+ !accept_remote_cmds);
+ goto qry;
+ }
+ rfbLog("remote_cmd: disabling remote commands.\n");
+ accept_remote_cmds = 0; /* cannot be turned back on. */
+
} else if (query) {
/* read-only variables that can only be queried: */
@@ -12469,7 +14142,23 @@ 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 */
+ /*
+ * XXX: not working yet, slow and overlaps with scan_display()
+ * probably slow because tall skinny rectangles very inefficient
+ * in general and in direct_fb_copy() (100X slower then horizontal).
+ */
+ int use_direct_fb_copy = 0;
+ int wh_min, wh_max;
+ static int first = 1, udfb = 0;
+ if (first) {
+ if (getenv("XD_DFC")) {
+ udfb = 1;
+ }
+ first = 0;
+ }
+ if (udfb) {
+ use_direct_fb_copy = 1;
+ }
if (xdamage_max_area <= 0) {
always_accept = 1;
@@ -12482,7 +14171,15 @@ void record_desired_xdamage_rect(int x, int y, int w, int h) {
dt_x = w / tile_x;
dt_y = h / tile_y;
- if (!always_accept && dt_y >= 3 && area > 2000) {
+ if (w < h) {
+ wh_min = w;
+ wh_max = h;
+ } else {
+ wh_min = h;
+ wh_max = w;
+ }
+
+ if (!always_accept && dt_y >= 3 && area > 4000) {
/*
* if it is real it should be caught by a normal scanline
* poll, but we might as well keep if small (tall line?).
@@ -12493,6 +14190,13 @@ void record_desired_xdamage_rect(int x, int y, int w, int h) {
if (use_direct_fb_copy) {
X_UNLOCK;
direct_fb_copy(x, y, x + w, y + h, 1);
+ xdamage_direct_count++;
+ X_LOCK;
+ } else if (0 && wh_min < tile_x/4 && wh_max > 30 * wh_min) {
+ /* try it for long, skinny rects, XXX still no good */
+ X_UNLOCK;
+ direct_fb_copy(x, y, x + w, y + h, 1);
+ xdamage_direct_count++;
X_LOCK;
} else {
nt_x1 = nfix( (x)/tile_x, ntiles_x);
@@ -12510,8 +14214,8 @@ void record_desired_xdamage_rect(int x, int y, int w, int h) {
cnt++;
if (! tile_has_xdamage_diff[nt]) {
XD_des++;
+ tile_has_xdamage_diff[nt] = 1;
}
- tile_has_xdamage_diff[nt] = 1;
/* not used: */
tile_row_has_xdamage_diff[iy] = 1;
xdamage_tile_count++;
@@ -12525,39 +14229,113 @@ void record_desired_xdamage_rect(int x, int y, int w, int h) {
}
}
-void collect_xdamage(int scancnt) {
+void add_region_xdamage(sraRegionPtr new_region) {
+ sraRegionPtr reg;
+ int prev_tick, nreg;
+
+ if (! xdamage_regions) {
+ return;
+ }
+
+ nreg = (xdamage_memory * NSCAN) + 1;
+ prev_tick = xdamage_ticker - 1;
+ if (prev_tick < 0) {
+ prev_tick = nreg - 1;
+ }
+
+ reg = xdamage_regions[prev_tick];
+ if (reg != NULL) {
+ sraRgnOr(reg, new_region);
+ }
+}
+
+void clear_xdamage_mark_region(sraRegionPtr markregion, int flush) {
+#if LIBVNCSERVER_HAVE_LIBXDAMAGE
+ XEvent ev;
+ sraRegionPtr tmpregion;
+
+ if (! xdamage_present || ! use_xdamage) {
+ return;
+ }
+ if (! xdamage) {
+ return;
+ }
+ if (! xdamage_base_event_type) {
+ return;
+ }
+
+ X_LOCK;
+ if (flush) {
+ XFlush(dpy);
+ }
+ while (XCheckTypedEvent(dpy, xdamage_base_event_type+XDamageNotify, &ev)) {
+ ;
+ }
+ /* clear the whole damage region */
+ XDamageSubtract(dpy, xdamage, None, None);
+ X_UNLOCK;
+
+ if (! markregion) {
+ /* NULL means mark the whole display */
+ tmpregion = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
+ add_region_xdamage(tmpregion);
+ sraRgnDestroy(tmpregion);
+ } else {
+ add_region_xdamage(markregion);
+ }
+#endif
+}
+
+int collect_xdamage(int scancnt, int call) {
#if LIBVNCSERVER_HAVE_LIBXDAMAGE
XDamageNotifyEvent *dev;
XEvent ev;
sraRegionPtr tmpregion;
sraRegionPtr reg;
static int rect_count = 0;
- int nreg, ccount = 0, dcount = 0;
+ int nreg, ccount = 0, dcount = 0, ecount = 0;
static time_t last_rpt = 0;
time_t now;
int x, y, w, h, x2, y2;
int i, dup, next, dup_max = 0;
#define DUPSZ 16
int dup_x[DUPSZ], dup_y[DUPSZ], dup_w[DUPSZ], dup_h[DUPSZ];
+ double tm, dt;
if (! xdamage_present || ! use_xdamage) {
- return;
+ return 0;
}
if (! xdamage) {
- return;
+ return 0;
}
if (! xdamage_base_event_type) {
- return;
+ return 0;
}
+ dtime0(&tm);
+
nreg = (xdamage_memory * NSCAN) + 1;
- xdamage_ticker = (xdamage_ticker+1) % nreg;
- reg = xdamage_regions[xdamage_ticker];
- sraRgnMakeEmpty(reg);
+
+ if (call == 0) {
+ xdamage_ticker = (xdamage_ticker+1) % nreg;
+ xdamage_direct_count = 0;
+ reg = xdamage_regions[xdamage_ticker];
+ sraRgnMakeEmpty(reg);
+ } else {
+ reg = xdamage_regions[xdamage_ticker];
+ }
+
X_LOCK;
+if (0) XFlush(dpy);
+if (0) XEventsQueued(dpy, QueuedAfterFlush);
while (XCheckTypedEvent(dpy, xdamage_base_event_type+XDamageNotify, &ev)) {
- /* TODO max cut off time in this loop? */
+ /*
+ * TODO max cut off time in this loop?
+ * Could check QLength and if huge just mark the whole
+ * screen.
+ */
+ ecount++;
if (ev.type != xdamage_base_event_type + XDamageNotify) {
break;
}
@@ -12646,9 +14424,22 @@ void collect_xdamage(int scancnt) {
ccount++;
}
/* clear the whole damage region for next time. XXX check */
- XDamageSubtract(dpy, xdamage, None, None);
+ if (call == 1) {
+ XDamageSubtract(dpy, xdamage, None, None);
+ }
X_UNLOCK;
+ if (0 && xdamage_direct_count) {
+ fb_push();
+ }
+
+ dt = dtime(&tm);
+ if ((debug_tiles > 1 && ecount) || (debug_tiles && ecount > 200)
+ || debug_xdamage > 1) {
+ fprintf(stderr, "collect_xdamage(%d): %.4f t: %.4f ev/dup/accept"
+ "/direct %d/%d/%d/%d\n", call, dt, tm - x11vnc_start, ecount,
+ dcount, ccount, xdamage_direct_count);
+ }
now = time(0);
if (! last_rpt) {
last_rpt = now;
@@ -12659,7 +14450,7 @@ void collect_xdamage(int scancnt) {
if (XD_tot) {
rat = ((double) XD_skip)/XD_tot;
}
- if (debug_xdamage) {
+ if (debug_tiles || debug_xdamage) {
fprintf(stderr, "xdamage: == scanline skip/tot: "
"%04d/%04d =%.3f rects: %d desired: %d\n",
XD_skip, XD_tot, rat, rect_count, XD_des);
@@ -12672,6 +14463,7 @@ void collect_xdamage(int scancnt) {
last_rpt = now;
}
#endif
+ return 0;
}
int xdamage_hint_skip(int y) {
@@ -13558,8 +15350,6 @@ typedef struct win_str_info {
* cursor. So far only used to detect if mouse is on root background or not.
* (returns 0 in that case, 1 otherwise).
*
- * It seems impossible to do, but if the actual cursor could ever be
- * determined we might want to hash that info on window ID or something...
*/
void tree_descend_cursor(int *depth, Window *w, win_str_info_t *winfo) {
Window r, c;
@@ -14531,7 +16321,7 @@ void set_colormap(int reset) {
while (c && tries++ < 16) {
/* XXX XQueryTree somehow? */
XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &m);
- if (c && XGetWindowAttributes(dpy, c, &attr)) {
+ if (valid_window(c, &attr, 0)) {
if (attr.colormap && attr.map_installed) {
cmap = attr.colormap;
vis = attr.visual;
@@ -15389,7 +17179,7 @@ XImage *initialize_xdisplay_fb(void) {
if (subwin_wait_mapped) {
wait_until_mapped(subwin);
}
- if (!valid_window((Window) subwin, NULL)) {
+ if (!valid_window((Window) subwin, NULL, 0)) {
rfbLog("invalid sub-window: 0x%lx\n", subwin);
X_UNLOCK;
clean_up_exit(1);
@@ -15515,7 +17305,7 @@ XImage *initialize_xdisplay_fb(void) {
}
if (! quiet) {
- rfbLog("default visual ID: 0x%x\n",
+ rfbLog("Default visual ID: 0x%x\n",
(int) XVisualIDFromVisual(default_visual));
}
@@ -16393,7 +18183,7 @@ void solid_cde(char *color) {
if (! twin) {
twin = rootwin;
}
- if (! valid_window(twin, NULL)) {
+ if (! valid_window(twin, NULL, 0)) {
continue;
}
@@ -17236,6 +19026,7 @@ static int scan_in_progress = 0;
typedef struct tile_change_region {
/* start and end lines, along y, of the changed area inside a tile. */
unsigned short first_line, last_line;
+ short first_x, last_x;
/* info about differences along edges. */
unsigned short left_diff, right_diff;
unsigned short top_diff, bot_diff;
@@ -17604,12 +19395,12 @@ void initialize_polling_images(void) {
* glued together. Ultimately, this information in a single hint is sent
* to libvncserver rather than sending each tile separately.
*/
-static void create_tile_hint(int x, int y, int th, hint_t *hint) {
+static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint) {
int w = dpy_x - x;
int h = dpy_y - y;
- if (w > tile_x) {
- w = tile_x;
+ if (w > tw) {
+ w = tw;
}
if (h > th) {
h = th;
@@ -17621,12 +19412,12 @@ static void create_tile_hint(int x, int y, int th, hint_t *hint) {
hint->h = h;
}
-static void extend_tile_hint(int x, int y, int th, hint_t *hint) {
+static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint) {
int w = dpy_x - x;
int h = dpy_y - y;
- if (w > tile_x) {
- w = tile_x;
+ if (w > tw) {
+ w = tw;
}
if (h > th) {
h = th;
@@ -17663,7 +19454,7 @@ static void save_hint(hint_t hint, int loc) {
*/
static void hint_updates(void) {
hint_t hint;
- int x, y, i, n, ty, th;
+ int x, y, i, n, ty, th, tx, tw;
int hint_count = 0, in_run = 0;
for (y=0; y < ntiles_y; y++) {
@@ -17673,13 +19464,21 @@ static void hint_updates(void) {
if (tile_has_diff[n]) {
ty = tile_region[n].first_line;
th = tile_region[n].last_line - ty + 1;
+
+ tx = tile_region[n].first_x;
+ tw = tile_region[n].last_x - tx + 1;
+ if (tx < 0) {
+ tx = 0;
+ tw = tile_x;
+ }
+
if (! in_run) {
- create_tile_hint( x * tile_x,
- y * tile_y + ty, th, &hint);
+ create_tile_hint( x * tile_x + tx,
+ y * tile_y + ty, tw, th, &hint);
in_run = 1;
} else {
- extend_tile_hint( x * tile_x,
- y * tile_y + ty, th, &hint);
+ extend_tile_hint( x * tile_x + tx,
+ y * tile_y + ty, tw, th, &hint);
}
} else {
if (in_run) {
@@ -18388,6 +20187,7 @@ static int copy_tiles(int tx, int ty, int nt) {
int w1, w2, dx1, dx2; /* tmps for normal and short tiles */
int pixelsize = bpp/8;
int first_min, last_max;
+ int first_x = -1, last_x = -1;
char *src, *dst, *s_src, *s_dst, *m_src, *m_dst;
char *h_src, *h_dst;
@@ -18427,7 +20227,7 @@ static int copy_tiles(int tx, int ty, int nt) {
/*
* If there are blackouts and this tile is completely covered
* no need to poll screen or do anything else..
- * n.b. we are int single copy_tile mode: nt=1
+ * n.b. we are in single copy_tile mode: nt=1
*/
tile_has_diff[n] = 0;
return(0);
@@ -18445,7 +20245,7 @@ static int copy_tiles(int tx, int ty, int nt) {
/*
* If there are blackouts and this tile is partially covered
* we should re-black-out the portion.
- * n.b. we are int single copy_tile mode: nt=1
+ * n.b. we are in single copy_tile mode: nt=1
*/
int x1, x2, y1, y2, b;
int w, s, fill = 0;
@@ -18644,6 +20444,27 @@ static int copy_tiles(int tx, int ty, int nt) {
for (line = first_min; line <= last_max; line++) {
/* for I/O speed we do not do this tile by tile */
memcpy(s_dst, s_src, size_x * pixelsize);
+ if (nt == 1) {
+ /*
+ * optimization for tall skinny lines, e.g. wm
+ * frame. try to find first_x and last_x
+ * we like to think the above memcpy leaves the
+ * data we use below in the cache... (but it
+ * could be two 128 byte segments at 32bpp)
+ */
+ int k, kx;
+ kx = pixelsize;
+ for (k=0; k<size_x; k++) {
+ if (memcmp(s_dst + k*kx, s_src + k*kx, kx)) {
+ if (first_x == -1 || k < first_x) {
+ first_x = k;
+ }
+ if (last_x == -1 || k > last_x) {
+ last_x = k;
+ }
+ }
+ }
+ }
s_src += tile_row[nt]->bytes_per_line;
s_dst += main_bytes_per_line;
}
@@ -18659,6 +20480,9 @@ static int copy_tiles(int tx, int ty, int nt) {
tile_region[n+s].first_line = first_line[t];
tile_region[n+s].last_line = last_line[t];
+ tile_region[n+s].first_x = first_x;
+ tile_region[n+s].last_x = last_x;
+
tile_region[n+s].top_diff = 0;
tile_region[n+s].bot_diff = 0;
if ( first_line[t] < tile_fuzz ) {
@@ -19075,7 +20899,7 @@ int copy_snap(void) {
int pixelsize = bpp/8;
char *fbp;
int i, y, block_size;
- double dt = 0.0;
+ double dt;
static int first = 1;
if (! fs_factor) {
@@ -19090,7 +20914,7 @@ int copy_snap(void) {
fbp = snap_fb;
y = 0;
- dtime(&dt);
+ dtime0(&dt);
X_LOCK;
/* screen may be too big for 1 shm area, so broken into fs_factor */
@@ -19210,7 +21034,7 @@ static void ping_clients(int tile_cnt) {
if (rfbMaxClientWait < 20000) {
rfbMaxClientWait = 20000;
- rfbLog("reset rfbMaxClientWait to %d ms.\n",
+ rfbLog("reset rfbMaxClientWait to %d msec.\n",
rfbMaxClientWait);
}
if (tile_cnt) {
@@ -19490,7 +21314,7 @@ int scan_for_updates(int count_only) {
set_colormap(0);
}
if (use_xdamage) {
- collect_xdamage(scan_count);
+ collect_xdamage(scan_count, 0);
}
}
@@ -19506,6 +21330,14 @@ int scan_for_updates(int count_only) {
tile_count = scan_display(scanlines[scan_count], 0);
SCAN_FATAL(tile_count);
+ /*
+ * we do the XDAMAGE here too since after scan_display()
+ * there is a better chance we have received the events from
+ * the server.
+ */
+ if (use_xdamage) {
+ collect_xdamage(scan_count, 1);
+ }
if (count_only) {
scan_in_progress = 0;
fb_copy_in_progress = 0;
@@ -19518,10 +21350,12 @@ int scan_for_updates(int count_only) {
if (tile_has_diff[i]) {
continue;
}
- if (tile_has_xdamage_diff[i] == 1) {
- tile_has_xdamage_diff[i] = 2;
+ if (tile_has_xdamage_diff[i]) {
tile_has_diff[i] = 1;
- tile_count++;
+ if (tile_has_xdamage_diff[i] == 1) {
+ tile_has_xdamage_diff[i] = 2;
+ tile_count++;
+ }
}
}
}
@@ -19667,7 +21501,7 @@ int scan_for_updates(int count_only) {
}
}
- hint_updates(); /* use krfb/x0rfbserver hints algorithm */
+ hint_updates(); /* use x0rfbserver hints algorithm */
/* Work around threaded rfbProcessClientMessage() calls timeouts */
if (use_threads) {
@@ -19984,23 +21818,133 @@ void do_gui(char *opts) {
* user input handling heuristics
*/
+Window descend_pointer(int depth, Window start, char *name_info, int len) {
+ Window r, c, clast;
+ int i, rx, ry, wx, wy;
+ int written = 0, filled = 0;
+ char *store = NULL;
+ unsigned int m;
+ static XClassHint *classhint = NULL;
+ static char *nm_cache = NULL;
+ static int nm_cache_len = 0;
+ static Window prev_start = None;
+
+ if (! classhint) {
+ classhint = XAllocClassHint();
+ }
+
+ if (! nm_cache) {
+ nm_cache = (char *)malloc(1024);
+ nm_cache_len = 1024;
+ nm_cache[0] = '\0';
+ }
+ if (name_info && nm_cache_len < len) {
+ if (nm_cache) {
+ free(nm_cache);
+ }
+ nm_cache_len = 2*len;
+ nm_cache = (char *)malloc(nm_cache_len);
+ }
+
+ if (name_info) {
+ if (start != None && start == prev_start) {
+ store = NULL;
+ strncpy(name_info, nm_cache, len);
+ } else {
+ store = name_info;
+ name_info[0] = '\0';
+ }
+ }
+
+ if (start != None) {
+ c = start;
+ if (name_info) {
+ prev_start = start;
+ }
+ } else {
+ c = rootwin;
+ }
+
+ for (i=0; i<depth; i++) {
+ clast = c;
+ if (store && ! filled) {
+ char *name;
+ if (XFetchName(dpy, clast, &name)) {
+ int l = strlen(name);
+ if (written + l+2 < len) {
+ strcat(store, "^^");
+ written += 2;
+ strcat(store, name);
+ written += l;
+ XFree(name);
+ } else {
+ filled = 1;
+ }
+ }
+ }
+ if (store && classhint && ! filled) {
+ classhint->res_name = NULL;
+ classhint->res_class = NULL;
+ if (XGetClassHint(dpy, clast, classhint)) {
+ char *p;
+ int l = 0;
+ if (classhint->res_class) {
+ l += strlen(classhint->res_class);
+ }
+ if (classhint->res_name) {
+ l += strlen(classhint->res_name);
+ }
+ if (written + l+4 < len) {
+ strcat(store, "##");
+ p = classhint->res_class;
+ if (p) {
+ strcat(store, p);
+ XFree(p);
+ }
+ strcat(store, "++");
+ p = classhint->res_name;
+ if (p) {
+ strcat(store, p);
+ XFree(p);
+ }
+ written += l+4;
+ } else {
+ filled = 1;
+ }
+ }
+ }
+ if (! XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &m)) {
+ break;
+ }
+ if (! c) {
+ break;
+ }
+ }
+ if (start != None && name_info) {
+ strncpy(nm_cache, name_info, nm_cache_len);
+ }
+
+ return clast;
+}
+
/*
* For -wireframe: find the direct child of rootwin that has the
* pointer, assume that is the WM frame that contains the application
* (i.e. wm reparents the app toplevel) return frame position and size
* if successful.
*/
-
-int get_wm_frame_pos(int *px, int *py, int *x, int *y, int *w, int *h, Window *win) {
+int get_wm_frame_pos(int *px, int *py, int *x, int *y, int *w, int *h,
+ Window *frame, Window *win) {
Window r, c;
XWindowAttributes attr;
Bool ret;
int rootx, rooty, wx, wy;
unsigned int mask;
- ret = XQueryPointer(dpy, rootwin, &r, &c, &rootx, &rooty, &wx, &wy, &mask);
+ ret = XQueryPointer(dpy, rootwin, &r, &c, &rootx, &rooty, &wx, &wy,
+ &mask);
- *win = c;
+ *frame = c;
/* current pointer position is returned too */
*px = rootx;
@@ -20012,19 +21956,19 @@ int get_wm_frame_pos(int *px, int *py, int *x, int *y, int *w, int *h, Window *w
}
/* child window position and size */
- if (! valid_window(c, &attr)) {
+ if (! valid_window(c, &attr, 1)) {
return 0;
}
-#if 0
- XTranslateCoordinates(dpy, c, rootwin, 0, 0, &rootx, &rooty, &c2);
- *x = rootx;
- *y = rooty;
-#endif
+
*x = attr.x;
*y = attr.y;
*w = attr.width;
*h = attr.height;
+ if (win != NULL) {
+ *win = descend_pointer(5, c, NULL, 0);
+ }
+
return 1;
}
@@ -20032,7 +21976,10 @@ static int defer_update_nofb = 6; /* defer a shorter time under -nofb */
int scrollcopyrect_top, scrollcopyrect_bot;
int scrollcopyrect_left, scrollcopyrect_right;
-double scr_key_time, scr_key_persist, scr_mouse_time, scr_mouse_persist;
+double scr_key_time, scr_key_persist;
+double scr_mouse_time, scr_mouse_persist, scr_mouse_maxtime;
+double scr_mouse_pointer_delay;
+double scr_key_bdpush_time, scr_mouse_bdpush_time;
void parse_scroll_copyrect_str(char *scr) {
char *p, *str;
@@ -20074,20 +22021,25 @@ void parse_scroll_copyrect_str(char *scr) {
/* key scrolling timing heuristics. */
if ((str = part[1]) != NULL) {
- double t1, t2;
- if (sscanf(str, "%lf+%lf", &t1, &t2) == 2) {
+ double t1, t2, t3;
+ if (sscanf(str, "%lf+%lf+%lf", &t1, &t2, &t3) == 3) {
scr_key_time = t1;
scr_key_persist = t2;
+ scr_key_bdpush_time = t3;
}
free(str);
}
/* mouse scrolling timing heuristics. */
if ((str = part[2]) != NULL) {
- double t1, t2;
- if (sscanf(str, "%lf+%lf", &t1, &t2) == 2) {
+ double t1, t2, t3, t4, t5;
+ if (sscanf(str, "%lf+%lf+%lf+%lf+%lf", &t1, &t2, &t3, &t4,
+ &t5) == 5) {
scr_mouse_time = t1;
scr_mouse_persist = t2;
+ scr_mouse_bdpush_time = t3;
+ scr_mouse_pointer_delay = t4;
+ scr_mouse_maxtime = t5;
}
free(str);
}
@@ -20303,6 +22255,240 @@ void set_scrollcopyrect_mode(char *str) {
}
}
+int match_str_list(char *str, char **list) {
+ int i = 0, matched = 0;
+
+ if (! list) {
+ return matched;
+ }
+ while (list[i] != NULL) {
+ if (!strcmp(list[i], "*")) {
+ matched = 1;
+ break;
+ } else if (strstr(str, list[i])) {
+ matched = 1;
+ break;
+ }
+ i++;
+ }
+ return matched;
+}
+
+char **create_str_list(char *cslist) {
+ int i, n;
+ char *p, *str = strdup(cslist);
+ char **list = NULL;
+
+ n = 1;
+ p = str;
+ while (*p != '\0') {
+ if (*p == ',') {
+ n++;
+ }
+ p++;
+ }
+
+ list = (char **) malloc((n+1)*sizeof(char *));
+ for(i=0; i < n+1; i++) {
+ list[i] = NULL;
+ }
+
+ p = strtok(str, ",");
+ i = 0;
+ while (p && i < n) {
+ list[i++] = strdup(p);
+ p = strtok(NULL, ",");
+ }
+ free(str);
+
+ return list;
+}
+
+void initialize_scroll_keys(void) {
+ char *str, *p;
+ int i, nkeys = 0, saw_builtin = 0;
+ int ks_max = 2 * 0xFFFF;
+
+ if (scroll_key_list) {
+ free(scroll_key_list);
+ scroll_key_list = NULL;
+ }
+ if (! scroll_key_list_str || *scroll_key_list_str == '\0') {
+ return;
+ }
+
+ if (strstr(scroll_key_list_str, "builtin")) {
+ int k;
+ /* add in number of keysyms builtin gives */
+ for (k=1; k<ks_max; k++) {
+ if (xrecord_scroll_keysym((rfbKeySym) k)) {
+ nkeys++;
+ }
+ }
+ }
+
+ nkeys++; /* first key, i.e. no commas. */
+ p = str = strdup(scroll_key_list_str);
+ while(*p) {
+ if (*p == ',') {
+ nkeys++; /* additional key. */
+ }
+ p++;
+ }
+
+ nkeys++; /* exclude/include 0 element */
+ nkeys++; /* trailing NoSymbol */
+
+ scroll_key_list = (KeySym *)malloc(nkeys*sizeof(KeySym));
+ for (i=0; i<nkeys; i++) {
+ scroll_key_list[i] = NoSymbol;
+ }
+ if (*str == '-') {
+ scroll_key_list[0] = 1;
+ p = strtok(str+1, ",");
+ } else {
+ p = strtok(str, ",");
+ }
+ i = 1;
+ while (p) {
+ if (!strcmp(p, "builtin")) {
+ int k;
+ if (saw_builtin) {
+ p = strtok(NULL, ",");
+ continue;
+ }
+ saw_builtin = 1;
+ for (k=1; k<ks_max; k++) {
+ if (xrecord_scroll_keysym((rfbKeySym) k)) {
+ scroll_key_list[i++] = (rfbKeySym) k;
+ }
+ }
+ } else {
+ int in;
+ if (sscanf(p, "%d", &in) == 1) {
+ scroll_key_list[i++] = (rfbKeySym) in;
+ } else if (sscanf(p, "0x%x", &in) == 1) {
+ scroll_key_list[i++] = (rfbKeySym) in;
+ } else if (XStringToKeysym(p) != NoSymbol) {
+ scroll_key_list[i++] = XStringToKeysym(p);
+ } else {
+ rfbLog("initialize_scroll_keys: skip unknown "
+ "keysym: %s\n", p);
+ }
+ }
+ p = strtok(NULL, ",");
+ }
+ free(str);
+}
+
+void destroy_str_list(char **list) {
+ int i = 0;
+ if (! list) {
+ return;
+ }
+ while (list[i] != NULL) {
+ free(list[i++]);
+ }
+ free(list);
+}
+
+void initialize_scroll_matches(void) {
+ char *str, *imp = "__IMPOSSIBLE_STR__";
+ int i, n, nkey, nmouse;
+
+ destroy_str_list(scroll_good_all);
+ scroll_good_all = NULL;
+ destroy_str_list(scroll_good_key);
+ scroll_good_key = NULL;
+ destroy_str_list(scroll_good_mouse);
+ scroll_good_mouse = NULL;
+
+ destroy_str_list(scroll_skip_all);
+ scroll_skip_all = NULL;
+ destroy_str_list(scroll_skip_key);
+ scroll_skip_key = NULL;
+ destroy_str_list(scroll_skip_mouse);
+ scroll_skip_mouse = NULL;
+
+ /* scroll_good: */
+ if (scroll_good_str != NULL && *scroll_good_str != '\0') {
+ str = scroll_good_str;
+ } else {
+ str = scroll_good_str0;
+ }
+ scroll_good_all = create_str_list(str);
+
+ nkey = 0;
+ nmouse = 0;
+ n = 0;
+ while (scroll_good_all[n] != NULL) {
+ char *s = scroll_good_all[n++];
+ if (strstr(s, "KEY:") == s) nkey++;
+ if (strstr(s, "MOUSE:") == s) nmouse++;
+ }
+ if (nkey++) {
+ scroll_good_key = (char **)malloc(nkey*sizeof(char *));
+ for (i=0; i<nkey; i++) scroll_good_key[i] = NULL;
+ }
+ if (nmouse++) {
+ scroll_good_mouse = (char **)malloc(nmouse*sizeof(char *));
+ for (i=0; i<nmouse; i++) scroll_good_mouse[i] = NULL;
+ }
+ nkey = 0;
+ nmouse = 0;
+ for (i=0; i<n; i++) {
+ char *s = scroll_good_all[i];
+ if (strstr(s, "KEY:") == s) {
+ scroll_good_key[nkey++] = strdup(s+strlen("KEY:"));
+ free(s);
+ scroll_good_all[i] = strdup(imp);
+ } else if (strstr(s, "MOUSE:") == s) {
+ scroll_good_mouse[nmouse++]=strdup(s+strlen("MOUSE:"));
+ free(s);
+ scroll_good_all[i] = strdup(imp);
+ }
+ }
+
+ /* scroll_skip: */
+ if (scroll_skip_str != NULL && *scroll_skip_str != '\0') {
+ str = scroll_skip_str;
+ } else {
+ str = scroll_skip_str0;
+ }
+ scroll_skip_all = create_str_list(str);
+
+ nkey = 0;
+ nmouse = 0;
+ n = 0;
+ while (scroll_skip_all[n] != NULL) {
+ char *s = scroll_skip_all[n++];
+ if (strstr(s, "KEY:") == s) nkey++;
+ if (strstr(s, "MOUSE:") == s) nmouse++;
+ }
+ if (nkey++) {
+ scroll_skip_key = (char **)malloc(nkey*sizeof(char *));
+ for (i=0; i<nkey; i++) scroll_skip_key[i] = NULL;
+ }
+ if (nmouse++) {
+ scroll_skip_mouse = (char **)malloc(nmouse*sizeof(char *));
+ for (i=0; i<nmouse; i++) scroll_skip_mouse[i] = NULL;
+ }
+ nkey = 0;
+ nmouse = 0;
+ for (i=0; i<n; i++) {
+ char *s = scroll_skip_all[i];
+ if (strstr(s, "KEY:") == s) {
+ scroll_skip_key[nkey++] = strdup(s+strlen("KEY:"));
+ free(s);
+ scroll_skip_all[i] = strdup(imp);
+ } else if (strstr(s, "MOUSE:") == s) {
+ scroll_skip_mouse[nmouse++]=strdup(s+strlen("MOUSE:"));
+ free(s);
+ scroll_skip_all[i] = strdup(imp);
+ }
+ }
+}
+
typedef struct saveline {
int x0, y0, x1, y1;
int shift;
@@ -20564,7 +22750,11 @@ 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;
+ int do_cmp = 2;
+ double tm;
+ int db = 0;
+
+if (db) dtime0(&tm);
x1 = nfix(x1, dpy_x);
y1 = nfix(y1, dpy_y);
@@ -20663,36 +22853,149 @@ int direct_fb_copy(int x1, int y1, int x2, int y2, int mark) {
if (mark) {
mark_rect_as_modified(xmin, ymin, xmax, ymax, 1);
}
+
+if (db) {
+ fprintf(stderr, "direct_fb_copy: %dx%d+%d+%d - %d %.4f\n",
+ x2 - x1, y2 - y1, x1, y1, mark, dtime(&tm));
+}
+
return 1;
}
+int do_bdpush(Window wm_win, int x0, int y0, int w0, int h0, int bdx,
+ int bdy, int bdskinny) {
+
+ XWindowAttributes attr;
+ sraRectangleIterator *iter;
+ sraRect rect;
+ sraRegionPtr frame, whole, tmpregion;
+ int tx1, ty1, tx2, ty2;
+ static Window last_wm_win = None;
+ static int last_x, last_y, last_w, last_h;
+ int do_fb_push = 0;
+ int db = debug_scroll;
+
+
+ if (wm_win == last_wm_win) {
+ attr.x = last_x;
+ attr.y = last_y;
+ attr.width = last_w;
+ attr.height = last_h;
+ } else {
+ if (!valid_window(wm_win, &attr, 1)) {
+ return do_fb_push;
+ }
+ last_wm_win = wm_win;
+ last_x = attr.x;
+ last_y = attr.y;
+ last_w = attr.width;
+ last_h = attr.height;
+ }
+if (db > 1) fprintf(stderr, "BDP %d %d %d %d %d %d %d %d %d %d %d\n",
+ x0, y0, w0, h0, bdx, bdy, bdskinny, last_x, last_y, last_w, last_h);
+
+ /* wm frame: */
+ tx1 = attr.x;
+ ty1 = attr.y;
+ tx2 = attr.x + attr.width;
+ ty2 = attr.y + attr.height;
+
+ whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
+ frame = sraRgnCreateRect(tx1, ty1, tx2, ty2);
+ sraRgnAnd(frame, whole);
+
+ /* scrolling window: */
+ tmpregion = sraRgnCreateRect(x0, y0, x0 + w0, y0 + h0);
+ sraRgnAnd(tmpregion, whole);
+
+ sraRgnSubtract(frame, tmpregion);
+ sraRgnDestroy(tmpregion);
+
+ if (!sraRgnEmpty(frame)) {
+ double dt = 0.0, dm;
+ dtime0(&dm);
+ iter = sraRgnGetIterator(frame);
+ while (sraRgnIteratorNext(iter, &rect)) {
+ tx1 = rect.x1;
+ ty1 = rect.y1;
+ tx2 = rect.x2;
+ ty2 = rect.y2;
+
+ if (bdskinny > 0) {
+ int ok = 0;
+ if (nabs(ty2-ty1) <= bdskinny) {
+ ok = 1;
+ }
+ if (nabs(tx2-tx1) <= bdskinny) {
+ ok = 1;
+ }
+ if (! ok) {
+ continue;
+ }
+ }
+
+ if (bdx >= 0) {
+ if (bdx < tx1 || tx2 <= bdx) {
+ continue;
+ }
+ }
+ if (bdy >= 0) {
+ if (bdy < ty1 || ty2 <= bdy) {
+ continue;
+ }
+ }
+
+ direct_fb_copy(tx1, ty1, tx2, ty2, 1);
+
+ do_fb_push++;
+ dt += dtime(&dm);
+if (db > 1) fprintf(stderr, " BDP(%d,%d-%d,%d) dt: %.4f\n", tx1, ty1, tx2, ty2, dt);
+ }
+ sraRgnReleaseIterator(iter);
+ }
+ sraRgnDestroy(whole);
+ sraRgnDestroy(frame);
+
+ return do_fb_push;
+}
+
#define PUSH_TEST(n) \
if (n) { \
- double dt = 0.0, tm = 0.0; dtime(&tm); \
+ double dt = 0.0, tm; dtime0(&tm); \
fprintf(stderr, "PUSH---\n"); \
while (dt < 2.0) { rfbPE(50000); dt += dtime(&tm); } \
fprintf(stderr, "---PUSH\n"); \
}
-int push_scr_ev(double bdpush) {
+int push_scr_ev(double max_age, int type, int bdpush, int bdx, int bdy,
+ int bdskinny) {
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 dret = 1, do_fb_push = 0, 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);
+ double tm, dt, st, dnow, waittime = 0.125;
+ int db = debug_scroll, rrate = get_read_rate();
+ sraRegionPtr backfill, whole, tmpregion, tmpregion2;
+ int link, latency, netrate;
if (ev_tot == 0) {
return dret;
}
+
+ link = link_rate(&latency, &netrate);
+
+ if (link == LR_DIALUP) {
+ waittime *= 5;
+ } else if (link == LR_BROADBAND) {
+ waittime *= 3;
+ } else if (latency > 80 || netrate < 40) {
+ waittime *= 3;
+ }
+
+ dtime0(&dnow);
+
backfill = sraRgnCreate();
whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
@@ -20719,10 +23022,10 @@ int push_scr_ev(double bdpush) {
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;
+ st = scr_ev[ev].t;
- if (dabs((dnow - servertime_diff) - st) > 0.15) {
-if (db) fprintf(stderr, "push_scr_ev: TOO OLD: %.4f\n", (dnow - servertime_diff) - st);
+ if (dabs((dnow - servertime_diff) - st) > max_age) {
+if (db) fprintf(stderr, "push_scr_ev: TOO OLD: %.4f :: (%.4f - %.4f) - %.4f \n", (dnow - servertime_diff) - st, dnow, servertime_diff, st);
dret = 0;
break;
} else {
@@ -20749,17 +23052,17 @@ if (db) fprintf(stderr, "push_scr_ev: NEGATIVE h/w: %d %d %d %d\n", w, h, ww, wh
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 > 1) fprintf(stderr, "push_scr_ev: got: %d x: %4d y: %3d"
+ " w: %4d h: %3d dx: %d dy: %d %dx%d+%d+%d win: 0x%lx\n",
+ ev, x, y, w, h, dx, dy, w, h, x, y, win);
-if (db) fprintf(stderr, "------------ got: %d x: %4d y: %3d"
- " w: %4d h: %3d %dx%d+%d+%d\n",
- ev, wx, wy, ww, wh, ww, wh, wx, wy);
+if (db > 1) fprintf(stderr, "------------ got: %d x: %4d y: %3d"
+ " w: %4d h: %3d %dx%d+%d+%d\n",
+ ev, 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);
+if (db > 1) fprintf(stderr, "------------ got: %d x: %4d y: %3d"
+ " w: %4d h: %3d %dx%d+%d+%d\n",
+ ev, nx, ny, nw, nh, nw, nh, nx, ny);
frame = None;
if (xrecord_wm_window) {
@@ -20778,15 +23081,65 @@ if (db) fprintf(stderr, "------------ got: %d x: %4d y: %3d"
frame = win;
}
- if (try_copyrect(frame, x, y, w, h, dx, dy, &obscured)) {
- fb_push();
+ dtime0(&tm);
+
+ tmpregion = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
+ tmpregion2 = sraRgnCreateRect(wx, wy, wx+ww, wy+wh);
+ sraRgnAnd(tmpregion2, whole);
+ sraRgnSubtract(tmpregion, tmpregion2);
+ sraRgnDestroy(tmpregion2);
+
+ /* do the wm frame just incase the above is bogus too. */
+ if (frame && frame != win) {
+ int k, gotk = -1;
+ for (k = stack_list_num - 1; k >= 0; k--) {
+ if (stack_list[k].win == frame &&
+ stack_list[k].fetched &&
+ stack_list[k].valid &&
+ stack_list[k].map_state == IsViewable) {
+ gotk = k;
+ break;
+ }
+ }
+ if (gotk != -1) {
+ int tx1, ty1, tx2, ty2;
+ tx1 = stack_list[gotk].x;
+ ty1 = stack_list[gotk].y;
+ tx2 = tx1 + stack_list[gotk].width;
+ ty2 = ty1 + stack_list[gotk].height;
+ tmpregion2 = sraRgnCreateRect(tx1,ty1,tx2,ty2);
+ sraRgnAnd(tmpregion2, whole);
+ sraRgnSubtract(tmpregion, tmpregion2);
+ sraRgnDestroy(tmpregion2);
+ }
+ }
+
+ /*
+ * XXX Need to also clip:
+ * children of win
+ * siblings of win higher in stacking order.
+ * ignore for now... probably will make some apps
+ * act very strangely.
+ */
+
+ if (try_copyrect(frame, x, y, w, h, dx, dy, &obscured,
+ tmpregion, waittime)) {
+ last_scroll_type = type;
+ dtime0(&last_scroll_event);
+
+ do_fb_push++;
urgent_update = 1;
+ sraRgnDestroy(tmpregion);
+
PUSH_TEST(0);
} else {
dret = 0;
+ sraRgnDestroy(tmpregion);
break;
}
+ dt = dtime(&tm);
+if (0) fprintf(stderr, " try_copyrect dt: %.4f\n", dt);
if (ev > 0) {
sraRgnOffset(backfill, dx, dy);
@@ -20799,12 +23152,12 @@ PUSH_TEST(0);
sraRgnDestroy(tmpregion);
}
+ /* try to update the backfill region (new window contents) */
if (dret != 0) {
double est, win_area = 0.0, area = 0.0;
sraRectangleIterator *iter;
sraRect rect;
int tx1, ty1, tx2, ty2;
- double tm = 0.0, dt = 0.0;
tmpregion = sraRgnCreateRect(x0, y0, x0 + w0, y0 + h0);
sraRgnAnd(tmpregion, whole);
@@ -20837,7 +23190,7 @@ PUSH_TEST(0);
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 (area > 0.90 * win_area) {
if (db) fprintf(stderr, " AREA_TOO_MUCH");
dret = 0;
} else if (est > 0.6) {
@@ -20846,7 +23199,7 @@ if (db) fprintf(stderr, " EST_TOO_LARGE");
} else if (area <= 0.0) {
;
} else {
- dtime(&tm);
+ dtime0(&tm);
iter = sraRgnGetIterator(backfill);
while (sraRgnIteratorNext(iter, &rect)) {
tx1 = rect.x1;
@@ -20861,13 +23214,12 @@ if (db) fprintf(stderr, " EST_TOO_LARGE");
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();
+ do_fb_push++;
PUSH_TEST(0);
}
dt = dtime(&tm);
sraRgnReleaseIterator(iter);
-if (db) fprintf(stderr, " dt: %.4f", dt);
+if (0) fprintf(stderr, " dfc---- dt: %.4f", dt);
}
if (db && dret) fprintf(stderr, " **** dret=%d", dret);
@@ -20875,82 +23227,50 @@ 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);
+if (db && bdpush) fprintf(stderr, "BDPUSH-TIME: 0x%lx\n", xrecord_wm_window);
- 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 (bdpush && xrecord_wm_window != None) {
+ int x, y, w, h;
+ x = scr_ev[0].x;
+ y = scr_ev[0].y;
+ w = scr_ev[0].w;
+ h = scr_ev[0].h;
+ do_fb_push += do_bdpush(xrecord_wm_window, x, y, w, h,
+ bdx, bdy, bdskinny);
+ }
- if (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);
+ if (do_fb_push) {
+ dtime0(&tm);
+ fb_push();
+ dt = dtime(&tm);
+if (0) fprintf(stderr, " fb_push dt: %.4f", dt);
}
sraRgnDestroy(backfill);
sraRgnDestroy(whole);
return dret;
}
+
+void get_client_regions(int *req, int *mod, int *cpy, int *num) {
+
+ rfbClientIteratorPtr i;
+ rfbClientPtr cl;
+
+ *req = 0;
+ *mod = 0;
+ *cpy = 0;
+ *num = 0;
+
+ i = rfbGetClientIterator(screen);
+ while( (cl = rfbClientIteratorNext(i)) ) {
+ *req += sraRgnCountRects(cl->requestedRegion);
+ *mod += sraRgnCountRects(cl->modifiedRegion);
+ *cpy += sraRgnCountRects(cl->copyRegion);
+ *num += 1;
+ }
+ rfbReleaseClientIterator(i);
+}
+
/*
* Wrapper to apply the rfbDoCopyRegion taking into account if scaling
* is being done. Note that copyrect under the scaling case is often
@@ -20962,13 +23282,22 @@ void do_copyregion(sraRegionPtr region, int dx, int dy) {
int Bpp = bpp/8;
int x1, y1, x2, y2, w, stride;
int sx1, sy1, sx2, sy2, sdx, sdy;
+ int req, mod, cpy, ncli;
char *dst, *src;
if (!scaling || rfb_fb == main_fb) {
+ /* normal case */
+ get_client_regions(&req, &mod, &cpy, &ncli);
+if (debug_scroll > 1) fprintf(stderr, "<<<-rfbDoCopyRect req: %d mod: %d cpy: %d\n", req, mod, cpy);
rfbDoCopyRegion(screen, region, dx, dy);
+
+ get_client_regions(&req, &mod, &cpy, &ncli);
+if (debug_scroll > 1) fprintf(stderr, ">>>-rfbDoCopyRect req: %d mod: %d cpy: %d\n", req, mod, cpy);
+
return;
}
+ /* rarer case, we need to call rfbDoCopyRect with scaled xy */
stride = dpy_x * Bpp;
iter = sraRgnGetIterator(region);
@@ -21012,65 +23341,45 @@ void do_copyregion(sraRegionPtr region, int dx, int dy) {
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);
+void fb_push_wait(double max_wait) {
+ double tm, dt = 0.0;
+ int req, mod, cpy, ncli;
+
+ dtime0(&tm);
+ while (dt < max_wait) {
+ rfbCFD(0);
+ get_client_regions(&req, &mod, &cpy, &ncli);
+ if (! cpy) {
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);
+ usleep(1000);
+ fb_push();
+ dt += dtime(&tm);
}
- 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;
+ int req0, mod0, cpy0, req1, mod1, cpy1, ncli;
+ int db = (debug_scroll || debug_wireframe);
screen->httpDir = NULL;
screen->deferUpdateTime = 0;
- get_client_regions(&req0, &mod0, &cpy0);
+if (db) get_client_regions(&req0, &mod0, &cpy0, &ncli);
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);
+if (db) {
+ get_client_regions(&req1, &mod1, &cpy1, &ncli);
+ fprintf(stderr, "\nFB_push: req: %d/%d mod: %d/%d cpy: %d/%d %.4f\n",
+ req0, req1, mod0, mod1, cpy0, cpy1, dnow() - x11vnc_start);
+}
- 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);
- }
}
/*
@@ -21092,36 +23401,168 @@ int crfix(int x, int dx, int Lx) {
return x;
}
+typedef struct scroll_result {
+ Window win;
+ double time;
+ int result;
+} scroll_result_t;
+
+#define SCR_RESULTS_MAX 64
+scroll_result_t scroll_results[SCR_RESULTS_MAX];
+
+int scrollability(Window win, int set) {
+ double oldest = -1.0;
+ int i, index = -1, next_index = -1;
+
+ if (win == None) {
+ return 0;
+ }
+ if (set == SCR_NONE) {
+ /* lookup case */
+ for (i=0; i<SCR_RESULTS_MAX; i++) {
+ if (win == scroll_results[i].win) {
+ return scroll_results[i].result;
+ }
+ }
+ return 0;
+ }
+
+ for (i=0; i<SCR_RESULTS_MAX; i++) {
+ if (oldest == -1.0 || scroll_results[i].time < oldest) {
+ next_index = i;
+ oldest = scroll_results[i].time;
+ }
+ if (win == scroll_results[i].win) {
+ index = i;
+ break;
+ }
+ }
+
+ if (set == SCR_SUCCESS) {
+ set = 1;
+ } else if (set == SCR_FAIL) {
+ set = -1;
+ } else {
+ set = 0;
+ }
+ if (index == -1) {
+ scroll_results[next_index].win = win;
+ scroll_results[next_index].time = dnow();
+ scroll_results[next_index].result = set;
+ } else {
+ if (scroll_results[index].result == 1) {
+ /*
+ * once a success, always a success, until they
+ * forget about us...
+ */
+ set = 1;
+ } else {
+ scroll_results[index].time = dnow();
+ scroll_results[index].result = set;
+ }
+ }
+ return set;
+}
+
+int eat_pointer(int max_ptr_eat, int keep) {
+ int i, count = 0, gp = got_pointer_input;
+
+ for (i=0; i<max_ptr_eat; i++) {
+ rfbCFD(0);
+ if (got_pointer_input > gp) {
+ count++;
+if (0) fprintf(stderr, "GP*-%d\n", i);
+ gp = got_pointer_input;
+ } else if (i > keep) {
+ break;
+ }
+ }
+ return count;
+}
+
+void set_bdpush(int type, double *last_bdpush, int *pushit) {
+ double now, delay = 0.0;
+ int link, latency, netrate;
+
+ *pushit = 0;
+
+ if (type == SCR_MOUSE) {
+ delay = scr_mouse_bdpush_time;
+ } else if (type == SCR_KEY) {
+ delay = scr_key_bdpush_time;
+ }
+
+ link = link_rate(&latency, &netrate);
+ if (link == LR_DIALUP) {
+ delay *= 1.5;
+ } else if (link == LR_BROADBAND) {
+ delay *= 1.25;
+ }
+
+ dtime0(&now);
+ if (delay > 0.0 && now > *last_bdpush + delay) {
+ *pushit = 1;
+ *last_bdpush = now;
+ }
+}
+
+void mark_for_xdamage(int x, int y, int w, int h) {
+ int tx1, ty1, tx2, ty2;
+ sraRegionPtr tmpregion;
+
+ if (! use_xdamage) {
+ return;
+ }
+
+ tx1 = nfix(x, dpy_x);
+ ty1 = nfix(y, dpy_y);
+ tx2 = nfix(x + w, dpy_x);
+ ty2 = nfix(y + h, dpy_y);
+
+ tmpregion = sraRgnCreateRect(tx1, ty1, tx2, ty2);
+ add_region_xdamage(tmpregion);
+ sraRgnDestroy(tmpregion);
+}
+
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 int last_wx, last_wy, last_ww, last_wh;
+ double spin = 0.0, tm, tnow;
+ int scr_cnt = 0, input = 0, scroll_rep;
+ int get_out, got_one = 0, flush1 = 0, flush2 = 0;
+ int gk, gk0, ret = 0, db = debug_scroll;
+ int fail = 0;
+ int link, latency, netrate;
+
static double last_key_scroll = 0.0;
static double persist_start = 0.0;
- double this_scroll, scroll_persist = scr_key_persist;
+ static double last_bdpush = 0.0;
+ static int persist_count = 0;
+ double last_scroll, scroll_persist = scr_key_persist;
double spin_fac = 1.0, scroll_fac = 2.0;
- double max_spin, tnow = 0.0;
-
-//db = 1;
+ double max_spin;
+ double max_long_spin = 0.3;
+ get_out = 1;
if (got_keyboard_input) {
get_out = 0;
}
- dtime(&tnow);
+ dtime0(&tnow);
if (tnow < last_key_scroll + scroll_persist) {
get_out = 0;
}
if (get_out) {
persist_start = 0.0;
- xrecord_watch(0);
+ persist_count = 0;
+ last_bdpush = 0.0;
+ if (xrecording) {
+ xrecord_watch(0, SCR_KEY);
+ }
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);
+ scroll_rep = scrollability(xrecord_ptr_window, SCR_NONE) + 1;
max_spin = scr_key_time;
@@ -21130,25 +23571,36 @@ if (db) fprintf(stderr, "xrecord_set_by_keys: %d\n", xrecord_set_by_keys);
} else if (xrecord_scroll_keysym(last_keysym)) {
spin_fac = scroll_fac;
}
+ if (max_spin > max_long_spin) {
+ max_spin = max_long_spin;
+ }
+
+ /* XXX use this somehow */
+ link = link_rate(&latency, &netrate);
gk = gk0 = got_keyboard_input;
- dtime(&tm);
+ dtime0(&tm);
-if (db) fprintf(stderr, "check_xrecord: LOOP: scr_ev_cnt: %d max: %.3f\n",
+if (db) fprintf(stderr, "check_xrecord_keys: BEGIN 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);
+
+ scrollability(xrecord_ptr_window, SCR_SUCCESS);
+ scroll_rep = 2;
+
+ dtime0(&last_scroll);
+ last_key_scroll = last_scroll;
+ scr_cnt++;
break;
}
X_LOCK;
- XFlush(dpy);
flush1 = 1;
+ XFlush(dpy);
X_UNLOCK;
if (use_threads) {
@@ -21158,207 +23610,421 @@ if (db) fprintf(stderr, "check_xrecord: LOOP: scr_ev_cnt: %d max: %.3f\n",
}
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;
- }
-
+ X_LOCK;
if (got_keyboard_input > gk) {
gk = got_keyboard_input;
+ 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);
+if (db) fprintf(stderr, "check_xrecord: more keys: %.3f\n", 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 (spin >= max_spin * spin_fac) {
+if (db) fprintf(stderr, "check_xrecord: SPIN-OUT: %.3f/%.3f\n", spin,
+ max_spin * spin_fac);
+ fail = 1;
+ break;
+ }
}
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;
+ int dret, ev = scr_ev_cnt - 1;
+ int bdx, bdy, bdskinny, bdpush = 0;
+
+ last_wx = scr_ev[ev].win_x;
+ last_wy = scr_ev[ev].win_y;
+ last_ww = scr_ev[ev].win_w;
+ last_wh = scr_ev[ev].win_h;
+
+ /* assume scrollbar on rhs: */
+ bdx = last_wx + last_ww + 3;
+ bdy = last_wy + last_wh/2;
+ bdskinny = 32;
+
+ if (persist_start == 0.0) {
+ bdpush = 0;
+ } else {
+ set_bdpush(SCR_KEY, &last_bdpush, &bdpush);
}
- dret = push_scr_ev(bdpush);
+
+ dret = push_scr_ev(0.25, SCR_KEY, bdpush, bdx, bdy, bdskinny);
+
ret = 1 + dret;
scr_ev_cnt = 0;
}
+ if ((got_one && ret < 2) || persist_count) {
+ mark_for_xdamage(last_wx, last_wy, last_ww, last_wh);
+ }
+
+ if (fail) {
+ scrollability(xrecord_ptr_window, SCR_FAIL);
+ }
+
if (xrecording) {
if (ret < 2) {
- xrecord_watch(0);
+ xrecord_watch(0, SCR_KEY);
}
}
- if (this_scroll > 0.0) {
- last_key_scroll = this_scroll;
- }
-
if (ret == 2) {
if (persist_start == 0.0) {
dtime(&persist_start);
+ last_bdpush = persist_start;
}
} else {
persist_start = 0.0;
+ last_bdpush = 0.0;
+ }
+
+ /* since we've flushed it, we might as well avoid -input_skip */
+ if (flush1 || flush2) {
+ got_keyboard_input = 0;
+ got_pointer_input = 0;
}
return ret;
}
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 int last_wx, last_wy, last_ww, last_wh;
+ double spin = 0.0, tm, tnow;
+ int i, scr_cnt = 0, input = 0, scroll_rep;
+ int get_out, got_one = 0, flush1 = 0, flush2 = 0;
+ int gp, gp0, ret = 0, db = debug_scroll;
+ int gk, gk0;
+ int fail = 0;
+ int link, latency, netrate;
+
+ int start_x, start_y, last_x, last_y;
static double last_mouse_scroll = 0.0;
- 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;
-
+ double last_scroll;
+ double max_spin[3], max_long[3], persist[3];
+ double flush1_time = 0.01;
+ static double last_flush = 0.0;
+ double last_bdpush = 0.0, button_up_time = 0.0;
+ int button_mask_save;
+ int already_down = 0, max_ptr_eat = 20;
+ static int want_back_in = 0;
+ int came_back_in;
+
+ get_out = 1;
if (button_mask) {
get_out = 0;
}
- dtime(&tnow);
- if (tnow < last_mouse_scroll + scroll_persist) {
+ if (want_back_in) {
get_out = 0;
- }
+ }
+ dtime0(&tnow);
+if (0) fprintf(stderr, "check_xrecord_mouse: IN xrecording: %d\n", xrecording);
if (get_out) {
- persist_start = 0.0;
- xrecord_watch(0);
+ if (xrecording) {
+ xrecord_watch(0, SCR_MOUSE);
+ }
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);
+ scroll_rep = scrollability(xrecord_ptr_window, SCR_NONE) + 1;
- max_spin = scr_mouse_time;
+ if (button_mask_prev) {
+ already_down = 1;
+ }
+ if (want_back_in) {
+ came_back_in = 1;
+ } else {
+ came_back_in = 0;
+ }
+ want_back_in = 0;
- if (tnow < last_mouse_scroll + scroll_persist) {
- max_spin = 1.25*(tnow - last_mouse_scroll);
+ /*
+ * set up times for the various "reputations"
+ *
+ * 0 => -1, has been tried but never found a scroll.
+ * 1 => 0, has not been tried.
+ * 2 => +1, has been tried and found a scroll.
+ */
+
+ /* first spin-out time (no events) */
+ max_spin[0] = 1*scr_mouse_time;
+ max_spin[1] = 2*scr_mouse_time;
+ max_spin[2] = 4*scr_mouse_time;
+ if (!already_down) {
+ for (i=0; i<3; i++) {
+ max_spin[i] *= 1.5;
+ }
+ }
+
+ /* max time between events */
+ persist[0] = 1*scr_mouse_persist;
+ persist[1] = 2*scr_mouse_persist;
+ persist[2] = 4*scr_mouse_persist;
+
+ /* absolute max time in the loop */
+ max_long[0] = scr_mouse_maxtime;
+ max_long[1] = scr_mouse_maxtime;
+ max_long[2] = scr_mouse_maxtime;
+
+ pointer_flush_delay = scr_mouse_pointer_delay;
+
+ /* slow links: */
+ link = link_rate(&latency, &netrate);
+ if (link == LR_DIALUP) {
+ for (i=0; i<3; i++) {
+ max_spin[i] *= 2.0;
+ }
+ pointer_flush_delay *= 2;
+ } else if (link == LR_BROADBAND) {
+ pointer_flush_delay *= 2;
}
gp = gp0 = got_pointer_input;
- dtime(&tm);
+ gk = gk0 = got_keyboard_input;
+ dtime0(&tm);
-if (db) fprintf(stderr, "check_xrecord: LOOP: scr_ev_cnt: %d max: %.3f\n",
- scr_ev_cnt, max_spin);
+ /*
+ * this is used for border pushes (bdpush) to guess location
+ * of scrollbar (region rects containing this point are pushed).
+ */
+ last_x = start_x = cursor_x;
+ last_y = start_y = cursor_y;
- while (1) {
+if (db) fprintf(stderr, "check_xrecord_mouse: BEGIN LOOP: scr_ev_cnt: %d max: %.3f\n",
+ scr_ev_cnt, max_spin[scroll_rep]);
+ while (1) {
+ double spin_check;
if (scr_ev_cnt) {
+ int dret, ev = scr_ev_cnt - 1;
+ int bdpush = 0, bdx, bdy, bdskinny;
+ double tm, dt;
+
got_one = 1;
- this_scroll = 0.0;
- dtime(&this_scroll);
- break;
+ scrollability(xrecord_ptr_window, SCR_SUCCESS);
+ scroll_rep = 2;
+
+ scr_cnt++;
+
+ dtime0(&last_scroll);
+ last_mouse_scroll = last_scroll;
+
+ if (last_bdpush == 0.0) {
+ last_bdpush = last_scroll;
+ }
+
+ bdx = start_x;
+ bdy = start_y;
+ bdskinny = 32;
+
+ set_bdpush(SCR_MOUSE, &last_bdpush, &bdpush);
+
+ dtime0(&tm);
+
+ dret = push_scr_ev(0.35, SCR_MOUSE, bdpush, bdx,
+ bdy, bdskinny);
+ ret = 1 + dret;
+
+ dt = dtime(&tm);
+
+if (db) fprintf(stderr, " dret: %d scr_ev_cnt: %d dt: %.4f\n",
+ dret, scr_ev_cnt, dt);
+
+ last_wx = scr_ev[ev].win_x;
+ last_wy = scr_ev[ev].win_y;
+ last_ww = scr_ev[ev].win_w;
+ last_wh = scr_ev[ev].win_h;
+ scr_ev_cnt = 0;
+
+ if (! dret) {
+ break;
+ }
+ if (0 && button_up_time > 0.0) {
+ /* we only take 1 more event with button up */
+if (db) fprintf(stderr, "check_xrecord: BUTTON_UP_SCROLL: %.3f\n", spin);
+ break;
+ }
}
- X_LOCK;
- XFlush(dpy);
- flush1 = 1;
- X_UNLOCK;
+
+ if (! flush1) {
+ if (! already_down || (!scr_cnt && spin>flush1_time)) {
+ flush1 = 1;
+ X_LOCK;
+ XFlush(dpy);
+ X_UNLOCK;
+ dtime0(&last_flush);
+ }
+ }
if (use_threads) {
usleep(1000);
} else {
rfbCFD(1000);
+ rfbCFD(0);
}
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;
+ input += eat_pointer(max_ptr_eat, 1);
+ gp = got_pointer_input;
}
-
- X_LOCK;
- if (flush2) {
- XFlush(dpy);
+ if (got_keyboard_input > gk) {
+ gk = got_keyboard_input;
+ input++;
}
+ X_LOCK;
XRecordProcessReplies(rdpy_data);
X_UNLOCK;
- if (! button_mask) {
- break;
+
+ if (! input) {
+ spin_check = 1.5 * max_spin[scroll_rep];
+ } else {
+ spin_check = max_spin[scroll_rep];
}
- }
-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 (button_up_time > 0.0) {
+ if (tm > button_up_time + max_spin[scroll_rep]) {
+if (db) fprintf(stderr, "check_xrecord: SPIN-OUT-BUTTON_UP: %.3f/%.3f\n", spin, tm - button_up_time);
+ break;
+ }
+ } else if (!scr_cnt) {
+ if (spin >= spin_check) {
- if (scr_ev_cnt) {
- int dret;
- double bdpush = 0.0;
- static double last_border_push = 0.0;
+if (db) fprintf(stderr, "check_xrecord: SPIN-OUT-1: %.3f/%.3f\n", spin, spin_check);
+ fail = 1;
+ break;
+ }
+ } else {
+ if (tm >= last_scroll + persist[scroll_rep]) {
- if (persist_start > 0.0 &&
- this_scroll > last_border_push + 1.00) {
- bdpush = 0.0;
- last_border_push = this_scroll;
+if (db) fprintf(stderr, "check_xrecord: SPIN-OUT-2: %.3f/%.3f\n", spin, tm - last_scroll);
+ break;
+ }
+ }
+ if (spin >= max_long[scroll_rep]) {
+
+if (db) fprintf(stderr, "check_xrecord: SPIN-OUT-3: %.3f/%.3f\n", spin, max_long[scroll_rep]);
+ break;
}
- dret = push_scr_ev(bdpush);
+
if (! button_mask) {
- dret = 0;
+ int doflush = 0;
+ if (button_up_time > 0.0) {
+ ;
+ } else if (came_back_in) {
+ dtime0(&button_up_time);
+ doflush = 1;
+ } else if (last_x == cursor_x && last_y == cursor_y) {
+if (db) fprintf(stderr, "check_xrecord: BUTTON-UP: %.3f/%.3f %d/%d %d/%d\n", spin, max_long[scroll_rep], last_x, last_y, cursor_x, cursor_y);
+ break;
+ } else {
+if (db) fprintf(stderr, "check_xrecord: BUTTON-UP-KEEP-GOING: %.3f/%.3f %d/%d %d/%d\n", spin, max_long[scroll_rep], last_x, last_y, cursor_x, cursor_y);
+ doflush = 1;
+ dtime0(&button_up_time);
+ }
+ if (doflush) {
+ flush1 = 1;
+ X_LOCK;
+ XFlush(dpy);
+ X_UNLOCK;
+ dtime0(&last_flush);
+ }
}
- ret = 1 + dret;
- scr_ev_cnt = 0;
+
+ last_x = cursor_x;
+ last_y = cursor_y;
}
- if (xrecording) {
- if (ret < 2) {
- xrecord_watch(0);
- }
+ if (fail) {
+ scrollability(xrecord_ptr_window, SCR_FAIL);
}
- if (this_scroll > 0.0) {
- last_mouse_scroll = this_scroll;
+ if (got_one) {
+ mark_for_xdamage(last_wx, last_wy, last_ww, last_wh);
}
- if (ret == 2) {
- if (persist_start == 0.0) {
- dtime(&persist_start);
+ /* flush any remaining pointer events. */
+ button_mask_save = button_mask;
+ pointer_queued_sent = 0;
+ last_x = cursor_x;
+ last_y = cursor_y;
+ pointer(-1, 0, 0, NULL);
+ pointer_flush_delay = 0.0;
+
+ if (xrecording && pointer_queued_sent && button_mask_save &&
+ (last_x != cursor_x || last_y != cursor_y) ) {
+if (db) fprintf(stderr, " pointer() push yields events on: ret=%d\n", ret);
+ if (ret == 2) {
+if (db) fprintf(stderr, " we decide to send ret=3\n");
+ want_back_in = 1;
+ ret = 3;
+ flush2 = 1;
+ } else {
+ if (ret) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ xrecord_watch(0, SCR_MOUSE);
}
} else {
- persist_start = 0.0;
+ if (ret) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ if (xrecording) {
+ xrecord_watch(0, SCR_MOUSE);
+ }
}
- return ret;
+ if (flush2) {
+ X_LOCK;
+ XFlush(dpy);
+ XFlush(rdpy_ctrl);
+ X_UNLOCK;
+
+ flush2 = 1;
+ dtime0(&last_flush);
+
+if (db) fprintf(stderr, "FLUSH-2\n");
+ }
+
+ /* since we've flushed it, we might as well avoid -input_skip */
+ if (flush1 || flush2) {
+ got_keyboard_input = 0;
+ got_pointer_input = 0;
+ }
+
+ if (ret) {
+ return ret;
+ } else if (scr_cnt) {
+ return 1;
+ } else {
+ return 0;
+ }
}
int check_xrecord(void) {
- int watch_keys = 0, watch_mouse = 0;
+ int watch_keys = 0, watch_mouse = 0, consider_mouse;
+ static int first = 1;
+ static int mouse_wants_back_in = 0;
+
+ if (first) {
+ int i;
+ for (i=0; i<SCR_RESULTS_MAX; i++) {
+ scroll_results[i].win = None;
+ scroll_results[i].time = 0.0;
+ scroll_results[i].result = 0;
+ }
+ first = 0;
+ }
if (! use_xrecord) {
return 0;
@@ -21367,6 +24033,8 @@ int check_xrecord(void) {
return 0;
}
+if (0) fprintf(stderr, "check_xrecord: IN xrecording: %d\n", xrecording);
+
if (! xrecording) {
return 0;
}
@@ -21380,11 +24048,26 @@ int check_xrecord(void) {
watch_mouse = 1;
}
- if (watch_mouse && button_mask && xrecord_set_by_mouse) {
- return check_xrecord_mouse();
+ if (button_mask || mouse_wants_back_in) {
+ consider_mouse = 1;
+ } else {
+ consider_mouse = 0;
+ }
+if (0) fprintf(stderr, "check_xrecord: button_mask: %d mouse_wants_back_in: %d\n", button_mask, mouse_wants_back_in);
+
+ if (watch_mouse && consider_mouse && xrecord_set_by_mouse) {
+ int ret = check_xrecord_mouse();
+ if (ret == 3) {
+ mouse_wants_back_in = 1;
+ } else {
+ mouse_wants_back_in = 0;
+ }
+ return ret;
} else if (watch_keys && xrecord_set_by_keys) {
+ mouse_wants_back_in = 0;
return check_xrecord_keys();
} else {
+ mouse_wants_back_in = 0;
return 0;
}
}
@@ -21404,13 +24087,26 @@ int check_xrecord(void) {
}
int try_copyrect(Window frame, int x, int y, int w, int h, int dx, int dy,
- int *obscured) {
+ int *obscured, sraRegionPtr extra_clip, double max_wait) {
static int dt_bad = 0;
static time_t dt_bad_check = 0;
int x1, y1, x2, y2, sent_copyrect = 0;
+ int req, mod, cpy, ncli;
+ double tm, dt;
DB_SET
+ get_client_regions(&req, &mod, &cpy, &ncli);
+ if (cpy) {
+ /* one is still pending... try to force it out: */
+ fb_push_wait(max_wait);
+
+ get_client_regions(&req, &mod, &cpy, &ncli);
+ }
+ if (cpy) {
+ return 0;
+ }
+
*obscured = 0;
/*
* XXX KDE and xfce do some weird things with the
@@ -21430,7 +24126,7 @@ int try_copyrect(Window frame, int x, int y, int w, int h, int dx, int dy,
dt_bad_check = time(0);
}
- if (dt_bad) {
+ if (0 && dt_bad) {
sraRegionPtr rect;
/* send the whole thing... */
x1 = crfix(nfix(x, dpy_x), dx, dpy_x);
@@ -21445,9 +24141,11 @@ int try_copyrect(Window frame, int x, int y, int w, int h, int dx, int dy,
sent_copyrect = 1;
*obscured = 1; /* set to avoid an aggressive push */
- } else if (stack_list) {
+ } else if (stack_list_num || dt_bad) {
int k, tx1, tx2, ty1, ty2;
- sraRegionPtr moved_win, tmp_win;
+ sraRegionPtr moved_win, tmp_win, whole;
+ sraRectangleIterator *iter;
+ sraRect rect;
int saw_me = 0;
int orig_x, orig_y;
XWindowAttributes attr;
@@ -21465,18 +24163,25 @@ if (db2) fprintf(stderr, "moved_win: %4d %3d, %4d %3d 0x%lx ---\n",
moved_win = sraRgnCreateRect(tx1, ty1, tx2, ty2);
+ dtime0(&tm);
+
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];
+ for (k = stack_list_num - 1; k >= 0; k--) {
+ Window swin;
+
+ if (dt_bad) {
+ break;
+ }
+
+ swin = stack_list[k].win;
if (swin == frame) {
if (db2) {
-saw_me = 1;
-fprintf(stderr, " ----------\n");
+saw_me = 1; fprintf(stderr, " ----------\n");
} else {
break;
}
@@ -21486,9 +24191,37 @@ fprintf(stderr, " ----------\n");
if (swin == None) {
continue;
}
- if (!valid_window(swin, &attr)) {
+ if (! stack_list[k].fetched ||
+ stack_list[k].time > tm + 2.0) {
+ if (!valid_window(swin, &attr, 1)) {
+ stack_list[k].valid = 0;
+ } else {
+ stack_list[k].valid = 1;
+ stack_list[k].x = attr.x;
+ stack_list[k].y = attr.y;
+ stack_list[k].width = attr.width;
+ stack_list[k].height = attr.height;
+ stack_list[k].depth = attr.depth;
+ stack_list[k].class = attr.class;
+ stack_list[k].backing_store =
+ attr.backing_store;
+ stack_list[k].map_state =
+ attr.map_state;
+ }
+ stack_list[k].fetched = 1;
+ stack_list[k].time = tm;
+ }
+ if (!stack_list[k].valid) {
continue;
}
+
+ attr.x = stack_list[k].x;
+ attr.y = stack_list[k].y;
+ attr.depth = stack_list[k].depth;
+ attr.width = stack_list[k].width;
+ attr.height = stack_list[k].height;
+ attr.map_state = stack_list[k].map_state;
+
if (attr.map_state != IsViewable) {
continue;
}
@@ -21523,8 +24256,7 @@ if (db2) fprintf(stderr, " : clips it.\n");
/*
* next, subtract from the initial window rectangle
- * anything that woul
- * window rectangle
+ * anything that would clip it.
*/
/* clip the window to the visible screen: */
@@ -21544,6 +24276,51 @@ if (db2 && saw_me) continue;
}
X_UNLOCK;
+ if (extra_clip && ! sraRgnEmpty(extra_clip)) {
+ whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
+
+ iter = sraRgnGetIterator(extra_clip);
+ while (sraRgnIteratorNext(iter, &rect)) {
+ /* clip the window to the visible screen: */
+ tx1 = rect.x1;
+ ty1 = rect.y1;
+ tx2 = rect.x2;
+ ty2 = rect.y2;
+ tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2);
+ sraRgnAnd(tmp_win, whole);
+
+ /* see if window clips us: */
+ if (sraRgnAnd(tmp_win, moved_win)) {
+ *obscured = 1;
+ }
+ sraRgnDestroy(tmp_win);
+
+ /* subtract it from our region: */
+ tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2);
+ sraRgnSubtract(moved_win, tmp_win);
+ sraRgnDestroy(tmp_win);
+
+ /*
+ * next, subtract from the initial window rectangle
+ * anything that would clip it.
+ */
+ tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2);
+ sraRgnOffset(tmp_win, -dx, -dy);
+
+ /* clip the window to the visible screen: */
+ sraRgnAnd(tmp_win, whole);
+
+ /* subtract it from our region: */
+ sraRgnSubtract(moved_win, tmp_win);
+ sraRgnDestroy(tmp_win);
+ }
+ sraRgnReleaseIterator(iter);
+ sraRgnDestroy(whole);
+ }
+
+ dt = dtime(&tm);
+if (db2) fprintf(stderr, " stack_work dt: %.4f\n", dt);
+
if (*obscured && !strcmp(wireframe_copyrect, "top")) {
; /* cannot send CopyRegion */
} else if (! sraRgnEmpty(moved_win)) {
@@ -21558,8 +24335,11 @@ if (db2 && saw_me) continue;
/* 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);
+ dtime0(&tm);
do_copyregion(shifted_region, dx, dy);
+ dt = dtime(&tm);
+if (db2) fprintf(stderr, "do_copyregion: %d %d %d %d dx: %d dy: %d dt: %.4f\n",
+ tx1, ty1, tx2, ty2, dx, dy, dt);
sent_copyrect = 1;
}
sraRgnDestroy(shifted_region);
@@ -21662,7 +24442,7 @@ int check_wireframe(void) {
int box_x, box_y, box_w, box_h;
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;
+ double spin = 0.0, tm, last_ptr, last_draw;
int frame_changed = 0, drew_box = 0, got_2nd_pointer = 0;
int special_t1 = 0, break_reason = 0;
static double first_dt_ave = 0.0;
@@ -21695,7 +24475,7 @@ if (db) fprintf(stderr, "\n*** button down!! x: %d y: %d\n", cursor_x, cursor_
* makes when it reparents the toplevel window.
*/
X_LOCK;
- if (! get_wm_frame_pos(&px, &py, &x, &y, &w, &h, &frame)) {
+ if (! get_wm_frame_pos(&px, &py, &x, &y, &w, &h, &frame, NULL)) {
if (db) fprintf(stderr, "NO get_wm_frame_pos: 0x%lx\n", frame);
X_UNLOCK;
return 0;
@@ -21731,11 +24511,11 @@ if (db) fprintf(stderr, "INTERIOR\n");
}
if (! wireframe_str || !strcmp(wireframe_str, WIREFRAME_PARMS)) {
- int latency = get_net_latency();
- int netrate = get_net_rate();
+ int link, latency, netrate;
static int didmsg = 0;
- if (latency > 100 || netrate < 10) { /* 100ms, 10KB/sec */
+ link = link_rate(&latency, &netrate);
+ if (link == LR_DIALUP || link == LR_BROADBAND) {
/* slow link, e.g. dialup, increase timeouts: */
first_event_spin *= 2.0;
frame_changed_spin *= 2.0;
@@ -21763,10 +24543,10 @@ if (db) fprintf(stderr, "INTERIOR\n");
* see if we can reuse the stack list (pause
* with button down)
*/
- if (stack_list) {
+ if (stack_list_num) {
int k, got_me = 0;
- for (k = stack_num -1; k >=0; k--) {
- if (frame == stack_list[k]) {
+ for (k = stack_list_num -1; k >=0; k--) {
+ if (frame == stack_list[k].win) {
got_me = 1;
break;
}
@@ -21777,7 +24557,7 @@ if (db) fprintf(stderr, "INTERIOR\n");
snapshot_stack_list(0, age);
}
}
- if (! stack_list) {
+ if (! stack_list_num) {
snapshot_stack_list(0, 0.0);
}
}
@@ -21801,7 +24581,7 @@ if (db) fprintf(stderr, "INTERIOR\n");
box_w = w;
box_h = h;
- dtime(&tm);
+ dtime0(&tm);
last_draw = spin;
@@ -21884,11 +24664,6 @@ if (db) fprintf(stderr, " ++pointer event!! [%02d] dt: %.3f x: %d y: %d mas
/* periodically try to let the wm get moving: */
if (!frame_changed && got_2nd_pointer % 4 == 0) {
-#if 0
- X_LOCK;
- XSync(dpy, False);
- X_UNLOCK;
-#endif
if (got_2nd_pointer == 0) {
usleep(50 * 1000);
} else {
@@ -21907,14 +24682,14 @@ if (db) fprintf(stderr, " ++pointer event!! [%02d] dt: %.3f x: %d y: %d mas
X_LOCK;
if (! get_wm_frame_pos(&px, &py, &x, &y, &w, &h,
- &frame)) {
+ &frame, NULL)) {
frame = 0x0;
if (db) fprintf(stderr, "NO get_wm_frame_pos: 0x%lx\n", frame);
}
if (frame != orig_frame) {
/* see if our original frame is still there */
- if (!valid_window(orig_frame, &attr)) {
+ if (!valid_window(orig_frame, &attr, 1)) {
X_UNLOCK;
/* our window frame went away! */
win_gone = 1;
@@ -22027,12 +24802,8 @@ if (db || db2) fprintf(stderr, "NO button_mask\n");
if (! drew_box) {
/* nice try, but no move or resize detected. cleanup. */
- if (stack_list) {
- X_LOCK;
- XFree(stack_list);
- X_UNLOCK;
- stack_list = NULL;
- stack_num = 0;
+ if (stack_list_num) {
+ stack_list_num = 0;
}
wireframe_in_progress = 0;
return 0;
@@ -22072,23 +24843,24 @@ if (db || db2) fprintf(stderr, "NO button_mask\n");
}
/* try to flush the wireframe removal: */
- fb_push();
+ fb_push_wait(0.1);
/* try to send a clipped copyrect of translation: */
sent_copyrect = try_copyrect(frame, x, y, w, h, dx, dy,
- &obscured);
+ &obscured, NULL, 0.15);
+if (db) fprintf(stderr, "send_copyrect: %d\n", sent_copyrect);
if (sent_copyrect) {
/* try to push the changes to viewers: */
if (! obscured) {
- fb_push();
+ fb_push_wait(0.1);
} else {
- fb_push();
+ fb_push_wait(0.1);
}
}
}
- if (stack_list) {
+ if (stack_list_num) {
/* clean up stack_list for next time: */
if (break_reason == 1 || break_reason == 2) {
/*
@@ -22097,17 +24869,19 @@ if (db || db2) fprintf(stderr, "NO button_mask\n");
*/
last_save_stacklist = time(0);
} else {
- X_LOCK;
- XFree(stack_list);
- X_UNLOCK;
- stack_list = NULL;
- stack_num = 0;
+ stack_list_num = 0;
}
}
/* final push (for -nowirecopyrect) */
rfbPE(1000);
wireframe_in_progress = 0;
+ urgent_update = 1;
+ if (use_xdamage) {
+ /* DAMAGE can queue ~1000 rectangles for a move */
+ clear_xdamage_mark_region(NULL, 1);
+ }
+
return 1;
}
@@ -22133,13 +24907,13 @@ if (db || db2) fprintf(stderr, "NO button_mask\n");
static void check_user_input2(double dt) {
- int eaten = 0, miss = 0, max_eat = 50;
+ int eaten = 0, miss = 0, max_eat = 50, do_flush = 1;
int g, g_in;
- double spin = 0.0, tm = 0.0;
+ double spin = 0.0, tm;
double quick_spin_fac = 0.40;
double grind_spin_time = 0.175;
- dtime(&tm);
+ dtime0(&tm);
g = g_in = got_pointer_input;
if (!got_pointer_input) {
return;
@@ -22163,9 +24937,7 @@ static void check_user_input2(double dt) {
} else {
rfbCFD(1000);
}
- X_LOCK;
- XFlush(dpy);
- X_UNLOCK;
+ rfbCFD(0);
spin += dtime(&tm);
@@ -22174,8 +24946,24 @@ static void check_user_input2(double dt) {
break;
}
if (got_pointer_input > g) {
+ int i, max_extra = max_eat / 2;
g = got_pointer_input;
- if (eaten++ < max_eat) {
+ eaten++;
+ for (i=0; i<max_extra; i++) {
+ rfbCFD(0);
+ if (got_pointer_input > g) {
+ g = got_pointer_input;
+ eaten++;
+ } else if (i > 1) {
+ break;
+ }
+ }
+ X_LOCK;
+ do_flush = 0;
+if (0) fprintf(stderr, "check_user_input2-A: XFlush %.4f\n", tm);
+ XFlush(dpy);
+ X_UNLOCK;
+ if (eaten < max_eat) {
continue;
}
} else {
@@ -22185,142 +24973,11 @@ static void check_user_input2(double dt) {
break;
}
}
-
-
- /*
- * Probably grinding with a lot of fb I/O if dt is this large.
- * (need to do this more elegantly)
- *
- * Current idea is to spin our wheels here *not* processing any
- * fb I/O, but still processing the user input. This user input
- * goes to the X display and changes it, but we don't poll it
- * while we "rest" here for a time on order of dt, the previous
- * scan_for_updates() time. We also break out if we miss enough
- * user input.
- */
- if (dt > grind_spin_time) {
- int i, ms, split = 30;
- double shim;
-
- /*
- * Break up our pause into 'split' steps. We get at
- * most one input per step.
- */
- shim = 0.75 * dt / split;
-
- ms = (int) (1000 * shim);
-
- /* cutoff how long the pause can be */
- if (split * ms > 300) {
- ms = 300 / split;
- }
-
- spin = 0.0;
- tm = 0.0;
- dtime(&tm);
-
- g = got_pointer_input;
- miss = 0;
- for (i=0; i<split; i++) {
- usleep(ms * 1000);
- if (show_multiple_cursors) {
- rfbPE(1000);
- } else {
- rfbCFD(1000);
- }
- spin += dtime(&tm);
- if (got_pointer_input > g) {
- X_LOCK;
- XFlush(dpy);
- X_UNLOCK;
- miss = 0;
- } else {
- miss++;
- }
- g = got_pointer_input;
- if (miss > 2) {
- break;
- }
- if (1000 * spin > ms * split) {
- break;
- }
- }
- }
-}
-
-static void check_user_input3(double dt) {
-
- int eaten = 0, miss = 0, max_eat = 50;
- int miss_max = 2;
- int g, g_in;
- double spin = 0.0, tm = 0.0;
- double quick_spin_fac = 0.40, button_down_time = 0.050;
- double grind_spin_time = 0.125;
-
- dtime(&tm);
- g = g_in = got_pointer_input;
- if (!got_pointer_input) {
- return;
- }
- /*
- * Try for some "quick" pointer input processing.
- *
- * About as fast as we can, we try to process user input calling
- * rfbProcessEvents or rfbCheckFds. We do this for a time on
- * order of the last scan_for_updates() time, dt, but if we stop
- * getting user input we break out. We will also break out if
- * we have processed max_eat inputs.
- *
- * Note that rfbCheckFds() does not send any framebuffer updates,
- * so is more what we want here, although it is likely they have
- * all be sent already.
- */
- while (1) {
- double spin_out;
- int mcut = miss_max;
-
- if (show_multiple_cursors) {
- rfbPE(1000);
- } else {
- rfbCFD(1000);
- }
+ if (do_flush) {
X_LOCK;
+if (0) fprintf(stderr, "check_user_input2-B: XFlush %.4f\n", tm);
XFlush(dpy);
X_UNLOCK;
-
-
- spin += dtime(&tm);
-
- spin_out = quick_spin_fac * dt;
- if (button_mask && button_down_time > spin_out) {
- spin_out = button_down_time;
- }
-
- if (spin > spin_out) {
- /* get out if spin time comparable to last scan time */
- break;
- }
-
- if (got_pointer_input > g) {
- g = got_pointer_input;
- if (button_mask) {
- miss = 0;
- }
- if (eaten++ < max_eat) {
- continue;
- }
- } else {
- miss++;
- }
-
- if (button_mask) {
- mcut = 4;
- }
- if (miss >= mcut) {
- if (! button_mask || spin > button_down_time) {
- break;
- }
- }
}
@@ -22353,8 +25010,7 @@ static void check_user_input3(double dt) {
}
spin = 0.0;
- tm = 0.0;
- dtime(&tm);
+ dtime0(&tm);
g = got_pointer_input;
miss = 0;
@@ -22367,7 +25023,17 @@ static void check_user_input3(double dt) {
}
spin += dtime(&tm);
if (got_pointer_input > g) {
+ int i, max_extra = max_eat / 2;
+ for (i=0; i<max_extra; i++) {
+ rfbCFD(0);
+ if (got_pointer_input > g) {
+ g = got_pointer_input;
+ } else if (i > 1) {
+ break;
+ }
+ }
X_LOCK;
+if (0) fprintf(stderr, "check_user_input2-C: XFlush %.4f\n", tm);
XFlush(dpy);
X_UNLOCK;
miss = 0;
@@ -22385,7 +25051,7 @@ static void check_user_input3(double dt) {
}
}
-static void check_user_input4(double dt, double dtr, int tile_diffs) {
+static void check_user_input3(double dt, double dtr, int tile_diffs) {
int allowed_misses, miss_tweak, i, g, g_in;
int last_was_miss, consecutive_misses;
@@ -22422,8 +25088,7 @@ static void check_user_input4(double dt, double dtr, int tile_diffs) {
gcnt = 0;
ginput = 0;
- tm = 0.0; /* timer variable */
- dtime(&tm);
+ dtime0(&tm);
to = tm; /* last time we did rfbPE() */
g = g_in = got_pointer_input;
@@ -22529,7 +25194,7 @@ int fb_update_sent(int *count) {
return rc;
}
-static void check_user_input5(double dt, double dtr, int tile_diffs) {
+static void check_user_input4(double dt, double dtr, int tile_diffs) {
int g, g_in, i, ginput, gcnt, tmp;
int last_was_miss, consecutive_misses;
@@ -22590,8 +25255,7 @@ static void check_user_input5(double dt, double dtr, int tile_diffs) {
first = 0;
}
- tm = 0.0; /* timer variable */
- dtime(&tm);
+ dtime0(&tm);
if (dt < dt_cut) {
dt_use = dt_cut;
@@ -22601,8 +25265,8 @@ static void check_user_input5(double dt, double dtr, int tile_diffs) {
if (push_frame) {
int cnt, iter = 0;
- double tp = 0.0, push_spin = 0.0;
- dtime(&tp);
+ double tp, push_spin = 0.0;
+ dtime0(&tp);
while (push_spin < dt_use * 0.5) {
fb_update_sent(&cnt);
if (cnt != update_count) {
@@ -22744,11 +25408,20 @@ static int check_user_input(double dt, double dtr, int tile_diffs, int *cnt) {
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;
+ /*
+ * 0: nothing found, proceed to other user input schemes.
+ * 1: events found, want to do a screen update now.
+ * 2: events found, want to loop back for some more.
+ * 3: events found, want to loop back for some more,
+ * and not have rfbPE() called.
+ *
+ * For 0, we precede below, otherwise return rc-1.
+ */
+if (debug_scroll && rc > 1) fprintf(stderr, " CXR: check_user_input ret %d\n", rc - 1);
+ if (rc == 0) {
+ ; /* proceed below. */
+ } else {
+ return rc - 1;
}
}
@@ -22787,11 +25460,9 @@ if (0) fprintf(stderr, " CXR: check_user_input ret 1\n");
if (pointer_mode == 2) {
check_user_input2(dt);
} else if (pointer_mode == 3) {
- check_user_input3(dt);
+ check_user_input3(dt, dtr, tile_diffs);
} else if (pointer_mode == 4) {
check_user_input4(dt, dtr, tile_diffs);
- } else if (pointer_mode == 5) {
- check_user_input5(dt, dtr, tile_diffs);
}
return 0;
}
@@ -22815,7 +25486,7 @@ double dtime(double *t_old) {
gettimeofday(&now, NULL);
t_now = now.tv_sec + ( (double) now.tv_usec/1000000. );
- if (*t_old == 0) {
+ if (*t_old == 0.0) {
*t_old = t_now;
return t_now;
}
@@ -22824,15 +25495,20 @@ double dtime(double *t_old) {
return(dt);
}
+/* common dtime() activities: */
+double dtime0(double *t_old) {
+ *t_old = 0.0;
+ return dtime(t_old);
+}
+
+double dnow(void) {
+ double t;
+ return dtime0(&t);
+}
+
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);
+ dtime0(&cd->timer);
}
int get_rate(int which) {
@@ -22841,7 +25517,7 @@ int get_rate(int which) {
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 */
+ static double save_rate = 1000 * NETRATE0;
iter = rfbGetClientIterator(screen);
while( (cl = rfbClientIteratorNext(iter)) ) {
@@ -22882,7 +25558,7 @@ int get_latency(void) {
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 */
+ static double save_lat = ((double) LATENCY0)/1000.0;
iter = rfbGetClientIterator(screen);
while( (cl = rfbClientIteratorNext(iter)) ) {
@@ -22965,8 +25641,8 @@ void initialize_speeds(void) {
if (! speeds_read_rate) {
int n = 0;
- double dt, timer = 0.0;
- dtime(&timer);
+ double dt, timer;
+ dtime0(&timer);
if (fullscreen) {
copy_image(fullscreen, 0, 0, 0, 0);
n = fullscreen->bytes_per_line * fullscreen->height;
@@ -22998,11 +25674,39 @@ int get_read_rate(void) {
return 0;
}
+int link_rate(int *latency, int *netrate) {
+ *latency = get_net_latency();
+ *netrate = get_net_rate();
+
+ if (speeds_str) {
+ if (!strcmp(speeds_str, "modem")) {
+ return LR_DIALUP;
+ } else if (!strcmp(speeds_str, "dsl")) {
+ return LR_BROADBAND;
+ } else if (!strcmp(speeds_str, "lan")) {
+ return LR_LAN;
+ }
+ }
+
+ if (*latency == LATENCY0 && *netrate == NETRATE0) {
+ return LR_UNSET;
+ } else if (*latency > 150 || *netrate < 20) {
+ return LR_DIALUP;
+ } else if (*latency > 50 || *netrate < 150) {
+ return LR_BROADBAND;
+ } else if (*latency < 10 && *netrate > 300) {
+ return LR_LAN;
+ } else {
+ return LR_UNKNOWN;
+ }
+}
+
int get_net_rate(void) {
+ int spm = speeds_net_rate_measured;
if (speeds_net_rate) {
return speeds_net_rate;
}
- if (! speeds_net_rate_measured) {
+ if (! spm || spm == NETRATE0) {
speeds_net_rate_measured = get_cmp_rate();
}
if (speeds_net_rate_measured) {
@@ -23012,10 +25716,11 @@ int get_net_rate(void) {
}
int get_net_latency(void) {
+ int spm = speeds_net_latency_measured;
if (speeds_net_latency) {
return speeds_net_latency;
}
- if (! speeds_net_latency_measured) {
+ if (! spm || spm == LATENCY0) {
speeds_net_latency_measured = get_latency();
}
if (speeds_net_latency_measured) {
@@ -23035,7 +25740,7 @@ void measure_send_rates(int init) {
int min_cmp = 10000, nclients;
rfbClientIteratorPtr iter;
rfbClientPtr cl;
- int db = 0;
+ int db = 0, msg = 0;
if (! measure_speeds) {
return;
@@ -23051,8 +25756,7 @@ void measure_send_rates(int init) {
if (start == 0.0) {
dtime(&start);
}
- now = 0.0;
- dtime(&now);
+ dtime0(&now);
now = now - start;
nclients = 0;
@@ -23078,15 +25782,13 @@ void measure_send_rates(int init) {
rbs = cl->rawBytesEquivalent;
if (init) {
- double tmr = 0.0;
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);
- dtime(&tmr);
- cd->timer = tmr;
+ cd->timer = dnow();
cd->cmp_bytes_sent = cbs;
cd->raw_bytes_sent = rbs;
continue;
@@ -23124,8 +25826,7 @@ if (db) fprintf(stderr, "%d client num rects req: %d mod: %d cbs: %d "
/* mark a small rectangle: */
mark_rect_as_modified(0, 0, 16, 16, 1);
- tm = 0.0;
- dtime(&tm);
+ dtime0(&tm);
dt2 = 0.0;
dt3 = 0.0;
@@ -23186,8 +25887,7 @@ if (db) fprintf(stderr, "dt2 calc: num rects req: %d/%d mod: %d/%d "
/* mark a 2nd small rectangle: */
mark_rect_as_modified(0, 0, 16, 16, 1);
i = 0;
- tm = 0.0;
- dtime(&tm);
+ dtime0(&tm);
dt3 = 0.0;
/*
@@ -23297,9 +25997,30 @@ if (db) fprintf(stderr, "dt3 calc: num rects req: %d/%d mod: %d/%d "
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);
+ msg = 1;
}
rfbReleaseClientIterator(iter);
+ if (msg) {
+ int link, latency, netrate;
+ char *str = "error";
+
+ link = link_rate(&latency, &netrate);
+ if (link == LR_UNSET) {
+ str = "LR_UNSET";
+ } else if (link == LR_UNKNOWN) {
+ str = "LR_UNKNOWN";
+ } else if (link == LR_DIALUP) {
+ str = "LR_DIALUP";
+ } else if (link == LR_BROADBAND) {
+ str = "LR_BROADBAND";
+ } else if (link == LR_LAN) {
+ str = "LR_LAN";
+ }
+ rfbLog("link_rate: %s - %d ms, %d KB/s\n", str, latency,
+ netrate);
+ }
+
if (init) {
if (nclients) {
screen->displayHook = measure_display_hook;
@@ -23313,10 +26034,15 @@ if (db) fprintf(stderr, "dt3 calc: num rects req: %d/%d mod: %d/%d "
* utility wrapper to call rfbProcessEvents
* checks that we are not in threaded mode.
*/
+#define USEC_MAX 999999 /* libvncsever assumes < 1 second */
void rfbPE(long usec) {
if (! screen) {
return;
}
+
+ if (usec > USEC_MAX) {
+ usec = USEC_MAX;
+ }
if (! use_threads) {
rfbProcessEvents(screen, usec);
}
@@ -23326,17 +26052,72 @@ void rfbCFD(long usec) {
if (! screen) {
return;
}
+ if (usec > USEC_MAX) {
+ usec = USEC_MAX;
+ }
if (! use_threads) {
rfbCheckFds(screen, usec);
}
}
+int choose_delay(void) {
+ static double t0 = 0.0, t1 = 0.0, t2 = 0.0;
+ static int x0, y0, x1, y1, x2, y2;
+ int dx0, dy0, dx1, dy1, dm, ms = waitms;
+ double cut1 = 0.2, cut2 = 0.1, cut3 = 0.25;
+ int fac = 1;
+
+ if (waitms == 0) {
+ return waitms;
+ }
+
+ t2 = dnow();
+ x2 = cursor_x;
+ y2 = cursor_y;
+
+ dx0 = nabs(x1 - x0);
+ dy0 = nabs(y1 - y0);
+ dx1 = nabs(x2 - x1);
+ dy1 = nabs(y2 - y1);
+ if (dx1 > dy1) {
+ dm = dx1;
+ } else {
+ dm = dy1;
+ }
+
+ if ((dx0 || dy0) && (dx1 || dy1)) {
+ if (t2 < t0 + cut1 || t2 < t1 + cut2 || dm > 20) {
+ fac = 3;
+ }
+ } else if ((dx1 || dy1) && dm > 40) {
+ fac = 3;
+ }
+
+ if (fac == 1 && t2 < last_keyboard_time + cut3) {
+ fac = 2;
+ }
+ ms = waitms / fac;
+ if (ms == 0) {
+ ms = 1;
+ }
+
+ x0 = x1;
+ y0 = y1;
+ t0 = t1;
+
+ x1 = x2;
+ y1 = y2;
+ t1 = t2;
+
+ return ms;
+}
+
/*
* main x11vnc loop: polls, checks for events, iterate libvncserver, etc.
*/
static void watch_loop(void) {
- int cnt = 0, tile_diffs = 0;
- double dt = 0.0, dtr = 0.0;
+ int cnt = 0, tile_diffs = 0, skip_pe = 0;
+ double tm = 0.0, dt = 0.0, dtr = 0.0;
time_t start = time(0);
if (use_threads) {
@@ -23351,27 +26132,35 @@ static void watch_loop(void) {
urgent_update = 0;
if (! use_threads) {
- double tm = 0.0;
dtime(&tm);
- measure_send_rates(1);
- rfbPE(-1);
- measure_send_rates(0);
+ if (! skip_pe) {
+ measure_send_rates(1);
+ rfbPE(-1);
+ measure_send_rates(0);
+ fb_update_sent(NULL);
+ }
dtr = dtime(&tm);
- fb_update_sent(NULL);
if (! cursor_shape_updates) {
/* undo any cursor shape requests */
disable_cursor_shape_updates(screen);
}
- if (screen && screen->clientHead &&
- check_user_input(dt, dtr, tile_diffs, &cnt)) {
+ if (screen && screen->clientHead) {
+ int ret = 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;
+ if (ret == 2) {
+ skip_pe = 1;
+ }
+ if (ret) {
+if (debug_scroll) fprintf(stderr, "watch_loop: LOOP-BACK: %d\n", ret);
+ continue;
+ }
}
} else if (use_threads && wireframe && button_mask) {
check_wireframe();
}
+ skip_pe = 0;
if (shut_down) {
clean_up_exit(0);
@@ -23385,9 +26174,11 @@ if (0) fprintf(stderr, "watch_loop: LOOP-BACK\n");
check_new_clients();
check_xevents();
+ check_autorepeat();
check_connect_inputs();
check_padded_fb();
check_xdamage_state();
+ check_add_keysyms();
if (started_as_root) {
check_switched_user();
}
@@ -23433,8 +26224,8 @@ if (0) fprintf(stderr, "watch_loop: LOOP-BACK\n");
X_UNLOCK;
} else {
/* for timing the scan to try to detect thrashing */
- double tm = 0.0;
- dtime(&tm);
+ double tm;
+ dtime0(&tm);
if (use_snapfb) {
int t, tries = 5;
@@ -23446,14 +26237,22 @@ if (0) fprintf(stderr, "watch_loop: LOOP-BACK\n");
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);
+
+if ((debug_tiles || debug_scroll > 1 || debug_wireframe > 1)
+ && (tile_diffs > 4 || debug_tiles > 1)) {
+ double rate = (tile_x * tile_y * bpp/8 * tile_diffs) / dt;
+ fprintf(stderr, "============================= TILES: %d dt: %.4f"
+ " t: %.4f %.2f MB/s\n", tile_diffs, dt, tm - x11vnc_start,
+ rate/1000000.0);
}
check_x11_pointer();
}
/* sleep a bit to lessen load */
- usleep(waitms * 1000);
+ if (! urgent_update) {
+ int wait = choose_delay();
+ usleep(wait * 1000);
+ }
cnt++;
}
}
@@ -23938,6 +26737,9 @@ static void print_help(int mode) {
"-?, -opts Only list the x11vnc options.\n"
"-V, -version Print program version and last modification date.\n"
"\n"
+"-dbg instead of exiting after cleaning up, run a simple\n"
+" \"debug crash shell\" when fatal errors are trapped.\n"
+"\n"
"-q Be quiet by printing less informational output to\n"
" stderr. Same as -quiet.\n"
"-bg Go into the background after screen setup. Messages to\n"
@@ -23961,9 +26763,13 @@ static void print_help(int mode) {
" the X server instead of Mode_switch (AltGr).\n"
#endif
"-xkb When in modtweak mode, use the XKEYBOARD extension (if\n"
-" the X display supports it) to do the modifier tweaking.\n"
+"-noxkb the X display supports it) to do the modifier tweaking.\n"
" This is powerful and should be tried if there are still\n"
" keymapping problems when using -modtweak by itself.\n"
+" The default is to check whether some common keysyms,\n"
+" e.g. !, @, [, are only accessible via -xkb mode and if\n"
+" so then automatically enable the mode. To disable this\n"
+" automatic detection use -noxkb.\n"
"-skip_keycodes string Ignore the comma separated list of decimal keycodes.\n"
" Perhaps these are keycodes not on your keyboard but\n"
" your X server thinks exist. Currently only applies\n"
@@ -23972,10 +26778,16 @@ static void print_help(int mode) {
" when ambiguities exist (more than one Keycode per\n"
" Keysym). Run 'xmodmap -pk' to see your keymapping.\n"
" Example: \"-skip_keycodes 94,114\"\n"
+"-skip_dups Some VNC viewers send impossible repeated key events,\n"
+"-noskip_dups e.g. key-down, key-down, key-up, key-up all for the\n"
+" same key, or 20 downs in a row for the same key!\n"
+" Setting -skip_dups means to skip these duplicates and\n"
+" just process the first event. Default: %s\n"
"-add_keysyms If a Keysym is received from a VNC viewer and\n"
-" that Keysym does not exist in the X server, then\n"
+"-noadd_keysyms that Keysym does not exist in the X server, then\n"
" add the Keysym to the X server's keyboard mapping.\n"
-" Added Keysyms will be removed when x11vnc exits.\n"
+" Added Keysyms will be removed periodically and also\n"
+" when x11vnc exits. Default: %s\n"
#if 0
"-xkbcompat Ignore the XKEYBOARD extension. Use as a workaround for\n"
" some keyboard mapping problems. E.g. if you are using\n"
@@ -24000,14 +26812,53 @@ static void print_help(int mode) {
" To map a key to a button click, use the fake Keysyms\n"
" \"Button1\", ..., etc. E.g: \"-remap Super_R-Button2\"\n"
" (useful for pasting on a laptop)\n"
-"-norepeat Option -norepeat disables X server key auto repeat\n"
-"-repeat when VNC clients are connected. This works around a\n"
+"\n"
+" Dead keys: \"dead\" (or silent, mute) keys are keys that\n"
+" do not produce a character but must be followed by a 2nd\n"
+" keystroke. This is often used for accenting characters,\n"
+" e.g. to put \"'\" on top of \"a\" by pressing the dead\n"
+" key and then \"a\". Note that this interpretation\n"
+" is not part of core X11, it is up to the toolkit or\n"
+" application to decide how to react to the sequence.\n"
+" The X11 names for these keysyms are \"dead_grave\",\n"
+" \"dead_acute\", etc. However some VNC viewers send the\n"
+" keysyms \"grave\", \"acute\" instead thereby disabling\n"
+" the accenting. To work around this -remap can be used.\n"
+" For example \"-remap grave-dead_grave,acute-dead_acute\"\n"
+" As a convenience, \"-remap DEAD\" applies these remaps:\n"
+"\n"
+" g grave-dead_grave\n"
+" a acute-dead_acute\n"
+" c asciicircum-dead_circumflex\n"
+" t asciitilde-dead_tilde\n"
+" m macron-dead_macron\n"
+" b breve-dead_breve\n"
+" D abovedot-dead_abovedot\n"
+" d diaeresis-dead_diaeresis\n"
+" o degree-dead_abovering\n"
+" A doubleacute-dead_doubleacute\n"
+" r caron-dead_caron\n"
+" e cedilla-dead_cedilla\n"
+"\n"
+" If you just want a subset use the first letter\n"
+" label, e.g. \"-remap DEAD=ga\" to get the first two.\n"
+" Additional remaps may also be supplied via commas,\n"
+" e.g. \"-remap DEAD=ga,Super_R-Button2\". Finally,\n"
+" \"DEAD=missing\" means to apply all of the above as\n"
+" long as the left hand member is not already in the\n"
+" X11 keymap.\n"
+"\n"
+"-norepeat Option -norepeat disables X server key auto repeat when\n"
+"-repeat VNC clients are connected and VNC keyboard input is\n"
+" not idle for more than 5 minutes. This works around a\n"
" repeating keystrokes bug (triggered by long processing\n"
-" delays between key down and key up client events:\n"
-" either from large screen changes or high latency).\n"
+" delays between key down and key up client events: either\n"
+" from large screen changes or high latency).\n"
+" Default: %s\n"
+"\n"
" Note: your VNC viewer side will likely do autorepeating,\n"
" so this is no loss unless someone is simultaneously at\n"
-" the real X display. Default: %s\n"
+" the real X display.\n"
"\n"
" Use \"-norepeat N\" to set how many times norepeat will\n"
" be reset if something else (e.g. X session manager)\n"
@@ -24237,6 +27088,10 @@ static void print_help(int mode) {
" so CopyRect is skipped when scaling unless you specify\n"
" -wirecopyrect on the command line or by remote-control.\n"
"\n"
+"-debug_wireframe Turn on debugging info printout for the wireframe\n"
+" heuristics. \"-dwf\" is an alias. Specify multiple\n"
+" times for more output.\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"
@@ -24253,12 +27108,13 @@ static void print_help(int mode) {
" 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"
+" framebuffer polling/copying. On the other hand, it may\n"
+" induce undesired transients (e.g. a terminal cursor\n"
+" being scrolled up when it should not be) or other\n"
+" painting errors (window tearing, bunching-up, etc).\n"
+" These are automatically repaired in a short period\n"
+" of time. If this is unacceptable disable the feature\n"
+" with -noscrollcopyrect.\n"
"\n"
" \"mode\" can be \"never\" (same as -noscrollcopyrect)\n"
" to never try the copyrect, \"keys\" means to try it\n"
@@ -24280,6 +27136,102 @@ static void print_help(int mode) {
" it controlled. We want to be sure to skip the small\n"
" scrollbar and get the large panel. Default: %d\n"
"\n"
+"-scr_skip list Skip scroll detection for applications matching\n"
+" the comma separated list of strings in \"list\".\n"
+" Some applications implement their scrolling in\n"
+" strange ways where the XCopyArea, etc, also applies\n"
+" to invisible portions of the window: if we CopyRect\n"
+" those areas it looks awful during the scroll and\n"
+" there may be painting errors left after the scroll.\n"
+" Soffice.bin is the worst known offender.\n"
+"\n"
+" Use \"##\" to denote the start of the application class\n"
+" (e.g. \"##XTerm\") and \"++\" to denote the start\n"
+" of the application instance name (e.g. \"++xterm\").\n"
+" The string your list is matched against is of the form\n"
+" \"^^WM_NAME##Class++Instance<same-for-any-subwindows>\"\n"
+" The \"xlsclients -la\" command will provide this info.\n"
+"\n"
+" If a pattern is prefixed with \"KEY:\" it only applies\n"
+" to Keystroke generated scrolls (e.g. Up arrow). If it\n"
+" is prefixed with \"MOUSE:\" it only applies to Mouse\n"
+" induced scrolls (e.g. dragging on a scrollbar).\n"
+" Default: %s\n"
+"\n"
+"-scr_inc list Opposite of -scr_skip: this list is consulted first\n"
+" and if there is a match the window will be monitored\n"
+" via RECORD for scrolls irrespective of -scr_skip.\n"
+" Use -scr_skip '*' to skip anything that does not match\n"
+" your -scr_inc. Use -scr_inc '*' to include everything.\n"
+"\n"
+"-scr_keys list For keystroke scroll detection, only apply the RECORD\n"
+" heuristics to the comma separated list of keysyms in\n"
+" \"list\". You may find the RECORD overhead for every\n"
+" one of your keystrokes disrupts typing too much, but you\n"
+" don't want to turn it off completely with \"-scr mouse\"\n"
+" and -scr_parms does not work or is too confusing.\n"
+"\n"
+" The listed keysyms can be numeric or the keysym\n"
+" names in the <X11/keysymdef.h> header file or from the\n"
+" xev(1) program. Example: \"-scr_keys Up,Down,Return\".\n"
+" One probably wants to have application specific lists\n"
+" (e.g. for terminals, etc) but that is too icky to think\n"
+" about for now...\n"
+"\n"
+" If \"list\" begins with the \"-\" character the list\n"
+" is taken as an exclude list: all keysyms except those\n"
+" list will be considered. The special string \"builtin\"\n"
+" expands to an internal list of keysyms that are likely\n"
+" to cause scrolls. BTW, by default modifier keys,\n"
+" Shift_L, Control_R, etc, are skipped since they almost\n"
+" never induce scrolling by themselves.\n"
+"\n"
+"-scr_parms string Set various parameters for the scrollcopyrect mode.\n"
+" The format is similar to that for -wireframe and packed\n"
+" with lots of parameters:\n"
+"\n"
+" Format: T+B+L+R,t1+t2+t3,s1+s2+s3+s4+s5\n"
+" Default: %s\n"
+"\n"
+" If you leave nothing between commas: \",,\" the default\n"
+" value is used. If you don't specify enough commas,\n"
+" the trailing parameters are set to their defaults.\n"
+"\n"
+" \"T+B+L+R\" indicates four integers for how close in\n"
+" pixels the pointer has to be from the Top, Bottom, Left,\n"
+" or Right edges of the window to consider scrollcopyrect.\n"
+" If -wireframe overlaps it takes precedence. This is a\n"
+" speedup to quickly exclude a window from being watched\n"
+" for scrollcopyrect: set them all to zero to not try\n"
+" the speedup (things like selecting text will likely\n"
+" be slower).\n"
+"\n"
+" \"t1+t2+t3\" specify three floating point times in\n"
+" seconds that apply to scrollcopyrect detection with\n"
+" *Keystroke* input: t1 is how long to wait after a key\n"
+" is pressed for the first scroll, t2 is how long to keep\n"
+" looking after a Keystroke scroll for more scrolls.\n"
+" t3 is how frequently to try to update surrounding\n"
+" scrollbars outside of the scrolling area (0.0 to\n"
+" disable)\n"
+"\n"
+" \"s1+s2+s3+s4+s5\" specify five floating point times\n"
+" in seconds that apply to scrollcopyrect detection with\n"
+" *Mouse* input: s1 is how long to wait after a mouse\n"
+" button is pressed for the first scroll, s2 is how long\n"
+" to keep waiting for additional scrolls after the first\n"
+" Mouse scroll was detected. s3 is how frequently to\n"
+" try to update surrounding scrollbars outside of the\n"
+" scrolling area (0.0 to disable). s4 is how long to\n"
+" buffer pointer motion (to try to get fewer, bigger\n"
+" mouse scrolls). s5 is the maximum time to spend just\n"
+" updating the scroll window without updating the rest\n"
+" of the screen.\n"
+"\n"
+"-debug_scroll Turn on debugging info printout for the scroll\n"
+" heuristics. \"-ds\" is an alias. Specify it multiple\n"
+" times for more output.\n"
+"\n"
"-pointer_mode n Various pointer motion update schemes. \"-pm\" is\n"
" an alias. The problem is pointer motion can cause\n"
" rapid changes on the screen: consider the rapid changes\n"
@@ -24304,22 +27256,18 @@ static void print_help(int mode) {
" of input events it tries to detect if it should try to\n"
" \"eat\" additional pointer events before continuing.\n"
"\n"
-" n=3 is basically the same as n=2 except with slightly\n"
-" tweaked parameters. We made this a new one so one\n"
-" could use -pm 2 for the old behavior. NOT FINISHED.\n"
-"\n"
-" n=4 is basically a dynamic -nodragging mode: it detects\n"
+" n=3 is basically a dynamic -nodragging mode: it detects\n"
" when the mouse motion has paused and then refreshes\n"
" the display.\n"
"\n"
-" n=5 attempts to measures network rates and latency,\n"
+" n=4 attempts to measures network rates and latency,\n"
" the video card read rate, and how many tiles have been\n"
" changed on the screen. From this, it aggressively tries\n"
" to push screen \"frames\" when it decides it has enough\n"
" resources to do so. NOT FINISHED.\n"
"\n"
-" The default n is %d. Note that modes 2, 3, 4, 5 will\n"
-" skip -input_skip keyboard events (but it will not count\n"
+" The default n is %d. Note that modes 2, 3, 4 will skip\n"
+" -input_skip keyboard events (but it will not count\n"
" pointer events). Also note that these modes are not\n"
" available in -threads mode which has its own pointer\n"
" event handling mechanism.\n"
@@ -24337,7 +27285,7 @@ static void print_help(int mode) {
"\n"
"-speeds rd,bw,lat x11vnc tries to estimate some speed parameters that\n"
" are used to optimize scheduling (e.g. -pointer_mode\n"
-" 5) and other things. Use the -speeds option to set\n"
+" 4) and other things. Use the -speeds option to set\n"
" these manually. The triple \"rd,bw,lat\" corresponds\n"
" to video h/w read rate in MB/sec, network bandwidth to\n"
" clients in KB/sec, and network latency to clients in\n"
@@ -24369,6 +27317,10 @@ static void print_help(int mode) {
" (deferUpdateTime) Default: %d\n"
"-wait time Time in ms to pause between screen polls. Used to cut\n"
" down on load. Default: %d\n"
+"-readtimeout n Set libvncserver rfbMaxClientWait to n seconds. On\n"
+" slow links that take a long time to paint the first\n"
+" screen libvncserver may hit the timeout and drop the\n"
+" connection. Default: %d seconds.\n"
"-nap Monitor activity and if it is low take longer naps\n"
"-nonap between screen polls to really cut down load when idle.\n"
" Default: %s\n"
@@ -24377,7 +27329,8 @@ static void print_help(int mode) {
" for about 1.5 secs). Use 0 to disable. Default: %d\n"
"\n"
"-noxdamage Do not use the X DAMAGE extension to detect framebuffer\n"
-" changes even if it is available.\n"
+" changes even if it is available. Use -xdamage if your\n"
+" default is to have it off.\n"
"\n"
" x11vnc's use of the DAMAGE extension: 1) significantly\n"
" reduces the load when the screen is not changing much,\n"
@@ -24423,6 +27376,7 @@ static void print_help(int mode) {
" by checking the tile near the boundary. Default: %d\n"
"-fuzz n Tolerance in pixels to mark a tiles edges as changed.\n"
" Default: %d\n"
+"-debug_tiles Print debugging output for tiles, fb updates, etc.\n"
"\n"
"-snapfb Instead of polling the X display framebuffer (fb) for\n"
" changes, periodically copy all of X display fb into main\n"
@@ -24689,6 +27643,8 @@ static void print_help(int mode) {
" xkb enable -xkb modtweak mode.\n"
" noxkb disable -xkb modtweak mode.\n"
" skip_keycodes:str enable -xkb -skip_keycodes \"str\".\n"
+" skip_dups enable -skip_dups mode.\n"
+" noskip_dups disable -skip_dups mode.\n"
" add_keysyms enable -add_keysyms mode.\n"
" noadd_keysyms stop adding keysyms. those added will\n"
" still be removed at exit.\n"
@@ -24742,6 +27698,10 @@ static void print_help(int mode) {
" scrollcopyrect:str set -scrollcopyrect string. same \"scr\"\n"
" noscrollcopyrect disable -scrollcopyrect mode. \"noscr\"\n"
" scr_area:n set -scr_area to n\n"
+" scr_skip:list set -scr_skip to \"list\"\n"
+" scr_inc:list set -scr_inc to \"list\"\n"
+" scr_keys:list set -scr_keys to \"list\"\n"
+" scr_parms:str set -scr_parms parameters.\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"
@@ -24751,7 +27711,7 @@ static void print_help(int mode) {
" nodebug_keyboard disable -debug_keyboard, same as \"nodk\"\n"
" defer:n set -defer to n ms,same as deferupdate:n\n"
" wait:n set -wait to n ms.\n"
-" rfbwait:n set -rfbwait (rfbMaxClientWait) to n ms.\n"
+" readtimeout:n set read timeout to n seconds.\n"
" nap enable -nap mode.\n"
" nonap disable -nap mode.\n"
" sb:n set -sb to n s, same as screen_blank:n\n"
@@ -24792,6 +27752,10 @@ static void print_help(int mode) {
" nodebug_wireframe disable debugging wireframe mechanism.\n"
" debug_scroll enable debugging scrollcopy mechanism.\n"
" nodebug_scroll disable debugging scrollcopy mechanism.\n"
+" debug_tiles enable -debug_tiles\n"
+" nodebug_tiles disable -debug_tiles\n"
+" dbg enable -dbg crash shell\n"
+" nodbg disable -dbg crash shell\n"
"\n"
" noremote disable the -remote command processing,\n"
" it cannot be turned back on.\n"
@@ -24846,38 +27810,40 @@ static void print_help(int mode) {
" solid nosolid blackout xinerama noxinerama xtrap\n"
" noxtrap xrandr noxrandr xrandr_mode padgeom quiet q\n"
" noquiet modtweak nomodtweak xkb noxkb skip_keycodes\n"
-" 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 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"
-" nap nonap sb screen_blank fs gaps grow fuzz snapfb\n"
-" nosnapfb rawfb progressive rfbport http nohttp httpport\n"
+" skip_dups noskip_dups add_keysyms noadd_keysyms\n"
+" clear_mods noclear_mods clear_keys noclear_keys\n"
+" remap repeat norepeat fb nofb bell nobell sel nosel\n"
+" primary noprimary cursorshape nocursorshape cursorpos\n"
+" nocursorpos cursor show_cursor noshow_cursor nocursor\n"
+" arrow xfixes noxfixes xdamage noxdamage xd_area xd_mem\n"
+" alphacut alphafrac alpharemove noalpharemove alphablend\n"
+" noalphablend xwarppointer xwarp noxwarppointer noxwarp\n"
+" buttonmap dragging nodragging wireframe_mode wireframe\n"
+" wf nowireframe nowf wirecopyrect wcr nowirecopyrect\n"
+" nowcr scr_area scr_skip scr_inc scr_keys scr_parms\n"
+" scrollcopyrect scr noscrollcopyrect noscr pointer_mode\n"
+" pm input_skip input client_input speeds debug_pointer dp\n"
+" nodebug_pointer nodp debug_keyboard dk nodebug_keyboard\n"
+" nodk deferupdate defer wait readtimeout nap nonap\n"
+" sb screen_blank fs gaps grow fuzz snapfb nosnapfb\n"
+" rawfb progressive rfbport http nohttp httpport\n"
" httpdir enablehttpproxy noenablehttpproxy alwaysshared\n"
" noalwaysshared nevershared noalwaysshared dontdisconnect\n"
-" nodontdisconnect desktop noremote\n"
+" nodontdisconnect desktop debug_xevents nodebug_xevents\n"
+" debug_xevents debug_xdamage nodebug_xdamage\n"
+" debug_xdamage debug_wireframe nodebug_wireframe\n"
+" debug_wireframe debug_scroll nodebug_scroll debug_scroll\n"
+" debug_tiles dbt nodebug_tiles nodbt debug_tiles dbg\n"
+" nodbg noremote\n"
"\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"
+" aro= display vncdisplay desktopname http_url auth\n"
+" users rootshift clipshift scale_str scaled_x scaled_y\n"
+" scale_numer scale_denom scale_fac scaling_blend\n"
+" scaling_nomult4 scaling_pad scaling_interpolate inetd\n"
+" privremote unsafe safer nocmds passwdfile using_shm\n"
+" logfile o flag rc norc h help V version lastmod bg\n"
+" sigpipe threads readrate netrate netlatency pipeinput\n"
+" clients 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"
@@ -24982,6 +27948,8 @@ static void print_help(int mode) {
shared ? "on":"off",
vnc_connect ? "-vncconnect":"-novncconnect",
use_modifier_tweak ? "-modtweak":"-nomodtweak",
+ skip_duplicate_key_events ? "-skip_dups":"-noskip_dups",
+ add_keysyms ? "-add_keysyms":"-noadd_keysyms",
no_autorepeat ? "-norepeat":"-repeat",
alt_arrow_max, alt_arrow,
alpha_threshold,
@@ -24992,10 +27960,13 @@ static void print_help(int mode) {
wireframe_copyrect_default,
scroll_copyrect_default,
scrollcopyrect_min_area,
+ scroll_skip_str0 ? scroll_skip_str0 : "(empty)",
+ SCROLL_COPYRECT_PARMS,
pointer_mode_max, pointer_mode,
ui_skip,
defer_update,
waitms,
+ rfbMaxClientWait/1000,
take_naps ? "take naps":"no naps",
screen_blank,
xdamage_max_area, NSCAN, xdamage_memory,
@@ -25121,7 +28092,7 @@ static char *choose_title(char *display) {
}
}
strncat(title, display, MAXN - strlen(title));
- if (subwin && valid_window(subwin, NULL)) {
+ if (subwin && valid_window(subwin, NULL, 0)) {
char *name;
if (XFetchName(dpy, subwin, &name)) {
strncat(title, " ", MAXN - strlen(title));
@@ -25367,7 +28338,7 @@ void immediate_switch_user(int argc, char* argv[]) {
int main(int argc, char* argv[]) {
- int i, len;
+ int i, len, tmpi;
int ev, er, maj, min;
char *arg;
int remote_sync = 0;
@@ -25383,6 +28354,8 @@ int main(int argc, char* argv[]) {
/* used to pass args we do not know about to rfbGetScreen(): */
int argc_vnc = 1; char *argv_vnc[128];
+ dtime0(&x11vnc_start);
+
if (!getuid() || !geteuid()) {
started_as_root = 1;
@@ -25392,6 +28365,7 @@ int main(int argc, char* argv[]) {
argv_vnc[0] = strdup(argv[0]);
program_name = strdup(argv[0]);
+ program_pid = (int) getpid();
solid_default = strdup(solid_default); /* for freeing with -R */
@@ -25614,6 +28588,8 @@ int main(int argc, char* argv[]) {
} else if (!strcmp(arg, "-V") || !strcmp(arg, "-version")) {
fprintf(stdout, "x11vnc: %s\n", lastmod);
exit(0);
+ } else if (!strcmp(arg, "-dbg")) {
+ crash_debug = 1;
} else if (!strcmp(arg, "-q") || !strcmp(arg, "-quiet")) {
quiet = 1;
} else if (!strcmp(arg, "-bg") || !strcmp(arg, "-background")) {
@@ -25627,17 +28603,28 @@ int main(int argc, char* argv[]) {
use_modifier_tweak = 1;
} else if (!strcmp(arg, "-nomodtweak")) {
use_modifier_tweak = 0;
+ got_nomodtweak = 1;
} else if (!strcmp(arg, "-isolevel3")) {
use_iso_level3 = 1;
} else if (!strcmp(arg, "-xkb")) {
+ use_modifier_tweak = 1;
use_xkb_modtweak = 1;
+ } else if (!strcmp(arg, "-noxkb")) {
+ use_xkb_modtweak = 0;
+ got_noxkb = 1;
} else if (!strcmp(arg, "-xkbcompat")) {
xkbcompat = 1;
} else if (!strcmp(arg, "-skip_keycodes")) {
CHECK_ARGC
skip_keycodes = strdup(argv[++i]);
+ } else if (!strcmp(arg, "-skip_dups")) {
+ skip_duplicate_key_events = 1;
+ } else if (!strcmp(arg, "-noskip_dups")) {
+ skip_duplicate_key_events = 0;
} else if (!strcmp(arg, "-add_keysyms")) {
add_keysyms++;
+ } else if (!strcmp(arg, "-noadd_keysyms")) {
+ add_keysyms = 0;
} else if (!strcmp(arg, "-clear_mods")) {
clear_mods = 1;
} else if (!strcmp(arg, "-clear_keys")) {
@@ -25734,6 +28721,9 @@ int main(int argc, char* argv[]) {
} else if (!strcmp(arg, "-nowirecopyrect")
|| !strcmp(arg, "-nowf")) {
set_wirecopyrect_mode("never");
+ } else if (!strcmp(arg, "-debug_wireframe")
+ || !strcmp(arg, "-dwf")) {
+ debug_wireframe++;
} else if (!strcmp(arg, "-scrollcopyrect")
|| !strcmp(arg, "-scr")) {
CHECK_ARGC
@@ -25749,6 +28739,21 @@ int main(int argc, char* argv[]) {
if (tn >= 0) {
scrollcopyrect_min_area = tn;
}
+ } else if (!strcmp(arg, "-scr_skip")) {
+ CHECK_ARGC
+ scroll_skip_str = strdup(argv[++i]);
+ } else if (!strcmp(arg, "-scr_inc")) {
+ CHECK_ARGC
+ scroll_good_str = strdup(argv[++i]);
+ } else if (!strcmp(arg, "-scr_keys")) {
+ CHECK_ARGC
+ scroll_key_list_str = strdup(argv[++i]);
+ } else if (!strcmp(arg, "-scr_parms")) {
+ CHECK_ARGC
+ scroll_copyrect_str = strdup(argv[++i]);
+ } else if (!strcmp(arg, "-debug_scroll")
+ || !strcmp(arg, "-ds")) {
+ debug_scroll++;
} else if (!strcmp(arg, "-pointer_mode")
|| !strcmp(arg, "-pm")) {
char *p, *s;
@@ -25786,6 +28791,9 @@ int main(int argc, char* argv[]) {
} else if (!strcmp(arg, "-wait")) {
CHECK_ARGC
waitms = atoi(argv[++i]);
+ } else if (!strcmp(arg, "-readtimeout")) {
+ CHECK_ARGC
+ rfbMaxClientWait = atoi(argv[++i]) * 1000;
} else if (!strcmp(arg, "-nap")) {
take_naps = 1;
} else if (!strcmp(arg, "-nonap")) {
@@ -25838,6 +28846,9 @@ int main(int argc, char* argv[]) {
} else if (!strcmp(arg, "-fuzz")) {
CHECK_ARGC
tile_fuzz = atoi(argv[++i]);
+ } else if (!strcmp(arg, "-debug_tiles")
+ || !strcmp(arg, "-dbt")) {
+ debug_tiles++;
} else if (!strcmp(arg, "-snapfb")) {
use_snapfb = 1;
} else if (!strcmp(arg, "-rawfb")) {
@@ -26143,17 +29154,11 @@ int main(int argc, char* argv[]) {
if (! scroll_copyrect) {
set_scrollcopyrect_mode(NULL);
}
+ initialize_scroll_matches();
/* increase rfbwait if threaded */
if (use_threads && ! got_rfbwait) {
- if (0) {
- /* -rfbwait = rfbScreen->maxClientWait is not used */
- argv_vnc[argc_vnc++] = strdup("-rfbwait");
- argv_vnc[argc_vnc++] = strdup("604800000");
- } else {
- /* set the global in sockets.c instead: */
- rfbMaxClientWait = 604800000;
- }
+ rfbMaxClientWait = 604800000;
}
/* no framebuffer (Win2VNC) mode */
@@ -26191,6 +29196,9 @@ int main(int argc, char* argv[]) {
}
}
+ /* initialize added_keysyms[] array to zeros */
+ add_keysym(NoSymbol);
+
/* tie together cases of -localhost vs. -listen localhost */
if (! listen_str) {
if (allow_list && !strcmp(allow_list, "127.0.0.1")) {
@@ -26206,6 +29214,8 @@ int main(int argc, char* argv[]) {
/* set OS struct UT */
uname(&UT);
+ initialize_crash_handler();
+
if (! quiet) {
fprintf(stderr, "\n");
fprintf(stderr, "Settings:\n");
@@ -26230,6 +29240,7 @@ int main(int argc, char* argv[]) {
fprintf(stderr, " conn_once: %d\n", connect_once);
fprintf(stderr, " timeout: %d\n", first_conn_timeout);
fprintf(stderr, " inetd: %d\n", inetd);
+ fprintf(stderr, " http: %d\n", try_http);
fprintf(stderr, " connect: %s\n", client_connect
? client_connect : "null");
fprintf(stderr, " connectfile %s\n", client_connect_file
@@ -26269,12 +29280,14 @@ int main(int argc, char* argv[]) {
fprintf(stderr, " rc_file: \"%s\"\n", rc_rcfile ? rc_rcfile
: "null");
fprintf(stderr, " norc: %d\n", rc_norc);
+ fprintf(stderr, " dbg: %d\n", crash_debug);
fprintf(stderr, " bg: %d\n", bg);
fprintf(stderr, " mod_tweak: %d\n", use_modifier_tweak);
fprintf(stderr, " isolevel3: %d\n", use_iso_level3);
fprintf(stderr, " xkb: %d\n", use_xkb_modtweak);
fprintf(stderr, " skipkeys: %s\n",
skip_keycodes ? skip_keycodes : "null");
+ fprintf(stderr, " skip_dups: %d\n", skip_duplicate_key_events);
fprintf(stderr, " addkeysyms: %d\n", add_keysyms);
fprintf(stderr, " xkbcompat: %d\n", xkbcompat);
fprintf(stderr, " clearmods: %d\n", clear_mods);
@@ -26290,6 +29303,7 @@ int main(int argc, char* argv[]) {
fprintf(stderr, " multicurs: %d\n", show_multiple_cursors);
fprintf(stderr, " curs_mode: %s\n", multiple_cursors_mode
? multiple_cursors_mode : "null");
+ fprintf(stderr, " arrow: %d\n", alt_arrow);
fprintf(stderr, " xfixes: %d\n", use_xfixes);
fprintf(stderr, " alphacut: %d\n", alpha_threshold);
fprintf(stderr, " alphafrac: %.2f\n", alpha_frac);
@@ -26303,6 +29317,19 @@ int main(int argc, char* argv[]) {
fprintf(stderr, " dragging: %d\n", show_dragging);
fprintf(stderr, " wireframe: %s\n", wireframe_str ?
wireframe_str : WIREFRAME_PARMS);
+ fprintf(stderr, " wirecopy: %s\n", wireframe_copyrect ?
+ wireframe_copyrect : wireframe_copyrect_default);
+ fprintf(stderr, " scrollcopy: %s\n", scroll_copyrect ?
+ scroll_copyrect : scroll_copyrect_default);
+ fprintf(stderr, " scr_area: %d\n", scrollcopyrect_min_area);
+ fprintf(stderr, " scr_skip: %s\n", scroll_skip_str ?
+ scroll_skip_str : scroll_skip_str0);
+ fprintf(stderr, " scr_inc: %s\n", scroll_good_str ?
+ scroll_good_str : scroll_good_str0);
+ fprintf(stderr, " scr_keys: %s\n", scroll_key_list_str ?
+ scroll_key_list_str : "null");
+ fprintf(stderr, " scr_parms: %s\n", scroll_copyrect_str ?
+ scroll_copyrect_str : SCROLL_COPYRECT_PARMS);
fprintf(stderr, " ptr_mode: %d\n", pointer_mode);
fprintf(stderr, " inputskip: %d\n", ui_skip);
fprintf(stderr, " speeds: %s\n", speeds_str
@@ -26311,6 +29338,7 @@ int main(int argc, char* argv[]) {
fprintf(stderr, " debug_key: %d\n", debug_keyboard);
fprintf(stderr, " defer: %d\n", defer_update);
fprintf(stderr, " waitms: %d\n", waitms);
+ fprintf(stderr, " readtimeout: %d\n", rfbMaxClientWait/1000);
fprintf(stderr, " take_naps: %d\n", take_naps);
fprintf(stderr, " sb: %d\n", screen_blank);
fprintf(stderr, " xdamage: %d\n", use_xdamage);
@@ -26332,7 +29360,7 @@ int main(int argc, char* argv[]) {
fprintf(stderr, " gui_mode: %s\n", gui_str
? gui_str : "null");
fprintf(stderr, " noremote: %d\n", !accept_remote_cmds);
- fprintf(stderr, " safemode: %d\n", safe_remote_only);
+ fprintf(stderr, " unsafe: %d\n", !safe_remote_only);
fprintf(stderr, " privremote: %d\n", priv_remote);
fprintf(stderr, " safer: %d\n", more_safe);
fprintf(stderr, " nocmds: %d\n", no_external_cmds);
@@ -26423,6 +29451,11 @@ int main(int argc, char* argv[]) {
scr = DefaultScreen(dpy);
rootwin = RootWindow(dpy, scr);
+ if (! quiet) {
+ rfbLog("\n");
+ rfbLog("------------------ USEFUL INFORMATION ------------------\n");
+ }
+
if (remote_cmd || query_cmd) {
int rc = do_remote_query(remote_cmd, query_cmd, remote_sync);
XFlush(dpy);
@@ -26474,11 +29507,40 @@ int main(int argc, char* argv[]) {
}
if (! quiet && xdamage_present && use_xdamage) {
rfbLog("X DAMAGE available on display, using it for"
- " polling hints\n");
- rfbLog(" to disable this behavior use: "
+ " polling hints.\n");
+ rfbLog(" To disable this behavior use: "
"'-noxdamage'\n");
}
+ if (! quiet && wireframe) {
+ rfbLog("Wireframing: -wireframe mode is in effect for window moves.\n");
+ rfbLog(" If this yields undesired behavior (poor response, painting\n");
+ rfbLog(" errors, etc) it may be disabled:\n");
+ rfbLog(" - use '-nowf' to disable wireframing completely.\n");
+ rfbLog(" - use '-nowcr' to disable the Copy Rectangle after the\n");
+ rfbLog(" moved window is released in the new position.\n");
+ rfbLog(" Also see the -help entry for tuning parameters.\n");
+ }
+
+ tmpi = 1;
+ if (scroll_copyrect) {
+ if (strstr(scroll_copyrect, "never")) {
+ tmpi = 0;
+ }
+ } else if (scroll_copyrect_default) {
+ if (strstr(scroll_copyrect_default, "never")) {
+ tmpi = 0;
+ }
+ }
+ if (! quiet && tmpi) {
+ rfbLog("Scroll Detection: -scrollcopyrect mode is in effect to\n");
+ rfbLog(" use RECORD extension to try to detect scrolling windows\n");
+ rfbLog(" (induced by either user keystroke or mouse input).\n");
+ rfbLog(" If this yields undesired behavior (poor response, painting\n");
+ rfbLog(" errors, etc) it may be disabled via: '-noscr'\n");
+ rfbLog(" Also see the -help entry for tuning parameters.\n");
+ }
+
overlay_present = 0;
#ifdef SOLARIS_OVERLAY
if (! XQueryExtension(dpy, "SUN_OVL", &maj, &ev, &er)) {
@@ -26666,6 +29728,12 @@ int main(int argc, char* argv[]) {
}
#endif
+ if (xkb_present && !use_xkb_modtweak && !got_noxkb) {
+ if (use_modifier_tweak) {
+ switch_to_xkb_if_better();
+ }
+ }
+
#if LIBVNCSERVER_HAVE_LIBXRANDR
if (! XRRQueryExtension(dpy, &xrandr_base_event_type, &er)) {
if (xrandr && ! quiet) {
@@ -26680,6 +29748,11 @@ int main(int argc, char* argv[]) {
}
#endif
+ if (! quiet) {
+ rfbLog("--------------------------------------------------------\n");
+ rfbLog("\n");
+ }
+
raw_fb_pass_go_and_collect_200_dollars:
if (! dt) {