Home | History | Annotate | Download | only in base
      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