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