summaryrefslogtreecommitdiffstats
path: root/x11vnc/pointer.c
diff options
context:
space:
mode:
Diffstat (limited to 'x11vnc/pointer.c')
-rw-r--r--x11vnc/pointer.c1029
1 files changed, 1029 insertions, 0 deletions
diff --git a/x11vnc/pointer.c b/x11vnc/pointer.c
new file mode 100644
index 0000000..6ff92d4
--- /dev/null
+++ b/x11vnc/pointer.c
@@ -0,0 +1,1029 @@
+/* -- pointer.c -- */
+
+#include "x11vnc.h"
+#include "xwrappers.h"
+#include "keyboard.h"
+#include "xinerama.h"
+#include "xrecord.h"
+#include "win_utils.h"
+#include "cursor.h"
+#include "userinput.h"
+#include "connections.h"
+#include "cleanup.h"
+
+int pointer_queued_sent = 0;
+
+void initialize_pointer_map(char *pointer_remap);
+void do_button_mask_change(int mask, int button);
+void pointer(int mask, int x, int y, rfbClientPtr client);
+void initialize_pipeinput(void);
+int check_pipeinput(void);
+
+
+static void buttonparse(int from, char **s);
+static void update_x11_pointer_position(int x, int y);
+static void update_x11_pointer_mask(int mask);
+static void pipe_pointer(int mask, int x, int y, rfbClientPtr client);
+
+
+/*
+ * pointer event (motion and button click) handling routines.
+ */
+typedef struct ptrremap {
+ KeySym keysym;
+ KeyCode keycode;
+ int end;
+ int button;
+ int down;
+ int up;
+} prtremap_t;
+
+#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
+MUTEX(pointerMutex);
+#endif
+#define MAX_BUTTONS 5
+#define MAX_BUTTON_EVENTS 50
+static prtremap_t pointer_map[MAX_BUTTONS+1][MAX_BUTTON_EVENTS];
+
+/*
+ * For parsing the -buttonmap sections, e.g. "4" or ":Up+Up+Up:"
+ */
+static void buttonparse(int from, char **s) {
+ char *q;
+ int to, i;
+ int modisdown[256];
+
+ q = *s;
+
+ for (i=0; i<256; i++) {
+ modisdown[i] = 0;
+ }
+
+ if (*q == ':') {
+ /* :sym1+sym2+...+symN: format */
+ int l = 0, n = 0;
+ char list[1000];
+ char *t, *kp = q + 1;
+ KeyCode kcode;
+
+ while (*(kp+l) != ':' && *(kp+l) != '\0') {
+ /* loop to the matching ':' */
+ l++;
+ if (l >= 1000) {
+ rfbLog("buttonparse: keysym list too long: "
+ "%s\n", q);
+ break;
+ }
+ }
+ *(kp+l) = '\0';
+ strncpy(list, kp, l);
+ list[l] = '\0';
+ rfbLog("remap button %d using \"%s\"\n", from, list);
+
+ /* loop over tokens separated by '+' */
+ t = strtok(list, "+");
+ while (t) {
+ KeySym ksym;
+ unsigned int ui;
+ int i;
+ if (n >= MAX_BUTTON_EVENTS - 20) {
+ rfbLog("buttonparse: too many button map "
+ "events: %s\n", list);
+ break;
+ }
+ if (sscanf(t, "0x%x", &ui) == 1) {
+ ksym = (KeySym) ui; /* hex value */
+ } else {
+ X_LOCK;
+ ksym = XStringToKeysym(t); /* string value */
+ X_UNLOCK;
+ }
+ if (ksym == NoSymbol) {
+ /* see if Button<N> "keysym" was used: */
+ if (sscanf(t, "Button%d", &i) == 1) {
+ rfbLog(" event %d: button %d\n",
+ from, n+1, i);
+ if (i == 0) i = -1; /* bah */
+ pointer_map[from][n].keysym = NoSymbol;
+ pointer_map[from][n].keycode = NoSymbol;
+ pointer_map[from][n].button = i;
+ pointer_map[from][n].end = 0;
+ pointer_map[from][n].down = 0;
+ pointer_map[from][n].up = 0;
+ } else {
+ rfbLog("buttonparse: ignoring unknown "
+ "keysym: %s\n", t);
+ n--;
+ }
+ } else {
+ /*
+ * XXX may not work with -modtweak or -xkb
+ */
+ X_LOCK;
+ kcode = XKeysymToKeycode(dpy, ksym);
+
+ pointer_map[from][n].keysym = ksym;
+ pointer_map[from][n].keycode = kcode;
+ pointer_map[from][n].button = 0;
+ pointer_map[from][n].end = 0;
+ if (! ismodkey(ksym) ) {
+ /* do both down then up */
+ pointer_map[from][n].down = 1;
+ pointer_map[from][n].up = 1;
+ } else {
+ if (modisdown[kcode]) {
+ pointer_map[from][n].down = 0;
+ pointer_map[from][n].up = 1;
+ modisdown[kcode] = 0;
+ } else {
+ pointer_map[from][n].down = 1;
+ pointer_map[from][n].up = 0;
+ modisdown[kcode] = 1;
+ }
+ }
+ rfbLog(" event %d: keysym %s (0x%x) -> "
+ "keycode 0x%x down=%d up=%d\n", n+1,
+ XKeysymToString(ksym), ksym, kcode,
+ pointer_map[from][n].down,
+ pointer_map[from][n].up);
+ X_UNLOCK;
+ }
+ t = strtok(NULL, "+");
+ n++;
+ }
+
+ /* we must release any modifiers that are still down: */
+ for (i=0; i<256; i++) {
+ kcode = (KeyCode) i;
+ if (n >= MAX_BUTTON_EVENTS) {
+ rfbLog("buttonparse: too many button map "
+ "events: %s\n", list);
+ break;
+ }
+ if (modisdown[kcode]) {
+ pointer_map[from][n].keysym = NoSymbol;
+ pointer_map[from][n].keycode = kcode;
+ pointer_map[from][n].button = 0;
+ pointer_map[from][n].end = 0;
+ pointer_map[from][n].down = 0;
+ pointer_map[from][n].up = 1;
+ modisdown[kcode] = 0;
+ n++;
+ }
+ }
+
+ /* advance the source pointer position */
+ (*s) += l+2;
+ } else {
+ /* single digit format */
+ char str[2];
+ str[0] = *q;
+ str[1] = '\0';
+
+ to = atoi(str);
+ if (to < 1) {
+ rfbLog("skipping invalid remap button \"%d\" for button"
+ " %d from string \"%s\"\n",
+ to, from, str);
+ } else {
+ rfbLog("remap button %d using \"%s\"\n", from, str);
+ rfbLog(" button: %d -> %d\n", from, to);
+ pointer_map[from][0].keysym = NoSymbol;
+ pointer_map[from][0].keycode = NoSymbol;
+ pointer_map[from][0].button = to;
+ pointer_map[from][0].end = 0;
+ pointer_map[from][0].down = 0;
+ pointer_map[from][0].up = 0;
+ }
+ /* advance the source pointer position */
+ (*s)++;
+ }
+}
+
+/*
+ * process the -buttonmap string
+ */
+void initialize_pointer_map(char *pointer_remap) {
+ unsigned char map[MAX_BUTTONS];
+ int i, k;
+ /*
+ * This routine counts the number of pointer buttons on the X
+ * server (to avoid problems, even crashes, if a client has more
+ * buttons). And also initializes any pointer button remapping
+ * from -buttonmap option.
+ */
+
+ X_LOCK;
+ num_buttons = XGetPointerMapping(dpy, map, MAX_BUTTONS);
+ X_UNLOCK;
+
+ if (num_buttons < 0) {
+ num_buttons = 0;
+ }
+
+ /* FIXME: should use info in map[] */
+ for (i=1; i<= MAX_BUTTONS; i++) {
+ for (k=0; k < MAX_BUTTON_EVENTS; k++) {
+ pointer_map[i][k].end = 1;
+ }
+ pointer_map[i][0].keysym = NoSymbol;
+ pointer_map[i][0].keycode = NoSymbol;
+ pointer_map[i][0].button = i;
+ pointer_map[i][0].end = 0;
+ pointer_map[i][0].down = 0;
+ pointer_map[i][0].up = 0;
+ }
+
+ if (pointer_remap && *pointer_remap != '\0') {
+ /* -buttonmap, format is like: 12-21=2 */
+ char *p, *q, *remap = strdup(pointer_remap);
+ int n;
+
+ if ((p = strchr(remap, '=')) != NULL) {
+ /* undocumented max button number */
+ n = atoi(p+1);
+ *p = '\0';
+ if (n < num_buttons || num_buttons == 0) {
+ num_buttons = n;
+ } else {
+ rfbLog("warning: increasing number of mouse "
+ "buttons from %d to %d\n", num_buttons, n);
+ num_buttons = n;
+ }
+ }
+ if ((q = strchr(remap, '-')) != NULL) {
+ /*
+ * The '-' separates the 'from' and 'to' lists,
+ * then it is kind of like tr(1).
+ */
+ char str[2];
+ int from;
+
+ rfbLog("remapping pointer buttons using string:\n");
+ rfbLog(" \"%s\"\n", remap);
+
+ p = remap;
+ q++;
+ i = 0;
+ str[1] = '\0';
+ while (*p != '-') {
+ str[0] = *p;
+ from = atoi(str);
+ buttonparse(from, &q);
+ p++;
+ }
+ }
+ free(remap);
+ }
+}
+
+/*
+ * Send a pointer position event to the X server.
+ */
+static void update_x11_pointer_position(int x, int y) {
+ int rc;
+
+ if (raw_fb && ! dpy) return; /* raw_fb hack */
+
+ X_LOCK;
+ if (use_xwarppointer) {
+ /*
+ * off_x and off_y not needed with XWarpPointer since
+ * window is used:
+ */
+ XWarpPointer(dpy, None, window, 0, 0, 0, 0, x + coff_x,
+ y + coff_y);
+ } else {
+ XTestFakeMotionEvent_wr(dpy, scr, x + off_x + coff_x,
+ y + off_y + coff_y, CurrentTime);
+ }
+ X_UNLOCK;
+
+ if (cursor_x != x || cursor_y != y) {
+ last_pointer_motion_time = dnow();
+ }
+
+ cursor_x = x;
+ cursor_y = y;
+
+ /* record the x, y position for the rfb screen as well. */
+ cursor_position(x, y);
+
+ /* change the cursor shape if necessary */
+ rc = set_cursor(x, y, get_which_cursor());
+ cursor_changes += rc;
+
+ 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 snapped, xr_mouse = 1, i;
+
+ last_event = last_input = last_pointer_input = time(0);
+
+ if (raw_fb && ! dpy) return; /* raw_fb hack */
+
+ if (mask != button_mask) {
+ last_pointer_click_time = dnow();
+ }
+
+ if (nofb) {
+ xr_mouse = 0;
+ } else if (!strcmp(scroll_copyrect, "never")) {
+ xr_mouse = 0;
+ } else if (!strcmp(scroll_copyrect, "keys")) {
+ xr_mouse = 0;
+ } else if (skip_cr_when_scaling("scroll")) {
+ xr_mouse = 0;
+ } else if (xrecord_skip_button(mask, button_mask)) {
+ xr_mouse = 0;
+ }
+
+ if (mask && use_xrecord && ! xrecording && xr_mouse) {
+ static int px, py, x, y, w, h, 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, &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 {
+ got_wm_frame = 0;
+ }
+ }
+ 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;
+ int btn4 = (1<<3);
+ int btn5 = (1<<4);
+
+ if (near_scrollbar_edge(x, y, w, h, px, py)) {
+ ok = 1;
+ }
+ if (mask & (btn4|btn5)) {
+ /* scroll wheel mouse */
+ ok = 1;
+ }
+ if (mwin != None) {
+ /* skinny internal window */
+ 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) {
+ xrecord_watch(1, SCR_MOUSE);
+ snapshot_stack_list(0, 0.50);
+ snapped = 1;
+ if (button_mask) {
+ xrecord_set_by_mouse = 1;
+ } else {
+ update_stack_list();
+ xrecord_set_by_mouse = 2;
+ }
+ }
+ }
+
+ if (mask && !button_mask) {
+ /* button down, snapshot the stacking list before flushing */
+ if (wireframe && !wireframe_in_progress &&
+ strcmp(wireframe_copyrect, "never")) {
+ if (! snapped) {
+ snapshot_stack_list(0, 0.0);
+ }
+ }
+ }
+
+ X_LOCK;
+
+ /* look for buttons that have be clicked or released: */
+ for (i=0; i < MAX_BUTTONS; i++) {
+ if ( (button_mask & (1<<i)) != (mask & (1<<i)) ) {
+ if (debug_pointer) {
+ rfbLog("pointer(): mask change: mask: 0x%x -> "
+ "0x%x button: %d\n", button_mask, mask,i+1);
+ }
+ do_button_mask_change(mask, i+1); /* button # is i+1 */
+ }
+ }
+
+ X_UNLOCK;
+
+ /*
+ * Remember the button state for next time and also for the
+ * -nodragging case:
+ */
+ button_mask_prev = button_mask;
+ button_mask = mask;
+}
+
+/* for -pipeinput */
+
+
+static void pipe_pointer(int mask, int x, int y, rfbClientPtr client) {
+ int can_input = 0, uid;
+ allowed_input_t input;
+ ClientData *cd = (ClientData *) client->clientData;
+ char hint[MAX_BUTTONS * 20];
+
+ if (pipeinput_fh == NULL) {
+ return;
+ }
+
+ if (! view_only) {
+ get_allowed_input(client, &input);
+ if (input.motion || input.button) {
+ can_input = 1; /* XXX distinguish later */
+ }
+ }
+ uid = cd->uid;
+ if (! can_input) {
+ uid = -uid;
+ }
+
+ hint[0] = '\0';
+ if (mask == button_mask) {
+ strcat(hint, "None");
+ } else {
+ int i, old, new, m = 1, cnt = 0;
+ for (i=0; i<MAX_BUTTONS; i++) {
+ char s[20];
+
+ old = button_mask & m;
+ new = mask & m;
+ m = m << 1;
+
+ if (old == new) {
+ continue;
+ }
+ if (hint[0] != '\0') {
+ strcat(hint, ",");
+ }
+ if (new && ! old) {
+ sprintf(s, "ButtonPress-%d", i+1);
+ cnt++;
+ } else if (! new && old) {
+ sprintf(s, "ButtonRelease-%d", i+1);
+ cnt++;
+ }
+ strcat(hint, s);
+ }
+ if (! cnt) {
+ strcpy(hint, "None");
+ }
+ }
+
+ fprintf(pipeinput_fh, "Pointer %d %d %d %d %s\n", uid, x, y,
+ mask, hint);
+ fflush(pipeinput_fh);
+ check_pipeinput();
+}
+
+/*
+ * Actual callback from libvncserver when it gets a pointer event.
+ * This may queue pointer events rather than sending them immediately
+ * to the X server. (see update_x11_pointer*())
+ */
+void pointer(int mask, int x, int y, rfbClientPtr client) {
+ allowed_input_t input;
+ int sent = 0, buffer_it = 0;
+ double now;
+
+ if (mask >= 0) {
+ got_pointer_calls++;
+ }
+
+ 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;
+ } else {
+ 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) "
+ "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) {
+ /* map from rfb size to X11 size: */
+ x = ((double) x / scaled_x) * dpy_x;
+ x = nfix(x, dpy_x);
+ y = ((double) y / scaled_y) * dpy_y;
+ y = nfix(y, dpy_y);
+ }
+
+ if (pipeinput_fh != NULL && mask >= 0) {
+ pipe_pointer(mask, x, y, client);
+ if (! pipeinput_tee) {
+ if (! view_only || raw_fb) { /* raw_fb hack */
+ got_user_input++;
+ got_keyboard_input++;
+ last_pointer_client = client;
+ }
+ if (view_only && raw_fb) {
+ /* raw_fb hack track button state */
+ button_mask_prev = button_mask;
+ button_mask = mask;
+ }
+ return;
+ }
+ }
+
+ if (view_only) {
+ return;
+ }
+
+ now = dnow();
+
+ 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;
+
+ last_pointer_time = now;
+
+ if (blackout_ptr && blackouts) {
+ int b, ok = 1;
+ /* see if it goes into the blacked out region */
+ for (b=0; b < blackouts; b++) {
+ if (x < blackr[b].x1 || x > blackr[b].x2) {
+ continue;
+ }
+ if (y < blackr[b].y1 || y > blackr[b].y2) {
+ continue;
+ }
+ /* x1 <= x <= x2 and y1 <= y <= y2 */
+ ok = 0;
+ break;
+ }
+ if (! ok) {
+ if (debug_pointer) {
+ rfbLog("pointer(): blackout_ptr skipping "
+ "x=%d y=%d in rectangle %d,%d %d,%d\n", x, y,
+ blackr[b].x1, blackr[b].y1,
+ blackr[b].x2, blackr[b].y2);
+ }
+ return;
+ }
+ }
+ }
+
+ /*
+ * The following is hopefully an improvement wrt response during
+ * pointer user input (window drags) for the threaded case.
+ * See check_user_input() for the more complicated things we do
+ * in the non-threaded case.
+ */
+ if ((use_threads && pointer_mode != 1) || pointer_flush_delay > 0.0) {
+# define NEV 32
+ /* storage for the event queue */
+ static int mutex_init = 0;
+ static int nevents = 0;
+ static int ev[NEV][3];
+ int i;
+ /* timer things */
+ static double dt = 0.0, tmr = 0.0, maxwait = 0.4;
+
+ if (! mutex_init) {
+ INIT_MUTEX(pointerMutex);
+ 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);
+
+ /*
+ * If the framebuffer is being copied in another thread
+ * (scan_for_updates()), we will queue up to 32 pointer
+ * events for later. The idea is by delaying these input
+ * events, the screen is less likely to change during the
+ * copying period, and so will give rise to less window
+ * "tearing".
+ *
+ * Tearing is not completely eliminated because we do
+ * not suspend work in the other libvncserver threads.
+ * Maybe that is a possibility with a mutex...
+ */
+ if (buffer_it) {
+ /*
+ * mask = -1 is an all-clear signal from
+ * scan_for_updates().
+ *
+ * dt is a timer in seconds; we only queue for so long.
+ */
+ dt += dtime(&tmr);
+
+ if (nevents < NEV && dt < maxwait) {
+ i = nevents++;
+ ev[i][0] = mask;
+ ev[i][1] = x;
+ ev[i][2] = y;
+ if (! input.button) {
+ ev[i][0] = -1;
+ }
+ if (! input.motion) {
+ ev[i][1] = -1;
+ ev[i][2] = -1;
+ }
+ UNLOCK(pointerMutex);
+ if (debug_pointer) {
+ rfbLog("pointer(): deferring event %d"
+ " %.4f\n", i, tmr - x11vnc_start);
+ }
+ return;
+ }
+ }
+
+ /* 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 %.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) {
+ 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;
+ }
+ }
+ }
+ nevents = 0; /* reset everything */
+ dt = 0.0;
+ dtime0(&tmr);
+
+ UNLOCK(pointerMutex);
+ }
+ if (mask < 0) { /* -1 just means flush the event queue */
+ if (debug_pointer) {
+ rfbLog("pointer(): flush only. %.4f\n",
+ dnow() - x11vnc_start);
+ }
+ return;
+ }
+
+ /* update the X display with the event: */
+ if (input.motion) {
+ update_x11_pointer_position(x, y);
+ sent = 1;
+ }
+ if (input.button) {
+ if (mask != button_mask) {
+ button_change_x = cursor_x;
+ button_change_y = cursor_y;
+ }
+ update_x11_pointer_mask(mask);
+ sent = 1;
+ }
+
+ if (nofb && sent) {
+ /*
+ * nofb is for, e.g. Win2VNC, where fastest pointer
+ * updates are desired.
+ */
+ 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;
+ }
+}
+
+void initialize_pipeinput(void) {
+ char *p;
+
+ if (pipeinput_fh != NULL) {
+ rfbLog("closing pipeinput stream: %p\n", pipeinput_fh);
+ pclose(pipeinput_fh);
+ pipeinput_fh = NULL;
+ }
+
+ pipeinput_tee = 0;
+ if (pipeinput_opts) {
+ free(pipeinput_opts);
+ pipeinput_opts = NULL;
+ }
+
+ if (! pipeinput_str) {
+ return;
+ }
+
+ /* look for options: tee, reopen, ... */
+ p = strchr(pipeinput_str, ':');
+ if (p != NULL) {
+ char *str, *opt, *q;
+ int got = 0;
+ *p = '\0';
+ str = strdup(pipeinput_str);
+ opt = strdup(pipeinput_str);
+ *p = ':';
+ q = strtok(str, ",");
+ while (q) {
+ if (!strcmp(q, "key") || !strcmp(q, "keycodes")) {
+ got = 1;
+ }
+ if (!strcmp(q, "reopen")) {
+ got = 1;
+ }
+ if (!strcmp(q, "tee")) {
+ pipeinput_tee = 1;
+ got = 1;
+ }
+ q = strtok(NULL, ",");
+ }
+ if (got) {
+ pipeinput_opts = opt;
+ } else {
+ free(opt);
+ }
+ free(str);
+ p++;
+ } else {
+ p = pipeinput_str;
+ }
+
+ set_child_info();
+ if (no_external_cmds) {
+ rfbLogEnable(1);
+ rfbLog("cannot run external commands in -nocmds mode:\n");
+ rfbLog(" \"%s\"\n", p);
+ rfbLog(" exiting.\n");
+ clean_up_exit(1);
+ }
+ rfbLog("pipeinput: starting: \"%s\"...\n", p);
+ pipeinput_fh = popen(p, "w");
+
+ if (! pipeinput_fh) {
+ rfbLog("popen(\"%s\", \"w\") failed.\n", p);
+ rfbLogPerror("popen");
+ rfbLog("Disabling -pipeinput mode.\n");
+ return;
+ }
+
+ fprintf(pipeinput_fh, "%s",
+"# \n"
+"# Format of the -pipeinput stream:\n"
+"# --------------------------------\n"
+"#\n"
+"# Lines like these beginning with '#' are to be ignored.\n"
+"#\n"
+"# Pointer events (mouse motion and button clicks) come in the form:\n"
+"#\n"
+"#\n"
+"# Pointer <client#> <x> <y> <mask> <hint>\n"
+"#\n"
+"#\n"
+"# The <client#> is a decimal integer uniquely identifying the client\n"
+"# that generated the event. If it is negative that means this event\n"
+"# would have been discarded since the client was viewonly.\n"
+"#\n"
+"# <x> and <y> are decimal integers reflecting the position on the screen\n"
+"# the event took place at.\n"
+"#\n"
+"# <mask> is the button mask indicating the button press state, as normal\n"
+"# 0 means no buttons pressed, 1 means button 1 is down 3 (11) means buttons\n"
+"# 1 and 2 are down, etc.\n"
+"#\n"
+"# <hint> is a string containing no spaces and may be ignored.\n"
+"# It contains some interpretation about what has happened.\n"
+"# It can be:\n"
+"#\n"
+"# None (nothing to report)\n"
+"# ButtonPress-N (this event will cause button-1 to be pressed) \n"
+"# ButtonRelease-N (this event will cause button-1 to be released) \n"
+"#\n"
+"# if two more more buttons change state in one event they are listed\n"
+"# separated by commas.\n"
+"#\n"
+"# One might parse a Pointer line with:\n"
+"#\n"
+"# int client, x, y, mask; char *hint;\n"
+"# sscanf(line, \"Pointer %d %d %d %s\", &client, &x, &y, &mask, &hint);\n"
+"#\n"
+"#\n"
+"# Keysym events (keyboard presses and releases) come in the form:\n"
+"#\n"
+"#\n"
+"# Keysym <client#> <down> <keysym#> <keysym-name> <hint>\n"
+"#\n"
+"#\n"
+"# The <client#> is as with Pointer.\n"
+"#\n"
+"# <down> is a decimal either 1 or 0 indicating KeyPress or KeyRelease,\n"
+"# respectively.\n"
+"#\n"
+"# <keysym#> is a decimal integer incidating the Keysym of the event.\n"
+"#\n"
+"# <keysym-name> is the corresponding Keysym name.\n"
+"#\n"
+"# See the file /usr/include/X11/keysymdef.h for the mappings.\n"
+"# You basically remove the leading 'XK_' prefix from the macro name in\n"
+"# that file to get the Keysym name.\n"
+"#\n"
+"# One might parse a Keysym line with:\n"
+"#\n"
+"# int client, down, keysym; char *name, *hint;\n"
+"# sscanf(line, \"Keysym %d %d %s %s\", &client, &down, &keysym, &name, &hint);\n"
+"#\n"
+"# The <hint> value is currently just None, KeyPress, or KeyRelease.\n"
+"#\n"
+"# In the future <hint> will provide a hint for the sequence of KeyCodes\n"
+"# (i.e. keyboard scancodes) that x11vnc would inject to an X display to\n"
+"# simulate the Keysym.\n"
+"#\n"
+"# You see, some Keysyms will require more than one injected Keycode to\n"
+"# generate the symbol. E.g. the Keysym \"ampersand\" going down usually\n"
+"# requires a Shift key going down, then the key with the \"&\" on it going\n"
+"# down, and, perhaps, the Shift key going up (that is how x11vnc does it).\n"
+"#\n"
+"# The Keysym => Keycode(s) stuff gets pretty messy. Hopefully the Keysym\n"
+"# info will be enough for most purposes (having identical keyboards on\n"
+"# both sides helps).\n"
+"#\n"
+"# Here comes your stream. The following token will always indicate the\n"
+"# end of this informational text:\n"
+"# END_OF_TOP\n"
+);
+ fflush(pipeinput_fh);
+ if (raw_fb_str) {
+ /* the pipe program may actually create the fb */
+ sleep(1);
+ }
+}
+
+int check_pipeinput(void) {
+ if (! pipeinput_fh) {
+ return 1;
+ }
+ if (ferror(pipeinput_fh)) {
+ rfbLog("pipeinput pipe has ferror. %p\n", pipeinput_fh);
+
+ if (pipeinput_opts && strstr(pipeinput_opts, "reopen")) {
+ rfbLog("restarting -pipeinput pipe...\n");
+ initialize_pipeinput();
+ if (pipeinput_fh) {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ rfbLog("closing -pipeinput pipe...\n");
+ pclose(pipeinput_fh);
+ pipeinput_fh = NULL;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+