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 /* -- win_utils.c -- */
     34 
     35 #include "x11vnc.h"
     36 #include "xinerama.h"
     37 #include "winattr_t.h"
     38 #include "cleanup.h"
     39 #include "xwrappers.h"
     40 #include "connections.h"
     41 #include "xrandr.h"
     42 #include "macosx.h"
     43 
     44 winattr_t *stack_list = NULL;
     45 int stack_list_len = 0;
     46 int stack_list_num = 0;
     47 
     48 
     49 Window parent_window(Window win, char **name);
     50 int valid_window(Window win, XWindowAttributes *attr_ret, int bequiet);
     51 Bool xtranslate(Window src, Window dst, int src_x, int src_y, int *dst_x,
     52     int *dst_y, Window *child, int bequiet);
     53 int get_window_size(Window win, int *w, int *h);
     54 void snapshot_stack_list(int free_only, double allowed_age);
     55 int get_boff(void);
     56 int get_bwin(void);
     57 void update_stack_list(void);
     58 Window query_pointer(Window start);
     59 unsigned int mask_state(void);
     60 int pick_windowid(unsigned long *num);
     61 Window descend_pointer(int depth, Window start, char *name_info, int len);
     62 void id_cmd(char *cmd);
     63 
     64 
     65 Window parent_window(Window win, char **name) {
     66 #if !NO_X11
     67 	Window r, parent;
     68 	Window *list;
     69 	XErrorHandler old_handler;
     70 	unsigned int nchild;
     71 	int rc;
     72 #endif
     73 
     74 	if (name != NULL) {
     75 		*name = NULL;
     76 	}
     77 	RAWFB_RET(None)
     78 #if NO_X11
     79 	nox11_exit(1);
     80 	if (!name || !win) {}
     81 	return None;
     82 #else
     83 
     84 	old_handler = XSetErrorHandler(trap_xerror);
     85 	trapped_xerror = 0;
     86 	rc = XQueryTree_wr(dpy, win, &r, &parent, &list, &nchild);
     87 	XSetErrorHandler(old_handler);
     88 
     89 	if (! rc || trapped_xerror) {
     90 		trapped_xerror = 0;
     91 		return None;
     92 	}
     93 	trapped_xerror = 0;
     94 
     95 	if (list) {
     96 		XFree_wr(list);
     97 	}
     98 	if (parent && name) {
     99 		XFetchName(dpy, parent, name);
    100 	}
    101 	return parent;
    102 #endif	/* NO_X11 */
    103 }
    104 
    105 /* trapping utility to check for a valid window: */
    106 int valid_window(Window win, XWindowAttributes *attr_ret, int bequiet) {
    107 	XWindowAttributes attr, *pattr;
    108 #if !NO_X11
    109 	XErrorHandler old_handler;
    110 	int ok = 0;
    111 #endif
    112 
    113 	if (attr_ret == NULL) {
    114 		pattr = &attr;
    115 	} else {
    116 		pattr = attr_ret;
    117 	}
    118 
    119 	if (win == None) {
    120 		return 0;
    121 	}
    122 
    123 #ifdef MACOSX
    124 	if (macosx_console) {
    125 		return macosx_valid_window(win, attr_ret);
    126 	}
    127 #endif
    128 
    129 	RAWFB_RET(0)
    130 
    131 #if NO_X11
    132 	nox11_exit(1);
    133 	if (!win || !attr_ret || !bequiet) {}
    134 	return 0;
    135 #else
    136 
    137 	old_handler = XSetErrorHandler(trap_xerror);
    138 	trapped_xerror = 0;
    139 	if (XGetWindowAttributes(dpy, win, pattr)) {
    140 		ok = 1;
    141 	}
    142 	if (trapped_xerror && trapped_xerror_event) {
    143 		if (! quiet && ! bequiet) {
    144 			rfbLog("valid_window: trapped XError: %s (0x%lx)\n",
    145 			    xerror_string(trapped_xerror_event), win);
    146 		}
    147 		ok = 0;
    148 	}
    149 	XSetErrorHandler(old_handler);
    150 	trapped_xerror = 0;
    151 
    152 	return ok;
    153 #endif	/* NO_X11 */
    154 }
    155 
    156 Bool xtranslate(Window src, Window dst, int src_x, int src_y, int *dst_x,
    157     int *dst_y, Window *child, int bequiet) {
    158 	XErrorHandler old_handler = NULL;
    159 	Bool ok = False;
    160 
    161 	RAWFB_RET(False)
    162 #if NO_X11
    163 	nox11_exit(1);
    164 	if (!src || !dst || !src_x || !src_y || !dst_x || !dst_y || !child || !bequiet) {}
    165 	if (!old_handler || !ok) {}
    166 	return False;
    167 #else
    168 
    169 	trapped_xerror = 0;
    170 	old_handler = XSetErrorHandler(trap_xerror);
    171 	if (XTranslateCoordinates(dpy, src, dst, src_x, src_y, dst_x,
    172 	    dst_y, child)) {
    173 		ok = True;
    174 	}
    175 	if (trapped_xerror && trapped_xerror_event) {
    176 		if (! quiet && ! bequiet) {
    177 			rfbLog("xtranslate: trapped XError: %s (0x%lx)\n",
    178 			    xerror_string(trapped_xerror_event), src);
    179 		}
    180 		ok = False;
    181 	}
    182 	XSetErrorHandler(old_handler);
    183 	trapped_xerror = 0;
    184 
    185 	return ok;
    186 #endif	/* NO_X11 */
    187 }
    188 
    189 int get_window_size(Window win, int *w, int *h) {
    190 	XWindowAttributes attr;
    191 	/* valid_window? */
    192 	if (valid_window(win, &attr, 1)) {
    193 		*w = attr.width;
    194 		*h = attr.height;
    195 		return 1;
    196 	} else {
    197 		return 0;
    198 	}
    199 }
    200 
    201 /*
    202  * For use in the -wireframe stuff, save the stacking order of the direct
    203  * children of the root window.  Ideally done before we send ButtonPress
    204  * to the X server.
    205  */
    206 void snapshot_stack_list(int free_only, double allowed_age) {
    207 	static double last_snap = 0.0, last_free = 0.0;
    208 	double now;
    209 	int num, rc, i, j;
    210 	unsigned int ui;
    211 	Window r, w;
    212 	Window *list;
    213 
    214 	if (! stack_list) {
    215 		stack_list = (winattr_t *) malloc(256*sizeof(winattr_t));
    216 		stack_list_num = 0;
    217 		stack_list_len = 256;
    218 	}
    219 
    220 	dtime0(&now);
    221 	if (free_only) {
    222 		/* we really don't free it, just reset to zero windows */
    223 		stack_list_num = 0;
    224 		last_free = now;
    225 		return;
    226 	}
    227 
    228 	if (stack_list_num && now < last_snap + allowed_age) {
    229 		return;
    230 	}
    231 
    232 	stack_list_num = 0;
    233 	last_free = now;
    234 
    235 #ifdef MACOSX
    236 	if (! macosx_console) {
    237 		RAWFB_RET_VOID
    238 	}
    239 #else
    240 	RAWFB_RET_VOID
    241 #endif
    242 
    243 #if NO_X11 && !defined(MACOSX)
    244 	num = rc = i = j = 0;	/* compiler warnings */
    245 	ui = 0;
    246 	r = w = None;
    247 	list = NULL;
    248 	return;
    249 #else
    250 
    251 	X_LOCK;
    252 	/* no need to trap error since rootwin */
    253 	rc = XQueryTree_wr(dpy, rootwin, &r, &w, &list, &ui);
    254 	num = (int) ui;
    255 
    256 	if (! rc) {
    257 		stack_list_num = 0;
    258 		last_free = now;
    259 		last_snap = 0.0;
    260 		X_UNLOCK;
    261 		return;
    262 	}
    263 
    264 	last_snap = now;
    265 	if (num > stack_list_len + blackouts) {
    266 		int n = 2*num;
    267 		free(stack_list);
    268 		stack_list = (winattr_t *) malloc(n*sizeof(winattr_t));
    269 		stack_list_len = n;
    270 	}
    271 	j = 0;
    272 	for (i=0; i<num; i++) {
    273 		stack_list[j].win = list[i];
    274 		stack_list[j].fetched = 0;
    275 		stack_list[j].valid = 0;
    276 		stack_list[j].time = now;
    277 		j++;
    278 	}
    279 	for (i=0; i<blackouts; i++) {
    280 		stack_list[j].win = get_boff() + 1;
    281 		stack_list[j].fetched = 1;
    282 		stack_list[j].valid = 1;
    283 		stack_list[j].x = blackr[i].x1;
    284 		stack_list[j].y = blackr[i].y1;
    285 		stack_list[j].width  = blackr[i].x2 - blackr[i].x1;
    286 		stack_list[j].height = blackr[i].y2 - blackr[i].y1;
    287 		stack_list[j].time = now;
    288 		stack_list[j].map_state = IsViewable;
    289 		stack_list[j].rx = -1;
    290 		stack_list[j].ry = -1;
    291 		j++;
    292 
    293 if (0) fprintf(stderr, "blackr: %d %dx%d+%d+%d\n", i,
    294 	stack_list[j-1].width, stack_list[j-1].height,
    295 	stack_list[j-1].x, stack_list[j-1].y);
    296 
    297 	}
    298 	stack_list_num = num + blackouts;
    299 	if (debug_wireframe > 1) {
    300 		fprintf(stderr, "snapshot_stack_list: num=%d len=%d\n",
    301 		    stack_list_num, stack_list_len);
    302 	}
    303 
    304 	XFree_wr(list);
    305 	X_UNLOCK;
    306 #endif	/* NO_X11 */
    307 }
    308 
    309 int get_boff(void) {
    310 	if (macosx_console) {
    311 		return 0x1000000;
    312 	} else {
    313 		return 0;
    314 	}
    315 }
    316 
    317 int get_bwin(void) {
    318 	return 10;
    319 }
    320 
    321 void update_stack_list(void) {
    322 	int k;
    323 	double now;
    324 	XWindowAttributes attr;
    325 	int boff, bwin;
    326 
    327 	if (! stack_list) {
    328 		return;
    329 	}
    330 	if (! stack_list_num) {
    331 		return;
    332 	}
    333 
    334 	dtime0(&now);
    335 
    336 	boff = get_boff();
    337 	bwin = get_bwin();
    338 
    339 	X_LOCK;
    340 	for (k=0; k < stack_list_num; k++) {
    341 		Window win = stack_list[k].win;
    342 		if (win != None && boff <= (int) win && (int) win < boff + bwin) {
    343 			;	/* special, blackout */
    344 		} else if (!valid_window(win, &attr, 1)) {
    345 			stack_list[k].valid = 0;
    346 		} else {
    347 			stack_list[k].valid = 1;
    348 			stack_list[k].x = attr.x;
    349 			stack_list[k].y = attr.y;
    350 			stack_list[k].width = attr.width;
    351 			stack_list[k].height = attr.height;
    352 			stack_list[k].border_width = attr.border_width;
    353 			stack_list[k].depth = attr.depth;
    354 			stack_list[k].class = attr.class;
    355 			stack_list[k].backing_store = attr.backing_store;
    356 			stack_list[k].map_state = attr.map_state;
    357 
    358 			/* root_x, root_y not used for stack_list usage: */
    359 			stack_list[k].rx = -1;
    360 			stack_list[k].ry = -1;
    361 		}
    362 		stack_list[k].fetched = 1;
    363 		stack_list[k].time = now;
    364 	}
    365 	X_UNLOCK;
    366 if (0) fprintf(stderr, "update_stack_list[%d]: %.4f  %.4f\n", stack_list_num, now - x11vnc_start, dtime(&now));
    367 }
    368 
    369 Window query_pointer(Window start) {
    370 	int rx, ry;
    371 #if !NO_X11
    372 	Window r, c;	/* compiler warnings */
    373 	int wx, wy;
    374 	unsigned int mask;
    375 #endif
    376 
    377 #ifdef MACOSX
    378 	if (macosx_console) {
    379 		macosx_get_cursor_pos(&rx, &ry);
    380 	}
    381 #endif
    382 
    383 	RAWFB_RET(None)
    384 
    385 #if NO_X11
    386 	if (!start) { rx = ry = 0; }
    387 	return None;
    388 #else
    389 	if (start == None) {
    390 		start = rootwin;
    391 	}
    392 	if (XQueryPointer_wr(dpy, start, &r, &c, &rx, &ry, &wx, &wy, &mask)) {
    393 		return c;
    394 	} else {
    395 		return None;
    396 	}
    397 #endif	/* NO_X11 */
    398 }
    399 
    400 unsigned int mask_state(void) {
    401 #if NO_X11
    402 	RAWFB_RET(0)
    403 	return 0;
    404 #else
    405 	Window r, c;
    406 	int rx, ry, wx, wy;
    407 	unsigned int mask;
    408 
    409 	RAWFB_RET(0)
    410 
    411 	if (XQueryPointer_wr(dpy, rootwin, &r, &c, &rx, &ry, &wx, &wy, &mask)) {
    412 		return mask;
    413 	} else {
    414 		return 0;
    415 	}
    416 #endif	/* NO_X11 */
    417 }
    418 
    419 int pick_windowid(unsigned long *num) {
    420 	char line[512];
    421 	int ok = 0, n = 0, msec = 10, secmax = 15;
    422 	FILE *p;
    423 
    424 	RAWFB_RET(0)
    425 
    426 	if (use_dpy) {
    427 		set_env("DISPLAY", use_dpy);
    428 	}
    429 	/* id */
    430 	if (no_external_cmds || !cmd_ok("id")) {
    431 		rfbLogEnable(1);
    432 		rfbLog("cannot run external commands in -nocmds mode:\n");
    433 		rfbLog("   \"%s\"\n", "xwininfo");
    434 		rfbLog("   exiting.\n");
    435 		clean_up_exit(1);
    436 	}
    437 	close_exec_fds();
    438 	p = popen("xwininfo", "r");
    439 
    440 	if (! p) {
    441 		return 0;
    442 	}
    443 
    444 	fprintf(stderr, "\n");
    445 	fprintf(stderr, "  Please select the window for x11vnc to poll\n");
    446 	fprintf(stderr, "  by clicking the mouse in that window.\n");
    447 	fprintf(stderr, "\n");
    448 
    449 	while (msec * n++ < 1000 * secmax) {
    450 		unsigned long tmp;
    451 		char *q;
    452 		fd_set set;
    453 		struct timeval tv;
    454 
    455 		if (screen && screen->clientHead) {
    456 			/* they may be doing the pointer-pick thru vnc: */
    457 			int nfds;
    458 			tv.tv_sec = 0;
    459 			tv.tv_usec = msec * 1000;
    460 			FD_ZERO(&set);
    461 			FD_SET(fileno(p), &set);
    462 
    463 			nfds = select(fileno(p)+1, &set, NULL, NULL, &tv);
    464 
    465 			if (nfds == 0 || nfds < 0) {
    466 				/*
    467 				 * select timedout or error.
    468 				 * note this rfbPE takes about 30ms too:
    469 				 */
    470 				rfbPE(-1);
    471 				XFlush_wr(dpy);
    472 				continue;
    473 			}
    474 		}
    475 
    476 		if (fgets(line, 512, p) == NULL) {
    477 			break;
    478 		}
    479 		q = strstr(line, " id: 0x");
    480 		if (q) {
    481 			q += 5;
    482 			if (sscanf(q, "0x%lx ", &tmp) == 1) {
    483 				ok = 1;
    484 				*num = tmp;
    485 				fprintf(stderr, "  Picked: 0x%lx\n\n", tmp);
    486 				break;
    487 			}
    488 		}
    489 	}
    490 	pclose(p);
    491 	return ok;
    492 }
    493 
    494 Window descend_pointer(int depth, Window start, char *name_info, int len) {
    495 #if NO_X11
    496 	RAWFB_RET(None)
    497 	if (!depth || !start || !name_info || !len) {}
    498 	return None;
    499 #else
    500 	Window r, c, clast = None;
    501 	int i, rx, ry, wx, wy;
    502 	int written = 0, filled = 0;
    503 	char *store = NULL;
    504 	unsigned int m;
    505 	static XClassHint *classhint = NULL;
    506 	static char *nm_cache = NULL;
    507 	static int nm_cache_len = 0;
    508 	static Window prev_start = None;
    509 
    510 	RAWFB_RET(None)
    511 
    512 	if (! classhint) {
    513 		classhint = XAllocClassHint();
    514 	}
    515 
    516 	if (! nm_cache) {
    517 		nm_cache = (char *) malloc(1024);
    518 		nm_cache_len = 1024;
    519 		nm_cache[0] = '\0';
    520 	}
    521 	if (name_info && nm_cache_len < len) {
    522 		if (nm_cache) {
    523 			free(nm_cache);
    524 		}
    525 		nm_cache_len = 2*len;
    526 		nm_cache = (char *) malloc(nm_cache_len);
    527 	}
    528 
    529 	if (name_info) {
    530 		if (start != None && start == prev_start) {
    531 			store = NULL;
    532 			strncpy(name_info, nm_cache, len);
    533 		} else {
    534 			store = name_info;
    535 			name_info[0] = '\0';
    536 		}
    537 	}
    538 
    539 	if (start != None) {
    540 		c = start;
    541 		if (name_info) {
    542 			prev_start = start;
    543 		}
    544 	} else {
    545 		c = rootwin;
    546 	}
    547 
    548 	for (i=0; i<depth; i++) {
    549 		clast = c;
    550 		if (store && ! filled) {
    551 			char *name;
    552 			if (XFetchName(dpy, clast, &name) && name != NULL) {
    553 				int l = strlen(name);
    554 				if (written + l+2 < len) {
    555 					strcat(store, "^^");
    556 					written += 2;
    557 					strcat(store, name);
    558 					written += l;
    559 				} else {
    560 					filled = 1;
    561 				}
    562 				XFree_wr(name);
    563 			}
    564 		}
    565 		if (store && classhint && ! filled) {
    566 			classhint->res_name = NULL;
    567 			classhint->res_class = NULL;
    568 			if (XGetClassHint(dpy, clast, classhint)) {
    569 				int l = 0;
    570 				if (classhint->res_class) {
    571 					l += strlen(classhint->res_class);
    572 				}
    573 				if (classhint->res_name) {
    574 					l += strlen(classhint->res_name);
    575 				}
    576 				if (written + l+4 < len) {
    577 					strcat(store, "##");
    578 					if (classhint->res_class) {
    579 						strcat(store,
    580 						    classhint->res_class);
    581 					}
    582 					strcat(store, "++");
    583 					if (classhint->res_name) {
    584 						strcat(store,
    585 						    classhint->res_name);
    586 					}
    587 					written += l+4;
    588 				} else {
    589 					filled = 1;
    590 				}
    591 				if (classhint->res_class) {
    592 					XFree_wr(classhint->res_class);
    593 				}
    594 				if (classhint->res_name) {
    595 					XFree_wr(classhint->res_name);
    596 				}
    597 			}
    598 		}
    599 		if (! XQueryPointer_wr(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &m)) {
    600 			break;
    601 		}
    602 		if (! c) {
    603 			break;
    604 		}
    605 	}
    606 	if (start != None && name_info) {
    607 		strncpy(nm_cache, name_info, nm_cache_len);
    608 	}
    609 
    610 	return clast;
    611 #endif	/* NO_X11 */
    612 }
    613 
    614 void id_cmd(char *cmd) {
    615 	int rc, dx = 0, dy = 0, dw = 0, dh = 0;
    616 	int x0, y0, w0, h0;
    617 	int x, y, w, h, do_move = 0, do_resize = 0;
    618 	int disp_x = DisplayWidth(dpy, scr);
    619 	int disp_y = DisplayHeight(dpy, scr);
    620 	Window win = subwin;
    621 	XWindowAttributes attr;
    622 	XErrorHandler old_handler = NULL;
    623 	Window twin;
    624 
    625 	if (!cmd || !strcmp(cmd, "")) {
    626 		return;
    627 	}
    628 	if (strstr(cmd, "win=") == cmd) {
    629 		if (! scan_hexdec(cmd + strlen("win="), &win)) {
    630 			rfbLog("id_cmd: incorrect win= hex/dec number: %s\n", cmd);
    631 			return;
    632 		} else {
    633 			char *q = strchr(cmd, ':');
    634 			if (!q) {
    635 				rfbLog("id_cmd: incorrect win=...: hex/dec number: %s\n", cmd);
    636 				return;
    637 			}
    638 			rfbLog("id_cmd:%s set window id to 0x%lx\n", cmd, win);
    639 			cmd = q+1;
    640 		}
    641 	}
    642 	if (!win) {
    643 		rfbLog("id_cmd:%s not in sub-window mode or no win=0xNNNN.\n", cmd);
    644 		return;
    645 	}
    646 #if !NO_X11
    647 	X_LOCK;
    648 	if (!valid_window(win, &attr, 1)) {
    649 		X_UNLOCK;
    650 		return;
    651 	}
    652 	w0 = w = attr.width;
    653 	h0 = h = attr.height;
    654 	old_handler = XSetErrorHandler(trap_xerror);
    655 	trapped_xerror = 0;
    656 	XTranslateCoordinates(dpy, win, rootwin, 0, 0, &x, &y, &twin);
    657 	x0 = x;
    658 	y0 = y;
    659 	if (strstr(cmd, "move:") == cmd) {
    660 		if (sscanf(cmd, "move:%d%d", &dx, &dy) == 2) {
    661 			x = x + dx;
    662 			y = y + dy;
    663 			do_move = 1;
    664 		}
    665 	} else if (strstr(cmd, "resize:") == cmd) {
    666 		if (sscanf(cmd, "resize:%d%d", &dw, &dh) == 2) {
    667 			w = w + dw;
    668 			h = h + dh;
    669 			do_move = 1;
    670 			do_resize = 1;
    671 		}
    672 	} else if (strstr(cmd, "geom:") == cmd) {
    673 		if (parse_geom(cmd+strlen("geom:"), &w, &h, &x, &y, disp_x, disp_y)) {
    674 			do_move = 1;
    675 			do_resize = 1;
    676 			if (w <= 0) {
    677 				w = w0;
    678 			}
    679 			if (h <= 0) {
    680 				h = h0;
    681 			}
    682 			if (scaling && getenv("X11VNC_APPSHARE_ACTIVE")) {
    683 				x /= scale_fac_x;
    684 				y /= scale_fac_y;
    685 			}
    686 		}
    687 	} else if (!strcmp(cmd, "raise")) {
    688 		rc = XRaiseWindow(dpy, win);
    689 		rfbLog("id_cmd:%s rc=%d\n", cmd, rc);
    690 	} else if (!strcmp(cmd, "lower")) {
    691 		rc = XLowerWindow(dpy, win);
    692 		rfbLog("id_cmd:%s rc=%d\n", cmd, rc);
    693 	} else if (!strcmp(cmd, "map")) {
    694 		rc= XMapRaised(dpy, win);
    695 		rfbLog("id_cmd:%s rc=%d\n", cmd, rc);
    696 	} else if (!strcmp(cmd, "unmap")) {
    697 		rc= XUnmapWindow(dpy, win);
    698 		rfbLog("id_cmd:%s rc=%d\n", cmd, rc);
    699 	} else if (!strcmp(cmd, "iconify")) {
    700 		rc= XIconifyWindow(dpy, win, scr);
    701 		rfbLog("id_cmd:%s rc=%d\n", cmd, rc);
    702 	} else if (strstr(cmd, "wm_name:") == cmd) {
    703 		rc= XStoreName(dpy, win, cmd+strlen("wm_name:"));
    704 		rfbLog("id_cmd:%s rc=%d\n", cmd, rc);
    705 	} else if (strstr(cmd, "icon_name:") == cmd) {
    706 		rc= XSetIconName(dpy, win, cmd+strlen("icon_name:"));
    707 		rfbLog("id_cmd:%s rc=%d\n", cmd, rc);
    708 	} else if (!strcmp(cmd, "wm_delete")) {
    709 		XClientMessageEvent ev;
    710 		memset(&ev, 0, sizeof(ev));
    711 		ev.type = ClientMessage;
    712 		ev.send_event = True;
    713 		ev.display = dpy;
    714 		ev.window = win;
    715 		ev.message_type = XInternAtom(dpy, "WM_PROTOCOLS", False);
    716 		ev.format = 32;
    717 		ev.data.l[0] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
    718 		rc = XSendEvent(dpy, win, False, 0, (XEvent *) &ev);
    719 		rfbLog("id_cmd:%s rc=%d\n", cmd, rc);
    720 	} else {
    721 		rfbLog("id_cmd:%s unrecognized command.\n", cmd);
    722 	}
    723 	if (do_move || do_resize) {
    724 		if (w >= disp_x) {
    725 			w = disp_x - 4;
    726 		}
    727 		if (h >= disp_y) {
    728 			h = disp_y - 4;
    729 		}
    730 		if (w < 1) {
    731 			w = 1;
    732 		}
    733 		if (h < 1) {
    734 			h = 1;
    735 		}
    736 		if (x + w > disp_x) {
    737 			x = disp_x - w - 1;
    738 		}
    739 		if (y + h > disp_y) {
    740 			y = disp_y - h - 1;
    741 		}
    742 		if (x < 0) {
    743 			x = 1;
    744 		}
    745 		if (y < 0) {
    746 			y = 1;
    747 		}
    748 		rc = 0;
    749 		rc += XMoveWindow(dpy, win, x, y);
    750 		off_x = x;
    751 		off_y = y;
    752 
    753 		rc += XResizeWindow(dpy, win, w, h);
    754 
    755 		rfbLog("id_cmd:%s rc=%d dx=%d dy=%d dw=%d dh=%d %dx%d+%d+%d -> %dx%d+%d+%d\n",
    756 		    cmd, rc, dx, dy, dw, dh, w0, h0, x0, y0, w, h, x, h);
    757 	}
    758 	XSync(dpy, False);
    759 	XSetErrorHandler(old_handler);
    760 	if (trapped_xerror) {
    761 		rfbLog("id_cmd:%s trapped_xerror.\n", cmd);
    762 	}
    763 	trapped_xerror = 0;
    764 	if (do_resize) {
    765 		rfbLog("id_cmd:%s calling check_xrandr_event.\n", cmd);
    766 		check_xrandr_event("id_cmd");
    767 	}
    768 	X_UNLOCK;
    769 #endif
    770 }
    771 
    772