1 /*
2    Copyright (C) 2002-2010 Karl J. Runge <runge@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 
parent_window(Window win,char ** name)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: */
valid_window(Window win,XWindowAttributes * attr_ret,int bequiet)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 
xtranslate(Window src,Window dst,int src_x,int src_y,int * dst_x,int * dst_y,Window * child,int bequiet)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 
get_window_size(Window win,int * w,int * h)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  */
snapshot_stack_list(int free_only,double allowed_age)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 
get_boff(void)309 int get_boff(void) {
310 	if (macosx_console) {
311 		return 0x1000000;
312 	} else {
313 		return 0;
314 	}
315 }
316 
get_bwin(void)317 int get_bwin(void) {
318 	return 10;
319 }
320 
update_stack_list(void)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 
query_pointer(Window start)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 
mask_state(void)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 
pick_windowid(unsigned long * num)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 
descend_pointer(int depth,Window start,char * name_info,int len)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 
id_cmd(char * cmd)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