summaryrefslogtreecommitdiffstats
path: root/x11vnc/xevents.c
diff options
context:
space:
mode:
authorrunge <runge>2006-01-09 01:54:38 +0000
committerrunge <runge>2006-01-09 01:54:38 +0000
commit71f2ec79180185a6c3db0c87f9d53c491dc31e76 (patch)
tree67c341571cbeb1bd9a0744cc8eb03b30ef04f381 /x11vnc/xevents.c
parentdef301266373e462f4a5e90eab443087ccfc7ccc (diff)
downloadlibvncserver-71f2ec79180185a6c3db0c87f9d53c491dc31e76.zip
libvncserver-71f2ec79180185a6c3db0c87f9d53c491dc31e76.tar.gz
x11vnc: the big split.
Diffstat (limited to 'x11vnc/xevents.c')
-rw-r--r--x11vnc/xevents.c981
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;
+}
+
+
OpenPOWER on IntegriCloud