diff options
author | runge <runge> | 2006-01-09 01:54:38 +0000 |
---|---|---|
committer | runge <runge> | 2006-01-09 01:54:38 +0000 |
commit | 71f2ec79180185a6c3db0c87f9d53c491dc31e76 (patch) | |
tree | 67c341571cbeb1bd9a0744cc8eb03b30ef04f381 /x11vnc/xevents.c | |
parent | def301266373e462f4a5e90eab443087ccfc7ccc (diff) | |
download | libtdevnc-71f2ec79180185a6c3db0c87f9d53c491dc31e76.tar.gz libtdevnc-71f2ec79180185a6c3db0c87f9d53c491dc31e76.zip |
x11vnc: the big split.
Diffstat (limited to 'x11vnc/xevents.c')
-rw-r--r-- | x11vnc/xevents.c | 981 |
1 files changed, 981 insertions, 0 deletions
diff --git a/x11vnc/xevents.c b/x11vnc/xevents.c new file mode 100644 index 0000000..5272333 --- /dev/null +++ b/x11vnc/xevents.c @@ -0,0 +1,981 @@ +/* -- xevents.c -- */ + +#include "x11vnc.h" +#include "xwrappers.h" +#include "xkb_bell.h" +#include "xrandr.h" +#include "xdamage.h" +#include "xrecord.h" +#include "selection.h" +#include "keyboard.h" +#include "cursor.h" +#include "gui.h" +#include "connections.h" + +/* XXX CHECK BEFORE RELEASE */ +int grab_buster = 0; +int sync_tod_delay = 3; + +void initialize_vnc_connect_prop(void); +void spawn_grab_buster(void); +void sync_tod_with_servertime(void); +void check_keycode_state(void); +void check_autorepeat(void); +void check_xevents(void); +void xcut_receive(char *text, int len, rfbClientPtr cl); + + +static void initialize_xevents(void); +static void print_xevent_bases(void); +static void get_prop(char *str, int len, Atom prop); +static void bust_grab(int reset); +static int process_watch(char *str, int parent, int db); +static void grab_buster_watch(int parent, char *dstr); + + +void initialize_vnc_connect_prop(void) { + vnc_connect_str[0] = '\0'; + vnc_connect_prop = XInternAtom(dpy, "VNC_CONNECT", False); +} + +static void initialize_xevents(void) { + static int did_xselect_input = 0; + static int did_xcreate_simple_window = 0; + static int did_vnc_connect_prop = 0; + static int did_xfixes = 0; + static int did_xdamage = 0; + static int did_xrandr = 0; + + if ((watch_selection || vnc_connect) && !did_xselect_input) { + /* + * register desired event(s) for notification. + * PropertyChangeMask is for CUT_BUFFER0 changes. + * XXX: does this cause a flood of other stuff? + */ + X_LOCK; + XSelectInput(dpy, rootwin, PropertyChangeMask); + X_UNLOCK; + did_xselect_input = 1; + } + if (watch_selection && !did_xcreate_simple_window) { + /* create fake window for our selection ownership, etc */ + + X_LOCK; + selwin = XCreateSimpleWindow(dpy, rootwin, 0, 0, 1, 1, 0, 0, 0); + X_UNLOCK; + did_xcreate_simple_window = 1; + } + + if (xrandr && !did_xrandr) { + initialize_xrandr(); + did_xrandr = 1; + } + if (vnc_connect && !did_vnc_connect_prop) { + initialize_vnc_connect_prop(); + did_vnc_connect_prop = 1; + } + if (xfixes_present && use_xfixes && !did_xfixes) { + initialize_xfixes(); + did_xfixes = 1; + } + if (xdamage_present && !did_xdamage) { + initialize_xdamage(); + did_xdamage = 1; + } +} + +static void print_xevent_bases(void) { + fprintf(stderr, "X event bases: xkb=%d, xtest=%d, xrandr=%d, " + "xfixes=%d, xdamage=%d, xtrap=%d\n", xkb_base_event_type, + xtest_base_event_type, xrandr_base_event_type, + xfixes_base_event_type, xdamage_base_event_type, + xtrap_base_event_type); + fprintf(stderr, " MapNotify=%d, ClientMsg=%d PropNotify=%d " + "SelNotify=%d, SelRequest=%d\n", MappingNotify, ClientMessage, + PropertyNotify, SelectionNotify, SelectionRequest); + fprintf(stderr, " SelClear=%d, Expose=%d\n", SelectionClear, Expose); +} + +static void get_prop(char *str, int len, Atom prop) { + Atom type; + int format, slen, dlen, i; + unsigned long nitems = 0, bytes_after = 0; + unsigned char* data = NULL; + + for (i=0; i<len; i++) { + str[i] = '\0'; + } + if (prop == None) { + return; + } + + slen = 0; + + do { + if (XGetWindowProperty(dpy, DefaultRootWindow(dpy), + prop, nitems/4, len/16, False, + AnyPropertyType, &type, &format, &nitems, &bytes_after, + &data) == Success) { + + dlen = nitems * (format/8); + if (slen + dlen > len) { + /* too big */ + XFree(data); + break; + } + memcpy(str+slen, data, dlen); + slen += dlen; + str[slen] = '\0'; + XFree(data); + } + } while (bytes_after > 0); +} + +static void bust_grab(int reset) { + static int bust_count = 0; + static time_t last_bust = 0; + time_t now = time(0); + KeyCode key; + int button, x, y, nb; + + if (now > last_bust + 180) { + bust_count = 0; + } + if (reset) { + bust_count = 0; + return; + } + + x = 0; + y = 0; + button = 0; + key = NoSymbol; + + nb = 8; + if (bust_count >= 3 * nb) { + fprintf(stderr, "too many bust_grab's %d for me\n", bust_count); + exit(0); + } + if (bust_count % nb == 0) { + button = 1; + } else if (bust_count % nb == 1) { + button = 1; + } else if (bust_count % nb == 2) { + key = XKeysymToKeycode(dpy, XK_Escape); + } else if (bust_count % nb == 3) { + button = 3; + } else if (bust_count % nb == 4) { + key = XKeysymToKeycode(dpy, XK_space); + } else if (bust_count % nb == 5) { + x = bust_count * 23; + y = bust_count * 17; + } else if (bust_count % nb == 5) { + button = 2; + } else if (bust_count % nb == 6) { + key = XKeysymToKeycode(dpy, XK_a); + } + + if (key == NoSymbol) { + key = XKeysymToKeycode(dpy, XK_a); + if (key == NoSymbol) { + button = 1; + } + } + + bust_count++; + + if (button) { + /* try button press+release */ + fprintf(stderr, "**bust_grab: button%d %.4f\n", + button, dnowx()); + XTestFakeButtonEvent_wr(dpy, button, True, CurrentTime); + XFlush(dpy); + usleep(50 * 1000); + XTestFakeButtonEvent_wr(dpy, button, False, CurrentTime); + } else if (x > 0) { + /* try button motion*/ + int scr = DefaultScreen(dpy); + + fprintf(stderr, "**bust_grab: x=%d y=%d %.4f\n", x, y, + dnowx()); + XTestFakeMotionEvent_wr(dpy, scr, x, y, CurrentTime); + XFlush(dpy); + usleep(50 * 1000); + + /* followed by button press */ + button = 1; + fprintf(stderr, "**bust_grab: button%d\n", button); + XTestFakeButtonEvent_wr(dpy, button, True, CurrentTime); + XFlush(dpy); + usleep(50 * 1000); + XTestFakeButtonEvent_wr(dpy, button, False, CurrentTime); + } else { + /* try Escape or Space press+release */ + fprintf(stderr, "**bust_grab: keycode: %d %.4f\n", + (int) key, dnowx()); + XTestFakeKeyEvent_wr(dpy, key, True, CurrentTime); + XFlush(dpy); + usleep(50 * 1000); + XTestFakeKeyEvent_wr(dpy, key, False, CurrentTime); + } + XFlush(dpy); + last_bust = time(0); +} + +typedef struct _grabwatch { + int pid; + int tick; + unsigned long time; + time_t change; +} grabwatch_t; +#define GRABWATCH 16 + +static int grab_buster_delay = 20; +static pid_t grab_buster_pid = 0; + +static int grab_npids = 1; + +static int process_watch(char *str, int parent, int db) { + int i, pid, ticker, npids; + char diff[128]; + unsigned long xtime; + static grabwatch_t watches[GRABWATCH]; + static int first = 1; + time_t now = time(0); + static time_t last_bust = 0; + int too_long, problems = 0; + + if (first) { + for (i=0; i < GRABWATCH; i++) { + watches[i].pid = 0; + watches[i].tick = 0; + watches[i].time = 0; + watches[i].change = 0; + } + first = 0; + } + + /* record latest value of prop */ + if (str && *str != '\0') { + if (sscanf(str, "%d/%d/%lu/%s", &pid, &ticker, &xtime, diff) + == 4) { + int got = -1, free = -1; + + if (db) fprintf(stderr, "grab_buster %d - %d - %lu - %s" + "\n", pid, ticker, xtime, diff); + + if (pid == parent && !strcmp(diff, "QUIT")) { + /* that's it. */ + return 0; + } + if (pid == 0 || ticker == 0 || xtime == 0) { + /* bad prop read. */ + goto badtickerstr; + } + for (i=0; i < GRABWATCH; i++) { + if (watches[i].pid == pid) { + got = i; + break; + } + if (free == -1 && watches[i].pid == 0) { + free = i; + } + } + if (got == -1) { + if (free == -1) { + /* bad news */; + free = GRABWATCH - 1; + } + watches[free].pid = pid; + watches[free].tick = ticker; + watches[free].time = xtime; + watches[free].change = now; + if (db) fprintf(stderr, "grab_buster free slot: %d\n", free); + } else { + if (db) fprintf(stderr, "grab_buster got slot: %d\n", got); + if (watches[got].tick != ticker) { + watches[got].change = now; + } + if (watches[got].time != xtime) { + watches[got].change = now; + } + watches[got].tick = ticker; + watches[got].time = xtime; + } + } else { + if (db) fprintf(stderr, "grab_buster bad prop str: %s\n", str); + } + } + + badtickerstr: + + too_long = grab_buster_delay; + if (too_long < 3 * sync_tod_delay) { + too_long = 3 * sync_tod_delay; + } + + npids = 0; + for (i=0; i < GRABWATCH; i++) { + if (watches[i].pid) { + npids++; + } + } + grab_npids = npids; + if (npids > 4) { + npids = 4; + } + + /* now check everyone we are tracking */ + for (i=0; i < GRABWATCH; i++) { + int fac = 1; + if (!watches[i].pid) { + continue; + } + if (watches[i].change == 0) { + watches[i].change = now; /* just to be sure */ + continue; + } + + pid = watches[i].pid; + + if (pid != parent) { + fac = 2; + } + if (npids > 0) { + fac *= npids; + } + + if (now > watches[i].change + fac*too_long) { + int process_alive = 1; + + fprintf(stderr, "grab_buster: problem with pid: " + "%d - %d/%d/%d\n", pid, (int) now, + (int) watches[i].change, too_long); + + if (kill((pid_t) pid, 0) != 0) { + if (1 || errno == ESRCH) { + process_alive = 0; + } + } + + if (!process_alive) { + watches[i].pid = 0; + watches[i].tick = 0; + watches[i].time = 0; + watches[i].change = 0; + fprintf(stderr, "grab_buster: pid gone: %d\n", + pid); + if (pid == parent) { + /* that's it */ + return 0; + } + } else { + int sleep = sync_tod_delay * 1000 * 1000; + + bust_grab(0); + problems++; + last_bust = now; + usleep(1 * sleep); + break; + } + } + } + + if (!problems) { + bust_grab(1); + } + return 1; +} + +static void grab_buster_watch(int parent, char *dstr) { + Atom ticker_atom = None; + int sleep = sync_tod_delay * 921 * 1000; + char propval[200]; + int ev, er, maj, min; + int db = 0; + + if (grab_buster > 1) { + db = 1; + } + + /* overwrite original dpy, we let orig connection sit unused. */ + dpy = XOpenDisplay(dstr); + if (!dpy) { + fprintf(stderr, "grab_buster_watch: could not reopen: %s\n", + dstr); + return; + } + rfbLogEnable(0); + + /* check for XTEST, etc, and then disable grabs for us */ + if (! XTestQueryExtension_wr(dpy, &ev, &er, &maj, &min)) { + xtest_present = 0; + } else { + xtest_present = 1; + } + if (! XETrapQueryExtension_wr(dpy, &ev, &er, &maj)) { + xtrap_present = 0; + } else { + xtrap_present = 1; + } + + if (! xtest_present && ! xtrap_present) { + fprintf(stderr, "grab_buster_watch: no grabserver " + "protection on display: %s\n", dstr); + return; + } + disable_grabserver(dpy, 0); + + usleep(3 * sleep); + + ticker_atom = XInternAtom(dpy, "X11VNC_TICKER", False); + if (! ticker_atom) { + fprintf(stderr, "grab_buster_watch: no ticker atom\n"); + return; + } + + while(1) { + int slp = sleep; + if (grab_npids > 1) { + slp = slp / 8; + } + usleep(slp); + usleep((int) (0.60 * rfac() * slp)); + + if (kill((pid_t) parent, 0) != 0) { + break; + } + + get_prop(propval, 128, ticker_atom); + if (db) fprintf(stderr, "got_prop: %s\n", propval); + + if (!process_watch(propval, parent, db)) { + break; + } + } +} + +void spawn_grab_buster(void) { +#if LIBVNCSERVER_HAVE_FORK + pid_t pid; + int parent = (int) getpid(); + char *dstr = strdup(DisplayString(dpy)); + + XCloseDisplay(dpy); + dpy = NULL; + + if ((pid = fork()) > 0) { + grab_buster_pid = pid; + if (! quiet) { + rfbLog("grab_buster pid is: %d\n", (int) pid); + } + } else if (pid == -1) { + fprintf(stderr, "spawn_grab_buster: could not fork\n"); + rfbLogPerror("fork"); + } else { + grab_buster_watch(parent, dstr); + exit(0); + } + + dpy = XOpenDisplay(dstr); + if (!dpy) { + rfbLog("failed to reopen display %s in spawn_grab_buster\n", + dstr); + exit(1); + } +#endif +} + +void sync_tod_with_servertime(void) { + static Atom ticker_atom = None; + XEvent xev; + char diff[128]; + static int seq = 0; + static unsigned long xserver_ticks = 1; + int i, db = 0; + + if (! ticker_atom) { + ticker_atom = XInternAtom(dpy, "X11VNC_TICKER", False); + } + if (! ticker_atom) { + return; + } + + XSync(dpy, False); + while (XCheckTypedEvent(dpy, PropertyNotify, &xev)) { + ; + } + + snprintf(diff, 128, "%d/%08d/%lu/%.6f", (int) getpid(), seq++, + xserver_ticks, servertime_diff); + XChangeProperty(dpy, rootwin, ticker_atom, XA_STRING, 8, + PropModeReplace, (unsigned char *) diff, strlen(diff)); + XSync(dpy, False); + + for (i=0; i < 10; i++) { + int k, got = 0; + + for (k=0; k < 5; k++) { + while (XCheckTypedEvent(dpy, PropertyNotify, &xev)) { + if (xev.xproperty.atom == ticker_atom) { + double stime; + + xserver_ticks = xev.xproperty.time; + stime = (double) xev.xproperty.time; + stime = stime/1000.0; + servertime_diff = dnow() - stime; + if (db) rfbLog("set servertime_diff: " + "%.6f\n", servertime_diff); + got = 1; + } + } + } + if (got) { + break; + } + usleep(1000); + } +} + +void check_keycode_state(void) { + static time_t last_check = 0; + int delay = 10, noinput = 3; + time_t now = time(0); + + if (! client_count) { + return; + } + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + + /* + * periodically update our model of the keycode_state[] + * by correlating with the Xserver. wait for a pause in + * keyboard input to be on the safe side. the idea here + * is to remove stale keycode state, not to be perfectly + * in sync with the Xserver at every instant of time. + */ + if (now > last_check + delay && now > last_keyboard_input + noinput) { + init_track_keycode_state(); + last_check = now; + } +} + +void check_autorepeat(void) { + 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. + */ +void check_xevents(void) { + XEvent xev; + int tmp, have_clients = 0; + static int sent_some_sel = 0; + static time_t last_request = 0; + static time_t last_call = 0; + static time_t last_bell = 0; + static time_t last_init_check = 0; + static time_t last_sync = 0; + static time_t last_time_sync = 0; + time_t now = time(0); + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + + if (now > last_init_check+1) { + last_init_check = now; + initialize_xevents(); + } + + if (screen && screen->clientHead) { + have_clients = 1; + } + + X_LOCK; + /* + * There is a bug where we have to wait before sending text to + * the client... so instead of sending right away we wait a + * the few seconds. + */ + if (have_clients && watch_selection && !sent_some_sel + && now > last_client + sel_waittime) { + if (XGetSelectionOwner(dpy, XA_PRIMARY) == None) { + cutbuffer_send(); + } + sent_some_sel = 1; + } + if (! have_clients) { + /* + * If we don't have clients we can miss the X server + * going away until a client connects. + */ + static time_t last_X_ping = 0; + if (now > last_X_ping + 5) { + last_X_ping = now; + XGetSelectionOwner(dpy, XA_PRIMARY); + } + } + + if (now > last_call+1) { + /* we only check these once a second or so. */ + int n = 0; + while (XCheckTypedEvent(dpy, MappingNotify, &xev)) { + XRefreshKeyboardMapping((XMappingEvent *) &xev); + n++; + } + if (n && use_modifier_tweak) { + X_UNLOCK; + initialize_modtweak(); + X_LOCK; + } + if (xtrap_base_event_type) { + int base = xtrap_base_event_type; + while (XCheckTypedEvent(dpy, base, &xev)) { + ; + } + } + if (xtest_base_event_type) { + int base = xtest_base_event_type; + while (XCheckTypedEvent(dpy, base, &xev)) { + ; + } + } + /* + * we can get ClientMessage from our XSendEvent() call in + * selection_request(). + */ + while (XCheckTypedEvent(dpy, ClientMessage, &xev)) { + ; + } + } + + /* check for CUT_BUFFER0 and VNC_CONNECT changes: */ + if (XCheckTypedEvent(dpy, PropertyNotify, &xev)) { + if (xev.type == PropertyNotify) { + if (xev.xproperty.atom == XA_CUT_BUFFER0) { + /* + * Go retrieve CUT_BUFFER0 and send it. + * + * set_cutbuffer is a flag to try to avoid + * processing our own cutbuffer changes. + */ + if (have_clients && watch_selection + && ! set_cutbuffer) { + cutbuffer_send(); + sent_some_sel = 1; + } + set_cutbuffer = 0; + } else if (vnc_connect && vnc_connect_prop != None + && xev.xproperty.atom == vnc_connect_prop) { + + /* + * Go retrieve VNC_CONNECT string. + */ + read_vnc_connect_prop(); + } + } + } + + /* do this now that we have just cleared PropertyNotify */ + tmp = 0; + if (rfac() < 0.6) { + tmp = 1; + } + if (now > last_time_sync + sync_tod_delay + tmp) { + sync_tod_with_servertime(); + last_time_sync = now; + } + +#if LIBVNCSERVER_HAVE_LIBXRANDR + if (xrandr) { + check_xrandr_event("check_xevents"); + } +#endif +#if LIBVNCSERVER_HAVE_LIBXFIXES + if (xfixes_present && use_xfixes && xfixes_base_event_type) { + if (XCheckTypedEvent(dpy, xfixes_base_event_type + + XFixesCursorNotify, &xev)) { + got_xfixes_cursor_notify++; + } + } +#endif + + /* check for our PRIMARY request notification: */ + if (watch_primary) { + if (XCheckTypedEvent(dpy, SelectionNotify, &xev)) { + if (xev.type == SelectionNotify && + xev.xselection.requestor == selwin && + xev.xselection.selection == XA_PRIMARY && + xev.xselection.property != None && + xev.xselection.target == XA_STRING) { + + /* go retrieve PRIMARY and check it */ + if (now > last_client + sel_waittime + || sent_some_sel) { + selection_send(&xev); + } + } + } + if (now > last_request) { + /* + * Every second or two, request PRIMARY, unless we + * already own it or there is no owner or we have + * no clients. + * TODO: even at this low rate we should look into + * and performance problems in odds cases, etc. + */ + last_request = now; + if (! own_selection && have_clients && + XGetSelectionOwner(dpy, XA_PRIMARY) != None) { + XConvertSelection(dpy, XA_PRIMARY, XA_STRING, + XA_STRING, selwin, CurrentTime); + } + } + } + + if (own_selection) { + /* we own PRIMARY, see if someone requested it: */ + if (XCheckTypedEvent(dpy, SelectionRequest, &xev)) { + if (xev.type == SelectionRequest && + xev.xselectionrequest.selection == XA_PRIMARY) { + selection_request(&xev); + } + } + + /* we own PRIMARY, see if we no longer own it: */ + if (XCheckTypedEvent(dpy, SelectionClear, &xev)) { + if (xev.type == SelectionClear && + xev.xselectionclear.selection == XA_PRIMARY) { + + own_selection = 0; + if (xcut_str) { + free(xcut_str); + xcut_str = NULL; + } + } + } + } + + if (watch_bell || now > last_bell+1) { + last_bell = now; + check_bell_event(); + } + if (tray_request != None) { + static time_t last_tray_request = 0; + if (now > last_tray_request + 2) { + last_tray_request = now; + if (tray_embed(tray_request, tray_unembed)) { + tray_window = tray_request; + tray_request = None; + } + } + } + +#ifndef DEBUG_XEVENTS +#define DEBUG_XEVENTS 1 +#endif +#if DEBUG_XEVENTS + if (debug_xevents) { + static time_t last_check = 0; + static time_t reminder = 0; + static int freq = 0; + + if (! freq) { + if (getenv("X11VNC_REMINDER_RATE")) { + freq = atoi(getenv("X11VNC_REMINDER_RATE")); + } else { + freq = 300; + } + } + + if (now > last_check + 1) { + int ev_type_max = 300, ev_size = 400; + XEvent xevs[400]; + int i, tot = XEventsQueued(dpy, QueuedAlready); + + if (reminder == 0 || (tot && now > reminder + freq)) { + print_xevent_bases(); + reminder = now; + } + last_check = now; + + if (tot) { + fprintf(stderr, "Total events queued: %d\n", + tot); + } + for (i=1; i<ev_type_max; i++) { + int k, n = 0; + while (XCheckTypedEvent(dpy, i, xevs+n)) { + if (++n >= ev_size) { + break; + } + } + if (n) { + fprintf(stderr, " %d%s events of type" + " %d queued\n", n, + (n >= ev_size) ? "+" : "", i); + } + for (k=n-1; k >= 0; k--) { + XPutBackEvent(dpy, xevs+k); + } + } + } + } +#endif + + if (now > last_sync + 1200) { + /* kludge for any remaining event leaks */ + int bugout = use_xdamage ? 500 : 50; + int qlen, i; + if (last_sync != 0) { + qlen = XEventsQueued(dpy, QueuedAlready); + if (qlen >= bugout) { + rfbLog("event leak: %d queued, " + " calling XSync(dpy, True)\n", qlen); + rfbLog(" for diagnostics run: 'x11vnc -R" + " debug_xevents:1'\n"); + XSync(dpy, True); + } + } + last_sync = now; + + /* clear these, we don't want any events on them */ + if (rdpy_ctrl) { + qlen = XEventsQueued(rdpy_ctrl, QueuedAlready); + for (i=0; i<qlen; i++) { + XNextEvent(rdpy_ctrl, &xev); + } + } + if (gdpy_ctrl) { + qlen = XEventsQueued(gdpy_ctrl, QueuedAlready); + for (i=0; i<qlen; i++) { + XNextEvent(gdpy_ctrl, &xev); + } + } + } + X_UNLOCK; + + last_call = now; +} + +/* + * hook called when a VNC client sends us some "XCut" text (rfbClientCutText). + */ +void xcut_receive(char *text, int len, rfbClientPtr cl) { + allowed_input_t input; + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + + if (!watch_selection) { + return; + } + if (view_only) { + return; + } + if (text == NULL || len == 0) { + return; + } + get_allowed_input(cl, &input); + if (!input.keystroke && !input.motion && !input.button) { + /* maybe someday KMBC for cut text... */ + return; + } + + if (! check_sel_direction("recv", "xcut_receive", text, len)) { + return; + } + + X_LOCK; + + /* associate this text with PRIMARY (and SECONDARY...) */ + if (! own_selection) { + own_selection = 1; + /* we need to grab the PRIMARY selection */ + XSetSelectionOwner(dpy, XA_PRIMARY, selwin, CurrentTime); + XFlush(dpy); + } + + /* duplicate the text string for our own use. */ + if (xcut_str != NULL) { + free(xcut_str); + xcut_str = NULL; + } + xcut_str = (char *) malloc((size_t) (len+1)); + strncpy(xcut_str, text, len); + xcut_str[len] = '\0'; /* make sure null terminated */ + + /* copy this text to CUT_BUFFER0 as well: */ + XChangeProperty(dpy, rootwin, XA_CUT_BUFFER0, XA_STRING, 8, + PropModeReplace, (unsigned char *) text, len); + XFlush(dpy); + + X_UNLOCK; + + set_cutbuffer = 1; +} + + |