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   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