1/* 2 Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com> 3 All rights reserved. 4 5This file is part of x11vnc. 6 7x11vnc is free software; you can redistribute it and/or modify 8it under the terms of the GNU General Public License as published by 9the Free Software Foundation; either version 2 of the License, or (at 10your option) any later version. 11 12x11vnc is distributed in the hope that it will be useful, 13but WITHOUT ANY WARRANTY; without even the implied warranty of 14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15GNU General Public License for more details. 16 17You should have received a copy of the GNU General Public License 18along with x11vnc; if not, write to the Free Software 19Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA 20or see <http://www.gnu.org/licenses/>. 21 22In addition, as a special exception, Karl J. Runge 23gives permission to link the code of its release of x11vnc with the 24OpenSSL project's "OpenSSL" library (or with modified versions of it 25that use the same license as the "OpenSSL" library), and distribute 26the linked executables. You must obey the GNU General Public License 27in all respects for all of the code used other than "OpenSSL". If you 28modify this file, you may extend this exception to your version of the 29file, but you are not obligated to do so. If you do not wish to do 30so, 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 44winattr_t *stack_list = NULL; 45int stack_list_len = 0; 46int stack_list_num = 0; 47 48 49Window parent_window(Window win, char **name); 50int valid_window(Window win, XWindowAttributes *attr_ret, int bequiet); 51Bool xtranslate(Window src, Window dst, int src_x, int src_y, int *dst_x, 52 int *dst_y, Window *child, int bequiet); 53int get_window_size(Window win, int *w, int *h); 54void snapshot_stack_list(int free_only, double allowed_age); 55int get_boff(void); 56int get_bwin(void); 57void update_stack_list(void); 58Window query_pointer(Window start); 59unsigned int mask_state(void); 60int pick_windowid(unsigned long *num); 61Window descend_pointer(int depth, Window start, char *name_info, int len); 62void id_cmd(char *cmd); 63 64 65Window 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: */ 106int 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 156Bool 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 189int 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 */ 206void 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 293if (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 309int get_boff(void) { 310 if (macosx_console) { 311 return 0x1000000; 312 } else { 313 return 0; 314 } 315} 316 317int get_bwin(void) { 318 return 10; 319} 320 321void 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; 366if (0) fprintf(stderr, "update_stack_list[%d]: %.4f %.4f\n", stack_list_num, now - x11vnc_start, dtime(&now)); 367} 368 369Window 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 400unsigned 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 419int 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 494Window 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 614void 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