You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libtdevnc/x11vnc/xevents.c

982 lines
22 KiB

/* -- 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;
}