| /* | 
 |  *  Copyright 2010 The WebRTC Project Authors. All rights reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 |  | 
 | #include "webrtc/base/linuxwindowpicker.h" | 
 |  | 
 | #include <math.h> | 
 | #include <string.h> | 
 |  | 
 | #include <algorithm> | 
 | #include <string> | 
 |  | 
 | #include <X11/Xatom.h> | 
 | #include <X11/extensions/Xcomposite.h> | 
 | #include <X11/extensions/Xrender.h> | 
 | #include <X11/Xutil.h> | 
 |  | 
 | #include "webrtc/base/logging.h" | 
 |  | 
 | namespace rtc { | 
 |  | 
 | // Convenience wrapper for XGetWindowProperty results. | 
 | template <class PropertyType> | 
 | class XWindowProperty { | 
 |  public: | 
 |   XWindowProperty(Display* display, Window window, Atom property) | 
 |       : data_(NULL) { | 
 |     const int kBitsPerByte = 8; | 
 |     Atom actual_type; | 
 |     int actual_format; | 
 |     unsigned long bytes_after;  // NOLINT: type required by XGetWindowProperty | 
 |     int status = XGetWindowProperty(display, window, property, 0L, ~0L, False, | 
 |                                     AnyPropertyType, &actual_type, | 
 |                                     &actual_format, &size_, | 
 |                                     &bytes_after, &data_); | 
 |     succeeded_ = (status == Success); | 
 |     if (!succeeded_) { | 
 |       data_ = NULL;  // Ensure nothing is freed. | 
 |     } else if (sizeof(PropertyType) * kBitsPerByte != actual_format) { | 
 |       LOG(LS_WARNING) << "Returned type size differs from " | 
 |           "requested type size."; | 
 |       succeeded_ = false; | 
 |       // We still need to call XFree in this case, so leave data_ alone. | 
 |     } | 
 |     if (!succeeded_) { | 
 |       size_ = 0; | 
 |     } | 
 |   } | 
 |  | 
 |   ~XWindowProperty() { | 
 |     if (data_) { | 
 |       XFree(data_); | 
 |     } | 
 |   } | 
 |  | 
 |   bool succeeded() const { return succeeded_; } | 
 |   size_t size() const { return size_; } | 
 |   const PropertyType* data() const { | 
 |     return reinterpret_cast<PropertyType*>(data_); | 
 |   } | 
 |   PropertyType* data() { | 
 |     return reinterpret_cast<PropertyType*>(data_); | 
 |   } | 
 |  | 
 |  private: | 
 |   bool succeeded_; | 
 |   unsigned long size_;  // NOLINT: type required by XGetWindowProperty | 
 |   unsigned char* data_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(XWindowProperty); | 
 | }; | 
 |  | 
 | // Stupid X11.  It seems none of the synchronous returns codes from X11 calls | 
 | // are meaningful unless an asynchronous error handler is configured.  This | 
 | // RAII class registers and unregisters an X11 error handler. | 
 | class XErrorSuppressor { | 
 |  public: | 
 |   explicit XErrorSuppressor(Display* display) | 
 |       : display_(display), original_error_handler_(NULL) { | 
 |     SuppressX11Errors(); | 
 |   } | 
 |   ~XErrorSuppressor() { | 
 |     UnsuppressX11Errors(); | 
 |   } | 
 |  | 
 |  private: | 
 |   static int ErrorHandler(Display* display, XErrorEvent* e) { | 
 |     char buf[256]; | 
 |     XGetErrorText(display, e->error_code, buf, sizeof buf); | 
 |     LOG(LS_WARNING) << "Received X11 error \"" << buf << "\" for request code " | 
 |                     << static_cast<unsigned int>(e->request_code); | 
 |     return 0; | 
 |   } | 
 |  | 
 |   void SuppressX11Errors() { | 
 |     XFlush(display_); | 
 |     XSync(display_, False); | 
 |     original_error_handler_ = XSetErrorHandler(&ErrorHandler); | 
 |   } | 
 |  | 
 |   void UnsuppressX11Errors() { | 
 |     XFlush(display_); | 
 |     XSync(display_, False); | 
 |     XErrorHandler handler = XSetErrorHandler(original_error_handler_); | 
 |     if (handler != &ErrorHandler) { | 
 |       LOG(LS_WARNING) << "Unbalanced XSetErrorHandler() calls detected. " | 
 |                       << "Final error handler may not be what you expect!"; | 
 |     } | 
 |     original_error_handler_ = NULL; | 
 |   } | 
 |  | 
 |   Display* display_; | 
 |   XErrorHandler original_error_handler_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(XErrorSuppressor); | 
 | }; | 
 |  | 
 | // Hiding all X11 specifics inside its own class. This to avoid | 
 | // conflicts between talk and X11 header declarations. | 
 | class XWindowEnumerator { | 
 |  public: | 
 |   XWindowEnumerator() | 
 |       : display_(NULL), | 
 |         has_composite_extension_(false), | 
 |         has_render_extension_(false) { | 
 |   } | 
 |  | 
 |   ~XWindowEnumerator() { | 
 |     if (display_ != NULL) { | 
 |       XCloseDisplay(display_); | 
 |     } | 
 |   } | 
 |  | 
 |   bool Init() { | 
 |     if (display_ != NULL) { | 
 |       // Already initialized. | 
 |       return true; | 
 |     } | 
 |     display_ = XOpenDisplay(NULL); | 
 |     if (display_ == NULL) { | 
 |       LOG(LS_ERROR) << "Failed to open display."; | 
 |       return false; | 
 |     } | 
 |  | 
 |     XErrorSuppressor error_suppressor(display_); | 
 |  | 
 |     wm_state_ = XInternAtom(display_, "WM_STATE", True); | 
 |     net_wm_icon_ = XInternAtom(display_, "_NET_WM_ICON", False); | 
 |  | 
 |     int event_base, error_base, major_version, minor_version; | 
 |     if (XCompositeQueryExtension(display_, &event_base, &error_base) && | 
 |         XCompositeQueryVersion(display_, &major_version, &minor_version) && | 
 |         // XCompositeNameWindowPixmap() requires version 0.2 | 
 |         (major_version > 0 || minor_version >= 2)) { | 
 |       has_composite_extension_ = true; | 
 |     } else { | 
 |       LOG(LS_INFO) << "Xcomposite extension not available or too old."; | 
 |     } | 
 |  | 
 |     if (XRenderQueryExtension(display_, &event_base, &error_base) && | 
 |         XRenderQueryVersion(display_, &major_version, &minor_version) && | 
 |         // XRenderSetPictureTransform() requires version 0.6 | 
 |         (major_version > 0 || minor_version >= 6)) { | 
 |       has_render_extension_ = true; | 
 |     } else { | 
 |       LOG(LS_INFO) << "Xrender extension not available or too old."; | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 |   bool EnumerateWindows(WindowDescriptionList* descriptions) { | 
 |     if (!Init()) { | 
 |       return false; | 
 |     } | 
 |     XErrorSuppressor error_suppressor(display_); | 
 |     int num_screens = XScreenCount(display_); | 
 |     bool result = false; | 
 |     for (int i = 0; i < num_screens; ++i) { | 
 |       if (EnumerateScreenWindows(descriptions, i)) { | 
 |         // We know we succeded on at least one screen. | 
 |         result = true; | 
 |       } | 
 |     } | 
 |     return result; | 
 |   } | 
 |  | 
 |   bool EnumerateDesktops(DesktopDescriptionList* descriptions) { | 
 |     if (!Init()) { | 
 |       return false; | 
 |     } | 
 |     XErrorSuppressor error_suppressor(display_); | 
 |     Window default_root_window = XDefaultRootWindow(display_); | 
 |     int num_screens = XScreenCount(display_); | 
 |     for (int i = 0; i < num_screens; ++i) { | 
 |       Window root_window = XRootWindow(display_, i); | 
 |       DesktopId id(DesktopId(root_window, i)); | 
 |       // TODO: Figure out an appropriate desktop title. | 
 |       DesktopDescription desc(id, ""); | 
 |       desc.set_primary(root_window == default_root_window); | 
 |       descriptions->push_back(desc); | 
 |     } | 
 |     return num_screens > 0; | 
 |   } | 
 |  | 
 |   bool IsVisible(const WindowId& id) { | 
 |     if (!Init()) { | 
 |       return false; | 
 |     } | 
 |     XErrorSuppressor error_suppressor(display_); | 
 |     XWindowAttributes attr; | 
 |     if (!XGetWindowAttributes(display_, id.id(), &attr)) { | 
 |       LOG(LS_ERROR) << "XGetWindowAttributes() failed"; | 
 |       return false; | 
 |     } | 
 |     return attr.map_state == IsViewable; | 
 |   } | 
 |  | 
 |   bool MoveToFront(const WindowId& id) { | 
 |     if (!Init()) { | 
 |       return false; | 
 |     } | 
 |     XErrorSuppressor error_suppressor(display_); | 
 |     unsigned int num_children; | 
 |     Window* children; | 
 |     Window parent; | 
 |     Window root; | 
 |  | 
 |     // Find root window to pass event to. | 
 |     int status = XQueryTree(display_, id.id(), &root, &parent, &children, | 
 |                             &num_children); | 
 |     if (status == 0) { | 
 |       LOG(LS_WARNING) << "Failed to query for child windows."; | 
 |       return false; | 
 |     } | 
 |     if (children != NULL) { | 
 |       XFree(children); | 
 |     } | 
 |  | 
 |     // Move the window to front. | 
 |     XRaiseWindow(display_, id.id()); | 
 |  | 
 |     // Some window managers (e.g., metacity in GNOME) consider it illegal to | 
 |     // raise a window without also giving it input focus with | 
 |     // _NET_ACTIVE_WINDOW, so XRaiseWindow() on its own isn't enough. | 
 |     Atom atom = XInternAtom(display_, "_NET_ACTIVE_WINDOW", True); | 
 |     if (atom != None) { | 
 |       XEvent xev; | 
 |       long event_mask; | 
 |  | 
 |       xev.xclient.type = ClientMessage; | 
 |       xev.xclient.serial = 0; | 
 |       xev.xclient.send_event = True; | 
 |       xev.xclient.window = id.id(); | 
 |       xev.xclient.message_type = atom; | 
 |  | 
 |       // The format member is set to 8, 16, or 32 and specifies whether the | 
 |       // data should be viewed as a list of bytes, shorts, or longs. | 
 |       xev.xclient.format = 32; | 
 |  | 
 |       xev.xclient.data.l[0] = 0; | 
 |       xev.xclient.data.l[1] = 0; | 
 |       xev.xclient.data.l[2] = 0; | 
 |       xev.xclient.data.l[3] = 0; | 
 |       xev.xclient.data.l[4] = 0; | 
 |  | 
 |       event_mask = SubstructureRedirectMask | SubstructureNotifyMask; | 
 |  | 
 |       XSendEvent(display_, root, False, event_mask, &xev); | 
 |     } | 
 |     XFlush(display_); | 
 |     return true; | 
 |   } | 
 |  | 
 |   uint8* GetWindowIcon(const WindowId& id, int* width, int* height) { | 
 |     if (!Init()) { | 
 |       return NULL; | 
 |     } | 
 |     XErrorSuppressor error_suppressor(display_); | 
 |     Atom ret_type; | 
 |     int format; | 
 |     unsigned long length, bytes_after, size; | 
 |     unsigned char* data = NULL; | 
 |  | 
 |     // Find out the size of the icon data. | 
 |     if (XGetWindowProperty( | 
 |             display_, id.id(), net_wm_icon_, 0, 0, False, XA_CARDINAL, | 
 |             &ret_type, &format, &length, &size, &data) == Success && | 
 |         data) { | 
 |       XFree(data); | 
 |     } else { | 
 |       LOG(LS_ERROR) << "Failed to get size of the icon."; | 
 |       return NULL; | 
 |     } | 
 |     // Get the icon data, the format is one uint32 each for width and height, | 
 |     // followed by the actual pixel data. | 
 |     if (size >= 2 && | 
 |         XGetWindowProperty( | 
 |             display_, id.id(), net_wm_icon_, 0, size, False, XA_CARDINAL, | 
 |             &ret_type, &format, &length, &bytes_after, &data) == Success && | 
 |         data) { | 
 |       uint32* data_ptr = reinterpret_cast<uint32*>(data); | 
 |       int w, h; | 
 |       w = data_ptr[0]; | 
 |       h = data_ptr[1]; | 
 |       if (size < static_cast<unsigned long>(w * h + 2)) { | 
 |         XFree(data); | 
 |         LOG(LS_ERROR) << "Not a vaild icon."; | 
 |         return NULL; | 
 |       } | 
 |       uint8* rgba = | 
 |           ArgbToRgba(&data_ptr[2], 0, 0, w, h, w, h, true); | 
 |       XFree(data); | 
 |       *width = w; | 
 |       *height = h; | 
 |       return rgba; | 
 |     } else { | 
 |       LOG(LS_ERROR) << "Failed to get window icon data."; | 
 |       return NULL; | 
 |     } | 
 |   } | 
 |  | 
 |   uint8* GetWindowThumbnail(const WindowId& id, int width, int height) { | 
 |     if (!Init()) { | 
 |       return NULL; | 
 |     } | 
 |  | 
 |     if (!has_composite_extension_) { | 
 |       // Without the Xcomposite extension we would only get a good thumbnail if | 
 |       // the whole window is visible on screen and not covered by any | 
 |       // other window. This is not something we want so instead, just | 
 |       // bail out. | 
 |       LOG(LS_INFO) << "No Xcomposite extension detected."; | 
 |       return NULL; | 
 |     } | 
 |     XErrorSuppressor error_suppressor(display_); | 
 |  | 
 |     Window root; | 
 |     int x; | 
 |     int y; | 
 |     unsigned int src_width; | 
 |     unsigned int src_height; | 
 |     unsigned int border_width; | 
 |     unsigned int depth; | 
 |  | 
 |     // In addition to needing X11 server-side support for Xcomposite, it | 
 |     // actually needs to be turned on for this window in order to get a good | 
 |     // thumbnail. If the user has modern hardware/drivers but isn't using a | 
 |     // compositing window manager, that won't be the case. Here we | 
 |     // automatically turn it on for shareable windows so that we can get | 
 |     // thumbnails. We used to avoid it because the transition is visually ugly, | 
 |     // but recent window managers don't always redirect windows which led to | 
 |     // no thumbnails at all, which is a worse experience. | 
 |  | 
 |     // Redirect drawing to an offscreen buffer (ie, turn on compositing). | 
 |     // X11 remembers what has requested this and will turn it off for us when | 
 |     // we exit. | 
 |     XCompositeRedirectWindow(display_, id.id(), CompositeRedirectAutomatic); | 
 |     Pixmap src_pixmap = XCompositeNameWindowPixmap(display_, id.id()); | 
 |     if (!src_pixmap) { | 
 |       // Even if the backing pixmap doesn't exist, this still should have | 
 |       // succeeded and returned a valid handle (it just wouldn't be a handle to | 
 |       // anything). So this is a real error path. | 
 |       LOG(LS_ERROR) << "XCompositeNameWindowPixmap() failed"; | 
 |       return NULL; | 
 |     } | 
 |     if (!XGetGeometry(display_, src_pixmap, &root, &x, &y, | 
 |                       &src_width, &src_height, &border_width, | 
 |                       &depth)) { | 
 |       // If the window does not actually have a backing pixmap, this is the path | 
 |       // that will "fail", so it's a warning rather than an error. | 
 |       LOG(LS_WARNING) << "XGetGeometry() failed (probably composite is not in " | 
 |                       << "use)"; | 
 |       XFreePixmap(display_, src_pixmap); | 
 |       return NULL; | 
 |     } | 
 |  | 
 |     // If we get to here, then composite is in use for this window and it has a | 
 |     // valid backing pixmap. | 
 |  | 
 |     XWindowAttributes attr; | 
 |     if (!XGetWindowAttributes(display_, id.id(), &attr)) { | 
 |       LOG(LS_ERROR) << "XGetWindowAttributes() failed"; | 
 |       XFreePixmap(display_, src_pixmap); | 
 |       return NULL; | 
 |     } | 
 |  | 
 |     uint8* data = GetDrawableThumbnail(src_pixmap, | 
 |                                        attr.visual, | 
 |                                        src_width, | 
 |                                        src_height, | 
 |                                        width, | 
 |                                        height); | 
 |     XFreePixmap(display_, src_pixmap); | 
 |     return data; | 
 |   } | 
 |  | 
 |   int GetNumDesktops() { | 
 |     if (!Init()) { | 
 |       return -1; | 
 |     } | 
 |  | 
 |     return XScreenCount(display_); | 
 |   } | 
 |  | 
 |   uint8* GetDesktopThumbnail(const DesktopId& id, int width, int height) { | 
 |     if (!Init()) { | 
 |       return NULL; | 
 |     } | 
 |     XErrorSuppressor error_suppressor(display_); | 
 |  | 
 |     Window root_window = id.id(); | 
 |     XWindowAttributes attr; | 
 |     if (!XGetWindowAttributes(display_, root_window, &attr)) { | 
 |       LOG(LS_ERROR) << "XGetWindowAttributes() failed"; | 
 |       return NULL; | 
 |     } | 
 |  | 
 |     return GetDrawableThumbnail(root_window, | 
 |                                 attr.visual, | 
 |                                 attr.width, | 
 |                                 attr.height, | 
 |                                 width, | 
 |                                 height); | 
 |   } | 
 |  | 
 |   bool GetDesktopDimensions(const DesktopId& id, int* width, int* height) { | 
 |     if (!Init()) { | 
 |       return false; | 
 |     } | 
 |     XErrorSuppressor error_suppressor(display_); | 
 |     XWindowAttributes attr; | 
 |     if (!XGetWindowAttributes(display_, id.id(), &attr)) { | 
 |       LOG(LS_ERROR) << "XGetWindowAttributes() failed"; | 
 |       return false; | 
 |     } | 
 |     *width = attr.width; | 
 |     *height = attr.height; | 
 |     return true; | 
 |   } | 
 |  | 
 |  private: | 
 |   uint8* GetDrawableThumbnail(Drawable src_drawable, | 
 |                               Visual* visual, | 
 |                               int src_width, | 
 |                               int src_height, | 
 |                               int dst_width, | 
 |                               int dst_height) { | 
 |     if (!has_render_extension_) { | 
 |       // Without the Xrender extension we would have to read the full window and | 
 |       // scale it down in our process. Xrender is over a decade old so we aren't | 
 |       // going to expend effort to support that situation. We still need to | 
 |       // check though because probably some virtual VNC displays are in this | 
 |       // category. | 
 |       LOG(LS_INFO) << "No Xrender extension detected."; | 
 |       return NULL; | 
 |     } | 
 |  | 
 |     XRenderPictFormat* format = XRenderFindVisualFormat(display_, | 
 |                                                         visual); | 
 |     if (!format) { | 
 |       LOG(LS_ERROR) << "XRenderFindVisualFormat() failed"; | 
 |       return NULL; | 
 |     } | 
 |  | 
 |     // Create a picture to reference the window pixmap. | 
 |     XRenderPictureAttributes pa; | 
 |     pa.subwindow_mode = IncludeInferiors;  // Don't clip child widgets | 
 |     Picture src = XRenderCreatePicture(display_, | 
 |                                        src_drawable, | 
 |                                        format, | 
 |                                        CPSubwindowMode, | 
 |                                        &pa); | 
 |     if (!src) { | 
 |       LOG(LS_ERROR) << "XRenderCreatePicture() failed"; | 
 |       return NULL; | 
 |     } | 
 |  | 
 |     // Create a picture to reference the destination pixmap. | 
 |     Pixmap dst_pixmap = XCreatePixmap(display_, | 
 |                                       src_drawable, | 
 |                                       dst_width, | 
 |                                       dst_height, | 
 |                                       format->depth); | 
 |     if (!dst_pixmap) { | 
 |       LOG(LS_ERROR) << "XCreatePixmap() failed"; | 
 |       XRenderFreePicture(display_, src); | 
 |       return NULL; | 
 |     } | 
 |  | 
 |     Picture dst = XRenderCreatePicture(display_, dst_pixmap, format, 0, NULL); | 
 |     if (!dst) { | 
 |       LOG(LS_ERROR) << "XRenderCreatePicture() failed"; | 
 |       XFreePixmap(display_, dst_pixmap); | 
 |       XRenderFreePicture(display_, src); | 
 |       return NULL; | 
 |     } | 
 |  | 
 |     // Clear the background. | 
 |     XRenderColor transparent = {0}; | 
 |     XRenderFillRectangle(display_, | 
 |                          PictOpSrc, | 
 |                          dst, | 
 |                          &transparent, | 
 |                          0, | 
 |                          0, | 
 |                          dst_width, | 
 |                          dst_height); | 
 |  | 
 |     // Calculate how much we need to scale the image. | 
 |     double scale_x = static_cast<double>(dst_width) / | 
 |         static_cast<double>(src_width); | 
 |     double scale_y = static_cast<double>(dst_height) / | 
 |         static_cast<double>(src_height); | 
 |     double scale = rtc::_min(scale_y, scale_x); | 
 |  | 
 |     int scaled_width = round(src_width * scale); | 
 |     int scaled_height = round(src_height * scale); | 
 |  | 
 |     // Render the thumbnail centered on both axis. | 
 |     int centered_x = (dst_width - scaled_width) / 2; | 
 |     int centered_y = (dst_height - scaled_height) / 2; | 
 |  | 
 |     // Scaling matrix | 
 |     XTransform xform = { { | 
 |         { XDoubleToFixed(1), XDoubleToFixed(0), XDoubleToFixed(0) }, | 
 |         { XDoubleToFixed(0), XDoubleToFixed(1), XDoubleToFixed(0) }, | 
 |         { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(scale) } | 
 |         } }; | 
 |     XRenderSetPictureTransform(display_, src, &xform); | 
 |  | 
 |     // Apply filter to smooth out the image. | 
 |     XRenderSetPictureFilter(display_, src, FilterBest, NULL, 0); | 
 |  | 
 |     // Render the image to the destination picture. | 
 |     XRenderComposite(display_, | 
 |                      PictOpSrc, | 
 |                      src, | 
 |                      None, | 
 |                      dst, | 
 |                      0, | 
 |                      0, | 
 |                      0, | 
 |                      0, | 
 |                      centered_x, | 
 |                      centered_y, | 
 |                      scaled_width, | 
 |                      scaled_height); | 
 |  | 
 |     // Get the pixel data from the X server. TODO: XGetImage | 
 |     // might be slow here, compare with ShmGetImage. | 
 |     XImage* image = XGetImage(display_, | 
 |                               dst_pixmap, | 
 |                               0, | 
 |                               0, | 
 |                               dst_width, | 
 |                               dst_height, | 
 |                               AllPlanes, ZPixmap); | 
 |     uint8* data = ArgbToRgba(reinterpret_cast<uint32*>(image->data), | 
 |                              centered_x, | 
 |                              centered_y, | 
 |                              scaled_width, | 
 |                              scaled_height, | 
 |                              dst_width, | 
 |                              dst_height, | 
 |                              false); | 
 |     XDestroyImage(image); | 
 |     XRenderFreePicture(display_, dst); | 
 |     XFreePixmap(display_, dst_pixmap); | 
 |     XRenderFreePicture(display_, src); | 
 |     return data; | 
 |   } | 
 |  | 
 |   uint8* ArgbToRgba(uint32* argb_data, int x, int y, int w, int h, | 
 |                     int stride_x, int stride_y, bool has_alpha) { | 
 |     uint8* p; | 
 |     int len = stride_x * stride_y * 4; | 
 |     uint8* data = new uint8[len]; | 
 |     memset(data, 0, len); | 
 |     p = data + 4 * (y * stride_x + x); | 
 |     for (int i = 0; i < h; ++i) { | 
 |       for (int j = 0; j < w; ++j) { | 
 |         uint32 argb; | 
 |         uint32 rgba; | 
 |         argb = argb_data[stride_x * (y + i) + x + j]; | 
 |         rgba = (argb << 8) | (argb >> 24); | 
 |         *p = rgba >> 24; | 
 |         ++p; | 
 |         *p = (rgba >> 16) & 0xff; | 
 |         ++p; | 
 |         *p = (rgba >> 8) & 0xff; | 
 |         ++p; | 
 |         *p = has_alpha ? rgba & 0xFF : 0xFF; | 
 |         ++p; | 
 |       } | 
 |       p += (stride_x - w) * 4; | 
 |     } | 
 |     return data; | 
 |   } | 
 |  | 
 |   bool EnumerateScreenWindows(WindowDescriptionList* descriptions, int screen) { | 
 |     Window parent; | 
 |     Window *children; | 
 |     int status; | 
 |     unsigned int num_children; | 
 |     Window root_window = XRootWindow(display_, screen); | 
 |     status = XQueryTree(display_, root_window, &root_window, &parent, &children, | 
 |                         &num_children); | 
 |     if (status == 0) { | 
 |       LOG(LS_ERROR) << "Failed to query for child windows."; | 
 |       return false; | 
 |     } | 
 |     for (unsigned int i = 0; i < num_children; ++i) { | 
 |       // Iterate in reverse order to display windows from front to back. | 
 | #ifdef CHROMEOS | 
 |       // TODO(jhorwich): Short-term fix for crbug.com/120229: Don't need to | 
 |       // filter, just return all windows and let the picker scan through them. | 
 |       Window app_window = children[num_children - 1 - i]; | 
 | #else | 
 |       Window app_window = GetApplicationWindow(children[num_children - 1 - i]); | 
 | #endif | 
 |       if (app_window && | 
 |           !LinuxWindowPicker::IsDesktopElement(display_, app_window)) { | 
 |         std::string title; | 
 |         if (GetWindowTitle(app_window, &title)) { | 
 |           WindowId id(app_window); | 
 |           WindowDescription desc(id, title); | 
 |           descriptions->push_back(desc); | 
 |         } | 
 |       } | 
 |     } | 
 |     if (children != NULL) { | 
 |       XFree(children); | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 |   bool GetWindowTitle(Window window, std::string* title) { | 
 |     int status; | 
 |     bool result = false; | 
 |     XTextProperty window_name; | 
 |     window_name.value = NULL; | 
 |     if (window) { | 
 |       status = XGetWMName(display_, window, &window_name); | 
 |       if (status && window_name.value && window_name.nitems) { | 
 |         int cnt; | 
 |         char **list = NULL; | 
 |         status = Xutf8TextPropertyToTextList(display_, &window_name, &list, | 
 |                                              &cnt); | 
 |         if (status >= Success && cnt && *list) { | 
 |           if (cnt > 1) { | 
 |             LOG(LS_INFO) << "Window has " << cnt | 
 |                          << " text properties, only using the first one."; | 
 |           } | 
 |           *title = *list; | 
 |           result = true; | 
 |         } | 
 |         if (list != NULL) { | 
 |           XFreeStringList(list); | 
 |         } | 
 |       } | 
 |       if (window_name.value != NULL) { | 
 |         XFree(window_name.value); | 
 |       } | 
 |     } | 
 |     return result; | 
 |   } | 
 |  | 
 |   Window GetApplicationWindow(Window window) { | 
 |     Window root, parent; | 
 |     Window app_window = 0; | 
 |     Window *children; | 
 |     unsigned int num_children; | 
 |     Atom type = None; | 
 |     int format; | 
 |     unsigned long nitems, after; | 
 |     unsigned char *data; | 
 |  | 
 |     int ret = XGetWindowProperty(display_, window, | 
 |                                  wm_state_, 0L, 2, | 
 |                                  False, wm_state_, &type, &format, | 
 |                                  &nitems, &after, &data); | 
 |     if (ret != Success) { | 
 |       LOG(LS_ERROR) << "XGetWindowProperty failed with return code " << ret | 
 |                     << " for window " << window << "."; | 
 |       return 0; | 
 |     } | 
 |     if (type != None) { | 
 |       int64 state = static_cast<int64>(*data); | 
 |       XFree(data); | 
 |       return state == NormalState ? window : 0; | 
 |     } | 
 |     XFree(data); | 
 |     if (!XQueryTree(display_, window, &root, &parent, &children, | 
 |                     &num_children)) { | 
 |       LOG(LS_ERROR) << "Failed to query for child windows although window" | 
 |                     << "does not have a valid WM_STATE."; | 
 |       return 0; | 
 |     } | 
 |     for (unsigned int i = 0; i < num_children; ++i) { | 
 |       app_window = GetApplicationWindow(children[i]); | 
 |       if (app_window) { | 
 |         break; | 
 |       } | 
 |     } | 
 |     if (children != NULL) { | 
 |       XFree(children); | 
 |     } | 
 |     return app_window; | 
 |   } | 
 |  | 
 |   Atom wm_state_; | 
 |   Atom net_wm_icon_; | 
 |   Display* display_; | 
 |   bool has_composite_extension_; | 
 |   bool has_render_extension_; | 
 | }; | 
 |  | 
 | LinuxWindowPicker::LinuxWindowPicker() : enumerator_(new XWindowEnumerator()) { | 
 | } | 
 |  | 
 | LinuxWindowPicker::~LinuxWindowPicker() { | 
 | } | 
 |  | 
 | bool LinuxWindowPicker::IsDesktopElement(_XDisplay* display, Window window) { | 
 |   if (window == 0) { | 
 |     LOG(LS_WARNING) << "Zero is never a valid window."; | 
 |     return false; | 
 |   } | 
 |  | 
 |   // First look for _NET_WM_WINDOW_TYPE. The standard | 
 |   // (http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#id2760306) | 
 |   // says this hint *should* be present on all windows, and we use the existence | 
 |   // of _NET_WM_WINDOW_TYPE_NORMAL in the property to indicate a window is not | 
 |   // a desktop element (that is, only "normal" windows should be shareable). | 
 |   Atom window_type_atom = XInternAtom(display, "_NET_WM_WINDOW_TYPE", True); | 
 |   XWindowProperty<uint32_t> window_type(display, window, window_type_atom); | 
 |   if (window_type.succeeded() && window_type.size() > 0) { | 
 |     Atom normal_window_type_atom = XInternAtom( | 
 |         display, "_NET_WM_WINDOW_TYPE_NORMAL", True); | 
 |     uint32_t* end = window_type.data() + window_type.size(); | 
 |     bool is_normal = (end != std::find( | 
 |         window_type.data(), end, normal_window_type_atom)); | 
 |     return !is_normal; | 
 |   } | 
 |  | 
 |   // Fall back on using the hint. | 
 |   XClassHint class_hint; | 
 |   Status s = XGetClassHint(display, window, &class_hint); | 
 |   bool result = false; | 
 |   if (s == 0) { | 
 |     // No hints, assume this is a normal application window. | 
 |     return result; | 
 |   } | 
 |   static const std::string gnome_panel("gnome-panel"); | 
 |   static const std::string desktop_window("desktop_window"); | 
 |  | 
 |   if (gnome_panel.compare(class_hint.res_name) == 0 || | 
 |       desktop_window.compare(class_hint.res_name) == 0) { | 
 |     result = true; | 
 |   } | 
 |   XFree(class_hint.res_name); | 
 |   XFree(class_hint.res_class); | 
 |   return result; | 
 | } | 
 |  | 
 | bool LinuxWindowPicker::Init() { | 
 |   return enumerator_->Init(); | 
 | } | 
 |  | 
 | bool LinuxWindowPicker::GetWindowList(WindowDescriptionList* descriptions) { | 
 |   return enumerator_->EnumerateWindows(descriptions); | 
 | } | 
 |  | 
 | bool LinuxWindowPicker::GetDesktopList(DesktopDescriptionList* descriptions) { | 
 |   return enumerator_->EnumerateDesktops(descriptions); | 
 | } | 
 |  | 
 | bool LinuxWindowPicker::IsVisible(const WindowId& id) { | 
 |   return enumerator_->IsVisible(id); | 
 | } | 
 |  | 
 | bool LinuxWindowPicker::MoveToFront(const WindowId& id) { | 
 |   return enumerator_->MoveToFront(id); | 
 | } | 
 |  | 
 |  | 
 | uint8* LinuxWindowPicker::GetWindowIcon(const WindowId& id, int* width, | 
 |                                         int* height) { | 
 |   return enumerator_->GetWindowIcon(id, width, height); | 
 | } | 
 |  | 
 | uint8* LinuxWindowPicker::GetWindowThumbnail(const WindowId& id, int width, | 
 |                                              int height) { | 
 |   return enumerator_->GetWindowThumbnail(id, width, height); | 
 | } | 
 |  | 
 | int LinuxWindowPicker::GetNumDesktops() { | 
 |   return enumerator_->GetNumDesktops(); | 
 | } | 
 |  | 
 | uint8* LinuxWindowPicker::GetDesktopThumbnail(const DesktopId& id, | 
 |                                               int width, | 
 |                                               int height) { | 
 |   return enumerator_->GetDesktopThumbnail(id, width, height); | 
 | } | 
 |  | 
 | bool LinuxWindowPicker::GetDesktopDimensions(const DesktopId& id, int* width, | 
 |                                              int* height) { | 
 |   return enumerator_->GetDesktopDimensions(id, width, height); | 
 | } | 
 |  | 
 | }  // namespace rtc |