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 RTC_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 RTC_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_t* 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_t 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_t* data_ptr = reinterpret_cast<uint32_t*>(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_t* rgba = ArgbToRgba(&data_ptr[2], 0, 0, w, h, w, h, true); 317 XFree(data); 318 *width = w; 319 *height = h; 320 return rgba; 321 } else { 322 LOG(LS_ERROR) << "Failed to get window icon data."; 323 return NULL; 324 } 325 } 326 327 uint8_t* GetWindowThumbnail(const WindowId& id, int width, int height) { 328 if (!Init()) { 329 return NULL; 330 } 331 332 if (!has_composite_extension_) { 333 // Without the Xcomposite extension we would only get a good thumbnail if 334 // the whole window is visible on screen and not covered by any 335 // other window. This is not something we want so instead, just 336 // bail out. 337 LOG(LS_INFO) << "No Xcomposite extension detected."; 338 return NULL; 339 } 340 XErrorSuppressor error_suppressor(display_); 341 342 Window root; 343 int x; 344 int y; 345 unsigned int src_width; 346 unsigned int src_height; 347 unsigned int border_width; 348 unsigned int depth; 349 350 // In addition to needing X11 server-side support for Xcomposite, it 351 // actually needs to be turned on for this window in order to get a good 352 // thumbnail. If the user has modern hardware/drivers but isn't using a 353 // compositing window manager, that won't be the case. Here we 354 // automatically turn it on for shareable windows so that we can get 355 // thumbnails. We used to avoid it because the transition is visually ugly, 356 // but recent window managers don't always redirect windows which led to 357 // no thumbnails at all, which is a worse experience. 358 359 // Redirect drawing to an offscreen buffer (ie, turn on compositing). 360 // X11 remembers what has requested this and will turn it off for us when 361 // we exit. 362 XCompositeRedirectWindow(display_, id.id(), CompositeRedirectAutomatic); 363 Pixmap src_pixmap = XCompositeNameWindowPixmap(display_, id.id()); 364 if (!src_pixmap) { 365 // Even if the backing pixmap doesn't exist, this still should have 366 // succeeded and returned a valid handle (it just wouldn't be a handle to 367 // anything). So this is a real error path. 368 LOG(LS_ERROR) << "XCompositeNameWindowPixmap() failed"; 369 return NULL; 370 } 371 if (!XGetGeometry(display_, src_pixmap, &root, &x, &y, 372 &src_width, &src_height, &border_width, 373 &depth)) { 374 // If the window does not actually have a backing pixmap, this is the path 375 // that will "fail", so it's a warning rather than an error. 376 LOG(LS_WARNING) << "XGetGeometry() failed (probably composite is not in " 377 << "use)"; 378 XFreePixmap(display_, src_pixmap); 379 return NULL; 380 } 381 382 // If we get to here, then composite is in use for this window and it has a 383 // valid backing pixmap. 384 385 XWindowAttributes attr; 386 if (!XGetWindowAttributes(display_, id.id(), &attr)) { 387 LOG(LS_ERROR) << "XGetWindowAttributes() failed"; 388 XFreePixmap(display_, src_pixmap); 389 return NULL; 390 } 391 392 uint8_t* data = GetDrawableThumbnail(src_pixmap, attr.visual, src_width, 393 src_height, width, height); 394 XFreePixmap(display_, src_pixmap); 395 return data; 396 } 397 398 int GetNumDesktops() { 399 if (!Init()) { 400 return -1; 401 } 402 403 return XScreenCount(display_); 404 } 405 406 uint8_t* GetDesktopThumbnail(const DesktopId& id, int width, int height) { 407 if (!Init()) { 408 return NULL; 409 } 410 XErrorSuppressor error_suppressor(display_); 411 412 Window root_window = id.id(); 413 XWindowAttributes attr; 414 if (!XGetWindowAttributes(display_, root_window, &attr)) { 415 LOG(LS_ERROR) << "XGetWindowAttributes() failed"; 416 return NULL; 417 } 418 419 return GetDrawableThumbnail(root_window, 420 attr.visual, 421 attr.width, 422 attr.height, 423 width, 424 height); 425 } 426 427 bool GetDesktopDimensions(const DesktopId& id, int* width, int* height) { 428 if (!Init()) { 429 return false; 430 } 431 XErrorSuppressor error_suppressor(display_); 432 XWindowAttributes attr; 433 if (!XGetWindowAttributes(display_, id.id(), &attr)) { 434 LOG(LS_ERROR) << "XGetWindowAttributes() failed"; 435 return false; 436 } 437 *width = attr.width; 438 *height = attr.height; 439 return true; 440 } 441 442 private: 443 uint8_t* GetDrawableThumbnail(Drawable src_drawable, 444 Visual* visual, 445 int src_width, 446 int src_height, 447 int dst_width, 448 int dst_height) { 449 if (!has_render_extension_) { 450 // Without the Xrender extension we would have to read the full window and 451 // scale it down in our process. Xrender is over a decade old so we aren't 452 // going to expend effort to support that situation. We still need to 453 // check though because probably some virtual VNC displays are in this 454 // category. 455 LOG(LS_INFO) << "No Xrender extension detected."; 456 return NULL; 457 } 458 459 XRenderPictFormat* format = XRenderFindVisualFormat(display_, 460 visual); 461 if (!format) { 462 LOG(LS_ERROR) << "XRenderFindVisualFormat() failed"; 463 return NULL; 464 } 465 466 // Create a picture to reference the window pixmap. 467 XRenderPictureAttributes pa; 468 pa.subwindow_mode = IncludeInferiors; // Don't clip child widgets 469 Picture src = XRenderCreatePicture(display_, 470 src_drawable, 471 format, 472 CPSubwindowMode, 473 &pa); 474 if (!src) { 475 LOG(LS_ERROR) << "XRenderCreatePicture() failed"; 476 return NULL; 477 } 478 479 // Create a picture to reference the destination pixmap. 480 Pixmap dst_pixmap = XCreatePixmap(display_, 481 src_drawable, 482 dst_width, 483 dst_height, 484 format->depth); 485 if (!dst_pixmap) { 486 LOG(LS_ERROR) << "XCreatePixmap() failed"; 487 XRenderFreePicture(display_, src); 488 return NULL; 489 } 490 491 Picture dst = XRenderCreatePicture(display_, dst_pixmap, format, 0, NULL); 492 if (!dst) { 493 LOG(LS_ERROR) << "XRenderCreatePicture() failed"; 494 XFreePixmap(display_, dst_pixmap); 495 XRenderFreePicture(display_, src); 496 return NULL; 497 } 498 499 // Clear the background. 500 XRenderColor transparent = {0}; 501 XRenderFillRectangle(display_, 502 PictOpSrc, 503 dst, 504 &transparent, 505 0, 506 0, 507 dst_width, 508 dst_height); 509 510 // Calculate how much we need to scale the image. 511 double scale_x = static_cast<double>(dst_width) / 512 static_cast<double>(src_width); 513 double scale_y = static_cast<double>(dst_height) / 514 static_cast<double>(src_height); 515 double scale = std::min(scale_y, scale_x); 516 517 int scaled_width = round(src_width * scale); 518 int scaled_height = round(src_height * scale); 519 520 // Render the thumbnail centered on both axis. 521 int centered_x = (dst_width - scaled_width) / 2; 522 int centered_y = (dst_height - scaled_height) / 2; 523 524 // Scaling matrix 525 XTransform xform = { { 526 { XDoubleToFixed(1), XDoubleToFixed(0), XDoubleToFixed(0) }, 527 { XDoubleToFixed(0), XDoubleToFixed(1), XDoubleToFixed(0) }, 528 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(scale) } 529 } }; 530 XRenderSetPictureTransform(display_, src, &xform); 531 532 // Apply filter to smooth out the image. 533 XRenderSetPictureFilter(display_, src, FilterBest, NULL, 0); 534 535 // Render the image to the destination picture. 536 XRenderComposite(display_, 537 PictOpSrc, 538 src, 539 None, 540 dst, 541 0, 542 0, 543 0, 544 0, 545 centered_x, 546 centered_y, 547 scaled_width, 548 scaled_height); 549 550 // Get the pixel data from the X server. TODO: XGetImage 551 // might be slow here, compare with ShmGetImage. 552 XImage* image = XGetImage(display_, 553 dst_pixmap, 554 0, 555 0, 556 dst_width, 557 dst_height, 558 AllPlanes, ZPixmap); 559 uint8_t* data = ArgbToRgba(reinterpret_cast<uint32_t*>(image->data), 560 centered_x, centered_y, scaled_width, 561 scaled_height, dst_width, dst_height, false); 562 XDestroyImage(image); 563 XRenderFreePicture(display_, dst); 564 XFreePixmap(display_, dst_pixmap); 565 XRenderFreePicture(display_, src); 566 return data; 567 } 568 569 uint8_t* ArgbToRgba(uint32_t* argb_data, 570 int x, 571 int y, 572 int w, 573 int h, 574 int stride_x, 575 int stride_y, 576 bool has_alpha) { 577 uint8_t* p; 578 int len = stride_x * stride_y * 4; 579 uint8_t* data = new uint8_t[len]; 580 memset(data, 0, len); 581 p = data + 4 * (y * stride_x + x); 582 for (int i = 0; i < h; ++i) { 583 for (int j = 0; j < w; ++j) { 584 uint32_t argb; 585 uint32_t rgba; 586 argb = argb_data[stride_x * (y + i) + x + j]; 587 rgba = (argb << 8) | (argb >> 24); 588 *p = rgba >> 24; 589 ++p; 590 *p = (rgba >> 16) & 0xff; 591 ++p; 592 *p = (rgba >> 8) & 0xff; 593 ++p; 594 *p = has_alpha ? rgba & 0xFF : 0xFF; 595 ++p; 596 } 597 p += (stride_x - w) * 4; 598 } 599 return data; 600 } 601 602 bool EnumerateScreenWindows(WindowDescriptionList* descriptions, int screen) { 603 Window parent; 604 Window *children; 605 int status; 606 unsigned int num_children; 607 Window root_window = XRootWindow(display_, screen); 608 status = XQueryTree(display_, root_window, &root_window, &parent, &children, 609 &num_children); 610 if (status == 0) { 611 LOG(LS_ERROR) << "Failed to query for child windows."; 612 return false; 613 } 614 for (unsigned int i = 0; i < num_children; ++i) { 615 // Iterate in reverse order to display windows from front to back. 616 #ifdef CHROMEOS 617 // TODO(jhorwich): Short-term fix for crbug.com/120229: Don't need to 618 // filter, just return all windows and let the picker scan through them. 619 Window app_window = children[num_children - 1 - i]; 620 #else 621 Window app_window = GetApplicationWindow(children[num_children - 1 - i]); 622 #endif 623 if (app_window && 624 !X11WindowPicker::IsDesktopElement(display_, app_window)) { 625 std::string title; 626 if (GetWindowTitle(app_window, &title)) { 627 WindowId id(app_window); 628 WindowDescription desc(id, title); 629 descriptions->push_back(desc); 630 } 631 } 632 } 633 if (children != NULL) { 634 XFree(children); 635 } 636 return true; 637 } 638 639 bool GetWindowTitle(Window window, std::string* title) { 640 int status; 641 bool result = false; 642 XTextProperty window_name; 643 window_name.value = NULL; 644 if (window) { 645 status = XGetWMName(display_, window, &window_name); 646 if (status && window_name.value && window_name.nitems) { 647 int cnt; 648 char **list = NULL; 649 status = Xutf8TextPropertyToTextList(display_, &window_name, &list, 650 &cnt); 651 if (status >= Success && cnt && *list) { 652 if (cnt > 1) { 653 LOG(LS_INFO) << "Window has " << cnt 654 << " text properties, only using the first one."; 655 } 656 *title = *list; 657 result = true; 658 } 659 if (list != NULL) { 660 XFreeStringList(list); 661 } 662 } 663 if (window_name.value != NULL) { 664 XFree(window_name.value); 665 } 666 } 667 return result; 668 } 669 670 Window GetApplicationWindow(Window window) { 671 Window root, parent; 672 Window app_window = 0; 673 Window *children; 674 unsigned int num_children; 675 Atom type = None; 676 int format; 677 unsigned long nitems, after; 678 unsigned char *data; 679 680 int ret = XGetWindowProperty(display_, window, 681 wm_state_, 0L, 2, 682 False, wm_state_, &type, &format, 683 &nitems, &after, &data); 684 if (ret != Success) { 685 LOG(LS_ERROR) << "XGetWindowProperty failed with return code " << ret 686 << " for window " << window << "."; 687 return 0; 688 } 689 if (type != None) { 690 int64_t state = static_cast<int64_t>(*data); 691 XFree(data); 692 return state == NormalState ? window : 0; 693 } 694 XFree(data); 695 if (!XQueryTree(display_, window, &root, &parent, &children, 696 &num_children)) { 697 LOG(LS_ERROR) << "Failed to query for child windows although window" 698 << "does not have a valid WM_STATE."; 699 return 0; 700 } 701 for (unsigned int i = 0; i < num_children; ++i) { 702 app_window = GetApplicationWindow(children[i]); 703 if (app_window) { 704 break; 705 } 706 } 707 if (children != NULL) { 708 XFree(children); 709 } 710 return app_window; 711 } 712 713 Atom wm_state_; 714 Atom net_wm_icon_; 715 Display* display_; 716 bool has_composite_extension_; 717 bool has_render_extension_; 718 }; 719 720 X11WindowPicker::X11WindowPicker() : enumerator_(new XWindowEnumerator()) { 721 } 722 723 X11WindowPicker::~X11WindowPicker() { 724 } 725 726 bool X11WindowPicker::IsDesktopElement(_XDisplay* display, Window window) { 727 if (window == 0) { 728 LOG(LS_WARNING) << "Zero is never a valid window."; 729 return false; 730 } 731 732 // First look for _NET_WM_WINDOW_TYPE. The standard 733 // (http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#id2760306) 734 // says this hint *should* be present on all windows, and we use the existence 735 // of _NET_WM_WINDOW_TYPE_NORMAL in the property to indicate a window is not 736 // a desktop element (that is, only "normal" windows should be shareable). 737 Atom window_type_atom = XInternAtom(display, "_NET_WM_WINDOW_TYPE", True); 738 XWindowProperty<uint32_t> window_type(display, window, window_type_atom); 739 if (window_type.succeeded() && window_type.size() > 0) { 740 Atom normal_window_type_atom = XInternAtom( 741 display, "_NET_WM_WINDOW_TYPE_NORMAL", True); 742 uint32_t* end = window_type.data() + window_type.size(); 743 bool is_normal = (end != std::find( 744 window_type.data(), end, normal_window_type_atom)); 745 return !is_normal; 746 } 747 748 // Fall back on using the hint. 749 XClassHint class_hint; 750 Status s = XGetClassHint(display, window, &class_hint); 751 bool result = false; 752 if (s == 0) { 753 // No hints, assume this is a normal application window. 754 return result; 755 } 756 static const std::string gnome_panel("gnome-panel"); 757 static const std::string desktop_window("desktop_window"); 758 759 if (gnome_panel.compare(class_hint.res_name) == 0 || 760 desktop_window.compare(class_hint.res_name) == 0) { 761 result = true; 762 } 763 XFree(class_hint.res_name); 764 XFree(class_hint.res_class); 765 return result; 766 } 767 768 bool X11WindowPicker::Init() { 769 return enumerator_->Init(); 770 } 771 772 bool X11WindowPicker::GetWindowList(WindowDescriptionList* descriptions) { 773 return enumerator_->EnumerateWindows(descriptions); 774 } 775 776 bool X11WindowPicker::GetDesktopList(DesktopDescriptionList* descriptions) { 777 return enumerator_->EnumerateDesktops(descriptions); 778 } 779 780 bool X11WindowPicker::IsVisible(const WindowId& id) { 781 return enumerator_->IsVisible(id); 782 } 783 784 bool X11WindowPicker::MoveToFront(const WindowId& id) { 785 return enumerator_->MoveToFront(id); 786 } 787 788 uint8_t* X11WindowPicker::GetWindowIcon(const WindowId& id, 789 int* width, 790 int* height) { 791 return enumerator_->GetWindowIcon(id, width, height); 792 } 793 794 uint8_t* X11WindowPicker::GetWindowThumbnail(const WindowId& id, 795 int width, 796 int height) { 797 return enumerator_->GetWindowThumbnail(id, width, height); 798 } 799 800 int X11WindowPicker::GetNumDesktops() { 801 return enumerator_->GetNumDesktops(); 802 } 803 804 uint8_t* X11WindowPicker::GetDesktopThumbnail(const DesktopId& id, 805 int width, 806 int height) { 807 return enumerator_->GetDesktopThumbnail(id, width, height); 808 } 809 810 bool X11WindowPicker::GetDesktopDimensions(const DesktopId& id, int* width, 811 int* height) { 812 return enumerator_->GetDesktopDimensions(id, width, height); 813 } 814 815 } // namespace rtc 816