Home | History | Annotate | Download | only in x11vnc
      1 /*
      2    Copyright (C) 2002-2010 Karl J. Runge <runge (at) karlrunge.com>
      3    All rights reserved.
      4 
      5 This file is part of x11vnc.
      6 
      7 x11vnc is free software; you can redistribute it and/or modify
      8 it under the terms of the GNU General Public License as published by
      9 the Free Software Foundation; either version 2 of the License, or (at
     10 your option) any later version.
     11 
     12 x11vnc is distributed in the hope that it will be useful,
     13 but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with x11vnc; if not, write to the Free Software
     19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
     20 or see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, as a special exception, Karl J. Runge
     23 gives permission to link the code of its release of x11vnc with the
     24 OpenSSL project's "OpenSSL" library (or with modified versions of it
     25 that use the same license as the "OpenSSL" library), and distribute
     26 the linked executables.  You must obey the GNU General Public License
     27 in all respects for all of the code used other than "OpenSSL".  If you
     28 modify this file, you may extend this exception to your version of the
     29 file, but you are not obligated to do so.  If you do not wish to do
     30 so, delete this exception statement from your version.
     31 */
     32 
     33 /* -- selection.c -- */
     34 
     35 #include "x11vnc.h"
     36 #include "cleanup.h"
     37 #include "connections.h"
     38 #include "unixpw.h"
     39 #include "win_utils.h"
     40 #include "xwrappers.h"
     41 
     42 /*
     43  * Selection/Cutbuffer/Clipboard handlers.
     44  */
     45 
     46 int own_primary = 0;	/* whether we currently own PRIMARY or not */
     47 int set_primary = 1;
     48 int own_clipboard = 0;	/* whether we currently own CLIPBOARD or not */
     49 int set_clipboard = 1;
     50 int set_cutbuffer = 0;	/* to avoid bouncing the CutText right back */
     51 int sel_waittime = 15;	/* some seconds to skip before first send */
     52 Window selwin = None;	/* special window for our selection */
     53 Atom clipboard_atom = None;
     54 
     55 /*
     56  * This is where we keep our selection: the string sent TO us from VNC
     57  * clients, and the string sent BY us to requesting X11 clients.
     58  */
     59 char *xcut_str_primary = NULL;
     60 char *xcut_str_clipboard = NULL;
     61 
     62 
     63 void selection_request(XEvent *ev, char *type);
     64 int check_sel_direction(char *dir, char *label, char *sel, int len);
     65 void cutbuffer_send(void);
     66 void selection_send(XEvent *ev);
     67 void resend_selection(char *type);
     68 
     69 
     70 /*
     71  * Our callbacks instruct us to check for changes in the cutbuffer
     72  * and PRIMARY and CLIPBOARD selection on the local X11 display.
     73  *
     74  * TODO: check if malloc does not cause performance issues (esp. WRT
     75  * SelectionNotify handling).
     76  */
     77 static char cutbuffer_str[PROP_MAX+1];
     78 static char primary_str[PROP_MAX+1];
     79 static char clipboard_str[PROP_MAX+1];
     80 static int cutbuffer_len = 0;
     81 static int primary_len   = 0;
     82 static int clipboard_len = 0;
     83 
     84 /*
     85  * An X11 (not VNC) client on the local display has requested the selection
     86  * from us (because we are the current owner).
     87  *
     88  * n.b.: our caller already has the X_LOCK.
     89  */
     90 void selection_request(XEvent *ev, char *type) {
     91 #if NO_X11
     92 	RAWFB_RET_VOID
     93 	if (!ev || !type) {}
     94 	return;
     95 #else
     96 	XSelectionEvent notify_event;
     97 	XSelectionRequestEvent *req_event;
     98 	XErrorHandler old_handler;
     99 	char *str;
    100 	unsigned int length;
    101 	unsigned char *data;
    102 	static Atom xa_targets = None;
    103 	static int sync_it = -1;
    104 # ifndef XA_LENGTH
    105 	unsigned long XA_LENGTH;
    106 # endif
    107 	RAWFB_RET_VOID
    108 
    109 # ifndef XA_LENGTH
    110 	XA_LENGTH = XInternAtom(dpy, "LENGTH", True);
    111 # endif
    112 
    113 	if (sync_it < 0) {
    114 		if (getenv("X11VNC_SENDEVENT_SYNC")) {
    115 			sync_it = 1;
    116 		} else {
    117 			sync_it = 0;
    118 		}
    119 	}
    120 
    121 	req_event = &(ev->xselectionrequest);
    122 	notify_event.type 	= SelectionNotify;
    123 	notify_event.display	= req_event->display;
    124 	notify_event.requestor	= req_event->requestor;
    125 	notify_event.selection	= req_event->selection;
    126 	notify_event.target	= req_event->target;
    127 	notify_event.time	= req_event->time;
    128 
    129 	if (req_event->property == None) {
    130 		notify_event.property = req_event->target;
    131 	} else {
    132 		notify_event.property = req_event->property;
    133 	}
    134 
    135 	if (!strcmp(type, "PRIMARY")) {
    136 		str = xcut_str_primary;
    137 	} else if (!strcmp(type, "CLIPBOARD")) {
    138 		str = xcut_str_clipboard;
    139 	} else {
    140 		return;
    141 	}
    142 	if (str) {
    143 		length = strlen(str);
    144 	} else {
    145 		length = 0;
    146 	}
    147 	if (debug_sel) {
    148 		rfbLog("%s\trequest event:   owner=0x%x requestor=0x%x sel=%03d targ=%d prop=%d\n",
    149 			type, req_event->owner, req_event->requestor, req_event->selection,
    150 			req_event->target, req_event->property);
    151 	}
    152 
    153 	if (xa_targets == None) {
    154 		xa_targets = XInternAtom(dpy, "TARGETS", False);
    155 	}
    156 
    157 	/* the window may have gone away, so trap errors */
    158 	trapped_xerror = 0;
    159 	old_handler = XSetErrorHandler(trap_xerror);
    160 
    161 	if (ev->xselectionrequest.target == XA_LENGTH) {
    162 		/* length request */
    163 		int ret;
    164 		long llength = (long) length;
    165 
    166 		ret = XChangeProperty(ev->xselectionrequest.display,
    167 		    ev->xselectionrequest.requestor,
    168 		    ev->xselectionrequest.property,
    169 		    ev->xselectionrequest.target, 32, PropModeReplace,
    170 		    (unsigned char *) &llength, 1);	/* had sizeof(unsigned int) = 4 before... */
    171 		if (debug_sel) {
    172 			rfbLog("LENGTH: XChangeProperty() -> %d\n", ret);
    173 		}
    174 
    175 	} else if (xa_targets != None && ev->xselectionrequest.target == xa_targets) {
    176 		/* targets request */
    177 		int ret;
    178 		Atom targets[2];
    179 		targets[0] = (Atom) xa_targets;
    180 		targets[1] = (Atom) XA_STRING;
    181 
    182 		ret = XChangeProperty(ev->xselectionrequest.display,
    183 		    ev->xselectionrequest.requestor,
    184 		    ev->xselectionrequest.property,
    185 		    ev->xselectionrequest.target, 32, PropModeReplace,
    186 		    (unsigned char *) targets, 2);
    187 		if (debug_sel) {
    188 			rfbLog("TARGETS: XChangeProperty() -> %d -- sz1: %d  sz2: %d\n",
    189 			    ret, sizeof(targets[0]), sizeof(targets)/sizeof(targets[0]));
    190 		}
    191 
    192 	} else {
    193 		/* data request */
    194 		int ret;
    195 
    196 		data = (unsigned char *)str;
    197 
    198 		ret = XChangeProperty(ev->xselectionrequest.display,
    199 		    ev->xselectionrequest.requestor,
    200 		    ev->xselectionrequest.property,
    201 		    ev->xselectionrequest.target, 8, PropModeReplace,
    202 		    data, length);
    203 		if (debug_sel) {
    204 			rfbLog("DATA: XChangeProperty() -> %d\n", ret);
    205 		}
    206 	}
    207 
    208 	if (! trapped_xerror) {
    209 		int ret = -2, skip_it = 0, ms = 0;
    210 		double now = dnow();
    211 		static double last_check = 0.0;
    212 
    213 		if (now > last_check + 0.2) {
    214 			XFlush_wr(dpy);
    215 			if (!valid_window(req_event->requestor , NULL, 1)) {
    216 				sync_it = 1;
    217 				skip_it = 1;
    218 				if (debug_sel) {
    219 					rfbLog("selection_request: not a valid window: 0x%x\n",
    220 					    req_event->requestor);
    221 				}
    222 				ms = 10;
    223 			}
    224 			if (trapped_xerror) {
    225 				sync_it = 1;
    226 				skip_it = 1;
    227 			}
    228 			last_check = dnow();
    229 		}
    230 
    231 		if (!skip_it) {
    232 			ret = XSendEvent(req_event->display, req_event->requestor, False, 0,
    233 			    (XEvent *)&notify_event);
    234 		}
    235 		if (debug_sel) {
    236 			rfbLog("XSendEvent() -> %d\n", ret);
    237 		}
    238 		if (ms > 0) {
    239 			usleep(ms * 1000);
    240 		}
    241 	}
    242 	if (trapped_xerror) {
    243 		rfbLog("selection_request: ignored XError while sending "
    244 		    "%s selection to 0x%x.\n", type, req_event->requestor);
    245 	}
    246 
    247 	XFlush_wr(dpy);
    248 	if (sync_it) {
    249 		usleep(10 * 1000);
    250 		XSync(dpy, False);
    251 	}
    252 
    253 	XSetErrorHandler(old_handler);
    254 	trapped_xerror = 0;
    255 
    256 #endif	/* NO_X11 */
    257 }
    258 
    259 int check_sel_direction(char *dir, char *label, char *sel, int len) {
    260 	int db = 0, ok = 1;
    261 	if (debug_sel) {
    262 		db = 1;
    263 	}
    264 	if (sel_direction) {
    265 		if (strstr(sel_direction, "debug")) {
    266 			db = 1;
    267 		}
    268 		if (strcmp(sel_direction, "debug")) {
    269 			if (strstr(sel_direction, dir) == NULL) {
    270 				ok = 0;
    271 			}
    272 		}
    273 	}
    274 	if (db) {
    275 		char str[40];
    276 		int n = 40;
    277 		strncpy(str, sel, n);
    278 		str[n-1] = '\0';
    279 		if (len < n) {
    280 			str[len] = '\0';
    281 		}
    282 		rfbLog("%s: '%s'\n", label, str);
    283 		if (ok) {
    284 			rfbLog("%s: %s-ing it.\n", label, dir);
    285 		} else {
    286 			rfbLog("%s: NOT %s-ing it.\n", label, dir);
    287 		}
    288 	}
    289 	return ok;
    290 }
    291 
    292 /*
    293  * CUT_BUFFER0 property on the local display has changed, we read and
    294  * store it and send it out to any connected VNC clients.
    295  *
    296  * n.b.: our caller already has the X_LOCK.
    297  */
    298 void cutbuffer_send(void) {
    299 #if NO_X11
    300 	RAWFB_RET_VOID
    301 	return;
    302 #else
    303 	Atom type;
    304 	int format, slen, dlen, len;
    305 	unsigned long nitems = 0, bytes_after = 0;
    306 	unsigned char* data = NULL;
    307 
    308 	cutbuffer_str[0] = '\0';
    309 	slen = 0;
    310 
    311 	RAWFB_RET_VOID
    312 
    313 	/* read the property value into cutbuffer_str: */
    314 	do {
    315 		if (XGetWindowProperty(dpy, DefaultRootWindow(dpy),
    316 		    XA_CUT_BUFFER0, nitems/4, PROP_MAX/16, False,
    317 		    AnyPropertyType, &type, &format, &nitems, &bytes_after,
    318 		    &data) == Success) {
    319 
    320 			dlen = nitems * (format/8);
    321 			if (slen + dlen > PROP_MAX) {
    322 				/* too big */
    323 				rfbLog("warning: truncating large CUT_BUFFER0"
    324 				   " selection > %d bytes.\n", PROP_MAX);
    325 				XFree_wr(data);
    326 				break;
    327 			}
    328 			memcpy(cutbuffer_str+slen, data, dlen);
    329 			slen += dlen;
    330 			cutbuffer_str[slen] = '\0';
    331 			XFree_wr(data);
    332 		}
    333 	} while (bytes_after > 0);
    334 
    335 	cutbuffer_str[PROP_MAX] = '\0';
    336 
    337 	if (debug_sel) {
    338 		rfbLog("cutbuffer_send: '%s'\n", cutbuffer_str);
    339 	}
    340 
    341 	if (! all_clients_initialized()) {
    342 		rfbLog("cutbuffer_send: no send: uninitialized clients\n");
    343 		return; /* some clients initializing, cannot send */
    344 	}
    345 	if (unixpw_in_progress) {
    346 		return;
    347 	}
    348 
    349 	/* now send it to any connected VNC clients (rfbServerCutText) */
    350 	if (!screen) {
    351 		return;
    352 	}
    353 	cutbuffer_len = len = strlen(cutbuffer_str);
    354 	if (check_sel_direction("send", "cutbuffer_send", cutbuffer_str, len)) {
    355 		rfbSendServerCutText(screen, cutbuffer_str, len);
    356 	}
    357 #endif	/* NO_X11 */
    358 }
    359 
    360 /*
    361  * "callback" for our SelectionNotify polling.  We try to determine if
    362  * the PRIMARY selection has changed (checking length and first CHKSZ bytes)
    363  * and if it has we store it and send it off to any connected VNC clients.
    364  *
    365  * n.b.: our caller already has the X_LOCK.
    366  *
    367  * TODO: if we were willing to use libXt, we could perhaps get selection
    368  * timestamps to speed up the checking... XtGetSelectionValue().
    369  *
    370  * Also: XFIXES has XFixesSelectSelectionInput().
    371  */
    372 #define CHKSZ 32
    373 
    374 void selection_send(XEvent *ev) {
    375 #if NO_X11
    376 	RAWFB_RET_VOID
    377 	if (!ev) {}
    378 	return;
    379 #else
    380 	Atom type;
    381 	int format, slen, dlen, oldlen, newlen, toobig = 0, len;
    382 	static int err = 0, sent_one = 0;
    383 	char before[CHKSZ], after[CHKSZ];
    384 	unsigned long nitems = 0, bytes_after = 0;
    385 	unsigned char* data = NULL;
    386 	char *selection_str;
    387 
    388 	RAWFB_RET_VOID
    389 	/*
    390 	 * remember info about our last value of PRIMARY (or CUT_BUFFER0)
    391 	 * so we can check for any changes below.
    392 	 */
    393 	if (ev->xselection.selection == XA_PRIMARY) {
    394 		if (! watch_primary) {
    395 			return;
    396 		}
    397 		selection_str = primary_str;
    398 		if (debug_sel) {
    399 			rfbLog("selection_send: event PRIMARY   prop: %d  requestor: 0x%x  atom: %d\n",
    400 			    ev->xselection.property, ev->xselection.requestor, ev->xselection.selection);
    401 		}
    402 	} else if (clipboard_atom && ev->xselection.selection == clipboard_atom)  {
    403 		if (! watch_clipboard) {
    404 			return;
    405 		}
    406 		selection_str = clipboard_str;
    407 		if (debug_sel) {
    408 			rfbLog("selection_send: event CLIPBOARD prop: %d  requestor: 0x%x atom: %d\n",
    409 			    ev->xselection.property, ev->xselection.requestor, ev->xselection.selection);
    410 		}
    411 	} else {
    412 		return;
    413 	}
    414 
    415 	oldlen = strlen(selection_str);
    416 	strncpy(before, selection_str, CHKSZ);
    417 
    418 	selection_str[0] = '\0';
    419 	slen = 0;
    420 
    421 	/* read in the current value of PRIMARY or CLIPBOARD: */
    422 	do {
    423 		if (XGetWindowProperty(dpy, ev->xselection.requestor,
    424 		    ev->xselection.property, nitems/4, PROP_MAX/16, True,
    425 		    AnyPropertyType, &type, &format, &nitems, &bytes_after,
    426 		    &data) == Success) {
    427 
    428 			dlen = nitems * (format/8);
    429 			if (slen + dlen > PROP_MAX) {
    430 				/* too big */
    431 				toobig = 1;
    432 				XFree_wr(data);
    433 				if (err) {	/* cut down on messages */
    434 					break;
    435 				} else {
    436 					err = 5;
    437 				}
    438 				rfbLog("warning: truncating large PRIMARY"
    439 				    "/CLIPBOARD selection > %d bytes.\n",
    440 				    PROP_MAX);
    441 				break;
    442 			}
    443 if (debug_sel) fprintf(stderr, "selection_send: data: '%s' dlen: %d nitems: %lu ba: %lu\n", data, dlen, nitems, bytes_after);
    444 			memcpy(selection_str+slen, data, dlen);
    445 			slen += dlen;
    446 			selection_str[slen] = '\0';
    447 			XFree_wr(data);
    448 		}
    449 	} while (bytes_after > 0);
    450 
    451 	if (! toobig) {
    452 		err = 0;
    453 	} else if (err) {
    454 		err--;
    455 	}
    456 
    457 	if (! sent_one) {
    458 		/* try to force a send first time in */
    459 		oldlen = -1;
    460 		sent_one = 1;
    461 	}
    462 	if (debug_sel) {
    463 		rfbLog("selection_send:  %s '%s'\n",
    464 		    ev->xselection.selection == XA_PRIMARY ? "PRIMARY  " : "CLIPBOARD",
    465 		    selection_str);
    466 	}
    467 
    468 	/* look for changes in the new value */
    469 	newlen = strlen(selection_str);
    470 	strncpy(after, selection_str, CHKSZ);
    471 
    472 	if (oldlen == newlen && strncmp(before, after, CHKSZ) == 0) {
    473 		/* evidently no change */
    474 		if (debug_sel) {
    475 			rfbLog("selection_send:  no change.\n");
    476 		}
    477 		return;
    478 	}
    479 	if (newlen == 0) {
    480 		/* do not bother sending a null string out */
    481 		return;
    482 	}
    483 
    484 	if (! all_clients_initialized()) {
    485 		rfbLog("selection_send: no send: uninitialized clients\n");
    486 		return; /* some clients initializing, cannot send */
    487 	}
    488 
    489 	if (unixpw_in_progress) {
    490 		return;
    491 	}
    492 
    493 	/* now send it to any connected VNC clients (rfbServerCutText) */
    494 	if (!screen) {
    495 		return;
    496 	}
    497 
    498 	len = newlen;
    499 	if (ev->xselection.selection == XA_PRIMARY) {
    500 		primary_len = len;
    501 	} else if (clipboard_atom && ev->xselection.selection == clipboard_atom)  {
    502 		clipboard_len = len;
    503 	}
    504 	if (check_sel_direction("send", "selection_send", selection_str, len)) {
    505 		rfbSendServerCutText(screen, selection_str, len);
    506 	}
    507 #endif	/* NO_X11 */
    508 }
    509 
    510 void resend_selection(char *type) {
    511 #if NO_X11
    512 	RAWFB_RET_VOID
    513 	if (!type) {}
    514 	return;
    515 #else
    516 	char *selection_str = "";
    517 	int len = 0;
    518 
    519 	RAWFB_RET_VOID
    520 
    521 	if (! all_clients_initialized()) {
    522 		rfbLog("selection_send: no send: uninitialized clients\n");
    523 		return; /* some clients initializing, cannot send */
    524 	}
    525 	if (unixpw_in_progress) {
    526 		return;
    527 	}
    528 	if (!screen) {
    529 		return;
    530 	}
    531 
    532 	if (!strcmp(type, "cutbuffer")) {
    533 		selection_str = cutbuffer_str;
    534 		len = cutbuffer_len;
    535 	} else if (!strcmp(type, "clipboard")) {
    536 		selection_str = clipboard_str;
    537 		len = clipboard_len;
    538 	} else if (!strcmp(type, "primary")) {
    539 		selection_str = primary_str;
    540 		len = primary_len;
    541 	}
    542 	if (check_sel_direction("send", "selection_send", selection_str, len)) {
    543 		rfbSendServerCutText(screen, selection_str, len);
    544 	}
    545 #endif	/* NO_X11 */
    546 }
    547 
    548 
    549