1 /* 2 * Copyright 2010 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "webrtc/base/x11windowpicker.h" 12 13 #include <math.h> 14 #include <string.h> 15 16 #include <algorithm> 17 #include <string> 18 19 #include <X11/Xatom.h> 20 #include <X11/extensions/Xcomposite.h> 21 #include <X11/extensions/Xrender.h> 22 #include <X11/Xutil.h> 23 24 #include "webrtc/base/logging.h" 25 26 namespace rtc { 27 28 // Convenience wrapper for XGetWindowProperty results. 29 template <class PropertyType> 30 class XWindowProperty { 31 public: 32 XWindowProperty(Display* display, Window window, Atom property) 33 : data_(NULL) { 34 const int kBitsPerByte = 8; 35 Atom actual_type; 36 int actual_format; 37 unsigned long bytes_after; // NOLINT: type required by XGetWindowProperty 38 int status = XGetWindowProperty(display, window, property, 0L, ~0L, False, 39 AnyPropertyType, &actual_type, 40 &actual_format, &size_, 41 &bytes_after, &data_); 42 succeeded_ = (status == Success); 43 if (!succeeded_) { 44 data_ = NULL; // Ensure nothing is freed. 45 } else if (sizeof(PropertyType) * kBitsPerByte != actual_format) { 46 LOG(LS_WARNING) << "Returned type size differs from " 47 "requested type size."; 48 succeeded_ = false; 49 // We still need to call XFree in this case, so leave data_ alone. 50 } 51 if (!succeeded_) { 52 size_ = 0; 53 } 54 } 55 56 ~XWindowProperty() { 57 if (data_) { 58 XFree(data_); 59 } 60 } 61 62 bool succeeded() const { return succeeded_; } 63 size_t size() const { return size_; } 64 const PropertyType* data() const { 65 return reinterpret_cast<PropertyType*>(data_); 66 } 67 PropertyType* data() { 68 return reinterpret_cast<PropertyType*>(data_); 69 } 70 71 private: 72 bool succeeded_; 73 unsigned long size_; // NOLINT: type required by XGetWindowProperty 74 unsigned char* data_; 75 76 DISALLOW_COPY_AND_ASSIGN(XWindowProperty); 77 }; 78 79 // Stupid X11. It seems none of the synchronous returns codes from X11 calls 80 // are meaningful unless an asynchronous error handler is configured. This 81 // RAII class registers and unregisters an X11 error handler. 82 class XErrorSuppressor { 83 public: 84 explicit XErrorSuppressor(Display* display) 85 : display_(display), original_error_handler_(NULL) { 86 SuppressX11Errors(); 87 } 88 ~XErrorSuppressor() { 89 UnsuppressX11Errors(); 90 } 91 92 private: 93 static int ErrorHandler(Display* display, XErrorEvent* e) { 94 char buf[256]; 95 XGetErrorText(display, e->error_code, buf, sizeof buf); 96 LOG(LS_WARNING) << "Received X11 error \"" << buf << "\" for request code " 97 << static_cast<unsigned int>(e->request_code); 98 return 0; 99 } 100 101 void SuppressX11Errors() { 102 XFlush(display_); 103 XSync(display_, False); 104 original_error_handler_ = XSetErrorHandler(&ErrorHandler); 105 } 106 107 void UnsuppressX11Errors() { 108 XFlush(display_); 109 XSync(display_, False); 110 XErrorHandler handler = XSetErrorHandler(original_error_handler_); 111 if (handler != &ErrorHandler) { 112 LOG(LS_WARNING) << "Unbalanced XSetErrorHandler() calls detected. " 113 << "Final error handler may not be what you expect!"; 114 } 115 original_error_handler_ = NULL; 116 } 117 118 Display* display_; 119 XErrorHandler original_error_handler_; 120 121 DISALLOW_COPY_AND_ASSIGN(XErrorSuppressor); 122 }; 123 124 // Hiding all X11 specifics inside its own class. This to avoid 125 // conflicts between talk and X11 header declarations. 126 class XWindowEnumerator { 127 public: 128 XWindowEnumerator() 129 : display_(NULL), 130 has_composite_extension_(false), 131 has_render_extension_(false) { 132 } 133 134 ~XWindowEnumerator() { 135 if (display_ != NULL) { 136 XCloseDisplay(display_); 137 } 138 } 139 140 bool Init() { 141 if (display_ != NULL) { 142 // Already initialized. 143 return true; 144 } 145 display_ = XOpenDisplay(NULL); 146 if (display_ == NULL) { 147 LOG(LS_ERROR) << "Failed to open display."; 148 return false; 149 } 150 151 XErrorSuppressor error_suppressor(display_); 152 153 wm_state_ = XInternAtom(display_, "WM_STATE", True); 154 net_wm_icon_ = XInternAtom(display_, "_NET_WM_ICON", False); 155 156 int event_base, error_base, major_version, minor_version; 157 if (XCompositeQueryExtension(display_, &event_base, &error_base) && 158 XCompositeQueryVersion(display_, &major_version, &minor_version) && 159 // XCompositeNameWindowPixmap() requires version 0.2 160 (major_version > 0 || minor_version >= 2)) { 161 has_composite_extension_ = true; 162 } else { 163 LOG(LS_INFO) << "Xcomposite extension not available or too old."; 164 } 165 166 if (XRenderQueryExtension(display_, &event_base, &error_base) && 167 XRenderQueryVersion(display_, &major_version, &minor_version) && 168 // XRenderSetPictureTransform() requires version 0.6 169 (major_version > 0 || minor_version >= 6)) { 170 has_render_extension_ = true; 171 } else { 172 LOG(LS_INFO) << "Xrender extension not available or too old."; 173 } 174 return true; 175 } 176 177 bool EnumerateWindows(WindowDescriptionList* descriptions) { 178 if (!Init()) { 179 return false; 180 } 181 XErrorSuppressor error_suppressor(display_); 182 int num_screens = XScreenCount(display_); 183 bool result = false; 184 for (int i = 0; i < num_screens; ++i) { 185 if (EnumerateScreenWindows(descriptions, i)) { 186 // We know we succeded on at least one screen. 187 result = true; 188 } 189 } 190 return result; 191 } 192 193 bool EnumerateDesktops(DesktopDescriptionList* descriptions) { 194 if (!Init()) { 195 return false; 196 } 197 XErrorSuppressor error_suppressor(display_); 198 Window default_root_window = XDefaultRootWindow(display_); 199 int num_screens = XScreenCount(display_); 200 for (int i = 0; i < num_screens; ++i) { 201 Window root_window = XRootWindow(display_, i); 202 DesktopId id(DesktopId(root_window, i)); 203 // TODO: Figure out an appropriate desktop title. 204 DesktopDescription desc(id, ""); 205 desc.set_primary(root_window == default_root_window); 206 descriptions->push_back(desc); 207 } 208 return num_screens > 0; 209 } 210 211 bool IsVisible(const WindowId& id) { 212 if (!Init()) { 213 return false; 214 } 215 XErrorSuppressor error_suppressor(display_); 216 XWindowAttributes attr; 217 if (!XGetWindowAttributes(display_, id.id(), &attr)) { 218 LOG(LS_ERROR) << "XGetWindowAttributes() failed"; 219 return false; 220 } 221 return attr.map_state == IsViewable; 222 } 223 224 bool MoveToFront(const WindowId& id) { 225 if (!Init()) { 226 return false; 227 } 228 XErrorSuppressor error_suppressor(display_); 229 unsigned int num_children; 230 Window* children; 231 Window parent; 232 Window root; 233 234 // Find root window to pass event to. 235 int status = XQueryTree(display_, id.id(), &root, &parent, &children, 236 &num_children); 237 if (status == 0) { 238 LOG(LS_WARNING) << "Failed to query for child windows."; 239 return false; 240 } 241 if (children != NULL) { 242 XFree(children); 243 } 244 245 // Move the window to front. 246 XRaiseWindow(display_, id.id()); 247 248 // Some window managers (e.g., metacity in GNOME) consider it illegal to 249 // raise a window without also giving it input focus with 250 // _NET_ACTIVE_WINDOW, so XRaiseWindow() on its own isn't enough. 251 Atom atom = XInternAtom(display_, "_NET_ACTIVE_WINDOW", True); 252 if (atom != None) { 253 XEvent xev; 254 long event_mask; 255 256 xev.xclient.type = ClientMessage; 257 xev.xclient.serial = 0; 258 xev.xclient.send_event = True; 259 xev.xclient.window = id.id(); 260 xev.xclient.message_type = atom; 261 262 // The format member is set to 8, 16, or 32 and specifies whether the 263 // data should be viewed as a list of bytes, shorts, or longs. 264 xev.xclient.format = 32; 265 266 xev.xclient.data.l[0] = 0; 267 xev.xclient.data.l[1] = 0; 268 xev.xclient.data.l[2] = 0; 269 xev.xclient.data.l[3] = 0; 270 xev.xclient.data.l[4] = 0; 271 272 event_mask = SubstructureRedirectMask | SubstructureNotifyMask; 273 274 XSendEvent(display_, root, False, event_mask, &xev); 275 } 276 XFlush(display_); 277 return true; 278 } 279 280 uint8* GetWindowIcon(const WindowId& id, int* width, int* height) { 281 if (!Init()) { 282 return NULL; 283 } 284 XErrorSuppressor error_suppressor(display_); 285 Atom ret_type; 286 int format; 287 unsigned long length, bytes_after, size; 288 unsigned char* data = NULL; 289 290 // Find out the size of the icon data. 291 if (XGetWindowProperty( 292 display_, id.id(), net_wm_icon_, 0, 0, False, XA_CARDINAL, 293 &ret_type, &format, &length, &size, &data) == Success && 294 data) { 295 XFree(data); 296 } else { 297 LOG(LS_ERROR) << "Failed to get size of the icon."; 298 return NULL; 299 } 300 // Get the icon data, the format is one uint32 each for width and height, 301 // followed by the actual pixel data. 302 if (size >= 2 && 303 XGetWindowProperty( 304 display_, id.id(), net_wm_icon_, 0, size, False, XA_CARDINAL, 305 &ret_type, &format, &length, &bytes_after, &data) == Success && 306 data) { 307 uint32* data_ptr = reinterpret_cast<uint32*>(data); 308 int w, h; 309 w = data_ptr[0]; 310 h = data_ptr[1]; 311 if (size < static_cast<unsigned long>(w * h + 2)) { 312 XFree(data); 313 LOG(LS_ERROR) << "Not a vaild icon."; 314 return NULL; 315 } 316 uint8* rgba = 317 ArgbToRgba(&data_ptr[2], 0, 0, w, h, w, h, true); 318 XFree(data); 319 *width = w; 320 *height = h; 321 return rgba; 322 } else { 323 LOG(LS_ERROR) << "Failed to get window icon data."; 324 return NULL; 325 } 326 } 327 328 uint8* GetWindowThumbnail(const WindowId& id, int width, int height) { 329 if (!Init()) { 330 return NULL; 331 } 332 333 if (!has_composite_extension_) { 334 // Without the Xcomposite extension we would only get a good thumbnail if 335 // the whole window is visible on screen and not covered by any 336 // other window. This is not something we want so instead, just 337 // bail out. 338 LOG(LS_INFO) << "No Xcomposite extension detected."; 339 return NULL; 340 } 341 XErrorSuppressor error_suppressor(display_); 342 343 Window root; 344 int x; 345 int y; 346 unsigned int src_width; 347 unsigned int src_height; 348 unsigned int border_width; 349 unsigned int depth; 350 351 // In addition to needing X11 server-side support for Xcomposite, it 352 // actually needs to be turned on for this window in order to get a good 353 // thumbnail. If the user has modern hardware/drivers but isn't using a 354 // compositing window manager, that won't be the case. Here we 355 // automatically turn it on for shareable windows so that we can get 356 // thumbnails. We used to avoid it because the transition is visually ugly, 357 // but recent window managers don't always redirect windows which led to 358 // no thumbnails at all, which is a worse experience. 359 360 // Redirect drawing to an offscreen buffer (ie, turn on compositing). 361 // X11 remembers what has requested this and will turn it off for us when 362 // we exit. 363 XCompositeRedirectWindow(display_, id.id(), CompositeRedirectAutomatic); 364 Pixmap src_pixmap = XCompositeNameWindowPixmap(display_, id.id()); 365 if (!src_pixmap) { 366 // Even if the backing pixmap doesn't exist, this still should have 367 // succeeded and returned a valid handle (it just wouldn't be a handle to 368 // anything). So this is a real error path. 369 LOG(LS_ERROR) << "XCompositeNameWindowPixmap() failed"; 370 return NULL; 371 } 372 if (!XGetGeometry(display_, src_pixmap, &root, &x, &y, 373 &src_width, &src_height, &border_width, 374 &depth)) { 375 // If the window does not actually have a backing pixmap, this is the path 376 // that will "fail", so it's a warning rather than an error. 377 LOG(LS_WARNING) << "XGetGeometry() failed (probably composite is not in " 378 << "use)"; 379 XFreePixmap(display_, src_pixmap); 380 return NULL; 381 } 382 383 // If we get to here, then composite is in use for this window and it has a 384 // valid backing pixmap. 385 386 XWindowAttributes attr; 387 if (!XGetWindowAttributes(display_, id.id(), &attr)) { 388 LOG(LS_ERROR) << "XGetWindowAttributes() failed"; 389 XFreePixmap(display_, src_pixmap); 390 return NULL; 391 } 392 393 uint8* data = GetDrawableThumbnail(src_pixmap, 394 attr.visual, 395 src_width, 396 src_height, 397 width, 398 height); 399 XFreePixmap(display_, src_pixmap); 400 return data; 401 } 402 403 int GetNumDesktops() { 404 if (!Init()) { 405 return -1; 406 } 407 408 return XScreenCount(display_); 409 } 410 411 uint8* GetDesktopThumbnail(const DesktopId& id, int width, int height) { 412 if (!Init()) { 413 return NULL; 414 } 415 XErrorSuppressor error_suppressor(display_); 416 417 Window root_window = id.id(); 418 XWindowAttributes attr; 419 if (!XGetWindowAttributes(display_, root_window, &attr)) { 420 LOG(LS_ERROR) << "XGetWindowAttributes() failed"; 421 return NULL; 422 } 423 424 return GetDrawableThumbnail(root_window, 425 attr.visual, 426 attr.width, 427 attr.height, 428 width, 429 height); 430 } 431 432 bool GetDesktopDimensions(const DesktopId& id, int* width, int* height) { 433 if (!Init()) { 434 return false; 435 } 436 XErrorSuppressor error_suppressor(display_); 437 XWindowAttributes attr; 438 if (!XGetWindowAttributes(display_, id.id(), &attr)) { 439 LOG(LS_ERROR) << "XGetWindowAttributes() failed"; 440 return false; 441 } 442 *width = attr.width; 443 *height = attr.height; 444 return true; 445 } 446 447 private: 448 uint8* GetDrawableThumbnail(Drawable src_drawable, 449 Visual* visual, 450 int src_width, 451 int src_height, 452 int dst_width, 453 int dst_height) { 454 if (!has_render_extension_) { 455 // Without the Xrender extension we would have to read the full window and 456 // scale it down in our process. Xrender is over a decade old so we aren't 457 // going to expend effort to support that situation. We still need to 458 // check though because probably some virtual VNC displays are in this 459 // category. 460 LOG(LS_INFO) << "No Xrender extension detected."; 461 return NULL; 462 } 463 464 XRenderPictFormat* format = XRenderFindVisualFormat(display_, 465 visual); 466 if (!format) { 467 LOG(LS_ERROR) << "XRenderFindVisualFormat() failed"; 468 return NULL; 469 } 470 471 // Create a picture to reference the window pixmap. 472 XRenderPictureAttributes pa; 473 pa.subwindow_mode = IncludeInferiors; // Don't clip child widgets 474 Picture src = XRenderCreatePicture(display_, 475 src_drawable, 476 format, 477 CPSubwindowMode, 478 &pa); 479 if (!src) { 480 LOG(LS_ERROR) << "XRenderCreatePicture() failed"; 481 return NULL; 482 } 483 484 // Create a picture to reference the destination pixmap. 485 Pixmap dst_pixmap = XCreatePixmap(display_, 486 src_drawable, 487 dst_width, 488 dst_height, 489 format->depth); 490 if (!dst_pixmap) { 491 LOG(LS_ERROR) << "XCreatePixmap() failed"; 492 XRenderFreePicture(display_, src); 493 return NULL; 494 } 495 496 Picture dst = XRenderCreatePicture(display_, dst_pixmap, format, 0, NULL); 497 if (!dst) { 498 LOG(LS_ERROR) << "XRenderCreatePicture() failed"; 499 XFreePixmap(display_, dst_pixmap); 500 XRenderFreePicture(display_, src); 501 return NULL; 502 } 503 504 // Clear the background. 505 XRenderColor transparent = {0}; 506 XRenderFillRectangle(display_, 507 PictOpSrc, 508 dst, 509 &transparent, 510 0, 511 0, 512 dst_width, 513 dst_height); 514 515 // Calculate how much we need to scale the image. 516 double scale_x = static_cast<double>(dst_width) / 517 static_cast<double>(src_width); 518 double scale_y = static_cast<double>(dst_height) / 519 static_cast<double>(src_height); 520 double scale = rtc::_min(scale_y, scale_x); 521 522 int scaled_width = round(src_width * scale); 523 int scaled_height = round(src_height * scale); 524 525 // Render the thumbnail centered on both axis. 526 int centered_x = (dst_width - scaled_width) / 2; 527 int centered_y = (dst_height - scaled_height) / 2; 528 529 // Scaling matrix 530 XTransform xform = { { 531 { XDoubleToFixed(1), XDoubleToFixed(0), XDoubleToFixed(0) }, 532 { XDoubleToFixed(0), XDoubleToFixed(1), XDoubleToFixed(0) }, 533 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(scale) } 534 } }; 535 XRenderSetPictureTransform(display_, src, &xform); 536 537 // Apply filter to smooth out the image. 538 XRenderSetPictureFilter(display_, src, FilterBest, NULL, 0); 539 540 // Render the image to the destination picture. 541 XRenderComposite(display_, 542 PictOpSrc, 543 src, 544 None, 545 dst, 546 0, 547 0, 548 0, 549 0, 550 centered_x, 551 centered_y, 552 scaled_width, 553 scaled_height); 554 555 // Get the pixel data from the X server. TODO: XGetImage 556 // might be slow here, compare with ShmGetImage. 557 XImage* image = XGetImage(display_, 558 dst_pixmap, 559 0, 560 0, 561 dst_width, 562 dst_height, 563 AllPlanes, ZPixmap); 564 uint8* data = ArgbToRgba(reinterpret_cast<uint32*>(image->data), 565 centered_x, 566 centered_y, 567 scaled_width, 568 scaled_height, 569 dst_width, 570 dst_height, 571 false); 572 XDestroyImage(image); 573 XRenderFreePicture(display_, dst); 574 XFreePixmap(display_, dst_pixmap); 575 XRenderFreePicture(display_, src); 576 return data; 577 } 578 579 uint8* ArgbToRgba(uint32* argb_data, int x, int y, int w, int h, 580 int stride_x, int stride_y, bool has_alpha) { 581 uint8* p; 582 int len = stride_x * stride_y * 4; 583 uint8* data = new uint8[len]; 584 memset(data, 0, len); 585 p = data + 4 * (y * stride_x + x); 586 for (int i = 0; i < h; ++i) { 587 for (int j = 0; j < w; ++j) { 588 uint32 argb; 589 uint32 rgba; 590 argb = argb_data[stride_x * (y + i) + x + j]; 591 rgba = (argb << 8) | (argb >> 24); 592 *p = rgba >> 24; 593 ++p; 594 *p = (rgba >> 16) & 0xff; 595 ++p; 596 *p = (rgba >> 8) & 0xff; 597 ++p; 598 *p = has_alpha ? rgba & 0xFF : 0xFF; 599 ++p; 600 } 601 p += (stride_x - w) * 4; 602 } 603 return data; 604 } 605 606 bool EnumerateScreenWindows(WindowDescriptionList* descriptions, int screen) { 607 Window parent; 608 Window *children; 609 int status; 610 unsigned int num_children; 611 Window root_window = XRootWindow(display_, screen); 612 status = XQueryTree(display_, root_window, &root_window, &parent, &children, 613 &num_children); 614 if (status == 0) { 615 LOG(LS_ERROR) << "Failed to query for child windows."; 616 return false; 617 } 618 for (unsigned int i = 0; i < num_children; ++i) { 619 // Iterate in reverse order to display windows from front to back. 620 #ifdef CHROMEOS 621 // TODO(jhorwich): Short-term fix for crbug.com/120229: Don't need to 622 // filter, just return all windows and let the picker scan through them. 623 Window app_window = children[num_children - 1 - i]; 624 #else 625 Window app_window = GetApplicationWindow(children[num_children - 1 - i]); 626 #endif 627 if (app_window && 628 !X11WindowPicker::IsDesktopElement(display_, app_window)) { 629 std::string title; 630 if (GetWindowTitle(app_window, &title)) { 631 WindowId id(app_window); 632 WindowDescription desc(id, title); 633 descriptions->push_back(desc); 634 } 635 } 636 } 637 if (children != NULL) { 638 XFree(children); 639 } 640 return true; 641 } 642 643 bool GetWindowTitle(Window window, std::string* title) { 644 int status; 645 bool result = false; 646 XTextProperty window_name; 647 window_name.value = NULL; 648 if (window) { 649 status = XGetWMName(display_, window, &window_name); 650 if (status && window_name.value && window_name.nitems) { 651 int cnt; 652 char **list = NULL; 653 status = Xutf8TextPropertyToTextList(display_, &window_name, &list, 654 &cnt); 655 if (status >= Success && cnt && *list) { 656 if (cnt > 1) { 657 LOG(LS_INFO) << "Window has " << cnt 658 << " text properties, only using the first one."; 659 } 660 *title = *list; 661 result = true; 662 } 663 if (list != NULL) { 664 XFreeStringList(list); 665 } 666 } 667 if (window_name.value != NULL) { 668 XFree(window_name.value); 669 } 670 } 671 return result; 672 } 673 674 Window GetApplicationWindow(Window window) { 675 Window root, parent; 676 Window app_window = 0; 677 Window *children; 678 unsigned int num_children; 679 Atom type = None; 680 int format; 681 unsigned long nitems, after; 682 unsigned char *data; 683 684 int ret = XGetWindowProperty(display_, window, 685 wm_state_, 0L, 2, 686 False, wm_state_, &type, &format, 687 &nitems, &after, &data); 688 if (ret != Success) { 689 LOG(LS_ERROR) << "XGetWindowProperty failed with return code " << ret 690 << " for window " << window << "."; 691 return 0; 692 } 693 if (type != None) { 694 int64 state = static_cast<int64>(*data); 695 XFree(data); 696 return state == NormalState ? window : 0; 697 } 698 XFree(data); 699 if (!XQueryTree(display_, window, &root, &parent, &children, 700 &num_children)) { 701 LOG(LS_ERROR) << "Failed to query for child windows although window" 702 << "does not have a valid WM_STATE."; 703 return 0; 704 } 705 for (unsigned int i = 0; i < num_children; ++i) { 706 app_window = GetApplicationWindow(children[i]); 707 if (app_window) { 708 break; 709 } 710 } 711 if (children != NULL) { 712 XFree(children); 713 } 714 return app_window; 715 } 716 717 Atom wm_state_; 718 Atom net_wm_icon_; 719 Display* display_; 720 bool has_composite_extension_; 721 bool has_render_extension_; 722 }; 723 724 X11WindowPicker::X11WindowPicker() : enumerator_(new XWindowEnumerator()) { 725 } 726 727 X11WindowPicker::~X11WindowPicker() { 728 } 729 730 bool X11WindowPicker::IsDesktopElement(_XDisplay* display, Window window) { 731 if (window == 0) { 732 LOG(LS_WARNING) << "Zero is never a valid window."; 733 return false; 734 } 735 736 // First look for _NET_WM_WINDOW_TYPE. The standard 737 // (http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#id2760306) 738 // says this hint *should* be present on all windows, and we use the existence 739 // of _NET_WM_WINDOW_TYPE_NORMAL in the property to indicate a window is not 740 // a desktop element (that is, only "normal" windows should be shareable). 741 Atom window_type_atom = XInternAtom(display, "_NET_WM_WINDOW_TYPE", True); 742 XWindowProperty<uint32_t> window_type(display, window, window_type_atom); 743 if (window_type.succeeded() && window_type.size() > 0) { 744 Atom normal_window_type_atom = XInternAtom( 745 display, "_NET_WM_WINDOW_TYPE_NORMAL", True); 746 uint32_t* end = window_type.data() + window_type.size(); 747 bool is_normal = (end != std::find( 748 window_type.data(), end, normal_window_type_atom)); 749 return !is_normal; 750 } 751 752 // Fall back on using the hint. 753 XClassHint class_hint; 754 Status s = XGetClassHint(display, window, &class_hint); 755 bool result = false; 756 if (s == 0) { 757 // No hints, assume this is a normal application window. 758 return result; 759 } 760 static const std::string gnome_panel("gnome-panel"); 761 static const std::string desktop_window("desktop_window"); 762 763 if (gnome_panel.compare(class_hint.res_name) == 0 || 764 desktop_window.compare(class_hint.res_name) == 0) { 765 result = true; 766 } 767 XFree(class_hint.res_name); 768 XFree(class_hint.res_class); 769 return result; 770 } 771 772 bool X11WindowPicker::Init() { 773 return enumerator_->Init(); 774 } 775 776 bool X11WindowPicker::GetWindowList(WindowDescriptionList* descriptions) { 777 return enumerator_->EnumerateWindows(descriptions); 778 } 779 780 bool X11WindowPicker::GetDesktopList(DesktopDescriptionList* descriptions) { 781 return enumerator_->EnumerateDesktops(descriptions); 782 } 783 784 bool X11WindowPicker::IsVisible(const WindowId& id) { 785 return enumerator_->IsVisible(id); 786 } 787 788 bool X11WindowPicker::MoveToFront(const WindowId& id) { 789 return enumerator_->MoveToFront(id); 790 } 791 792 793 uint8* X11WindowPicker::GetWindowIcon(const WindowId& id, int* width, 794 int* height) { 795 return enumerator_->GetWindowIcon(id, width, height); 796 } 797 798 uint8* X11WindowPicker::GetWindowThumbnail(const WindowId& id, int width, 799 int height) { 800 return enumerator_->GetWindowThumbnail(id, width, height); 801 } 802 803 int X11WindowPicker::GetNumDesktops() { 804 return enumerator_->GetNumDesktops(); 805 } 806 807 uint8* X11WindowPicker::GetDesktopThumbnail(const DesktopId& id, 808 int width, 809 int height) { 810 return enumerator_->GetDesktopThumbnail(id, width, height); 811 } 812 813 bool X11WindowPicker::GetDesktopDimensions(const DesktopId& id, int* width, 814 int* height) { 815 return enumerator_->GetDesktopDimensions(id, width, height); 816 } 817 818 } // namespace rtc 819