1e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org/*
2e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org *
4e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org *  Use of this source code is governed by a BSD-style license
5e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org *  that can be found in the LICENSE file in the root of the source
6e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org *  tree. An additional intellectual property rights grant can be found
7e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org *  in the file PATENTS.  All contributing project authors may
8e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org *  be found in the AUTHORS file in the root of the source tree.
9e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org */
10e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
11e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org#include "webrtc/modules/desktop_capture/window_capturer.h"
12e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
13bcf0a1019f34cac346bd8349c2206f9d06adbe4epbos@webrtc.org#include <assert.h>
14e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org#include <string.h>
15e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org#include <X11/Xatom.h>
16e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org#include <X11/extensions/Xcomposite.h>
17e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org#include <X11/extensions/Xrender.h>
18e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org#include <X11/Xutil.h>
19bcf0a1019f34cac346bd8349c2206f9d06adbe4epbos@webrtc.org
20e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org#include <algorithm>
21e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
2291685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
23e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org#include "webrtc/modules/desktop_capture/desktop_frame.h"
2491685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org#include "webrtc/modules/desktop_capture/x11/shared_x_display.h"
25e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org#include "webrtc/modules/desktop_capture/x11/x_error_trap.h"
26e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org#include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
27e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org#include "webrtc/system_wrappers/interface/logging.h"
28e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org#include "webrtc/system_wrappers/interface/scoped_ptr.h"
2991685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org#include "webrtc/system_wrappers/interface/scoped_refptr.h"
30e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
31e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.orgnamespace webrtc {
32e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
33e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.orgnamespace {
34e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
35e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org// Convenience wrapper for XGetWindowProperty() results.
36e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.orgtemplate <class PropertyType>
37e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.orgclass XWindowProperty {
38e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org public:
39e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  XWindowProperty(Display* display, Window window, Atom property)
40e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      : is_valid_(false),
41e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org        size_(0),
42e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org        data_(NULL) {
43e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    const int kBitsPerByte = 8;
44e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    Atom actual_type;
45e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    int actual_format;
46e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    unsigned long bytes_after;  // NOLINT: type required by XGetWindowProperty
47e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    int status = XGetWindowProperty(display, window, property, 0L, ~0L, False,
48e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                                    AnyPropertyType, &actual_type,
49e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                                    &actual_format, &size_,
50e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                                    &bytes_after, &data_);
51e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    if (status != Success) {
52e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      data_ = NULL;
53e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      return;
54e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    }
55e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    if (sizeof(PropertyType) * kBitsPerByte != actual_format) {
56e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      size_ = 0;
57e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      return;
58e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    }
59e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
60e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    is_valid_ = true;
61e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
62e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
63e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  ~XWindowProperty() {
64e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    if (data_)
65e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      XFree(data_);
66e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
67e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
68e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // True if we got properly value successfully.
69e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  bool is_valid() const { return is_valid_; }
70e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
71e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // Size and value of the property.
72e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  size_t size() const { return size_; }
73e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  const PropertyType* data() const {
74e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    return reinterpret_cast<PropertyType*>(data_);
75e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
76e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  PropertyType* data() {
77e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    return reinterpret_cast<PropertyType*>(data_);
78e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
79e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
80e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org private:
81e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  bool is_valid_;
82e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  unsigned long size_;  // NOLINT: type required by XGetWindowProperty
83e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  unsigned char* data_;
84e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
85e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  DISALLOW_COPY_AND_ASSIGN(XWindowProperty);
86e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org};
87e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
88ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.orgclass WindowCapturerLinux : public WindowCapturer,
89ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org                            public SharedXDisplay::XEventHandler {
90e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org public:
9191685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org  WindowCapturerLinux(const DesktopCaptureOptions& options);
92e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  virtual ~WindowCapturerLinux();
93e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
94e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // WindowCapturer interface.
95e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  virtual bool GetWindowList(WindowList* windows) OVERRIDE;
96e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  virtual bool SelectWindow(WindowId id) OVERRIDE;
97dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  virtual bool BringSelectedWindowToFront() OVERRIDE;
98e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
99e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // DesktopCapturer interface.
100e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  virtual void Start(Callback* callback) OVERRIDE;
101e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  virtual void Capture(const DesktopRegion& region) OVERRIDE;
102e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
103ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org  // SharedXDisplay::XEventHandler interface.
104ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org  virtual bool HandleXEvent(const XEvent& event) OVERRIDE;
105ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org
106e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org private:
10791685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org  Display* display() { return x_display_->display(); }
10891685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org
109e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // Iterates through |window| hierarchy to find first visible window, i.e. one
110e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // that has WM_STATE property set to NormalState.
111e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // See http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1 .
112e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  ::Window GetApplicationWindow(::Window window);
113e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
114e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // Returns true if the |window| is a desktop element.
115e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  bool IsDesktopElement(::Window window);
116e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
117e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // Returns window title for the specified X |window|.
118e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  bool GetWindowTitle(::Window window, std::string* title);
119e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
120e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  Callback* callback_;
121e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
12291685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org  scoped_refptr<SharedXDisplay> x_display_;
123e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
124e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  Atom wm_state_atom_;
125e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  Atom window_type_atom_;
126e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  Atom normal_window_type_atom_;
127e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  bool has_composite_extension_;
128e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
129e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  ::Window selected_window_;
130e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  XServerPixelBuffer x_server_pixel_buffer_;
131e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
132e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  DISALLOW_COPY_AND_ASSIGN(WindowCapturerLinux);
133e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org};
134e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
13591685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.orgWindowCapturerLinux::WindowCapturerLinux(const DesktopCaptureOptions& options)
136e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    : callback_(NULL),
13791685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org      x_display_(options.x_display()),
138e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      has_composite_extension_(false),
139e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      selected_window_(0) {
140e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // Create Atoms so we don't need to do it every time they are used.
14191685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org  wm_state_atom_ = XInternAtom(display(), "WM_STATE", True);
14291685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org  window_type_atom_ = XInternAtom(display(), "_NET_WM_WINDOW_TYPE", True);
143e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  normal_window_type_atom_ = XInternAtom(
14491685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org      display(), "_NET_WM_WINDOW_TYPE_NORMAL", True);
145e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
146e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  int event_base, error_base, major_version, minor_version;
14791685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org  if (XCompositeQueryExtension(display(), &event_base, &error_base) &&
14891685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org      XCompositeQueryVersion(display(), &major_version, &minor_version) &&
149e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      // XCompositeNameWindowPixmap() requires version 0.2
150e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      (major_version > 0 || minor_version >= 2)) {
151e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    has_composite_extension_ = true;
152e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  } else {
153e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    LOG(LS_INFO) << "Xcomposite extension not available or too old.";
154e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
155ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org
156ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org  x_display_->AddEventHandler(ConfigureNotify, this);
157e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org}
158e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
159ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.orgWindowCapturerLinux::~WindowCapturerLinux() {
160ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org  x_display_->RemoveEventHandler(ConfigureNotify, this);
161ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org}
162e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
163e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.orgbool WindowCapturerLinux::GetWindowList(WindowList* windows) {
164e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  WindowList result;
165e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
16691685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org  XErrorTrap error_trap(display());
167e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
16891685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org  int num_screens = XScreenCount(display());
169e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  for (int screen = 0; screen < num_screens; ++screen) {
17091685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org    ::Window root_window = XRootWindow(display(), screen);
171e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    ::Window parent;
172e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    ::Window *children;
173e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    unsigned int num_children;
17491685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org    int status = XQueryTree(display(), root_window, &root_window, &parent,
175e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                            &children, &num_children);
176e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    if (status == 0) {
177e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      LOG(LS_ERROR) << "Failed to query for child windows for screen "
178e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                    << screen;
179e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      continue;
180e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    }
181e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
182e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    for (unsigned int i = 0; i < num_children; ++i) {
183e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      // Iterate in reverse order to return windows from front to back.
184e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      ::Window app_window =
185e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org          GetApplicationWindow(children[num_children - 1 - i]);
186e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      if (app_window && !IsDesktopElement(app_window)) {
187e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org        Window w;
188e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org        w.id = app_window;
189e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org        if (GetWindowTitle(app_window, &w.title))
190e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org          result.push_back(w);
191e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      }
192e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    }
193e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
194e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    if (children)
195e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      XFree(children);
196e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
197e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
198e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  windows->swap(result);
199e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
200e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  return true;
201e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org}
202e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
203e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.orgbool WindowCapturerLinux::SelectWindow(WindowId id) {
20491685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org  if (!x_server_pixel_buffer_.Init(display(), id))
205e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    return false;
206e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
207ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org  // Tell the X server to send us window resizing events.
208ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org  XSelectInput(display(), id, StructureNotifyMask);
209ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org
210e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  selected_window_ = id;
211e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
212e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // In addition to needing X11 server-side support for Xcomposite, it actually
213e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // needs to be turned on for the window. If the user has modern
214e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // hardware/drivers but isn't using a compositing window manager, that won't
215e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // be the case. Here we automatically turn it on.
216e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
217e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // Redirect drawing to an offscreen buffer (ie, turn on compositing). X11
218e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // remembers who has requested this and will turn it off for us when we exit.
21991685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org  XCompositeRedirectWindow(display(), id, CompositeRedirectAutomatic);
220e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
221e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  return true;
222e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org}
223e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
224dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.orgbool WindowCapturerLinux::BringSelectedWindowToFront() {
225dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  if (!selected_window_)
226dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org    return false;
227dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org
228dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  unsigned int num_children;
229dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  ::Window* children;
230dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  ::Window parent;
231dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  ::Window root;
232dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  // Find the root window to pass event to.
233dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  int status = XQueryTree(
234dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org      display(), selected_window_, &root, &parent, &children, &num_children);
235dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  if (status == 0) {
236dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org    LOG(LS_ERROR) << "Failed to query for the root window.";
237dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org    return false;
238dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  }
239dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org
240dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  if (children)
241dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org    XFree(children);
242dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org
243dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  XRaiseWindow(display(), selected_window_);
244dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org
245dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  // Some window managers (e.g., metacity in GNOME) consider it illegal to
246dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  // raise a window without also giving it input focus with
247dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  // _NET_ACTIVE_WINDOW, so XRaiseWindow() on its own isn't enough.
248dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  Atom atom = XInternAtom(display(), "_NET_ACTIVE_WINDOW", True);
249dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  if (atom != None) {
250dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org    XEvent xev;
251dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org    xev.xclient.type = ClientMessage;
252dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org    xev.xclient.serial = 0;
253dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org    xev.xclient.send_event = True;
254dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org    xev.xclient.window = selected_window_;
255dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org    xev.xclient.message_type = atom;
256dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org
257dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org    // The format member is set to 8, 16, or 32 and specifies whether the
258dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org    // data should be viewed as a list of bytes, shorts, or longs.
259dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org    xev.xclient.format = 32;
260dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org
261dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org    memset(xev.xclient.data.l, 0, sizeof(xev.xclient.data.l));
262dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org
263dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org    XSendEvent(display(),
264dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org               root,
265dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org               False,
266dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org               SubstructureRedirectMask | SubstructureNotifyMask,
267dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org               &xev);
268dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  }
269dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  XFlush(display());
270dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org  return true;
271dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org}
272dd6cf04628fd4772e543b5008b5328f6c7988f5ajiayl@webrtc.org
273e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.orgvoid WindowCapturerLinux::Start(Callback* callback) {
274e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  assert(!callback_);
275e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  assert(callback);
276e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
277e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  callback_ = callback;
278e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org}
279e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
280e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.orgvoid WindowCapturerLinux::Capture(const DesktopRegion& region) {
281dcbf62b4cf1d7a28fb002efdf671f784378c8c98jiayl@webrtc.org  if (!x_server_pixel_buffer_.IsWindowValid()) {
282dcbf62b4cf1d7a28fb002efdf671f784378c8c98jiayl@webrtc.org    LOG(LS_INFO) << "The window is no longer valid.";
283dcbf62b4cf1d7a28fb002efdf671f784378c8c98jiayl@webrtc.org    callback_->OnCaptureCompleted(NULL);
284dcbf62b4cf1d7a28fb002efdf671f784378c8c98jiayl@webrtc.org    return;
285dcbf62b4cf1d7a28fb002efdf671f784378c8c98jiayl@webrtc.org  }
286dcbf62b4cf1d7a28fb002efdf671f784378c8c98jiayl@webrtc.org
287ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org  x_display_->ProcessPendingXEvents();
288ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org
289e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  if (!has_composite_extension_) {
290e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    // Without the Xcomposite extension we capture when the whole window is
291e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    // visible on screen and not covered by any other window. This is not
292e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    // something we want so instead, just bail out.
293e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    LOG(LS_INFO) << "No Xcomposite extension detected.";
294e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    callback_->OnCaptureCompleted(NULL);
295e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    return;
296e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
297e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
298e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  DesktopFrame* frame =
299e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      new BasicDesktopFrame(x_server_pixel_buffer_.window_size());
300e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
301e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  x_server_pixel_buffer_.Synchronize();
302e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  x_server_pixel_buffer_.CaptureRect(DesktopRect::MakeSize(frame->size()),
303e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                                     frame);
304e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
3058150ff1d7130289b6a65633c67e68ac92ddf4513sergeyu@chromium.org  frame->mutable_updated_region()->SetRect(
3068150ff1d7130289b6a65633c67e68ac92ddf4513sergeyu@chromium.org      DesktopRect::MakeSize(frame->size()));
3078150ff1d7130289b6a65633c67e68ac92ddf4513sergeyu@chromium.org
308e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  callback_->OnCaptureCompleted(frame);
309e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org}
310e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
311ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.orgbool WindowCapturerLinux::HandleXEvent(const XEvent& event) {
312ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org  if (event.type == ConfigureNotify) {
313ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org    XConfigureEvent xce = event.xconfigure;
314ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org    if (!DesktopSize(xce.width, xce.height).equals(
315ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org            x_server_pixel_buffer_.window_size())) {
316ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org      if (!x_server_pixel_buffer_.Init(display(), selected_window_)) {
317ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org        LOG(LS_ERROR) << "Failed to initialize pixel buffer after resizing.";
318ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org      }
319ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org      return true;
320ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org    }
321ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org  }
322ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org  return false;
323ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org}
324ab23a2d259a9754d3f09b96c1482323c7b9f43aejiayl@webrtc.org
325e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org::Window WindowCapturerLinux::GetApplicationWindow(::Window window) {
326e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // Get WM_STATE property of the window.
32791685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org  XWindowProperty<uint32_t> window_state(display(), window, wm_state_atom_);
328e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
329e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // WM_STATE is considered to be set to WithdrawnState when it missing.
330e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  int32_t state = window_state.is_valid() ?
331e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      *window_state.data() : WithdrawnState;
332e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
333e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  if (state == NormalState) {
334e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    // Window has WM_STATE==NormalState. Return it.
335e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    return window;
336e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  } else if (state == IconicState) {
337e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    // Window is in minimized. Skip it.
338e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    return 0;
339e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
340e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
341e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // If the window is in WithdrawnState then look at all of its children.
342e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  ::Window root, parent;
343e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  ::Window *children;
344e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  unsigned int num_children;
34591685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org  if (!XQueryTree(display(), window, &root, &parent, &children,
346e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                  &num_children)) {
347e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    LOG(LS_ERROR) << "Failed to query for child windows although window"
348e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                  << "does not have a valid WM_STATE.";
349e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    return 0;
350e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
351e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  ::Window app_window = 0;
352e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  for (unsigned int i = 0; i < num_children; ++i) {
353e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    app_window = GetApplicationWindow(children[i]);
354e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    if (app_window)
355e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      break;
356e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
357e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
358e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  if (children)
359e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    XFree(children);
360e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  return app_window;
361e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org}
362e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
363e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.orgbool WindowCapturerLinux::IsDesktopElement(::Window window) {
364e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  if (window == 0)
365e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    return false;
366e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
367e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // First look for _NET_WM_WINDOW_TYPE. The standard
368e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // (http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#id2760306)
369e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // says this hint *should* be present on all windows, and we use the existence
370e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // of _NET_WM_WINDOW_TYPE_NORMAL in the property to indicate a window is not
371e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // a desktop element (that is, only "normal" windows should be shareable).
37291685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org  XWindowProperty<uint32_t> window_type(display(), window, window_type_atom_);
373e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  if (window_type.is_valid() && window_type.size() > 0) {
374e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    uint32_t* end = window_type.data() + window_type.size();
375e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    bool is_normal = (end != std::find(
376e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org        window_type.data(), end, normal_window_type_atom_));
377e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    return !is_normal;
378e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
379e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
380e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  // Fall back on using the hint.
381e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  XClassHint class_hint;
38291685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org  Status status = XGetClassHint(display(), window, &class_hint);
383e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  bool result = false;
384e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  if (status == 0) {
385e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    // No hints, assume this is a normal application window.
386e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    return result;
387e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
388e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
389e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  if (strcmp("gnome-panel", class_hint.res_name) == 0 ||
390e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      strcmp("desktop_window", class_hint.res_name) == 0) {
391e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    result = true;
392e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
393e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  XFree(class_hint.res_name);
394e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  XFree(class_hint.res_class);
395e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  return result;
396e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org}
397e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
398e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.orgbool WindowCapturerLinux::GetWindowTitle(::Window window, std::string* title) {
399e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  int status;
400e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  bool result = false;
401e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  XTextProperty window_name;
402e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  window_name.value = NULL;
403e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  if (window) {
40491685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org    status = XGetWMName(display(), window, &window_name);
405e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    if (status && window_name.value && window_name.nitems) {
406e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      int cnt;
407e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      char **list = NULL;
40891685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org      status = Xutf8TextPropertyToTextList(display(), &window_name, &list,
409e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                                           &cnt);
410e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      if (status >= Success && cnt && *list) {
411e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org        if (cnt > 1) {
412e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org          LOG(LS_INFO) << "Window has " << cnt
413e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org                       << " text properties, only using the first one.";
414e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org        }
415e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org        *title = *list;
416e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org        result = true;
417e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      }
418e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      if (list)
419e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org        XFreeStringList(list);
420e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    }
421e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org    if (window_name.value)
422e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org      XFree(window_name.value);
423e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  }
424e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org  return result;
425e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org}
426e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
427e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org}  // namespace
428e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
429e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org// static
43091685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.orgWindowCapturer* WindowCapturer::Create(const DesktopCaptureOptions& options) {
43191685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org  if (!options.x_display())
43291685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org    return NULL;
43391685dc421db7dcdc24f6a154de9b92eababf6fdsergeyu@chromium.org  return new WindowCapturerLinux(options);
434e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org}
435e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org
436e562e02f31a3d39f06d6cdab11a28104c60bccd8sergeyu@chromium.org}  // namespace webrtc
437