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