screen_capturer_x11.cc revision 894e6fe9ea16a63537ec6d453c81566d02f66059
13d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org/*
23d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
33d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org *
43d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org *  Use of this source code is governed by a BSD-style license
53d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org *  that can be found in the LICENSE file in the root of the source
63d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org *  tree. An additional intellectual property rights grant can be found
73d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org *  in the file PATENTS.  All contributing project authors may
83d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org *  be found in the AUTHORS file in the root of the source tree.
93d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org */
103d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
113d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/modules/desktop_capture/screen_capturer.h"
123d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
133d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include <string.h>
143d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include <set>
153d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
163d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include <X11/extensions/Xdamage.h>
173d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include <X11/extensions/Xfixes.h>
183d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include <X11/Xlib.h>
193d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include <X11/Xutil.h>
203d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
21894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
223d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/modules/desktop_capture/desktop_frame.h"
233d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/modules/desktop_capture/differ.h"
243d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/modules/desktop_capture/mouse_cursor_shape.h"
253d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h"
263d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/modules/desktop_capture/screen_capturer_helper.h"
273d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
283d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/system_wrappers/interface/logging.h"
293d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/system_wrappers/interface/scoped_ptr.h"
303d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/system_wrappers/interface/tick_util.h"
313d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
323d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org// TODO(sergeyu): Move this to a header where it can be shared.
333d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#if defined(NDEBUG)
343d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#define DCHECK(condition) (void)(condition)
353d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#else  // NDEBUG
363d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#define DCHECK(condition) if (!(condition)) {abort();}
373d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#endif
383d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
393d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgnamespace webrtc {
403d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
413d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgnamespace {
423d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
433d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org// A class to perform video frame capturing for Linux.
443d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgclass ScreenCapturerLinux : public ScreenCapturer {
453d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org public:
463d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  ScreenCapturerLinux();
473d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  virtual ~ScreenCapturerLinux();
483d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
493d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // TODO(ajwong): Do we really want this to be synchronous?
50894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  bool Init(const DesktopCaptureOptions& options);
513d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
523d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // DesktopCapturer interface.
533d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  virtual void Start(Callback* delegate) OVERRIDE;
543d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  virtual void Capture(const DesktopRegion& region) OVERRIDE;
553d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
563d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // ScreenCapturer interface.
573d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  virtual void SetMouseShapeObserver(
583d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      MouseShapeObserver* mouse_shape_observer) OVERRIDE;
593d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
603d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org private:
61894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  Display* display() { return options_.x_display()->display(); }
62894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org
633d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  void InitXDamage();
643d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
653d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Read and handle all currently-pending XEvents.
663d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // In the DAMAGE case, process the XDamage events and store the resulting
673d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // damage rectangles in the ScreenCapturerHelper.
683d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // In all cases, call ScreenConfigurationChanged() in response to any
693d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // ConfigNotify events.
703d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  void ProcessPendingXEvents();
713d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
723d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Capture the cursor image and notify the delegate if it was captured.
733d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  void CaptureCursor();
743d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
753d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Capture screen pixels to the current buffer in the queue. In the DAMAGE
763d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // case, the ScreenCapturerHelper already holds the list of invalid rectangles
773d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // from ProcessPendingXEvents(). In the non-DAMAGE case, this captures the
783d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // whole screen, then calculates some invalid rectangles that include any
793d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // differences between this and the previous capture.
803d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DesktopFrame* CaptureScreen();
813d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
829f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  // Called when the screen configuration is changed.
839f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  void ScreenConfigurationChanged();
843d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
853d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Synchronize the current buffer with |last_buffer_|, by copying pixels from
863d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // the area of |last_invalid_rects|.
873d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Note this only works on the assumption that kNumBuffers == 2, as
883d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // |last_invalid_rects| holds the differences from the previous buffer and
893d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // the one prior to that (which will then be the current buffer).
903d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  void SynchronizeFrame();
913d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
923d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  void DeinitXlib();
933d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
94894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  DesktopCaptureOptions options_;
95894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org
963d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  Callback* callback_;
973d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  MouseShapeObserver* mouse_shape_observer_;
983d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
993d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // X11 graphics context.
1003d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  GC gc_;
1013d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  Window root_window_;
1023d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1033d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // XFixes.
1043d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  bool has_xfixes_;
1053d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  int xfixes_event_base_;
1063d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  int xfixes_error_base_;
1073d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1083d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // XDamage information.
1093d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  bool use_damage_;
1103d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  Damage damage_handle_;
1113d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  int damage_event_base_;
1123d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  int damage_error_base_;
1133d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  XserverRegion damage_region_;
1143d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1153d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Access to the X Server's pixel buffer.
1163d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  XServerPixelBuffer x_server_pixel_buffer_;
1173d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1183d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // A thread-safe list of invalid rectangles, and the size of the most
1193d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // recently captured screen.
1203d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  ScreenCapturerHelper helper_;
1213d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1223d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Queue of the frames buffers.
1233d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  ScreenCaptureFrameQueue queue_;
1243d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1253d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Invalid region from the previous capture. This is used to synchronize the
1263d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // current with the last buffer used.
1273d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DesktopRegion last_invalid_region_;
1283d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1293d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // |Differ| for use when polling for changes.
1303d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  scoped_ptr<Differ> differ_;
1313d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1323d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DISALLOW_COPY_AND_ASSIGN(ScreenCapturerLinux);
1333d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org};
1343d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1353d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgScreenCapturerLinux::ScreenCapturerLinux()
1363d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    : callback_(NULL),
1373d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      mouse_shape_observer_(NULL),
1383d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      gc_(NULL),
1393d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      root_window_(BadValue),
1403d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      has_xfixes_(false),
1413d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      xfixes_event_base_(-1),
1423d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      xfixes_error_base_(-1),
1433d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      use_damage_(false),
1443d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      damage_handle_(0),
1453d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      damage_event_base_(-1),
1463d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      damage_error_base_(-1),
1473d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      damage_region_(0) {
1483d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  helper_.SetLogGridSize(4);
1493d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
1503d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1513d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgScreenCapturerLinux::~ScreenCapturerLinux() {
1523d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DeinitXlib();
1533d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
1543d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
155894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.orgbool ScreenCapturerLinux::Init(const DesktopCaptureOptions& options) {
156894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  options_ = options;
1573d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
158894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  root_window_ = RootWindow(display(), DefaultScreen(display()));
1593d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (root_window_ == BadValue) {
1603d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    LOG(LS_ERROR) << "Unable to get the root window";
1613d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    DeinitXlib();
1623d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    return false;
1633d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
1643d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
165894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  gc_ = XCreateGC(display(), root_window_, 0, NULL);
1663d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (gc_ == NULL) {
1673d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    LOG(LS_ERROR) << "Unable to get graphics context";
1683d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    DeinitXlib();
1693d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    return false;
1703d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
1713d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1723d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Check for XFixes extension. This is required for cursor shape
1733d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // notifications, and for our use of XDamage.
174894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  if (XFixesQueryExtension(display(), &xfixes_event_base_,
1753d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org                           &xfixes_error_base_)) {
1763d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    has_xfixes_ = true;
1773d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  } else {
1783d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    LOG(LS_INFO) << "X server does not support XFixes.";
1793d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
1803d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1813d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Register for changes to the dimensions of the root window.
182894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  XSelectInput(display(), root_window_, StructureNotifyMask);
1833d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
184894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  if (!x_server_pixel_buffer_.Init(display(), DefaultRootWindow(display()))) {
1859f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org    LOG(LS_ERROR) << "Failed to initialize pixel buffer.";
1869f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org    return false;
1879f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  }
1883d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1893d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (has_xfixes_) {
1903d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    // Register for changes to the cursor shape.
191894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org    XFixesSelectCursorInput(display(), root_window_,
1923d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org                            XFixesDisplayCursorNotifyMask);
1933d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
1943d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
195894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  if (options_.use_update_notifications()) {
1963d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    InitXDamage();
1973d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
1983d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1993d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  return true;
2003d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
2013d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2023d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgvoid ScreenCapturerLinux::InitXDamage() {
2033d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Our use of XDamage requires XFixes.
2043d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (!has_xfixes_) {
2053d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    return;
2063d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
2073d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2083d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Check for XDamage extension.
209894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  if (!XDamageQueryExtension(display(), &damage_event_base_,
2103d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org                             &damage_error_base_)) {
2113d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    LOG(LS_INFO) << "X server does not support XDamage.";
2123d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    return;
2133d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
2143d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2153d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // TODO(lambroslambrou): Disable DAMAGE in situations where it is known
2163d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // to fail, such as when Desktop Effects are enabled, with graphics
2173d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // drivers (nVidia, ATI) that fail to report DAMAGE notifications
2183d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // properly.
2193d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2203d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Request notifications every time the screen becomes damaged.
221894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  damage_handle_ = XDamageCreate(display(), root_window_,
2223d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org                                 XDamageReportNonEmpty);
2233d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (!damage_handle_) {
2243d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    LOG(LS_ERROR) << "Unable to initialize XDamage.";
2253d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    return;
2263d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
2273d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2283d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Create an XFixes server-side region to collate damage into.
229894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  damage_region_ = XFixesCreateRegion(display(), 0, 0);
2303d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (!damage_region_) {
231894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org    XDamageDestroy(display(), damage_handle_);
2323d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    LOG(LS_ERROR) << "Unable to create XFixes region.";
2333d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    return;
2343d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
2353d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2363d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  use_damage_ = true;
2373d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  LOG(LS_INFO) << "Using XDamage extension.";
2383d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
2393d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2403d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgvoid ScreenCapturerLinux::Start(Callback* callback) {
2413d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DCHECK(!callback_);
2423d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DCHECK(callback);
2433d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2443d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  callback_ = callback;
2453d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
2463d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2473d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgvoid ScreenCapturerLinux::Capture(const DesktopRegion& region) {
2483d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  TickTime capture_start_time = TickTime::Now();
2493d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2503d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  queue_.MoveToNextFrame();
2513d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2523d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Process XEvents for XDamage and cursor shape tracking.
2533d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  ProcessPendingXEvents();
2543d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2559f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  // ProcessPendingXEvents() may call ScreenConfigurationChanged() which
2569f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  // reinitializes |x_server_pixel_buffer_|. Check if the pixel buffer is still
2579f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  // in a good shape.
2589f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  if (!x_server_pixel_buffer_.is_initialized()) {
2599f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org     // We failed to initialize pixel buffer.
2609f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org     callback_->OnCaptureCompleted(NULL);
2619f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org     return;
2629f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  }
2639f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org
2643d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // If the current frame is from an older generation then allocate a new one.
2653d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Note that we can't reallocate other buffers at this point, since the caller
2663d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // may still be reading from them.
2673d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (!queue_.current_frame()) {
2683d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    scoped_ptr<DesktopFrame> frame(
2699f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org        new BasicDesktopFrame(x_server_pixel_buffer_.window_size()));
2703d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    queue_.ReplaceCurrentFrame(frame.release());
2713d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
2723d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2733d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Refresh the Differ helper used by CaptureFrame(), if needed.
2743d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DesktopFrame* frame = queue_.current_frame();
2753d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (!use_damage_ && (
2763d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      !differ_.get() ||
2773d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      (differ_->width() != frame->size().width()) ||
2783d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      (differ_->height() != frame->size().height()) ||
2793d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      (differ_->bytes_per_row() != frame->stride()))) {
2803d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    differ_.reset(new Differ(frame->size().width(), frame->size().height(),
2813d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org                             DesktopFrame::kBytesPerPixel,
2823d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org                             frame->stride()));
2833d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
2843d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2853d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DesktopFrame* result = CaptureScreen();
2863d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  last_invalid_region_ = result->updated_region();
2873d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  result->set_capture_time_ms(
2883d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      (TickTime::Now() - capture_start_time).Milliseconds());
2893d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  callback_->OnCaptureCompleted(result);
2903d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
2913d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2923d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgvoid ScreenCapturerLinux::SetMouseShapeObserver(
2933d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      MouseShapeObserver* mouse_shape_observer) {
2943d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DCHECK(!mouse_shape_observer_);
2953d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DCHECK(mouse_shape_observer);
2963d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2973d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  mouse_shape_observer_ = mouse_shape_observer;
2983d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
2993d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3003d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgvoid ScreenCapturerLinux::ProcessPendingXEvents() {
3013d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Find the number of events that are outstanding "now."  We don't just loop
3023d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // on XPending because we want to guarantee this terminates.
303894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  int events_to_process = XPending(display());
3043d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  XEvent e;
3053d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3063d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  for (int i = 0; i < events_to_process; i++) {
307894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org    XNextEvent(display(), &e);
3083d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    if (use_damage_ && (e.type == damage_event_base_ + XDamageNotify)) {
3093d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      XDamageNotifyEvent* event = reinterpret_cast<XDamageNotifyEvent*>(&e);
3103d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      DCHECK(event->level == XDamageReportNonEmpty);
3113d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    } else if (e.type == ConfigureNotify) {
3129f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org      ScreenConfigurationChanged();
3133d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    } else if (has_xfixes_ &&
3143d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org               e.type == xfixes_event_base_ + XFixesCursorNotify) {
3153d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      XFixesCursorNotifyEvent* cne;
3163d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      cne = reinterpret_cast<XFixesCursorNotifyEvent*>(&e);
3173d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      if (cne->subtype == XFixesDisplayCursorNotify) {
3183d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org        CaptureCursor();
3193d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      }
3203d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    } else {
3213d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      LOG(LS_WARNING) << "Got unknown event type: " << e.type;
3223d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    }
3233d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
3243d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
3253d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3263d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgvoid ScreenCapturerLinux::CaptureCursor() {
3273d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DCHECK(has_xfixes_);
3283d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
329894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  XFixesCursorImage* img = XFixesGetCursorImage(display());
3303d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (!img) {
3313d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    return;
3323d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
3333d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3343d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  scoped_ptr<MouseCursorShape> cursor(new MouseCursorShape());
3353d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  cursor->size = DesktopSize(img->width, img->height);
3363d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  cursor->hotspot = DesktopVector(img->xhot, img->yhot);
3373d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3383d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  int total_bytes = cursor->size.width ()* cursor->size.height() *
3393d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      DesktopFrame::kBytesPerPixel;
3403d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  cursor->data.resize(total_bytes);
3413d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3423d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Xlib stores 32-bit data in longs, even if longs are 64-bits long.
3433d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  unsigned long* src = img->pixels;
3443d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  uint32_t* dst = reinterpret_cast<uint32_t*>(&*(cursor->data.begin()));
3453d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  uint32_t* dst_end = dst + (img->width * img->height);
3463d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  while (dst < dst_end) {
3473d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    *dst++ = static_cast<uint32_t>(*src++);
3483d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
3493d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  XFree(img);
3503d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3513d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (mouse_shape_observer_)
3523d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    mouse_shape_observer_->OnCursorShapeChanged(cursor.release());
3533d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
3543d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3553d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgDesktopFrame* ScreenCapturerLinux::CaptureScreen() {
3563d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DesktopFrame* frame = queue_.current_frame()->Share();
3579f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  assert(x_server_pixel_buffer_.window_size().equals(frame->size()));
3583d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3593d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Pass the screen size to the helper, so it can clip the invalid region if it
3603d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // expands that region to a grid.
3613d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  helper_.set_size_most_recent(frame->size());
3623d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3633d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // In the DAMAGE case, ensure the frame is up-to-date with the previous frame
3643d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // if any.  If there isn't a previous frame, that means a screen-resolution
3653d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // change occurred, and |invalid_rects| will be updated to include the whole
3663d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // screen.
3673d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (use_damage_ && queue_.previous_frame())
3683d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    SynchronizeFrame();
3693d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3703d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DesktopRegion* updated_region = frame->mutable_updated_region();
3713d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3723d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  x_server_pixel_buffer_.Synchronize();
3733d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (use_damage_ && queue_.previous_frame()) {
3743d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    // Atomically fetch and clear the damage region.
375894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org    XDamageSubtract(display(), damage_handle_, None, damage_region_);
3763d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    int rects_num = 0;
3773d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    XRectangle bounds;
378894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org    XRectangle* rects = XFixesFetchRegionAndBounds(display(), damage_region_,
3793d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org                                                   &rects_num, &bounds);
3803d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    for (int i = 0; i < rects_num; ++i) {
3813d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      updated_region->AddRect(DesktopRect::MakeXYWH(
3823d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org          rects[i].x, rects[i].y, rects[i].width, rects[i].height));
3833d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    }
3843d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    XFree(rects);
3853d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    helper_.InvalidateRegion(*updated_region);
3863d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3873d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    // Capture the damaged portions of the desktop.
3883d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    helper_.TakeInvalidRegion(updated_region);
3893d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3903d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    // Clip the damaged portions to the current screen size, just in case some
3913d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    // spurious XDamage notifications were received for a previous (larger)
3923d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    // screen size.
3933d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    updated_region->IntersectWith(
3949f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org        DesktopRect::MakeSize(x_server_pixel_buffer_.window_size()));
3953d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3963d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    for (DesktopRegion::Iterator it(*updated_region);
3973d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org         !it.IsAtEnd(); it.Advance()) {
3989f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org      x_server_pixel_buffer_.CaptureRect(it.rect(), frame);
3993d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    }
4003d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  } else {
4013d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    // Doing full-screen polling, or this is the first capture after a
4023d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    // screen-resolution change.  In either case, need a full-screen capture.
4039f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org    DesktopRect screen_rect = DesktopRect::MakeSize(frame->size());
4049f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org    x_server_pixel_buffer_.CaptureRect(screen_rect, frame);
4053d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4063d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    if (queue_.previous_frame()) {
4073d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      // Full-screen polling, so calculate the invalid rects here, based on the
4083d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      // changed pixels between current and previous buffers.
4093d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      DCHECK(differ_.get() != NULL);
4103d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      DCHECK(queue_.previous_frame()->data());
4113d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      differ_->CalcDirtyRegion(queue_.previous_frame()->data(),
4123d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org                               frame->data(), updated_region);
4133d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    } else {
4143d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      // No previous buffer, so always invalidate the whole screen, whether
4153d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      // or not DAMAGE is being used.  DAMAGE doesn't necessarily send a
4163d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      // full-screen notification after a screen-resolution change, so
4173d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      // this is done here.
4183d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      updated_region->SetRect(screen_rect);
4193d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    }
4203d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
4213d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4223d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  return frame;
4233d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
4243d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4259f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.orgvoid ScreenCapturerLinux::ScreenConfigurationChanged() {
4263d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Make sure the frame buffers will be reallocated.
4273d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  queue_.Reset();
4283d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4293d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  helper_.ClearInvalidRegion();
430894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  if (!x_server_pixel_buffer_.Init(display(), DefaultRootWindow(display()))) {
4319f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org    LOG(LS_ERROR) << "Failed to initialize pixel buffer after screen "
4329f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org        "configuration change.";
4339f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  }
4343d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
4353d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4363d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgvoid ScreenCapturerLinux::SynchronizeFrame() {
4373d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Synchronize the current buffer with the previous one since we do not
4383d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // capture the entire desktop. Note that encoder may be reading from the
4393d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // previous buffer at this time so thread access complaints are false
4403d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // positives.
4413d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4423d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // TODO(hclam): We can reduce the amount of copying here by subtracting
4433d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // |capturer_helper_|s region from |last_invalid_region_|.
4443d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // http://crbug.com/92354
4453d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DCHECK(queue_.previous_frame());
4463d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4473d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DesktopFrame* current = queue_.current_frame();
4483d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DesktopFrame* last = queue_.previous_frame();
4493d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DCHECK(current != last);
4503d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  for (DesktopRegion::Iterator it(last_invalid_region_);
4513d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org       !it.IsAtEnd(); it.Advance()) {
4523d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    const DesktopRect& r = it.rect();
4533d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    int offset = r.top() * current->stride() +
4543d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org        r.left() * DesktopFrame::kBytesPerPixel;
4553d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    for (int i = 0; i < r.height(); ++i) {
4563d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      memcpy(current->data() + offset, last->data() + offset,
4573d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org             r.width() * DesktopFrame::kBytesPerPixel);
4583d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      offset += current->size().width() * DesktopFrame::kBytesPerPixel;
4593d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    }
4603d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
4613d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
4623d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4633d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgvoid ScreenCapturerLinux::DeinitXlib() {
4643d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (gc_) {
465894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org    XFreeGC(display(), gc_);
4663d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    gc_ = NULL;
4673d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
4683d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4693d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  x_server_pixel_buffer_.Release();
4703d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
471894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  if (display()) {
472894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org    if (damage_handle_) {
473894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org      XDamageDestroy(display(), damage_handle_);
474894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org      damage_handle_ = 0;
475894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org    }
476894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org
477894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org    if (damage_region_) {
478894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org      XFixesDestroyRegion(display(), damage_region_);
479894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org      damage_region_ = 0;
480894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org    }
4813d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
4823d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
4833d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4843d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}  // namespace
4853d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4863d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org// static
487894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.orgScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) {
488894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  if (!options.x_display())
489894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org    return NULL;
4903d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4913d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  scoped_ptr<ScreenCapturerLinux> capturer(new ScreenCapturerLinux());
492894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.org  if (!capturer->Init(options))
4933d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    capturer.reset();
4943d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  return capturer.release();
4953d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
4963d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4973d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}  // namespace webrtc
498