screen_capturer_x11.cc revision 9f282403f258162ca53eb2f16b8e9a26e7970096
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
213d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/modules/desktop_capture/desktop_frame.h"
223d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/modules/desktop_capture/differ.h"
233d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/modules/desktop_capture/mouse_cursor_shape.h"
243d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h"
253d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/modules/desktop_capture/screen_capturer_helper.h"
263d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
273d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/system_wrappers/interface/logging.h"
283d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/system_wrappers/interface/scoped_ptr.h"
293d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#include "webrtc/system_wrappers/interface/tick_util.h"
303d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
313d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org// TODO(sergeyu): Move this to a header where it can be shared.
323d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#if defined(NDEBUG)
333d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#define DCHECK(condition) (void)(condition)
343d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#else  // NDEBUG
353d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#define DCHECK(condition) if (!(condition)) {abort();}
363d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org#endif
373d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
383d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgnamespace webrtc {
393d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
403d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgnamespace {
413d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
423d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org// A class to perform video frame capturing for Linux.
433d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgclass ScreenCapturerLinux : public ScreenCapturer {
443d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org public:
453d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  ScreenCapturerLinux();
463d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  virtual ~ScreenCapturerLinux();
473d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
483d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // TODO(ajwong): Do we really want this to be synchronous?
493d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  bool Init(bool use_x_damage);
503d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
513d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // DesktopCapturer interface.
523d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  virtual void Start(Callback* delegate) OVERRIDE;
533d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  virtual void Capture(const DesktopRegion& region) OVERRIDE;
543d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
553d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // ScreenCapturer interface.
563d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  virtual void SetMouseShapeObserver(
573d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      MouseShapeObserver* mouse_shape_observer) OVERRIDE;
583d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
593d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org private:
603d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  void InitXDamage();
613d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
623d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Read and handle all currently-pending XEvents.
633d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // In the DAMAGE case, process the XDamage events and store the resulting
643d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // damage rectangles in the ScreenCapturerHelper.
653d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // In all cases, call ScreenConfigurationChanged() in response to any
663d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // ConfigNotify events.
673d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  void ProcessPendingXEvents();
683d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
693d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Capture the cursor image and notify the delegate if it was captured.
703d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  void CaptureCursor();
713d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
723d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Capture screen pixels to the current buffer in the queue. In the DAMAGE
733d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // case, the ScreenCapturerHelper already holds the list of invalid rectangles
743d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // from ProcessPendingXEvents(). In the non-DAMAGE case, this captures the
753d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // whole screen, then calculates some invalid rectangles that include any
763d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // differences between this and the previous capture.
773d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DesktopFrame* CaptureScreen();
783d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
799f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  // Called when the screen configuration is changed.
809f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  void ScreenConfigurationChanged();
813d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
823d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Synchronize the current buffer with |last_buffer_|, by copying pixels from
833d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // the area of |last_invalid_rects|.
843d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Note this only works on the assumption that kNumBuffers == 2, as
853d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // |last_invalid_rects| holds the differences from the previous buffer and
863d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // the one prior to that (which will then be the current buffer).
873d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  void SynchronizeFrame();
883d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
893d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  void DeinitXlib();
903d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
913d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  Callback* callback_;
923d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  MouseShapeObserver* mouse_shape_observer_;
933d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
943d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // X11 graphics context.
953d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  Display* display_;
963d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  GC gc_;
973d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  Window root_window_;
983d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
993d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // XFixes.
1003d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  bool has_xfixes_;
1013d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  int xfixes_event_base_;
1023d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  int xfixes_error_base_;
1033d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1043d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // XDamage information.
1053d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  bool use_damage_;
1063d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  Damage damage_handle_;
1073d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  int damage_event_base_;
1083d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  int damage_error_base_;
1093d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  XserverRegion damage_region_;
1103d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1113d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Access to the X Server's pixel buffer.
1123d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  XServerPixelBuffer x_server_pixel_buffer_;
1133d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1143d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // A thread-safe list of invalid rectangles, and the size of the most
1153d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // recently captured screen.
1163d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  ScreenCapturerHelper helper_;
1173d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1183d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Queue of the frames buffers.
1193d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  ScreenCaptureFrameQueue queue_;
1203d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1213d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Invalid region from the previous capture. This is used to synchronize the
1223d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // current with the last buffer used.
1233d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DesktopRegion last_invalid_region_;
1243d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1253d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // |Differ| for use when polling for changes.
1263d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  scoped_ptr<Differ> differ_;
1273d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1283d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DISALLOW_COPY_AND_ASSIGN(ScreenCapturerLinux);
1293d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org};
1303d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1313d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgScreenCapturerLinux::ScreenCapturerLinux()
1323d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    : callback_(NULL),
1333d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      mouse_shape_observer_(NULL),
1343d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      display_(NULL),
1353d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      gc_(NULL),
1363d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      root_window_(BadValue),
1373d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      has_xfixes_(false),
1383d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      xfixes_event_base_(-1),
1393d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      xfixes_error_base_(-1),
1403d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      use_damage_(false),
1413d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      damage_handle_(0),
1423d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      damage_event_base_(-1),
1433d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      damage_error_base_(-1),
1443d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      damage_region_(0) {
1453d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  helper_.SetLogGridSize(4);
1463d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
1473d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1483d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgScreenCapturerLinux::~ScreenCapturerLinux() {
1493d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DeinitXlib();
1503d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
1513d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1523d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgbool ScreenCapturerLinux::Init(bool use_x_damage) {
1533d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // TODO(ajwong): We should specify the display string we are attaching to
1543d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // in the constructor.
1553d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  display_ = XOpenDisplay(NULL);
1563d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (!display_) {
1573d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    LOG(LS_ERROR) << "Unable to open display";
1583d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    return false;
1593d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
1603d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1613d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  root_window_ = RootWindow(display_, DefaultScreen(display_));
1623d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (root_window_ == BadValue) {
1633d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    LOG(LS_ERROR) << "Unable to get the root window";
1643d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    DeinitXlib();
1653d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    return false;
1663d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
1673d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1683d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  gc_ = XCreateGC(display_, root_window_, 0, NULL);
1693d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (gc_ == NULL) {
1703d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    LOG(LS_ERROR) << "Unable to get graphics context";
1713d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    DeinitXlib();
1723d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    return false;
1733d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
1743d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1753d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Check for XFixes extension. This is required for cursor shape
1763d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // notifications, and for our use of XDamage.
1773d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (XFixesQueryExtension(display_, &xfixes_event_base_,
1783d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org                           &xfixes_error_base_)) {
1793d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    has_xfixes_ = true;
1803d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  } else {
1813d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    LOG(LS_INFO) << "X server does not support XFixes.";
1823d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
1833d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1843d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Register for changes to the dimensions of the root window.
1853d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  XSelectInput(display_, root_window_, StructureNotifyMask);
1863d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1879f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  if (!x_server_pixel_buffer_.Init(display_, DefaultRootWindow(display_))) {
1889f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org    LOG(LS_ERROR) << "Failed to initialize pixel buffer.";
1899f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org    return false;
1909f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  }
1913d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1923d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (has_xfixes_) {
1933d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    // Register for changes to the cursor shape.
1943d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    XFixesSelectCursorInput(display_, root_window_,
1953d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org                            XFixesDisplayCursorNotifyMask);
1963d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
1973d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
1983d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (use_x_damage) {
1993d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    InitXDamage();
2003d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
2013d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2023d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  return true;
2033d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
2043d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2053d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgvoid ScreenCapturerLinux::InitXDamage() {
2063d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Our use of XDamage requires XFixes.
2073d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (!has_xfixes_) {
2083d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    return;
2093d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
2103d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2113d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Check for XDamage extension.
2123d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (!XDamageQueryExtension(display_, &damage_event_base_,
2133d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org                             &damage_error_base_)) {
2143d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    LOG(LS_INFO) << "X server does not support XDamage.";
2153d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    return;
2163d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
2173d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2183d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // TODO(lambroslambrou): Disable DAMAGE in situations where it is known
2193d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // to fail, such as when Desktop Effects are enabled, with graphics
2203d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // drivers (nVidia, ATI) that fail to report DAMAGE notifications
2213d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // properly.
2223d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2233d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Request notifications every time the screen becomes damaged.
2243d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  damage_handle_ = XDamageCreate(display_, root_window_,
2253d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org                                 XDamageReportNonEmpty);
2263d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (!damage_handle_) {
2273d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    LOG(LS_ERROR) << "Unable to initialize XDamage.";
2283d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    return;
2293d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
2303d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2313d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Create an XFixes server-side region to collate damage into.
2323d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  damage_region_ = XFixesCreateRegion(display_, 0, 0);
2333d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (!damage_region_) {
2343d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    XDamageDestroy(display_, damage_handle_);
2353d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    LOG(LS_ERROR) << "Unable to create XFixes region.";
2363d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    return;
2373d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
2383d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2393d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  use_damage_ = true;
2403d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  LOG(LS_INFO) << "Using XDamage extension.";
2413d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
2423d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2433d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgvoid ScreenCapturerLinux::Start(Callback* callback) {
2443d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DCHECK(!callback_);
2453d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DCHECK(callback);
2463d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2473d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  callback_ = callback;
2483d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
2493d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2503d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgvoid ScreenCapturerLinux::Capture(const DesktopRegion& region) {
2513d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  TickTime capture_start_time = TickTime::Now();
2523d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2533d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  queue_.MoveToNextFrame();
2543d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2553d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Process XEvents for XDamage and cursor shape tracking.
2563d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  ProcessPendingXEvents();
2573d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2589f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  // ProcessPendingXEvents() may call ScreenConfigurationChanged() which
2599f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  // reinitializes |x_server_pixel_buffer_|. Check if the pixel buffer is still
2609f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  // in a good shape.
2619f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  if (!x_server_pixel_buffer_.is_initialized()) {
2629f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org     // We failed to initialize pixel buffer.
2639f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org     callback_->OnCaptureCompleted(NULL);
2649f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org     return;
2659f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  }
2669f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org
2673d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // If the current frame is from an older generation then allocate a new one.
2683d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Note that we can't reallocate other buffers at this point, since the caller
2693d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // may still be reading from them.
2703d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (!queue_.current_frame()) {
2713d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    scoped_ptr<DesktopFrame> frame(
2729f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org        new BasicDesktopFrame(x_server_pixel_buffer_.window_size()));
2733d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    queue_.ReplaceCurrentFrame(frame.release());
2743d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
2753d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2763d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Refresh the Differ helper used by CaptureFrame(), if needed.
2773d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DesktopFrame* frame = queue_.current_frame();
2783d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (!use_damage_ && (
2793d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      !differ_.get() ||
2803d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      (differ_->width() != frame->size().width()) ||
2813d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      (differ_->height() != frame->size().height()) ||
2823d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      (differ_->bytes_per_row() != frame->stride()))) {
2833d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    differ_.reset(new Differ(frame->size().width(), frame->size().height(),
2843d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org                             DesktopFrame::kBytesPerPixel,
2853d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org                             frame->stride()));
2863d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
2873d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2883d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DesktopFrame* result = CaptureScreen();
2893d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  last_invalid_region_ = result->updated_region();
2903d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  result->set_capture_time_ms(
2913d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      (TickTime::Now() - capture_start_time).Milliseconds());
2923d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  callback_->OnCaptureCompleted(result);
2933d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
2943d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
2953d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgvoid ScreenCapturerLinux::SetMouseShapeObserver(
2963d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      MouseShapeObserver* mouse_shape_observer) {
2973d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DCHECK(!mouse_shape_observer_);
2983d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DCHECK(mouse_shape_observer);
2993d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3003d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  mouse_shape_observer_ = mouse_shape_observer;
3013d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
3023d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3033d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgvoid ScreenCapturerLinux::ProcessPendingXEvents() {
3043d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Find the number of events that are outstanding "now."  We don't just loop
3053d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // on XPending because we want to guarantee this terminates.
3063d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  int events_to_process = XPending(display_);
3073d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  XEvent e;
3083d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3093d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  for (int i = 0; i < events_to_process; i++) {
3103d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    XNextEvent(display_, &e);
3113d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    if (use_damage_ && (e.type == damage_event_base_ + XDamageNotify)) {
3123d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      XDamageNotifyEvent* event = reinterpret_cast<XDamageNotifyEvent*>(&e);
3133d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      DCHECK(event->level == XDamageReportNonEmpty);
3143d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    } else if (e.type == ConfigureNotify) {
3159f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org      ScreenConfigurationChanged();
3163d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    } else if (has_xfixes_ &&
3173d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org               e.type == xfixes_event_base_ + XFixesCursorNotify) {
3183d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      XFixesCursorNotifyEvent* cne;
3193d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      cne = reinterpret_cast<XFixesCursorNotifyEvent*>(&e);
3203d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      if (cne->subtype == XFixesDisplayCursorNotify) {
3213d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org        CaptureCursor();
3223d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      }
3233d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    } else {
3243d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      LOG(LS_WARNING) << "Got unknown event type: " << e.type;
3253d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    }
3263d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
3273d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
3283d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3293d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgvoid ScreenCapturerLinux::CaptureCursor() {
3303d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DCHECK(has_xfixes_);
3313d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3323d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  XFixesCursorImage* img = XFixesGetCursorImage(display_);
3333d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (!img) {
3343d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    return;
3353d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
3363d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3373d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  scoped_ptr<MouseCursorShape> cursor(new MouseCursorShape());
3383d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  cursor->size = DesktopSize(img->width, img->height);
3393d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  cursor->hotspot = DesktopVector(img->xhot, img->yhot);
3403d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3413d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  int total_bytes = cursor->size.width ()* cursor->size.height() *
3423d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      DesktopFrame::kBytesPerPixel;
3433d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  cursor->data.resize(total_bytes);
3443d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3453d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Xlib stores 32-bit data in longs, even if longs are 64-bits long.
3463d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  unsigned long* src = img->pixels;
3473d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  uint32_t* dst = reinterpret_cast<uint32_t*>(&*(cursor->data.begin()));
3483d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  uint32_t* dst_end = dst + (img->width * img->height);
3493d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  while (dst < dst_end) {
3503d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    *dst++ = static_cast<uint32_t>(*src++);
3513d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
3523d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  XFree(img);
3533d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3543d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (mouse_shape_observer_)
3553d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    mouse_shape_observer_->OnCursorShapeChanged(cursor.release());
3563d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
3573d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3583d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgDesktopFrame* ScreenCapturerLinux::CaptureScreen() {
3593d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DesktopFrame* frame = queue_.current_frame()->Share();
3609f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  assert(x_server_pixel_buffer_.window_size().equals(frame->size()));
3613d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3623d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Pass the screen size to the helper, so it can clip the invalid region if it
3633d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // expands that region to a grid.
3643d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  helper_.set_size_most_recent(frame->size());
3653d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3663d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // In the DAMAGE case, ensure the frame is up-to-date with the previous frame
3673d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // if any.  If there isn't a previous frame, that means a screen-resolution
3683d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // change occurred, and |invalid_rects| will be updated to include the whole
3693d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // screen.
3703d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (use_damage_ && queue_.previous_frame())
3713d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    SynchronizeFrame();
3723d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3733d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DesktopRegion* updated_region = frame->mutable_updated_region();
3743d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3753d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  x_server_pixel_buffer_.Synchronize();
3763d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (use_damage_ && queue_.previous_frame()) {
3773d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    // Atomically fetch and clear the damage region.
3783d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    XDamageSubtract(display_, damage_handle_, None, damage_region_);
3793d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    int rects_num = 0;
3803d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    XRectangle bounds;
3813d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    XRectangle* rects = XFixesFetchRegionAndBounds(display_, damage_region_,
3823d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org                                                   &rects_num, &bounds);
3833d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    for (int i = 0; i < rects_num; ++i) {
3843d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      updated_region->AddRect(DesktopRect::MakeXYWH(
3853d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org          rects[i].x, rects[i].y, rects[i].width, rects[i].height));
3863d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    }
3873d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    XFree(rects);
3883d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    helper_.InvalidateRegion(*updated_region);
3893d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3903d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    // Capture the damaged portions of the desktop.
3913d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    helper_.TakeInvalidRegion(updated_region);
3923d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3933d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    // Clip the damaged portions to the current screen size, just in case some
3943d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    // spurious XDamage notifications were received for a previous (larger)
3953d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    // screen size.
3963d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    updated_region->IntersectWith(
3979f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org        DesktopRect::MakeSize(x_server_pixel_buffer_.window_size()));
3983d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
3993d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    for (DesktopRegion::Iterator it(*updated_region);
4003d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org         !it.IsAtEnd(); it.Advance()) {
4019f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org      x_server_pixel_buffer_.CaptureRect(it.rect(), frame);
4023d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    }
4033d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  } else {
4043d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    // Doing full-screen polling, or this is the first capture after a
4053d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    // screen-resolution change.  In either case, need a full-screen capture.
4069f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org    DesktopRect screen_rect = DesktopRect::MakeSize(frame->size());
4079f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org    x_server_pixel_buffer_.CaptureRect(screen_rect, frame);
4083d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4093d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    if (queue_.previous_frame()) {
4103d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      // Full-screen polling, so calculate the invalid rects here, based on the
4113d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      // changed pixels between current and previous buffers.
4123d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      DCHECK(differ_.get() != NULL);
4133d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      DCHECK(queue_.previous_frame()->data());
4143d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      differ_->CalcDirtyRegion(queue_.previous_frame()->data(),
4153d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org                               frame->data(), updated_region);
4163d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    } else {
4173d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      // No previous buffer, so always invalidate the whole screen, whether
4183d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      // or not DAMAGE is being used.  DAMAGE doesn't necessarily send a
4193d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      // full-screen notification after a screen-resolution change, so
4203d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      // this is done here.
4213d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      updated_region->SetRect(screen_rect);
4223d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    }
4233d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
4243d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4253d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  return frame;
4263d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
4273d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4289f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.orgvoid ScreenCapturerLinux::ScreenConfigurationChanged() {
4293d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Make sure the frame buffers will be reallocated.
4303d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  queue_.Reset();
4313d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4323d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  helper_.ClearInvalidRegion();
4339f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  if (!x_server_pixel_buffer_.Init(display_, DefaultRootWindow(display_))) {
4349f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org    LOG(LS_ERROR) << "Failed to initialize pixel buffer after screen "
4359f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org        "configuration change.";
4369f282403f258162ca53eb2f16b8e9a26e7970096sergeyu@chromium.org  }
4373d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
4383d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4393d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgvoid ScreenCapturerLinux::SynchronizeFrame() {
4403d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // Synchronize the current buffer with the previous one since we do not
4413d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // capture the entire desktop. Note that encoder may be reading from the
4423d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // previous buffer at this time so thread access complaints are false
4433d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // positives.
4443d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4453d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // TODO(hclam): We can reduce the amount of copying here by subtracting
4463d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // |capturer_helper_|s region from |last_invalid_region_|.
4473d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  // http://crbug.com/92354
4483d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DCHECK(queue_.previous_frame());
4493d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4503d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DesktopFrame* current = queue_.current_frame();
4513d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DesktopFrame* last = queue_.previous_frame();
4523d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  DCHECK(current != last);
4533d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  for (DesktopRegion::Iterator it(last_invalid_region_);
4543d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org       !it.IsAtEnd(); it.Advance()) {
4553d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    const DesktopRect& r = it.rect();
4563d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    int offset = r.top() * current->stride() +
4573d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org        r.left() * DesktopFrame::kBytesPerPixel;
4583d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    for (int i = 0; i < r.height(); ++i) {
4593d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      memcpy(current->data() + offset, last->data() + offset,
4603d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org             r.width() * DesktopFrame::kBytesPerPixel);
4613d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      offset += current->size().width() * DesktopFrame::kBytesPerPixel;
4623d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    }
4633d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
4643d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
4653d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4663d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgvoid ScreenCapturerLinux::DeinitXlib() {
4673d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (gc_) {
4683d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    XFreeGC(display_, gc_);
4693d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    gc_ = NULL;
4703d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
4713d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4723d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  x_server_pixel_buffer_.Release();
4733d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4743d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (display_) {
4753d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    if (damage_handle_)
4763d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      XDamageDestroy(display_, damage_handle_);
4773d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    if (damage_region_)
4783d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org      XFixesDestroyRegion(display_, damage_region_);
4793d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    XCloseDisplay(display_);
4803d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    display_ = NULL;
4813d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    damage_handle_ = 0;
4823d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    damage_region_ = 0;
4833d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  }
4843d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
4853d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4863d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}  // namespace
4873d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4883d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org// static
4893d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgScreenCapturer* ScreenCapturer::Create() {
4903d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  scoped_ptr<ScreenCapturerLinux> capturer(new ScreenCapturerLinux());
4913d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (!capturer->Init(false))
4923d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    capturer.reset();
4933d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  return capturer.release();
4943d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
4953d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
4963d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org// static
4973d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.orgScreenCapturer* ScreenCapturer::CreateWithXDamage(bool use_x_damage) {
4983d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  scoped_ptr<ScreenCapturerLinux> capturer(new ScreenCapturerLinux());
4993d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  if (!capturer->Init(use_x_damage))
5003d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org    capturer.reset();
5013d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org  return capturer.release();
5023d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}
5033d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org
5043d34f66292ad4d3950d94026d08c3659880d30e2sergeyu@chromium.org}  // namespace webrtc
505