WindowCapturer implementation for Linux.

Window enumeration is based on the code used by hangouts plugin
(see libjingle/talk/base/linuxwindowpicker.cc). XServerPixelBuffer
is used to capture windows. It had to be refactored to support window
capturing (previously it worked only for the whole screen).

R=wez@chromium.org

Review URL: https://webrtc-codereview.appspot.com/1741004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@4605 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/desktop_capture/desktop_capture.gypi b/webrtc/modules/desktop_capture/desktop_capture.gypi
index 269c583..0bc3839 100644
--- a/webrtc/modules/desktop_capture/desktop_capture.gypi
+++ b/webrtc/modules/desktop_capture/desktop_capture.gypi
@@ -53,9 +53,11 @@
         "win/scoped_thread_desktop.cc",
         "win/scoped_thread_desktop.h",
         "window_capturer.h",
-        "window_capturer_linux.cc",
         "window_capturer_mac.cc",
         "window_capturer_win.cc",
+        "window_capturer_x11.cc",
+        "x11/x_error_trap.cc",
+        "x11/x_error_trap.h",
         "x11/x_server_pixel_buffer.cc",
         "x11/x_server_pixel_buffer.h",
       ],
@@ -69,15 +71,18 @@
           'link_settings': {
             'libraries': [
               '-lX11',
+              '-lXcomposite',
               '-lXdamage',
               '-lXext',
               '-lXfixes',
+              '-lXrender',
             ],
           },
         }],
         ['OS!="win" and OS!="mac" and use_x11==0', {
           'sources': [
             "screen_capturer_null.cc",
+            "window_capturer_null.cc",
           ],
         }],
         ['OS=="mac"', {
diff --git a/webrtc/modules/desktop_capture/screen_capturer_x11.cc b/webrtc/modules/desktop_capture/screen_capturer_x11.cc
index e730b71..5cca65b 100644
--- a/webrtc/modules/desktop_capture/screen_capturer_x11.cc
+++ b/webrtc/modules/desktop_capture/screen_capturer_x11.cc
@@ -76,9 +76,8 @@
   // differences between this and the previous capture.
   DesktopFrame* CaptureScreen();
 
-  // Called when the screen configuration is changed. |root_window_size|
-  // specifies the most recent size of the root window.
-  void ScreenConfigurationChanged(const DesktopSize& root_window_size);
+  // Called when the screen configuration is changed.
+  void ScreenConfigurationChanged();
 
   // Synchronize the current buffer with |last_buffer_|, by copying pixels from
   // the area of |last_invalid_rects|.
@@ -89,25 +88,6 @@
 
   void DeinitXlib();
 
-  // Capture a rectangle from |x_server_pixel_buffer_|, and copy the data into
-  // |frame|.
-  void CaptureRect(const DesktopRect& rect,
-                   DesktopFrame* frame);
-
-  // We expose two forms of blitting to handle variations in the pixel format.
-  // In FastBlit, the operation is effectively a memcpy.
-  void FastBlit(uint8_t* image,
-                const DesktopRect& rect,
-                DesktopFrame* frame);
-  void SlowBlit(uint8_t* image,
-                const DesktopRect& rect,
-                DesktopFrame* frame);
-
-  // Returns the number of bits |mask| has to be shifted left so its last
-  // (most-significant) bit set becomes the most-significant bit of the word.
-  // When |mask| is 0 the function returns 31.
-  static uint32_t GetRgbShift(uint32_t mask);
-
   Callback* callback_;
   MouseShapeObserver* mouse_shape_observer_;
 
@@ -116,9 +96,6 @@
   GC gc_;
   Window root_window_;
 
-  // Last known dimensions of the root window.
-  DesktopSize root_window_size_;
-
   // XFixes.
   bool has_xfixes_;
   int xfixes_event_base_;
@@ -207,8 +184,10 @@
   // Register for changes to the dimensions of the root window.
   XSelectInput(display_, root_window_, StructureNotifyMask);
 
-  root_window_size_ = XServerPixelBuffer::GetRootWindowSize(display_);
-  x_server_pixel_buffer_.Init(display_, root_window_size_);
+  if (!x_server_pixel_buffer_.Init(display_, DefaultRootWindow(display_))) {
+    LOG(LS_ERROR) << "Failed to initialize pixel buffer.";
+    return false;
+  }
 
   if (has_xfixes_) {
     // Register for changes to the cursor shape.
@@ -276,12 +255,21 @@
   // Process XEvents for XDamage and cursor shape tracking.
   ProcessPendingXEvents();
 
+  // ProcessPendingXEvents() may call ScreenConfigurationChanged() which
+  // reinitializes |x_server_pixel_buffer_|. Check if the pixel buffer is still
+  // in a good shape.
+  if (!x_server_pixel_buffer_.is_initialized()) {
+     // We failed to initialize pixel buffer.
+     callback_->OnCaptureCompleted(NULL);
+     return;
+  }
+
   // If the current frame is from an older generation then allocate a new one.
   // Note that we can't reallocate other buffers at this point, since the caller
   // may still be reading from them.
   if (!queue_.current_frame()) {
     scoped_ptr<DesktopFrame> frame(
-        new BasicDesktopFrame(root_window_size_));
+        new BasicDesktopFrame(x_server_pixel_buffer_.window_size()));
     queue_.ReplaceCurrentFrame(frame.release());
   }
 
@@ -324,9 +312,7 @@
       XDamageNotifyEvent* event = reinterpret_cast<XDamageNotifyEvent*>(&e);
       DCHECK(event->level == XDamageReportNonEmpty);
     } else if (e.type == ConfigureNotify) {
-      const XConfigureEvent& event = e.xconfigure;
-      ScreenConfigurationChanged(
-          DesktopSize(event.width, event.height));
+      ScreenConfigurationChanged();
     } else if (has_xfixes_ &&
                e.type == xfixes_event_base_ + XFixesCursorNotify) {
       XFixesCursorNotifyEvent* cne;
@@ -371,6 +357,7 @@
 
 DesktopFrame* ScreenCapturerLinux::CaptureScreen() {
   DesktopFrame* frame = queue_.current_frame()->Share();
+  assert(x_server_pixel_buffer_.window_size().equals(frame->size()));
 
   // Pass the screen size to the helper, so it can clip the invalid region if it
   // expands that region to a grid.
@@ -407,18 +394,17 @@
     // spurious XDamage notifications were received for a previous (larger)
     // screen size.
     updated_region->IntersectWith(
-        DesktopRect::MakeSize(root_window_size_));
+        DesktopRect::MakeSize(x_server_pixel_buffer_.window_size()));
 
     for (DesktopRegion::Iterator it(*updated_region);
          !it.IsAtEnd(); it.Advance()) {
-      CaptureRect(it.rect(), frame);
+      x_server_pixel_buffer_.CaptureRect(it.rect(), frame);
     }
   } else {
     // Doing full-screen polling, or this is the first capture after a
     // screen-resolution change.  In either case, need a full-screen capture.
-    DesktopRect screen_rect =
-        DesktopRect::MakeSize(frame->size());
-    CaptureRect(screen_rect, frame);
+    DesktopRect screen_rect = DesktopRect::MakeSize(frame->size());
+    x_server_pixel_buffer_.CaptureRect(screen_rect, frame);
 
     if (queue_.previous_frame()) {
       // Full-screen polling, so calculate the invalid rects here, based on the
@@ -439,15 +425,15 @@
   return frame;
 }
 
-void ScreenCapturerLinux::ScreenConfigurationChanged(
-    const DesktopSize& root_window_size) {
-  root_window_size_ = root_window_size;
-
+void ScreenCapturerLinux::ScreenConfigurationChanged() {
   // Make sure the frame buffers will be reallocated.
   queue_.Reset();
 
   helper_.ClearInvalidRegion();
-  x_server_pixel_buffer_.Init(display_, root_window_size_);
+  if (!x_server_pixel_buffer_.Init(display_, DefaultRootWindow(display_))) {
+    LOG(LS_ERROR) << "Failed to initialize pixel buffer after screen "
+        "configuration change.";
+  }
 }
 
 void ScreenCapturerLinux::SynchronizeFrame() {
@@ -497,114 +483,6 @@
   }
 }
 
-void ScreenCapturerLinux::CaptureRect(const DesktopRect& rect,
-                                      DesktopFrame* frame) {
-  uint8_t* image = x_server_pixel_buffer_.CaptureRect(rect);
-  int depth = x_server_pixel_buffer_.GetDepth();
-  if ((depth == 24 || depth == 32) &&
-      x_server_pixel_buffer_.GetBitsPerPixel() == 32 &&
-      x_server_pixel_buffer_.GetRedMask() == 0xff0000 &&
-      x_server_pixel_buffer_.GetGreenMask() == 0xff00 &&
-      x_server_pixel_buffer_.GetBlueMask() == 0xff) {
-    FastBlit(image, rect, frame);
-  } else {
-    SlowBlit(image, rect, frame);
-  }
-}
-
-void ScreenCapturerLinux::FastBlit(uint8_t* image,
-                                   const DesktopRect& rect,
-                                   DesktopFrame* frame) {
-  uint8_t* src_pos = image;
-  int src_stride = x_server_pixel_buffer_.GetStride();
-  int dst_x = rect.left(), dst_y = rect.top();
-
-  uint8_t* dst_pos = frame->data() + frame->stride() * dst_y;
-  dst_pos += dst_x * DesktopFrame::kBytesPerPixel;
-
-  int height = rect.height();
-  int row_bytes = rect.width() * DesktopFrame::kBytesPerPixel;
-  for (int y = 0; y < height; ++y) {
-    memcpy(dst_pos, src_pos, row_bytes);
-    src_pos += src_stride;
-    dst_pos += frame->stride();
-  }
-}
-
-void ScreenCapturerLinux::SlowBlit(uint8_t* image,
-                                   const DesktopRect& rect,
-                                   DesktopFrame* frame) {
-  int src_stride = x_server_pixel_buffer_.GetStride();
-  int dst_x = rect.left(), dst_y = rect.top();
-  int width = rect.width(), height = rect.height();
-
-  uint32_t red_mask = x_server_pixel_buffer_.GetRedMask();
-  uint32_t green_mask = x_server_pixel_buffer_.GetGreenMask();
-  uint32_t blue_mask = x_server_pixel_buffer_.GetBlueMask();
-
-  uint32_t red_shift = GetRgbShift(red_mask);
-  uint32_t green_shift = GetRgbShift(green_mask);
-  uint32_t blue_shift = GetRgbShift(blue_mask);
-
-  unsigned int bits_per_pixel = x_server_pixel_buffer_.GetBitsPerPixel();
-
-  uint8_t* dst_pos = frame->data() + frame->stride() * dst_y;
-  uint8_t* src_pos = image;
-  dst_pos += dst_x * DesktopFrame::kBytesPerPixel;
-  // TODO(hclam): Optimize, perhaps using MMX code or by converting to
-  // YUV directly
-  for (int y = 0; y < height; y++) {
-    uint32_t* dst_pos_32 = reinterpret_cast<uint32_t*>(dst_pos);
-    uint32_t* src_pos_32 = reinterpret_cast<uint32_t*>(src_pos);
-    uint16_t* src_pos_16 = reinterpret_cast<uint16_t*>(src_pos);
-    for (int x = 0; x < width; x++) {
-      // Dereference through an appropriately-aligned pointer.
-      uint32_t pixel;
-      if (bits_per_pixel == 32) {
-        pixel = src_pos_32[x];
-      } else if (bits_per_pixel == 16) {
-        pixel = src_pos_16[x];
-      } else {
-        pixel = src_pos[x];
-      }
-      uint32_t r = (pixel & red_mask) << red_shift;
-      uint32_t g = (pixel & green_mask) << green_shift;
-      uint32_t b = (pixel & blue_mask) << blue_shift;
-
-      // Write as 32-bit RGB.
-      dst_pos_32[x] = ((r >> 8) & 0xff0000) | ((g >> 16) & 0xff00) |
-          ((b >> 24) & 0xff);
-    }
-    dst_pos += frame->stride();
-    src_pos += src_stride;
-  }
-}
-
-// static
-uint32_t ScreenCapturerLinux::GetRgbShift(uint32_t mask) {
-  int shift = 0;
-  if ((mask & 0xffff0000u) == 0) {
-    mask <<= 16;
-    shift += 16;
-  }
-  if ((mask & 0xff000000u) == 0) {
-    mask <<= 8;
-    shift += 8;
-  }
-  if ((mask & 0xf0000000u) == 0) {
-    mask <<= 4;
-    shift += 4;
-  }
-  if ((mask & 0xc0000000u) == 0) {
-    mask <<= 2;
-    shift += 2;
-  }
-  if ((mask & 0x80000000u) == 0)
-    shift += 1;
-
-  return shift;
-}
-
 }  // namespace
 
 // static
diff --git a/webrtc/modules/desktop_capture/window_capturer_linux.cc b/webrtc/modules/desktop_capture/window_capturer_null.cc
similarity index 70%
rename from webrtc/modules/desktop_capture/window_capturer_linux.cc
rename to webrtc/modules/desktop_capture/window_capturer_null.cc
index e95cad8..8ea723a 100755
--- a/webrtc/modules/desktop_capture/window_capturer_linux.cc
+++ b/webrtc/modules/desktop_capture/window_capturer_null.cc
@@ -18,10 +18,10 @@
 
 namespace {
 
-class WindowCapturerLinux : public WindowCapturer {
+class WindowCapturerNull : public WindowCapturer {
  public:
-  WindowCapturerLinux();
-  virtual ~WindowCapturerLinux();
+  WindowCapturerNull();
+  virtual ~WindowCapturerNull();
 
   // WindowCapturer interface.
   virtual bool GetWindowList(WindowList* windows) OVERRIDE;
@@ -34,34 +34,34 @@
  private:
   Callback* callback_;
 
-  DISALLOW_COPY_AND_ASSIGN(WindowCapturerLinux);
+  DISALLOW_COPY_AND_ASSIGN(WindowCapturerNull);
 };
 
-WindowCapturerLinux::WindowCapturerLinux()
+WindowCapturerNull::WindowCapturerNull()
     : callback_(NULL) {
 }
 
-WindowCapturerLinux::~WindowCapturerLinux() {
+WindowCapturerNull::~WindowCapturerNull() {
 }
 
-bool WindowCapturerLinux::GetWindowList(WindowList* windows) {
+bool WindowCapturerNull::GetWindowList(WindowList* windows) {
   // Not implemented yet.
   return false;
 }
 
-bool WindowCapturerLinux::SelectWindow(WindowId id) {
+bool WindowCapturerNull::SelectWindow(WindowId id) {
   // Not implemented yet.
   return false;
 }
 
-void WindowCapturerLinux::Start(Callback* callback) {
+void WindowCapturerNull::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
 
   callback_ = callback;
 }
 
-void WindowCapturerLinux::Capture(const DesktopRegion& region) {
+void WindowCapturerNull::Capture(const DesktopRegion& region) {
   // Not implemented yet.
   callback_->OnCaptureCompleted(NULL);
 }
@@ -70,7 +70,7 @@
 
 // static
 WindowCapturer* WindowCapturer::Create() {
-  return new WindowCapturerLinux();
+  return new WindowCapturerNull();
 }
 
 }  // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/window_capturer_unittest.cc b/webrtc/modules/desktop_capture/window_capturer_unittest.cc
index 2c11d84..c6de16a 100644
--- a/webrtc/modules/desktop_capture/window_capturer_unittest.cc
+++ b/webrtc/modules/desktop_capture/window_capturer_unittest.cc
@@ -10,6 +10,8 @@
 
 #include "webrtc/modules/desktop_capture/window_capturer.h"
 
+#include <iostream>
+
 #include "gtest/gtest.h"
 #include "webrtc/modules/desktop_capture/desktop_frame.h"
 #include "webrtc/modules/desktop_capture/desktop_region.h"
@@ -42,8 +44,6 @@
   scoped_ptr<DesktopFrame> frame_;
 };
 
-#if defined(WEBRTC_WIN) || defined(WEBRTC_MAC)
-
 // Verify that we can enumerate windows.
 TEST_F(WindowCapturerTest, Enumerate) {
   WindowCapturer::WindowList windows;
@@ -92,6 +92,4 @@
   }
 }
 
-#endif  // defined(WEBRTC_WIN) || defined(WEBRTC_MAC)
-
 }  // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/window_capturer_x11.cc b/webrtc/modules/desktop_capture/window_capturer_x11.cc
new file mode 100755
index 0000000..adafa94
--- /dev/null
+++ b/webrtc/modules/desktop_capture/window_capturer_x11.cc
@@ -0,0 +1,354 @@
+/*
+ *  Copyright (c) 2013 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/modules/desktop_capture/window_capturer.h"
+
+#include <string.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/Xcomposite.h>
+#include <X11/extensions/Xrender.h>
+#include <X11/Xutil.h>
+#include <algorithm>
+#include <cassert>
+
+#include "webrtc/modules/desktop_capture/desktop_frame.h"
+#include "webrtc/modules/desktop_capture/x11/x_error_trap.h"
+#include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
+#include "webrtc/system_wrappers/interface/logging.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+
+namespace webrtc {
+
+namespace {
+
+// Convenience wrapper for XGetWindowProperty() results.
+template <class PropertyType>
+class XWindowProperty {
+ public:
+  XWindowProperty(Display* display, Window window, Atom property)
+      : is_valid_(false),
+        size_(0),
+        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_);
+    if (status != Success) {
+      data_ = NULL;
+      return;
+    }
+    if (sizeof(PropertyType) * kBitsPerByte != actual_format) {
+      size_ = 0;
+      return;
+    }
+
+    is_valid_ = true;
+  }
+
+  ~XWindowProperty() {
+    if (data_)
+      XFree(data_);
+  }
+
+  // True if we got properly value successfully.
+  bool is_valid() const { return is_valid_; }
+
+  // Size and value of the property.
+  size_t size() const { return size_; }
+  const PropertyType* data() const {
+    return reinterpret_cast<PropertyType*>(data_);
+  }
+  PropertyType* data() {
+    return reinterpret_cast<PropertyType*>(data_);
+  }
+
+ private:
+  bool is_valid_;
+  unsigned long size_;  // NOLINT: type required by XGetWindowProperty
+  unsigned char* data_;
+
+  DISALLOW_COPY_AND_ASSIGN(XWindowProperty);
+};
+
+class WindowCapturerLinux : public WindowCapturer {
+ public:
+  WindowCapturerLinux();
+  virtual ~WindowCapturerLinux();
+
+  // WindowCapturer interface.
+  virtual bool GetWindowList(WindowList* windows) OVERRIDE;
+  virtual bool SelectWindow(WindowId id) OVERRIDE;
+
+  // DesktopCapturer interface.
+  virtual void Start(Callback* callback) OVERRIDE;
+  virtual void Capture(const DesktopRegion& region) OVERRIDE;
+
+ private:
+  // Iterates through |window| hierarchy to find first visible window, i.e. one
+  // that has WM_STATE property set to NormalState.
+  // See http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1 .
+  ::Window GetApplicationWindow(::Window window);
+
+  // Returns true if the |window| is a desktop element.
+  bool IsDesktopElement(::Window window);
+
+  // Returns window title for the specified X |window|.
+  bool GetWindowTitle(::Window window, std::string* title);
+
+  Callback* callback_;
+
+  Display* display_;
+
+  Atom wm_state_atom_;
+  Atom window_type_atom_;
+  Atom normal_window_type_atom_;
+  bool has_composite_extension_;
+
+  ::Window selected_window_;
+  XServerPixelBuffer x_server_pixel_buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowCapturerLinux);
+};
+
+WindowCapturerLinux::WindowCapturerLinux()
+    : callback_(NULL),
+      display_(NULL),
+      has_composite_extension_(false),
+      selected_window_(0) {
+  display_ = XOpenDisplay(NULL);
+  if (!display_) {
+    LOG(LS_ERROR) << "Failed to open display.";
+    return;
+  }
+
+  // Create Atoms so we don't need to do it every time they are used.
+  wm_state_atom_ = XInternAtom(display_, "WM_STATE", True);
+  window_type_atom_ = XInternAtom(display_, "_NET_WM_WINDOW_TYPE", True);
+  normal_window_type_atom_ = XInternAtom(
+      display_, "_NET_WM_WINDOW_TYPE_NORMAL", True);
+
+  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.";
+  }
+}
+
+WindowCapturerLinux::~WindowCapturerLinux() {
+  if (display_)
+    XCloseDisplay(display_);
+}
+
+bool WindowCapturerLinux::GetWindowList(WindowList* windows) {
+  if (!display_)
+    return false;
+
+  WindowList result;
+
+  XErrorTrap error_trap(display_);
+
+  int num_screens = XScreenCount(display_);
+  for (int screen = 0; screen < num_screens; ++screen) {
+    ::Window root_window = XRootWindow(display_, screen);
+    ::Window parent;
+    ::Window *children;
+    unsigned int num_children;
+    int status = XQueryTree(display_, root_window, &root_window, &parent,
+                            &children, &num_children);
+    if (status == 0) {
+      LOG(LS_ERROR) << "Failed to query for child windows for screen "
+                    << screen;
+      continue;
+    }
+
+    for (unsigned int i = 0; i < num_children; ++i) {
+      // Iterate in reverse order to return windows from front to back.
+      ::Window app_window =
+          GetApplicationWindow(children[num_children - 1 - i]);
+      if (app_window && !IsDesktopElement(app_window)) {
+        Window w;
+        w.id = app_window;
+        if (GetWindowTitle(app_window, &w.title))
+          result.push_back(w);
+      }
+    }
+
+    if (children)
+      XFree(children);
+  }
+
+  windows->swap(result);
+
+  return true;
+}
+
+bool WindowCapturerLinux::SelectWindow(WindowId id) {
+  if (!x_server_pixel_buffer_.Init(display_, id))
+    return false;
+
+  selected_window_ = id;
+
+  // In addition to needing X11 server-side support for Xcomposite, it actually
+  // needs to be turned on for the window. 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.
+
+  // Redirect drawing to an offscreen buffer (ie, turn on compositing). X11
+  // remembers who has requested this and will turn it off for us when we exit.
+  XCompositeRedirectWindow(display_, id, CompositeRedirectAutomatic);
+
+  return true;
+}
+
+void WindowCapturerLinux::Start(Callback* callback) {
+  assert(!callback_);
+  assert(callback);
+
+  callback_ = callback;
+}
+
+void WindowCapturerLinux::Capture(const DesktopRegion& region) {
+  if (!has_composite_extension_) {
+    // Without the Xcomposite extension we capture when 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.";
+    callback_->OnCaptureCompleted(NULL);
+    return;
+  }
+
+  DesktopFrame* frame =
+      new BasicDesktopFrame(x_server_pixel_buffer_.window_size());
+
+  x_server_pixel_buffer_.Synchronize();
+  x_server_pixel_buffer_.CaptureRect(DesktopRect::MakeSize(frame->size()),
+                                     frame);
+
+  callback_->OnCaptureCompleted(frame);
+}
+
+::Window WindowCapturerLinux::GetApplicationWindow(::Window window) {
+  // Get WM_STATE property of the window.
+  XWindowProperty<uint32_t> window_state(display_, window, wm_state_atom_);
+
+  // WM_STATE is considered to be set to WithdrawnState when it missing.
+  int32_t state = window_state.is_valid() ?
+      *window_state.data() : WithdrawnState;
+
+  if (state == NormalState) {
+    // Window has WM_STATE==NormalState. Return it.
+    return window;
+  } else if (state == IconicState) {
+    // Window is in minimized. Skip it.
+    return 0;
+  }
+
+  // If the window is in WithdrawnState then look at all of its children.
+  ::Window root, parent;
+  ::Window *children;
+  unsigned int num_children;
+  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;
+  }
+  ::Window app_window = 0;
+  for (unsigned int i = 0; i < num_children; ++i) {
+    app_window = GetApplicationWindow(children[i]);
+    if (app_window)
+      break;
+  }
+
+  if (children)
+    XFree(children);
+  return app_window;
+}
+
+bool WindowCapturerLinux::IsDesktopElement(::Window window) {
+  if (window == 0)
+    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).
+  XWindowProperty<uint32_t> window_type(display_, window, window_type_atom_);
+  if (window_type.is_valid() && window_type.size() > 0) {
+    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 status = XGetClassHint(display_, window, &class_hint);
+  bool result = false;
+  if (status == 0) {
+    // No hints, assume this is a normal application window.
+    return result;
+  }
+
+  if (strcmp("gnome-panel", class_hint.res_name) == 0 ||
+      strcmp("desktop_window", class_hint.res_name) == 0) {
+    result = true;
+  }
+  XFree(class_hint.res_name);
+  XFree(class_hint.res_class);
+  return result;
+}
+
+bool WindowCapturerLinux::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)
+        XFreeStringList(list);
+    }
+    if (window_name.value)
+      XFree(window_name.value);
+  }
+  return result;
+}
+
+}  // namespace
+
+// static
+WindowCapturer* WindowCapturer::Create() {
+  return new WindowCapturerLinux();
+}
+
+}  // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/x11/x_error_trap.cc b/webrtc/modules/desktop_capture/x11/x_error_trap.cc
new file mode 100644
index 0000000..458fbe6
--- /dev/null
+++ b/webrtc/modules/desktop_capture/x11/x_error_trap.cc
@@ -0,0 +1,69 @@
+/*
+ *  Copyright (c) 2013 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/modules/desktop_capture/x11/x_error_trap.h"
+
+#include <assert.h>
+
+#if defined(TOOLKIT_GTK)
+#include <gdk/gdk.h>
+#endif  // !defined(TOOLKIT_GTK)
+
+namespace webrtc {
+
+namespace {
+
+#if !defined(TOOLKIT_GTK)
+
+// TODO(sergeyu): This code is not thread safe. Fix it. Bug 2202.
+static bool g_xserver_error_trap_enabled = false;
+static int g_last_xserver_error_code = 0;
+
+int XServerErrorHandler(Display* display, XErrorEvent* error_event) {
+  assert(g_xserver_error_trap_enabled);
+  g_last_xserver_error_code = error_event->error_code;
+  return 0;
+}
+
+#endif  // !defined(TOOLKIT_GTK)
+
+}  // namespace
+
+XErrorTrap::XErrorTrap(Display* display)
+    : original_error_handler_(NULL),
+      enabled_(true) {
+#if defined(TOOLKIT_GTK)
+  gdk_error_trap_push();
+#else  // !defined(TOOLKIT_GTK)
+  assert(!g_xserver_error_trap_enabled);
+  original_error_handler_ = XSetErrorHandler(&XServerErrorHandler);
+  g_xserver_error_trap_enabled = true;
+  g_last_xserver_error_code = 0;
+#endif  // !defined(TOOLKIT_GTK)
+}
+
+int XErrorTrap::GetLastErrorAndDisable() {
+  enabled_ = false;
+#if defined(TOOLKIT_GTK)
+  return gdk_error_trap_push();
+#else  // !defined(TOOLKIT_GTK)
+  assert(g_xserver_error_trap_enabled);
+  XSetErrorHandler(original_error_handler_);
+  g_xserver_error_trap_enabled = false;
+  return g_last_xserver_error_code;
+#endif  // !defined(TOOLKIT_GTK)
+}
+
+XErrorTrap::~XErrorTrap() {
+  if (enabled_)
+    GetLastErrorAndDisable();
+}
+
+}  // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/x11/x_error_trap.h b/webrtc/modules/desktop_capture/x11/x_error_trap.h
new file mode 100644
index 0000000..fd83469
--- /dev/null
+++ b/webrtc/modules/desktop_capture/x11/x_error_trap.h
@@ -0,0 +1,39 @@
+/*
+ *  Copyright (c) 2013 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.
+ */
+
+#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_X11_X_ERROR_TRAP_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_X11_X_ERROR_TRAP_H_
+
+#include <X11/Xlib.h>
+
+#include "webrtc/system_wrappers/interface/constructor_magic.h"
+
+namespace webrtc {
+
+// Helper class that registers X Window error handler. Caller can use
+// GetLastErrorAndDisable() to get the last error that was caught, if any.
+class XErrorTrap {
+ public:
+  explicit XErrorTrap(Display* display);
+  ~XErrorTrap();
+
+  // Returns last error and removes unregisters the error handler.
+  int GetLastErrorAndDisable();
+
+ private:
+  XErrorHandler original_error_handler_;
+  bool enabled_;
+
+  DISALLOW_COPY_AND_ASSIGN(XErrorTrap);
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_MODULES_DESKTOP_CAPTURE_X11_X_ERROR_TRAP_H_
diff --git a/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc b/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc
index 88f7738..6983a6d 100644
--- a/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc
+++ b/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc
@@ -11,62 +11,56 @@
 #include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
 
 #include <assert.h>
+#include <string.h>
 #include <sys/shm.h>
 
+#include "webrtc/modules/desktop_capture/desktop_frame.h"
+#include "webrtc/modules/desktop_capture/x11/x_error_trap.h"
 #include "webrtc/system_wrappers/interface/logging.h"
 
-#if defined(TOOLKIT_GTK)
-#include <gdk/gdk.h>
-#else  // !defined(TOOLKIT_GTK)
-#include <X11/Xlib.h>
-#endif  // !defined(TOOLKIT_GTK)
-
 namespace {
 
-#if defined(TOOLKIT_GTK)
-// GDK sets error handler for Xlib errors, so we need to use it to
-// trap X errors when this code is compiled with GTK.
-void EnableXServerErrorTrap() {
-  gdk_error_trap_push();
+// Returns the number of bits |mask| has to be shifted left so its last
+// (most-significant) bit set becomes the most-significant bit of the word.
+// When |mask| is 0 the function returns 31.
+uint32_t MaskToShift(uint32_t mask) {
+  int shift = 0;
+  if ((mask & 0xffff0000u) == 0) {
+    mask <<= 16;
+    shift += 16;
+  }
+  if ((mask & 0xff000000u) == 0) {
+    mask <<= 8;
+    shift += 8;
+  }
+  if ((mask & 0xf0000000u) == 0) {
+    mask <<= 4;
+    shift += 4;
+  }
+  if ((mask & 0xc0000000u) == 0) {
+    mask <<= 2;
+    shift += 2;
+  }
+  if ((mask & 0x80000000u) == 0)
+    shift += 1;
+
+  return shift;
 }
 
-int GetLastXServerError() {
-  return gdk_error_trap_pop();
+// Returns true if |image| is in RGB format.
+bool IsXImageRGBFormat(XImage* image) {
+  return image->bits_per_pixel == 32 &&
+      image->red_mask == 0xff0000 &&
+      image->green_mask == 0xff00 &&
+      image->blue_mask == 0xff;
 }
 
-#else  // !defined(TOOLKIT_GTK)
-
-static bool g_xserver_error_trap_enabled = false;
-static int g_last_xserver_error_code = 0;
-
-int XServerErrorHandler(Display* display, XErrorEvent* error_event) {
-  assert(g_xserver_error_trap_enabled);
-  g_last_xserver_error_code = error_event->error_code;
-  return 0;
-}
-
-void EnableXServerErrorTrap() {
-  assert(!g_xserver_error_trap_enabled);
-  XSetErrorHandler(&XServerErrorHandler);
-  g_xserver_error_trap_enabled = true;
-  g_last_xserver_error_code = 0;
-}
-
-int GetLastXServerError() {
-  assert(g_xserver_error_trap_enabled);
-  XSetErrorHandler(NULL);
-  g_xserver_error_trap_enabled = false;
-  return g_last_xserver_error_code;
-}
-
-#endif  // !defined(TOOLKIT_GTK)
-
 }  // namespace
 
 namespace webrtc {
 
 XServerPixelBuffer::XServerPixelBuffer()
-    : display_(NULL), root_window_(0),
+    : display_(NULL), window_(0),
       x_image_(NULL),
       shm_segment_info_(NULL), shm_pixmap_(0), shm_gc_(NULL) {
 }
@@ -96,34 +90,39 @@
     delete shm_segment_info_;
     shm_segment_info_ = NULL;
   }
+  window_ = 0;
 }
 
-void XServerPixelBuffer::Init(Display* display,
-                              const DesktopSize& screen_size) {
+bool XServerPixelBuffer::Init(Display* display, Window window) {
   Release();
   display_ = display;
-  root_window_size_ = screen_size;
-  int default_screen = DefaultScreen(display_);
-  root_window_ = RootWindow(display_, default_screen);
-  InitShm(default_screen);
+
+  XWindowAttributes attributes;
+  {
+    XErrorTrap error_trap(display_);
+    if (!XGetWindowAttributes(display_, window, &attributes) ||
+        error_trap.GetLastErrorAndDisable() != 0) {
+      return false;
+    }
+  }
+
+  window_size_ = DesktopSize(attributes.width, attributes.height);
+  window_ = window;
+  InitShm(attributes);
+
+  return true;
 }
 
-// static
-DesktopSize XServerPixelBuffer::GetRootWindowSize(Display* display) {
-  XWindowAttributes root_attr;
-  XGetWindowAttributes(display, DefaultRootWindow(display), &root_attr);
-  return DesktopSize(root_attr.width, root_attr.height);
-}
-
-void XServerPixelBuffer::InitShm(int screen) {
-  Visual* default_visual = DefaultVisual(display_, screen);
-  int default_depth = DefaultDepth(display_, screen);
+void XServerPixelBuffer::InitShm(const XWindowAttributes& attributes) {
+  Visual* default_visual = attributes.visual;
+  int default_depth = attributes.depth;
 
   int major, minor;
-  Bool havePixmaps;
-  if (!XShmQueryVersion(display_, &major, &minor, &havePixmaps))
+  Bool have_pixmaps;
+  if (!XShmQueryVersion(display_, &major, &minor, &have_pixmaps)) {
     // Shared memory not supported. CaptureRect will use the XImage API instead.
     return;
+  }
 
   bool using_shm = false;
   shm_segment_info_ = new XShmSegmentInfo;
@@ -131,8 +130,8 @@
   shm_segment_info_->shmaddr = reinterpret_cast<char*>(-1);
   shm_segment_info_->readOnly = False;
   x_image_ = XShmCreateImage(display_, default_visual, default_depth, ZPixmap,
-                             0, shm_segment_info_, root_window_size_.width(),
-                             root_window_size_.height());
+                             0, shm_segment_info_, window_size_.width(),
+                             window_size_.height());
   if (x_image_) {
     shm_segment_info_->shmid = shmget(
         IPC_PRIVATE, x_image_->bytes_per_line * x_image_->height,
@@ -141,10 +140,10 @@
       shm_segment_info_->shmaddr = x_image_->data =
           reinterpret_cast<char*>(shmat(shm_segment_info_->shmid, 0, 0));
       if (x_image_->data != reinterpret_cast<char*>(-1)) {
-        EnableXServerErrorTrap();
+        XErrorTrap error_trap(display_);
         using_shm = XShmAttach(display_, shm_segment_info_);
         XSync(display_, False);
-        if (GetLastXServerError() != 0)
+        if (error_trap.GetLastErrorAndDisable() != 0)
           using_shm = false;
         if (using_shm) {
           LOG(LS_VERBOSE) << "Using X shared memory segment "
@@ -163,48 +162,52 @@
     return;
   }
 
-  if (havePixmaps)
-    havePixmaps = InitPixmaps(default_depth);
+  if (have_pixmaps)
+    have_pixmaps = InitPixmaps(default_depth);
 
   shmctl(shm_segment_info_->shmid, IPC_RMID, 0);
   shm_segment_info_->shmid = -1;
 
   LOG(LS_VERBOSE) << "Using X shared memory extension v"
                   << major << "." << minor
-                  << " with" << (havePixmaps ? "" : "out") << " pixmaps.";
+                  << " with" << (have_pixmaps ? "" : "out") << " pixmaps.";
 }
 
 bool XServerPixelBuffer::InitPixmaps(int depth) {
   if (XShmPixmapFormat(display_) != ZPixmap)
     return false;
 
-  EnableXServerErrorTrap();
-  shm_pixmap_ = XShmCreatePixmap(display_, root_window_,
-                                 shm_segment_info_->shmaddr,
-                                 shm_segment_info_,
-                                 root_window_size_.width(),
-                                 root_window_size_.height(), depth);
-  XSync(display_, False);
-  if (GetLastXServerError() != 0) {
-    // |shm_pixmap_| is not not valid because the request was not processed
-    // by the X Server, so zero it.
-    shm_pixmap_ = 0;
-    return false;
+  {
+    XErrorTrap error_trap(display_);
+    shm_pixmap_ = XShmCreatePixmap(display_, window_,
+                                   shm_segment_info_->shmaddr,
+                                   shm_segment_info_,
+                                   window_size_.width(),
+                                   window_size_.height(), depth);
+    XSync(display_, False);
+    if (error_trap.GetLastErrorAndDisable() != 0) {
+      // |shm_pixmap_| is not not valid because the request was not processed
+      // by the X Server, so zero it.
+      shm_pixmap_ = 0;
+      return false;
+    }
   }
 
-  EnableXServerErrorTrap();
-  XGCValues shm_gc_values;
-  shm_gc_values.subwindow_mode = IncludeInferiors;
-  shm_gc_values.graphics_exposures = False;
-  shm_gc_ = XCreateGC(display_, root_window_,
-                      GCSubwindowMode | GCGraphicsExposures,
-                      &shm_gc_values);
-  XSync(display_, False);
-  if (GetLastXServerError() != 0) {
-    XFreePixmap(display_, shm_pixmap_);
-    shm_pixmap_ = 0;
-    shm_gc_ = 0;  // See shm_pixmap_ comment above.
-    return false;
+  {
+    XErrorTrap error_trap(display_);
+    XGCValues shm_gc_values;
+    shm_gc_values.subwindow_mode = IncludeInferiors;
+    shm_gc_values.graphics_exposures = False;
+    shm_gc_ = XCreateGC(display_, window_,
+                        GCSubwindowMode | GCGraphicsExposures,
+                        &shm_gc_values);
+    XSync(display_, False);
+    if (error_trap.GetLastErrorAndDisable() != 0) {
+      XFreePixmap(display_, shm_pixmap_);
+      shm_pixmap_ = 0;
+      shm_gc_ = 0;  // See shm_pixmap_ comment above.
+      return false;
+    }
   }
 
   return true;
@@ -213,57 +216,110 @@
 void XServerPixelBuffer::Synchronize() {
   if (shm_segment_info_ && !shm_pixmap_) {
     // XShmGetImage can fail if the display is being reconfigured.
-    EnableXServerErrorTrap();
-    XShmGetImage(display_, root_window_, x_image_, 0, 0, AllPlanes);
-    GetLastXServerError();
+    XErrorTrap error_trap(display_);
+    XShmGetImage(display_, window_, x_image_, 0, 0, AllPlanes);
   }
 }
 
-uint8_t* XServerPixelBuffer::CaptureRect(const DesktopRect& rect) {
-  assert(rect.right() <= root_window_size_.width());
-  assert(rect.bottom() <= root_window_size_.height());
+void XServerPixelBuffer::CaptureRect(const DesktopRect& rect,
+                                     DesktopFrame* frame) {
+  assert(rect.right() <= window_size_.width());
+  assert(rect.bottom() <= window_size_.height());
+
+  uint8_t* data;
 
   if (shm_segment_info_) {
     if (shm_pixmap_) {
-      XCopyArea(display_, root_window_, shm_pixmap_, shm_gc_,
+      XCopyArea(display_, window_, shm_pixmap_, shm_gc_,
                 rect.left(), rect.top(), rect.width(), rect.height(),
                 rect.left(), rect.top());
       XSync(display_, False);
     }
-    return reinterpret_cast<uint8_t*>(x_image_->data) +
+    data = reinterpret_cast<uint8_t*>(x_image_->data) +
         rect.top() * x_image_->bytes_per_line +
         rect.left() * x_image_->bits_per_pixel / 8;
   } else {
     if (x_image_)
       XDestroyImage(x_image_);
-    x_image_ = XGetImage(display_, root_window_, rect.left(), rect.top(),
+    x_image_ = XGetImage(display_, window_, rect.left(), rect.top(),
                          rect.width(), rect.height(), AllPlanes, ZPixmap);
-    return reinterpret_cast<uint8_t*>(x_image_->data);
+    data = reinterpret_cast<uint8_t*>(x_image_->data);
+  }
+
+  if (IsXImageRGBFormat(x_image_)) {
+    FastBlit(data, rect, frame);
+  } else {
+    SlowBlit(data, rect, frame);
   }
 }
 
-int XServerPixelBuffer::GetStride() const {
-  return x_image_->bytes_per_line;
+void XServerPixelBuffer::FastBlit(uint8_t* image,
+                                  const DesktopRect& rect,
+                                  DesktopFrame* frame) {
+  uint8_t* src_pos = image;
+  int src_stride = x_image_->bytes_per_line;
+  int dst_x = rect.left(), dst_y = rect.top();
+
+  uint8_t* dst_pos = frame->data() + frame->stride() * dst_y;
+  dst_pos += dst_x * DesktopFrame::kBytesPerPixel;
+
+  int height = rect.height();
+  int row_bytes = rect.width() * DesktopFrame::kBytesPerPixel;
+  for (int y = 0; y < height; ++y) {
+    memcpy(dst_pos, src_pos, row_bytes);
+    src_pos += src_stride;
+    dst_pos += frame->stride();
+  }
 }
 
-int XServerPixelBuffer::GetDepth() const {
-  return x_image_->depth;
-}
+void XServerPixelBuffer::SlowBlit(uint8_t* image,
+                                  const DesktopRect& rect,
+                                  DesktopFrame* frame) {
+  int src_stride = x_image_->bytes_per_line;
+  int dst_x = rect.left(), dst_y = rect.top();
+  int width = rect.width(), height = rect.height();
 
-int XServerPixelBuffer::GetBitsPerPixel() const {
-  return x_image_->bits_per_pixel;
-}
+  uint32_t red_mask = x_image_->red_mask;
+  uint32_t green_mask = x_image_->red_mask;
+  uint32_t blue_mask = x_image_->blue_mask;
 
-int XServerPixelBuffer::GetRedMask() const {
-  return x_image_->red_mask;
-}
+  uint32_t red_shift = MaskToShift(red_mask);
+  uint32_t green_shift = MaskToShift(green_mask);
+  uint32_t blue_shift = MaskToShift(blue_mask);
 
-int XServerPixelBuffer::GetBlueMask() const {
-  return x_image_->blue_mask;
-}
+  int bits_per_pixel = x_image_->bits_per_pixel;
 
-int XServerPixelBuffer::GetGreenMask() const {
-  return x_image_->green_mask;
+  uint8_t* dst_pos = frame->data() + frame->stride() * dst_y;
+  uint8_t* src_pos = image;
+  dst_pos += dst_x * DesktopFrame::kBytesPerPixel;
+  // TODO(hclam): Optimize, perhaps using MMX code or by converting to
+  // YUV directly.
+  // TODO(sergeyu): This code doesn't handle XImage byte order properly and
+  // won't work with 24bpp images. Fix it.
+  for (int y = 0; y < height; y++) {
+    uint32_t* dst_pos_32 = reinterpret_cast<uint32_t*>(dst_pos);
+    uint32_t* src_pos_32 = reinterpret_cast<uint32_t*>(src_pos);
+    uint16_t* src_pos_16 = reinterpret_cast<uint16_t*>(src_pos);
+    for (int x = 0; x < width; x++) {
+      // Dereference through an appropriately-aligned pointer.
+      uint32_t pixel;
+      if (bits_per_pixel == 32) {
+        pixel = src_pos_32[x];
+      } else if (bits_per_pixel == 16) {
+        pixel = src_pos_16[x];
+      } else {
+        pixel = src_pos[x];
+      }
+      uint32_t r = (pixel & red_mask) << red_shift;
+      uint32_t g = (pixel & green_mask) << green_shift;
+      uint32_t b = (pixel & blue_mask) << blue_shift;
+      // Write as 32-bit RGB.
+      dst_pos_32[x] = ((r >> 8) & 0xff0000) | ((g >> 16) & 0xff00) |
+          ((b >> 24) & 0xff);
+    }
+    dst_pos += frame->stride();
+    src_pos += src_stride;
+  }
 }
 
 }  // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h b/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h
index 8759be3..b81096c 100644
--- a/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h
+++ b/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h
@@ -20,6 +20,8 @@
 
 namespace webrtc {
 
+class DesktopFrame;
+
 // A class to allow the X server's pixel buffer to be accessed as efficiently
 // as possible.
 class XServerPixelBuffer {
@@ -29,14 +31,14 @@
 
   void Release();
 
-  // Allocate (or reallocate) the pixel buffer with the given size, which is
-  // assumed to be the current size of the root window.
-  // |screen_size| should either come from GetRootWindowSize(), or
-  // from a recent ConfigureNotify event on the root window.
-  void Init(Display* display, const DesktopSize& screen_size);
+  // Allocate (or reallocate) the pixel buffer for |window|. Returns false in
+  // case of an error (e.g. window doesn't exist).
+  bool Init(Display* display, Window window);
 
-  // Request the current size of the root window from the X Server.
-  static DesktopSize GetRootWindowSize(Display* display);
+  bool is_initialized() { return window_ != 0; }
+
+  // Returns the size of the window the buffer was initialized for.
+  const DesktopSize& window_size() { return window_size_; }
 
   // If shared memory is being used without pixmaps, synchronize this pixel
   // buffer with the root window contents (otherwise, this is a no-op).
@@ -45,31 +47,28 @@
   // beginning.
   void Synchronize();
 
-  // Capture the specified rectangle and return a pointer to its top-left pixel
-  // or NULL if capture fails. The returned pointer remains valid until the next
-  // call to CaptureRect.
-  // In the case where the full-screen data is captured by Synchronize(), this
-  // simply returns the pointer without doing any more work.
-  // The caller must ensure that |rect| is no larger than the screen size
-  // supplied to Init().
-  uint8_t* CaptureRect(const DesktopRect& rect);
-
-  // Return information about the most recent capture. This is only guaranteed
-  // to be valid between CaptureRect calls.
-  int GetStride() const;
-  int GetDepth() const;
-  int GetBitsPerPixel() const;
-  int GetRedMask() const;
-  int GetBlueMask() const;
-  int GetGreenMask() const;
+  // Capture the specified rectangle and stores it in the |frame|. In the case
+  // where the full-screen data is captured by Synchronize(), this simply
+  // returns the pointer without doing any more work. The caller must ensure
+  // that |rect| is not larger than window_size().
+  void CaptureRect(const DesktopRect& rect, DesktopFrame* frame);
 
  private:
-  void InitShm(int screen);
+  void InitShm(const XWindowAttributes& attributes);
   bool InitPixmaps(int depth);
 
+  // We expose two forms of blitting to handle variations in the pixel format.
+  // In FastBlit(), the operation is effectively a memcpy.
+  void FastBlit(uint8_t* image,
+                const DesktopRect& rect,
+                DesktopFrame* frame);
+  void SlowBlit(uint8_t* image,
+                const DesktopRect& rect,
+                DesktopFrame* frame);
+
   Display* display_;
-  Window root_window_;
-  DesktopSize root_window_size_;
+  Window window_;
+  DesktopSize window_size_;
   XImage* x_image_;
   XShmSegmentInfo* shm_segment_info_;
   Pixmap shm_pixmap_;