summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/desktop_capture/linux/x11
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc258
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.h68
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.cc517
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.h147
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.cc112
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.h88
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.cc257
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.h78
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.cc52
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.h35
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.cc198
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.h56
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.cc51
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.h45
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.cc70
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.h51
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.cc370
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.h89
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.cc43
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.h63
20 files changed, 2648 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc
new file mode 100644
index 0000000000..d4b85af6bd
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.h"
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xfixes.h>
+#include <X11/extensions/xfixeswire.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/linux/x11/x_error_trap.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "modules/desktop_capture/mouse_cursor_monitor.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace {
+
+// WindowCapturer returns window IDs of X11 windows with WM_STATE attribute.
+// These windows may not be immediate children of the root window, because
+// window managers may re-parent them to add decorations. However,
+// XQueryPointer() expects to be passed children of the root. This function
+// searches up the list of the windows to find the root child that corresponds
+// to `window`.
+Window GetTopLevelWindow(Display* display, Window window) {
+ webrtc::XErrorTrap error_trap(display);
+ while (true) {
+ // If the window is in WithdrawnState then look at all of its children.
+ ::Window root, parent;
+ ::Window* children;
+ unsigned int num_children;
+ if (!XQueryTree(display, window, &root, &parent, &children,
+ &num_children)) {
+ RTC_LOG(LS_ERROR) << "Failed to query for child windows although window"
+ "does not have a valid WM_STATE.";
+ return None;
+ }
+ if (children)
+ XFree(children);
+
+ if (parent == root)
+ break;
+
+ window = parent;
+ }
+
+ return window;
+}
+
+} // namespace
+
+namespace webrtc {
+
+MouseCursorMonitorX11::MouseCursorMonitorX11(
+ const DesktopCaptureOptions& options,
+ Window window)
+ : x_display_(options.x_display()),
+ callback_(NULL),
+ mode_(SHAPE_AND_POSITION),
+ window_(window),
+ have_xfixes_(false),
+ xfixes_event_base_(-1),
+ xfixes_error_base_(-1) {
+ // Set a default initial cursor shape in case XFixes is not present.
+ const int kSize = 5;
+ std::unique_ptr<DesktopFrame> default_cursor(
+ new BasicDesktopFrame(DesktopSize(kSize, kSize)));
+ const uint8_t pixels[kSize * kSize] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ uint8_t* ptr = default_cursor->data();
+ for (int y = 0; y < kSize; ++y) {
+ for (int x = 0; x < kSize; ++x) {
+ *ptr++ = pixels[kSize * y + x];
+ *ptr++ = pixels[kSize * y + x];
+ *ptr++ = pixels[kSize * y + x];
+ *ptr++ = 0xff;
+ }
+ }
+ DesktopVector hotspot(2, 2);
+ cursor_shape_.reset(new MouseCursor(default_cursor.release(), hotspot));
+}
+
+MouseCursorMonitorX11::~MouseCursorMonitorX11() {
+ if (have_xfixes_) {
+ x_display_->RemoveEventHandler(xfixes_event_base_ + XFixesCursorNotify,
+ this);
+ }
+}
+
+void MouseCursorMonitorX11::Init(Callback* callback, Mode mode) {
+ // Init can be called only if not started
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+
+ callback_ = callback;
+ mode_ = mode;
+
+ have_xfixes_ =
+ XFixesQueryExtension(display(), &xfixes_event_base_, &xfixes_error_base_);
+
+ if (have_xfixes_) {
+ // Register for changes to the cursor shape.
+ XErrorTrap error_trap(display());
+ XFixesSelectCursorInput(display(), window_, XFixesDisplayCursorNotifyMask);
+ x_display_->AddEventHandler(xfixes_event_base_ + XFixesCursorNotify, this);
+
+ CaptureCursor();
+ } else {
+ RTC_LOG(LS_INFO) << "X server does not support XFixes.";
+ }
+}
+
+void MouseCursorMonitorX11::Capture() {
+ RTC_DCHECK(callback_);
+
+ // Process X11 events in case XFixes has sent cursor notification.
+ x_display_->ProcessPendingXEvents();
+
+ // cursor_shape_| is set only if we were notified of a cursor shape change.
+ if (cursor_shape_.get())
+ callback_->OnMouseCursor(cursor_shape_.release());
+
+ // Get cursor position if necessary.
+ if (mode_ == SHAPE_AND_POSITION) {
+ int root_x;
+ int root_y;
+ int win_x;
+ int win_y;
+ Window root_window;
+ Window child_window;
+ unsigned int mask;
+
+ CursorState state;
+ {
+ XErrorTrap error_trap(display());
+ Bool result =
+ XQueryPointer(display(), window_, &root_window, &child_window,
+ &root_x, &root_y, &win_x, &win_y, &mask);
+ if (!result || error_trap.GetLastErrorAndDisable() != 0) {
+ state = OUTSIDE;
+ } else {
+ // In screen mode (window_ == root_window) the mouse is always inside.
+ // XQueryPointer() sets `child_window` to None if the cursor is outside
+ // `window_`.
+ state =
+ (window_ == root_window || child_window != None) ? INSIDE : OUTSIDE;
+ }
+ }
+
+ // As the comments to GetTopLevelWindow() above indicate, in window capture,
+ // the cursor position capture happens in `window_`, while the frame catpure
+ // happens in `child_window`. These two windows are not alwyas same, as
+ // window manager may add some decorations to the `window_`. So translate
+ // the coordinate in `window_` to the coordinate space of `child_window`.
+ if (window_ != root_window && state == INSIDE) {
+ int translated_x, translated_y;
+ Window unused;
+ if (XTranslateCoordinates(display(), window_, child_window, win_x, win_y,
+ &translated_x, &translated_y, &unused)) {
+ win_x = translated_x;
+ win_y = translated_y;
+ }
+ }
+
+ // X11 always starts the coordinate from (0, 0), so we do not need to
+ // translate here.
+ callback_->OnMouseCursorPosition(DesktopVector(root_x, root_y));
+ }
+}
+
+bool MouseCursorMonitorX11::HandleXEvent(const XEvent& event) {
+ if (have_xfixes_ && event.type == xfixes_event_base_ + XFixesCursorNotify) {
+ const XFixesCursorNotifyEvent* cursor_event =
+ reinterpret_cast<const XFixesCursorNotifyEvent*>(&event);
+ if (cursor_event->subtype == XFixesDisplayCursorNotify) {
+ CaptureCursor();
+ }
+ // Return false, even if the event has been handled, because there might be
+ // other listeners for cursor notifications.
+ }
+ return false;
+}
+
+void MouseCursorMonitorX11::CaptureCursor() {
+ RTC_DCHECK(have_xfixes_);
+
+ XFixesCursorImage* img;
+ {
+ XErrorTrap error_trap(display());
+ img = XFixesGetCursorImage(display());
+ if (!img || error_trap.GetLastErrorAndDisable() != 0)
+ return;
+ }
+
+ std::unique_ptr<DesktopFrame> image(
+ new BasicDesktopFrame(DesktopSize(img->width, img->height)));
+
+ // Xlib stores 32-bit data in longs, even if longs are 64-bits long.
+ unsigned long* src = img->pixels; // NOLINT(runtime/int)
+ uint32_t* dst = reinterpret_cast<uint32_t*>(image->data());
+ uint32_t* dst_end = dst + (img->width * img->height);
+ while (dst < dst_end) {
+ *dst++ = static_cast<uint32_t>(*src++);
+ }
+
+ DesktopVector hotspot(std::min(img->width, img->xhot),
+ std::min(img->height, img->yhot));
+
+ XFree(img);
+
+ cursor_shape_.reset(new MouseCursor(image.release(), hotspot));
+}
+
+// static
+MouseCursorMonitor* MouseCursorMonitorX11::CreateForWindow(
+ const DesktopCaptureOptions& options,
+ WindowId window) {
+ if (!options.x_display())
+ return NULL;
+ window = GetTopLevelWindow(options.x_display()->display(), window);
+ if (window == None)
+ return NULL;
+ return new MouseCursorMonitorX11(options, window);
+}
+
+MouseCursorMonitor* MouseCursorMonitorX11::CreateForScreen(
+ const DesktopCaptureOptions& options,
+ ScreenId screen) {
+ if (!options.x_display())
+ return NULL;
+ return new MouseCursorMonitorX11(
+ options, DefaultRootWindow(options.x_display()->display()));
+}
+
+std::unique_ptr<MouseCursorMonitor> MouseCursorMonitorX11::Create(
+ const DesktopCaptureOptions& options) {
+ return std::unique_ptr<MouseCursorMonitor>(
+ CreateForScreen(options, kFullDesktopScreenId));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.h
new file mode 100644
index 0000000000..980d254a0a
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_MOUSE_CURSOR_MONITOR_X11_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_MOUSE_CURSOR_MONITOR_X11_H_
+
+#include <X11/X.h>
+
+#include <memory>
+
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/linux/x11/shared_x_display.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "modules/desktop_capture/mouse_cursor_monitor.h"
+
+namespace webrtc {
+
+class MouseCursorMonitorX11 : public MouseCursorMonitor,
+ public SharedXDisplay::XEventHandler {
+ public:
+ MouseCursorMonitorX11(const DesktopCaptureOptions& options, Window window);
+ ~MouseCursorMonitorX11() override;
+
+ static MouseCursorMonitor* CreateForWindow(
+ const DesktopCaptureOptions& options,
+ WindowId window);
+ static MouseCursorMonitor* CreateForScreen(
+ const DesktopCaptureOptions& options,
+ ScreenId screen);
+ static std::unique_ptr<MouseCursorMonitor> Create(
+ const DesktopCaptureOptions& options);
+
+ void Init(Callback* callback, Mode mode) override;
+ void Capture() override;
+
+ private:
+ // SharedXDisplay::XEventHandler interface.
+ bool HandleXEvent(const XEvent& event) override;
+
+ Display* display() { return x_display_->display(); }
+
+ // Captures current cursor shape and stores it in `cursor_shape_`.
+ void CaptureCursor();
+
+ rtc::scoped_refptr<SharedXDisplay> x_display_;
+ Callback* callback_;
+ Mode mode_;
+ Window window_;
+
+ bool have_xfixes_;
+ int xfixes_event_base_;
+ int xfixes_error_base_;
+
+ std::unique_ptr<MouseCursor> cursor_shape_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_MOUSE_CURSOR_MONITOR_X11_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.cc
new file mode 100644
index 0000000000..fa6334e8ba
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.cc
@@ -0,0 +1,517 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/screen_capturer_x11.h"
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xdamage.h>
+#include <X11/extensions/Xfixes.h>
+#include <X11/extensions/damagewire.h>
+#include <dlfcn.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <memory>
+#include <utility>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/linux/x11/x_server_pixel_buffer.h"
+#include "modules/desktop_capture/screen_capture_frame_queue.h"
+#include "modules/desktop_capture/screen_capturer_helper.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/sanitizer.h"
+#include "rtc_base/time_utils.h"
+#include "rtc_base/trace_event.h"
+
+namespace webrtc {
+
+ScreenCapturerX11::ScreenCapturerX11() {
+ helper_.SetLogGridSize(4);
+}
+
+ScreenCapturerX11::~ScreenCapturerX11() {
+ options_.x_display()->RemoveEventHandler(ConfigureNotify, this);
+ if (use_damage_) {
+ options_.x_display()->RemoveEventHandler(damage_event_base_ + XDamageNotify,
+ this);
+ }
+ if (use_randr_) {
+ options_.x_display()->RemoveEventHandler(
+ randr_event_base_ + RRScreenChangeNotify, this);
+ }
+ DeinitXlib();
+}
+
+bool ScreenCapturerX11::Init(const DesktopCaptureOptions& options) {
+ TRACE_EVENT0("webrtc", "ScreenCapturerX11::Init");
+ options_ = options;
+
+ atom_cache_ = std::make_unique<XAtomCache>(display());
+
+ root_window_ = RootWindow(display(), DefaultScreen(display()));
+ if (root_window_ == BadValue) {
+ RTC_LOG(LS_ERROR) << "Unable to get the root window";
+ DeinitXlib();
+ return false;
+ }
+
+ gc_ = XCreateGC(display(), root_window_, 0, NULL);
+ if (gc_ == NULL) {
+ RTC_LOG(LS_ERROR) << "Unable to get graphics context";
+ DeinitXlib();
+ return false;
+ }
+
+ options_.x_display()->AddEventHandler(ConfigureNotify, this);
+
+ // Check for XFixes extension. This is required for cursor shape
+ // notifications, and for our use of XDamage.
+ if (XFixesQueryExtension(display(), &xfixes_event_base_,
+ &xfixes_error_base_)) {
+ has_xfixes_ = true;
+ } else {
+ RTC_LOG(LS_INFO) << "X server does not support XFixes.";
+ }
+
+ // Register for changes to the dimensions of the root window.
+ XSelectInput(display(), root_window_, StructureNotifyMask);
+
+ if (!x_server_pixel_buffer_.Init(atom_cache_.get(),
+ DefaultRootWindow(display()))) {
+ RTC_LOG(LS_ERROR) << "Failed to initialize pixel buffer.";
+ return false;
+ }
+
+ if (options_.use_update_notifications()) {
+ InitXDamage();
+ }
+
+ InitXrandr();
+
+ // Default source set here so that selected_monitor_rect_ is sized correctly.
+ SelectSource(kFullDesktopScreenId);
+
+ return true;
+}
+
+void ScreenCapturerX11::InitXDamage() {
+ // Our use of XDamage requires XFixes.
+ if (!has_xfixes_) {
+ return;
+ }
+
+ // Check for XDamage extension.
+ if (!XDamageQueryExtension(display(), &damage_event_base_,
+ &damage_error_base_)) {
+ RTC_LOG(LS_INFO) << "X server does not support XDamage.";
+ return;
+ }
+
+ // TODO(lambroslambrou): Disable DAMAGE in situations where it is known
+ // to fail, such as when Desktop Effects are enabled, with graphics
+ // drivers (nVidia, ATI) that fail to report DAMAGE notifications
+ // properly.
+
+ // Request notifications every time the screen becomes damaged.
+ damage_handle_ =
+ XDamageCreate(display(), root_window_, XDamageReportNonEmpty);
+ if (!damage_handle_) {
+ RTC_LOG(LS_ERROR) << "Unable to initialize XDamage.";
+ return;
+ }
+
+ // Create an XFixes server-side region to collate damage into.
+ damage_region_ = XFixesCreateRegion(display(), 0, 0);
+ if (!damage_region_) {
+ XDamageDestroy(display(), damage_handle_);
+ RTC_LOG(LS_ERROR) << "Unable to create XFixes region.";
+ return;
+ }
+
+ options_.x_display()->AddEventHandler(damage_event_base_ + XDamageNotify,
+ this);
+
+ use_damage_ = true;
+ RTC_LOG(LS_INFO) << "Using XDamage extension.";
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+void ScreenCapturerX11::InitXrandr() {
+ int major_version = 0;
+ int minor_version = 0;
+ int error_base_ignored = 0;
+ if (XRRQueryExtension(display(), &randr_event_base_, &error_base_ignored) &&
+ XRRQueryVersion(display(), &major_version, &minor_version)) {
+ if (major_version > 1 || (major_version == 1 && minor_version >= 5)) {
+ // Dynamically link XRRGetMonitors and XRRFreeMonitors as a workaround
+ // to avoid a dependency issue with Debian 8.
+ get_monitors_ = reinterpret_cast<get_monitors_func>(
+ dlsym(RTLD_DEFAULT, "XRRGetMonitors"));
+ free_monitors_ = reinterpret_cast<free_monitors_func>(
+ dlsym(RTLD_DEFAULT, "XRRFreeMonitors"));
+ if (get_monitors_ && free_monitors_) {
+ use_randr_ = true;
+ RTC_LOG(LS_INFO) << "Using XRandR extension v" << major_version << '.'
+ << minor_version << '.';
+ monitors_ =
+ get_monitors_(display(), root_window_, true, &num_monitors_);
+
+ // Register for screen change notifications
+ XRRSelectInput(display(), root_window_, RRScreenChangeNotifyMask);
+ options_.x_display()->AddEventHandler(
+ randr_event_base_ + RRScreenChangeNotify, this);
+ } else {
+ RTC_LOG(LS_ERROR) << "Unable to link XRandR monitor functions.";
+ }
+ } else {
+ RTC_LOG(LS_ERROR) << "XRandR entension is older than v1.5.";
+ }
+ } else {
+ RTC_LOG(LS_ERROR) << "X server does not support XRandR.";
+ }
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+void ScreenCapturerX11::UpdateMonitors() {
+ // The queue should be reset whenever |selected_monitor_rect_| changes, so
+ // that the DCHECKs in CaptureScreen() are satisfied.
+ queue_.Reset();
+
+ if (monitors_) {
+ free_monitors_(monitors_);
+ monitors_ = nullptr;
+ }
+
+ monitors_ = get_monitors_(display(), root_window_, true, &num_monitors_);
+
+ if (selected_monitor_name_) {
+ if (selected_monitor_name_ == static_cast<Atom>(kFullDesktopScreenId)) {
+ selected_monitor_rect_ =
+ DesktopRect::MakeSize(x_server_pixel_buffer_.window_size());
+ return;
+ }
+
+ for (int i = 0; i < num_monitors_; ++i) {
+ XRRMonitorInfo& m = monitors_[i];
+ if (selected_monitor_name_ == m.name) {
+ RTC_LOG(LS_INFO) << "XRandR monitor " << m.name << " rect updated.";
+ selected_monitor_rect_ =
+ DesktopRect::MakeXYWH(m.x, m.y, m.width, m.height);
+ const auto& pixel_buffer_rect = x_server_pixel_buffer_.window_rect();
+ if (!pixel_buffer_rect.ContainsRect(selected_monitor_rect_)) {
+ // This is never expected to happen, but crop the rectangle anyway
+ // just in case the server returns inconsistent information.
+ // CaptureScreen() expects `selected_monitor_rect_` to lie within
+ // the pixel-buffer's rectangle.
+ RTC_LOG(LS_WARNING)
+ << "Cropping selected monitor rect to fit the pixel-buffer.";
+ selected_monitor_rect_.IntersectWith(pixel_buffer_rect);
+ }
+ return;
+ }
+ }
+
+ // The selected monitor is not connected anymore
+ RTC_LOG(LS_INFO) << "XRandR selected monitor " << selected_monitor_name_
+ << " lost.";
+ selected_monitor_rect_ = DesktopRect::MakeWH(0, 0);
+ }
+}
+
+void ScreenCapturerX11::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+
+ callback_ = callback;
+}
+
+void ScreenCapturerX11::CaptureFrame() {
+ TRACE_EVENT0("webrtc", "ScreenCapturerX11::CaptureFrame");
+ int64_t capture_start_time_nanos = rtc::TimeNanos();
+
+ queue_.MoveToNextFrame();
+ if (queue_.current_frame() && queue_.current_frame()->IsShared()) {
+ RTC_DLOG(LS_WARNING) << "Overwriting frame that is still shared.";
+ }
+
+ // Process XEvents for XDamage and cursor shape tracking.
+ options_.x_display()->ProcessPendingXEvents();
+
+ // ProcessPendingXEvents() may call ScreenConfigurationChanged() which
+ // reinitializes `x_server_pixel_buffer_`. Check if the pixel buffer is still
+ // in a good shape.
+ if (!x_server_pixel_buffer_.is_initialized()) {
+ // We failed to initialize pixel buffer.
+ RTC_LOG(LS_ERROR) << "Pixel buffer is not initialized.";
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+
+ // Allocate the current frame buffer only if it is not already allocated.
+ // Note that we can't reallocate other buffers at this point, since the caller
+ // may still be reading from them.
+ if (!queue_.current_frame()) {
+ std::unique_ptr<DesktopFrame> frame(
+ new BasicDesktopFrame(selected_monitor_rect_.size()));
+
+ // We set the top-left of the frame so the mouse cursor will be composited
+ // properly, and our frame buffer will not be overrun while blitting.
+ frame->set_top_left(selected_monitor_rect_.top_left());
+ queue_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(std::move(frame)));
+ }
+
+ std::unique_ptr<DesktopFrame> result = CaptureScreen();
+ if (!result) {
+ RTC_LOG(LS_WARNING) << "Temporarily failed to capture screen.";
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+ return;
+ }
+
+ last_invalid_region_ = result->updated_region();
+ result->set_capture_time_ms((rtc::TimeNanos() - capture_start_time_nanos) /
+ rtc::kNumNanosecsPerMillisec);
+ result->set_capturer_id(DesktopCapturerId::kX11CapturerLinux);
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(result));
+}
+
+bool ScreenCapturerX11::GetSourceList(SourceList* sources) {
+ RTC_DCHECK(sources->size() == 0);
+ if (!use_randr_) {
+ sources->push_back({});
+ return true;
+ }
+
+ // Ensure that `monitors_` is updated with changes that may have happened
+ // between calls to GetSourceList().
+ options_.x_display()->ProcessPendingXEvents();
+
+ for (int i = 0; i < num_monitors_; ++i) {
+ XRRMonitorInfo& m = monitors_[i];
+ char* monitor_title = XGetAtomName(display(), m.name);
+
+ // Note name is an X11 Atom used to id the monitor.
+ sources->push_back({static_cast<SourceId>(m.name), 0, monitor_title});
+ XFree(monitor_title);
+ }
+
+ return true;
+}
+
+bool ScreenCapturerX11::SelectSource(SourceId id) {
+ // Prevent the reuse of any frame buffers allocated for a previously selected
+ // source. This is required to stop crashes, or old data from appearing in
+ // a captured frame, when the new source is sized differently then the source
+ // that was selected at the time a reused frame buffer was created.
+ queue_.Reset();
+
+ if (!use_randr_ || id == kFullDesktopScreenId) {
+ selected_monitor_name_ = kFullDesktopScreenId;
+ selected_monitor_rect_ =
+ DesktopRect::MakeSize(x_server_pixel_buffer_.window_size());
+ return true;
+ }
+
+ for (int i = 0; i < num_monitors_; ++i) {
+ if (id == static_cast<SourceId>(monitors_[i].name)) {
+ RTC_LOG(LS_INFO) << "XRandR selected source: " << id;
+ XRRMonitorInfo& m = monitors_[i];
+ selected_monitor_name_ = m.name;
+ selected_monitor_rect_ =
+ DesktopRect::MakeXYWH(m.x, m.y, m.width, m.height);
+ const auto& pixel_buffer_rect = x_server_pixel_buffer_.window_rect();
+ if (!pixel_buffer_rect.ContainsRect(selected_monitor_rect_)) {
+ RTC_LOG(LS_WARNING)
+ << "Cropping selected monitor rect to fit the pixel-buffer.";
+ selected_monitor_rect_.IntersectWith(pixel_buffer_rect);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ScreenCapturerX11::HandleXEvent(const XEvent& event) {
+ if (use_damage_ && (event.type == damage_event_base_ + XDamageNotify)) {
+ const XDamageNotifyEvent* damage_event =
+ reinterpret_cast<const XDamageNotifyEvent*>(&event);
+ if (damage_event->damage != damage_handle_)
+ return false;
+ RTC_DCHECK(damage_event->level == XDamageReportNonEmpty);
+ return true;
+ } else if (use_randr_ &&
+ event.type == randr_event_base_ + RRScreenChangeNotify) {
+ XRRUpdateConfiguration(const_cast<XEvent*>(&event));
+ UpdateMonitors();
+ RTC_LOG(LS_INFO) << "XRandR screen change event received.";
+ return false;
+ } else if (event.type == ConfigureNotify) {
+ ScreenConfigurationChanged();
+ return false;
+ }
+ return false;
+}
+
+std::unique_ptr<DesktopFrame> ScreenCapturerX11::CaptureScreen() {
+ std::unique_ptr<SharedDesktopFrame> frame = queue_.current_frame()->Share();
+ RTC_DCHECK(selected_monitor_rect_.size().equals(frame->size()));
+ RTC_DCHECK(selected_monitor_rect_.top_left().equals(frame->top_left()));
+
+ // Pass the screen size to the helper, so it can clip the invalid region if it
+ // expands that region to a grid. Note that the helper operates in the
+ // DesktopFrame coordinate system where the top-left pixel is (0, 0), even for
+ // a monitor with non-zero offset relative to `x_server_pixel_buffer_`.
+ helper_.set_size_most_recent(frame->size());
+
+ // In the DAMAGE case, ensure the frame is up-to-date with the previous frame
+ // if any. If there isn't a previous frame, that means a screen-resolution
+ // change occurred, and `invalid_rects` will be updated to include the whole
+ // screen.
+ if (use_damage_ && queue_.previous_frame())
+ SynchronizeFrame();
+
+ DesktopRegion* updated_region = frame->mutable_updated_region();
+
+ x_server_pixel_buffer_.Synchronize();
+ if (use_damage_ && queue_.previous_frame()) {
+ // Atomically fetch and clear the damage region.
+ XDamageSubtract(display(), damage_handle_, None, damage_region_);
+ int rects_num = 0;
+ XRectangle bounds;
+ XRectangle* rects = XFixesFetchRegionAndBounds(display(), damage_region_,
+ &rects_num, &bounds);
+ for (int i = 0; i < rects_num; ++i) {
+ auto damage_rect = DesktopRect::MakeXYWH(rects[i].x, rects[i].y,
+ rects[i].width, rects[i].height);
+
+ // Damage-regions are relative to `x_server_pixel_buffer`, so convert the
+ // region to DesktopFrame coordinates where the top-left is always (0, 0),
+ // before adding to the frame's updated_region. `helper_` also operates in
+ // DesktopFrame coordinates, and it will take care of cropping away any
+ // damage-regions that lie outside the selected monitor.
+ damage_rect.Translate(-frame->top_left());
+ updated_region->AddRect(damage_rect);
+ }
+ XFree(rects);
+ helper_.InvalidateRegion(*updated_region);
+
+ // Capture the damaged portions of the desktop.
+ helper_.TakeInvalidRegion(updated_region);
+
+ for (DesktopRegion::Iterator it(*updated_region); !it.IsAtEnd();
+ it.Advance()) {
+ auto rect = it.rect();
+ rect.Translate(frame->top_left());
+ if (!x_server_pixel_buffer_.CaptureRect(rect, frame.get()))
+ return nullptr;
+ }
+ } else {
+ // Doing full-screen polling, or this is the first capture after a
+ // screen-resolution change. In either case, need a full-screen capture.
+ if (!x_server_pixel_buffer_.CaptureRect(selected_monitor_rect_,
+ frame.get())) {
+ return nullptr;
+ }
+ updated_region->SetRect(DesktopRect::MakeSize(frame->size()));
+ }
+
+ return std::move(frame);
+}
+
+void ScreenCapturerX11::ScreenConfigurationChanged() {
+ TRACE_EVENT0("webrtc", "ScreenCapturerX11::ScreenConfigurationChanged");
+ // Make sure the frame buffers will be reallocated.
+ queue_.Reset();
+
+ helper_.ClearInvalidRegion();
+ if (!x_server_pixel_buffer_.Init(atom_cache_.get(),
+ DefaultRootWindow(display()))) {
+ RTC_LOG(LS_ERROR) << "Failed to initialize pixel buffer after screen "
+ "configuration change.";
+ }
+
+ if (use_randr_) {
+ // Adding/removing RANDR monitors can generate a ConfigureNotify event
+ // without generating any RRScreenChangeNotify event. So it is important to
+ // update the monitors here even if the screen resolution hasn't changed.
+ UpdateMonitors();
+ } else {
+ selected_monitor_rect_ =
+ DesktopRect::MakeSize(x_server_pixel_buffer_.window_size());
+ }
+}
+
+void ScreenCapturerX11::SynchronizeFrame() {
+ // Synchronize the current buffer with the previous one since we do not
+ // capture the entire desktop. Note that encoder may be reading from the
+ // previous buffer at this time so thread access complaints are false
+ // positives.
+
+ // TODO(hclam): We can reduce the amount of copying here by subtracting
+ // `capturer_helper_`s region from `last_invalid_region_`.
+ // http://crbug.com/92354
+ RTC_DCHECK(queue_.previous_frame());
+
+ DesktopFrame* current = queue_.current_frame();
+ DesktopFrame* last = queue_.previous_frame();
+ RTC_DCHECK(current != last);
+ for (DesktopRegion::Iterator it(last_invalid_region_); !it.IsAtEnd();
+ it.Advance()) {
+ const DesktopRect& r = it.rect();
+ current->CopyPixelsFrom(*last, r.top_left(), r);
+ }
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+void ScreenCapturerX11::DeinitXlib() {
+ if (monitors_) {
+ free_monitors_(monitors_);
+ monitors_ = nullptr;
+ }
+
+ if (gc_) {
+ XFreeGC(display(), gc_);
+ gc_ = nullptr;
+ }
+
+ x_server_pixel_buffer_.Release();
+
+ if (display()) {
+ if (damage_handle_) {
+ XDamageDestroy(display(), damage_handle_);
+ damage_handle_ = 0;
+ }
+
+ if (damage_region_) {
+ XFixesDestroyRegion(display(), damage_region_);
+ damage_region_ = 0;
+ }
+ }
+}
+
+// static
+std::unique_ptr<DesktopCapturer> ScreenCapturerX11::CreateRawScreenCapturer(
+ const DesktopCaptureOptions& options) {
+ if (!options.x_display())
+ return nullptr;
+
+ std::unique_ptr<ScreenCapturerX11> capturer(new ScreenCapturerX11());
+ if (!capturer.get()->Init(options)) {
+ return nullptr;
+ }
+
+ return std::move(capturer);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.h
new file mode 100644
index 0000000000..d2a437aaa2
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_SCREEN_CAPTURER_X11_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_SCREEN_CAPTURER_X11_H_
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/Xdamage.h>
+#include <X11/extensions/Xfixes.h>
+#include <X11/extensions/Xrandr.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/linux/x11/shared_x_display.h"
+#include "modules/desktop_capture/linux/x11/x_atom_cache.h"
+#include "modules/desktop_capture/linux/x11/x_server_pixel_buffer.h"
+#include "modules/desktop_capture/screen_capture_frame_queue.h"
+#include "modules/desktop_capture/screen_capturer_helper.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+
+namespace webrtc {
+
+// A class to perform video frame capturing for Linux on X11.
+//
+// If XDamage is used, this class sets DesktopFrame::updated_region() according
+// to the areas reported by XDamage. Otherwise this class does not detect
+// DesktopFrame::updated_region(), the field is always set to the entire frame
+// rectangle. ScreenCapturerDifferWrapper should be used if that functionality
+// is necessary.
+class ScreenCapturerX11 : public DesktopCapturer,
+ public SharedXDisplay::XEventHandler {
+ public:
+ ScreenCapturerX11();
+ ~ScreenCapturerX11() override;
+
+ ScreenCapturerX11(const ScreenCapturerX11&) = delete;
+ ScreenCapturerX11& operator=(const ScreenCapturerX11&) = delete;
+
+ static std::unique_ptr<DesktopCapturer> CreateRawScreenCapturer(
+ const DesktopCaptureOptions& options);
+
+ // TODO(ajwong): Do we really want this to be synchronous?
+ bool Init(const DesktopCaptureOptions& options);
+
+ // DesktopCapturer interface.
+ void Start(Callback* delegate) override;
+ void CaptureFrame() override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+
+ private:
+ Display* display() { return options_.x_display()->display(); }
+
+ // SharedXDisplay::XEventHandler interface.
+ bool HandleXEvent(const XEvent& event) override;
+
+ void InitXDamage();
+ void InitXrandr();
+ void UpdateMonitors();
+
+ // Capture screen pixels to the current buffer in the queue. In the DAMAGE
+ // case, the ScreenCapturerHelper already holds the list of invalid rectangles
+ // from HandleXEvent(). In the non-DAMAGE case, this captures the
+ // whole screen, then calculates some invalid rectangles that include any
+ // differences between this and the previous capture.
+ std::unique_ptr<DesktopFrame> CaptureScreen();
+
+ // Called when the screen configuration is changed.
+ void ScreenConfigurationChanged();
+
+ // Synchronize the current buffer with `last_buffer_`, by copying pixels from
+ // the area of `last_invalid_rects`.
+ // Note this only works on the assumption that kNumBuffers == 2, as
+ // `last_invalid_rects` holds the differences from the previous buffer and
+ // the one prior to that (which will then be the current buffer).
+ void SynchronizeFrame();
+
+ void DeinitXlib();
+
+ DesktopCaptureOptions options_;
+
+ Callback* callback_ = nullptr;
+
+ // X11 graphics context.
+ GC gc_ = nullptr;
+ Window root_window_ = BadValue;
+
+ // XRandR 1.5 monitors.
+ bool use_randr_ = false;
+ int randr_event_base_ = 0;
+ XRRMonitorInfo* monitors_ = nullptr;
+ int num_monitors_ = 0;
+ DesktopRect selected_monitor_rect_;
+ // selected_monitor_name_ will be changed to kFullDesktopScreenId
+ // by a call to SelectSource() at the end of Init() because
+ // selected_monitor_rect_ should be updated as well.
+ // Setting it to kFullDesktopScreenId here might be misleading.
+ Atom selected_monitor_name_ = 0;
+ typedef XRRMonitorInfo* (*get_monitors_func)(Display*, Window, Bool, int*);
+ typedef void (*free_monitors_func)(XRRMonitorInfo*);
+ get_monitors_func get_monitors_ = nullptr;
+ free_monitors_func free_monitors_ = nullptr;
+
+ // XFixes.
+ bool has_xfixes_ = false;
+ int xfixes_event_base_ = -1;
+ int xfixes_error_base_ = -1;
+
+ // XDamage information.
+ bool use_damage_ = false;
+ Damage damage_handle_ = 0;
+ int damage_event_base_ = -1;
+ int damage_error_base_ = -1;
+ XserverRegion damage_region_ = 0;
+
+ // Access to the X Server's pixel buffer.
+ XServerPixelBuffer x_server_pixel_buffer_;
+
+ // A thread-safe list of invalid rectangles, and the size of the most
+ // recently captured screen.
+ ScreenCapturerHelper helper_;
+
+ // Queue of the frames buffers.
+ ScreenCaptureFrameQueue<SharedDesktopFrame> queue_;
+
+ // Invalid region from the previous capture. This is used to synchronize the
+ // current with the last buffer used.
+ DesktopRegion last_invalid_region_;
+
+ std::unique_ptr<XAtomCache> atom_cache_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_SCREEN_CAPTURER_X11_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.cc
new file mode 100644
index 0000000000..3f3617b074
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.cc
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/shared_x_display.h"
+
+#include <X11/Xlib.h>
+#if !defined(WEBRTC_MOZILLA_BUILD)
+#include <X11/extensions/XTest.h>
+#endif
+
+#include <algorithm>
+
+#include "absl/strings/string_view.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+SharedXDisplay::SharedXDisplay(Display* display) : display_(display) {
+ RTC_DCHECK(display_);
+}
+
+SharedXDisplay::~SharedXDisplay() {
+ RTC_DCHECK(event_handlers_.empty());
+ XCloseDisplay(display_);
+}
+
+// static
+rtc::scoped_refptr<SharedXDisplay> SharedXDisplay::Create(
+ absl::string_view display_name) {
+ Display* display = XOpenDisplay(
+ display_name.empty() ? NULL : std::string(display_name).c_str());
+ if (!display) {
+ RTC_LOG(LS_ERROR) << "Unable to open display";
+ return nullptr;
+ }
+ return rtc::scoped_refptr<SharedXDisplay>(new SharedXDisplay(display));
+}
+
+// static
+rtc::scoped_refptr<SharedXDisplay> SharedXDisplay::CreateDefault() {
+ return Create(std::string());
+}
+
+void SharedXDisplay::AddEventHandler(int type, XEventHandler* handler) {
+ MutexLock lock(&mutex_);
+ event_handlers_[type].push_back(handler);
+}
+
+void SharedXDisplay::RemoveEventHandler(int type, XEventHandler* handler) {
+ MutexLock lock(&mutex_);
+ EventHandlersMap::iterator handlers = event_handlers_.find(type);
+ if (handlers == event_handlers_.end())
+ return;
+
+ std::vector<XEventHandler*>::iterator new_end =
+ std::remove(handlers->second.begin(), handlers->second.end(), handler);
+ handlers->second.erase(new_end, handlers->second.end());
+
+ // Check if no handlers left for this event.
+ if (handlers->second.empty())
+ event_handlers_.erase(handlers);
+}
+
+void SharedXDisplay::ProcessPendingXEvents() {
+ // Hold reference to `this` to prevent it from being destroyed while
+ // processing events.
+ rtc::scoped_refptr<SharedXDisplay> self(this);
+
+ // Protect access to `event_handlers_` after incrementing the refcount for
+ // `this` to ensure the instance is still valid when the lock is acquired.
+ MutexLock lock(&mutex_);
+
+ // Find the number of events that are outstanding "now." We don't just loop
+ // on XPending because we want to guarantee this terminates.
+ int events_to_process = XPending(display());
+ XEvent e;
+
+ for (int i = 0; i < events_to_process; i++) {
+ XNextEvent(display(), &e);
+ EventHandlersMap::iterator handlers = event_handlers_.find(e.type);
+ if (handlers == event_handlers_.end())
+ continue;
+ for (std::vector<XEventHandler*>::iterator it = handlers->second.begin();
+ it != handlers->second.end(); ++it) {
+ if ((*it)->HandleXEvent(e))
+ break;
+ }
+ }
+}
+
+void SharedXDisplay::IgnoreXServerGrabs() {
+#if !defined(WEBRTC_MOZILLA_BUILD)
+ int test_event_base = 0;
+ int test_error_base = 0;
+ int major = 0;
+ int minor = 0;
+ if (XTestQueryExtension(display(), &test_event_base, &test_error_base, &major,
+ &minor)) {
+ XTestGrabControl(display(), true);
+ }
+#endif
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.h
new file mode 100644
index 0000000000..c05fc46546
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_SHARED_X_DISPLAY_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_SHARED_X_DISPLAY_H_
+
+#include <map>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "api/ref_counted_base.h"
+#include "api/scoped_refptr.h"
+#include "rtc_base/synchronization/mutex.h"
+#include "rtc_base/system/rtc_export.h"
+#include "rtc_base/thread_annotations.h"
+
+// Including Xlib.h will involve evil defines (Bool, Status, True, False), which
+// easily conflict with other headers.
+typedef struct _XDisplay Display;
+typedef union _XEvent XEvent;
+
+namespace webrtc {
+
+// A ref-counted object to store XDisplay connection.
+class RTC_EXPORT SharedXDisplay
+ : public rtc::RefCountedNonVirtual<SharedXDisplay> {
+ public:
+ class XEventHandler {
+ public:
+ virtual ~XEventHandler() {}
+
+ // Processes XEvent. Returns true if the event has been handled.
+ virtual bool HandleXEvent(const XEvent& event) = 0;
+ };
+
+ // Creates a new X11 Display for the `display_name`. NULL is returned if X11
+ // connection failed. Equivalent to CreateDefault() when `display_name` is
+ // empty.
+ static rtc::scoped_refptr<SharedXDisplay> Create(
+ absl::string_view display_name);
+
+ // Creates X11 Display connection for the default display (e.g. specified in
+ // DISPLAY). NULL is returned if X11 connection failed.
+ static rtc::scoped_refptr<SharedXDisplay> CreateDefault();
+
+ Display* display() { return display_; }
+
+ // Adds a new event `handler` for XEvent's of `type`.
+ void AddEventHandler(int type, XEventHandler* handler);
+
+ // Removes event `handler` added using `AddEventHandler`. Doesn't do anything
+ // if `handler` is not registered.
+ void RemoveEventHandler(int type, XEventHandler* handler);
+
+ // Processes pending XEvents, calling corresponding event handlers.
+ void ProcessPendingXEvents();
+
+ void IgnoreXServerGrabs();
+
+ ~SharedXDisplay();
+
+ SharedXDisplay(const SharedXDisplay&) = delete;
+ SharedXDisplay& operator=(const SharedXDisplay&) = delete;
+
+ protected:
+ // Takes ownership of `display`.
+ explicit SharedXDisplay(Display* display);
+
+ private:
+ typedef std::map<int, std::vector<XEventHandler*> > EventHandlersMap;
+
+ Display* display_;
+
+ Mutex mutex_;
+
+ EventHandlersMap event_handlers_ RTC_GUARDED_BY(mutex_);
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_SHARED_X_DISPLAY_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.cc
new file mode 100644
index 0000000000..2b1392ced0
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.cc
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/window_capturer_x11.h"
+
+#include <X11/Xutil.h>
+#include <X11/extensions/Xcomposite.h>
+#include <X11/extensions/composite.h>
+#include <string.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/linux/x11/shared_x_display.h"
+#include "modules/desktop_capture/linux/x11/window_finder_x11.h"
+#include "modules/desktop_capture/linux/x11/window_list_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/trace_event.h"
+
+namespace webrtc {
+
+WindowCapturerX11::WindowCapturerX11(const DesktopCaptureOptions& options)
+ : x_display_(options.x_display()),
+ atom_cache_(display()),
+ window_finder_(&atom_cache_) {
+ int event_base, error_base, major_version, minor_version;
+ if (XCompositeQueryExtension(display(), &event_base, &error_base) &&
+ XCompositeQueryVersion(display(), &major_version, &minor_version) &&
+ // XCompositeNameWindowPixmap() requires version 0.2
+ (major_version > 0 || minor_version >= 2)) {
+ has_composite_extension_ = true;
+ } else {
+ RTC_LOG(LS_INFO) << "Xcomposite extension not available or too old.";
+ }
+
+ x_display_->AddEventHandler(ConfigureNotify, this);
+}
+
+WindowCapturerX11::~WindowCapturerX11() {
+ x_display_->RemoveEventHandler(ConfigureNotify, this);
+}
+
+bool WindowCapturerX11::GetSourceList(SourceList* sources) {
+ return GetWindowList(&atom_cache_, [this, sources](::Window window) {
+ Source w;
+ w.id = window;
+ w.pid = (pid_t)GetWindowProcessID(window);
+ if (this->GetWindowTitle(window, &w.title)) {
+ sources->push_back(w);
+ }
+ return true;
+ });
+}
+
+bool WindowCapturerX11::SelectSource(SourceId id) {
+ if (!x_server_pixel_buffer_.Init(&atom_cache_, id))
+ return false;
+
+ // Tell the X server to send us window resizing events.
+ XSelectInput(display(), id, StructureNotifyMask);
+
+ selected_window_ = id;
+
+ // In addition to needing X11 server-side support for Xcomposite, it actually
+ // needs to be turned on for the window. If the user has modern
+ // hardware/drivers but isn't using a compositing window manager, that won't
+ // be the case. Here we automatically turn it on.
+
+ // Redirect drawing to an offscreen buffer (ie, turn on compositing). X11
+ // remembers who has requested this and will turn it off for us when we exit.
+ XCompositeRedirectWindow(display(), id, CompositeRedirectAutomatic);
+
+ return true;
+}
+
+bool WindowCapturerX11::FocusOnSelectedSource() {
+ if (!selected_window_)
+ return false;
+
+ unsigned int num_children;
+ ::Window* children;
+ ::Window parent;
+ ::Window root;
+ // Find the root window to pass event to.
+ int status = XQueryTree(display(), selected_window_, &root, &parent,
+ &children, &num_children);
+ if (status == 0) {
+ RTC_LOG(LS_ERROR) << "Failed to query for the root window.";
+ return false;
+ }
+
+ if (children)
+ XFree(children);
+
+ XRaiseWindow(display(), selected_window_);
+
+ // Some window managers (e.g., metacity in GNOME) consider it illegal to
+ // raise a window without also giving it input focus with
+ // _NET_ACTIVE_WINDOW, so XRaiseWindow() on its own isn't enough.
+ Atom atom = XInternAtom(display(), "_NET_ACTIVE_WINDOW", True);
+ if (atom != None) {
+ XEvent xev;
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = True;
+ xev.xclient.window = selected_window_;
+ xev.xclient.message_type = atom;
+
+ // The format member is set to 8, 16, or 32 and specifies whether the
+ // data should be viewed as a list of bytes, shorts, or longs.
+ xev.xclient.format = 32;
+
+ memset(xev.xclient.data.l, 0, sizeof(xev.xclient.data.l));
+
+ XSendEvent(display(), root, False,
+ SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+ }
+ XFlush(display());
+ return true;
+}
+
+void WindowCapturerX11::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+
+ callback_ = callback;
+}
+
+void WindowCapturerX11::CaptureFrame() {
+ TRACE_EVENT0("webrtc", "WindowCapturerX11::CaptureFrame");
+
+ if (!x_server_pixel_buffer_.IsWindowValid()) {
+ RTC_LOG(LS_ERROR) << "The window is no longer valid.";
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+
+ x_display_->ProcessPendingXEvents();
+
+ if (!has_composite_extension_) {
+ // Without the Xcomposite extension we capture when the whole window is
+ // visible on screen and not covered by any other window. This is not
+ // something we want so instead, just bail out.
+ RTC_LOG(LS_ERROR) << "No Xcomposite extension detected.";
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+
+ if (GetWindowState(&atom_cache_, selected_window_) == IconicState) {
+ // Window is in minimized. Return a 1x1 frame as same as OSX/Win does.
+ std::unique_ptr<DesktopFrame> frame(
+ new BasicDesktopFrame(DesktopSize(1, 1)));
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
+ return;
+ }
+
+ std::unique_ptr<DesktopFrame> frame(
+ new BasicDesktopFrame(x_server_pixel_buffer_.window_size()));
+
+ x_server_pixel_buffer_.Synchronize();
+ if (!x_server_pixel_buffer_.CaptureRect(DesktopRect::MakeSize(frame->size()),
+ frame.get())) {
+ RTC_LOG(LS_WARNING) << "Temporarily failed to capture winodw.";
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+ return;
+ }
+
+ frame->mutable_updated_region()->SetRect(
+ DesktopRect::MakeSize(frame->size()));
+ frame->set_top_left(x_server_pixel_buffer_.window_rect().top_left());
+ frame->set_capturer_id(DesktopCapturerId::kX11CapturerLinux);
+
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
+}
+
+bool WindowCapturerX11::IsOccluded(const DesktopVector& pos) {
+ return window_finder_.GetWindowUnderPoint(pos) !=
+ static_cast<WindowId>(selected_window_);
+}
+
+bool WindowCapturerX11::HandleXEvent(const XEvent& event) {
+ if (event.type == ConfigureNotify) {
+ XConfigureEvent xce = event.xconfigure;
+ if (xce.window == selected_window_) {
+ if (!DesktopRectFromXAttributes(xce).equals(
+ x_server_pixel_buffer_.window_rect())) {
+ if (!x_server_pixel_buffer_.Init(&atom_cache_, selected_window_)) {
+ RTC_LOG(LS_ERROR)
+ << "Failed to initialize pixel buffer after resizing.";
+ }
+ }
+ }
+ }
+
+ // Always returns false, so other observers can still receive the events.
+ return false;
+}
+
+bool WindowCapturerX11::GetWindowTitle(::Window window, std::string* title) {
+ int status;
+ bool result = false;
+ XTextProperty window_name;
+ window_name.value = nullptr;
+ if (window) {
+ status = XGetWMName(display(), window, &window_name);
+ if (status && window_name.value && window_name.nitems) {
+ int cnt;
+ char** list = nullptr;
+ status =
+ Xutf8TextPropertyToTextList(display(), &window_name, &list, &cnt);
+ if (status >= Success && cnt && *list) {
+ if (cnt > 1) {
+ RTC_LOG(LS_INFO) << "Window has " << cnt
+ << " text properties, only using the first one.";
+ }
+ *title = *list;
+ result = true;
+ }
+ if (list)
+ XFreeStringList(list);
+ }
+ if (window_name.value)
+ XFree(window_name.value);
+ }
+ return result;
+}
+
+int WindowCapturerX11::GetWindowProcessID(::Window window) {
+ // Get _NET_WM_PID property of the window.
+ Atom process_atom = XInternAtom(display(), "_NET_WM_PID", True);
+ XWindowProperty<uint32_t> process_id(display(), window, process_atom);
+
+ return process_id.is_valid() ? *process_id.data() : 0;
+}
+
+// static
+std::unique_ptr<DesktopCapturer> WindowCapturerX11::CreateRawWindowCapturer(
+ const DesktopCaptureOptions& options) {
+ if (!options.x_display())
+ return nullptr;
+ return std::unique_ptr<DesktopCapturer>(new WindowCapturerX11(options));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.h
new file mode 100644
index 0000000000..cfd29eca66
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_CAPTURER_X11_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_CAPTURER_X11_H_
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+
+#include <memory>
+#include <string>
+
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/linux/x11/shared_x_display.h"
+#include "modules/desktop_capture/linux/x11/x_window_property.h"
+#include "modules/desktop_capture/linux/x11/window_finder_x11.h"
+#include "modules/desktop_capture/linux/x11/x_atom_cache.h"
+#include "modules/desktop_capture/linux/x11/x_server_pixel_buffer.h"
+
+namespace webrtc {
+
+class WindowCapturerX11 : public DesktopCapturer,
+ public SharedXDisplay::XEventHandler {
+ public:
+ explicit WindowCapturerX11(const DesktopCaptureOptions& options);
+ ~WindowCapturerX11() override;
+
+ WindowCapturerX11(const WindowCapturerX11&) = delete;
+ WindowCapturerX11& operator=(const WindowCapturerX11&) = delete;
+
+ static std::unique_ptr<DesktopCapturer> CreateRawWindowCapturer(
+ const DesktopCaptureOptions& options);
+
+ // DesktopCapturer interface.
+ void Start(Callback* callback) override;
+ void CaptureFrame() override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+ bool FocusOnSelectedSource() override;
+ bool IsOccluded(const DesktopVector& pos) override;
+
+ // SharedXDisplay::XEventHandler interface.
+ bool HandleXEvent(const XEvent& event) override;
+
+ private:
+ Display* display() { return x_display_->display(); }
+
+ // Returns window title for the specified X `window`.
+ bool GetWindowTitle(::Window window, std::string* title);
+
+ // Returns the id of the owning process.
+ int GetWindowProcessID(::Window window);
+
+ Callback* callback_ = nullptr;
+
+ rtc::scoped_refptr<SharedXDisplay> x_display_;
+
+ bool has_composite_extension_ = false;
+
+ ::Window selected_window_ = 0;
+ XServerPixelBuffer x_server_pixel_buffer_;
+ XAtomCache atom_cache_;
+ WindowFinderX11 window_finder_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_CAPTURER_X11_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.cc
new file mode 100644
index 0000000000..dec17ab51f
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/window_finder_x11.h"
+
+#include <X11/X.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/linux/x11/window_list_utils.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+WindowFinderX11::WindowFinderX11(XAtomCache* cache) : cache_(cache) {
+ RTC_DCHECK(cache_);
+}
+
+WindowFinderX11::~WindowFinderX11() = default;
+
+WindowId WindowFinderX11::GetWindowUnderPoint(DesktopVector point) {
+ WindowId id = kNullWindowId;
+ GetWindowList(cache_, [&id, this, point](::Window window) {
+ DesktopRect rect;
+ if (GetWindowRect(this->cache_->display(), window, &rect) &&
+ rect.Contains(point)) {
+ id = window;
+ return false;
+ }
+ return true;
+ });
+ return id;
+}
+
+// static
+std::unique_ptr<WindowFinder> WindowFinder::Create(
+ const WindowFinder::Options& options) {
+ if (options.cache == nullptr) {
+ return nullptr;
+ }
+
+ return std::make_unique<WindowFinderX11>(options.cache);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.h
new file mode 100644
index 0000000000..91de876417
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_FINDER_X11_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_FINDER_X11_H_
+
+#include "modules/desktop_capture/window_finder.h"
+
+namespace webrtc {
+
+class XAtomCache;
+
+// The implementation of WindowFinder for X11.
+class WindowFinderX11 final : public WindowFinder {
+ public:
+ explicit WindowFinderX11(XAtomCache* cache);
+ ~WindowFinderX11() override;
+
+ // WindowFinder implementation.
+ WindowId GetWindowUnderPoint(DesktopVector point) override;
+
+ private:
+ XAtomCache* const cache_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_FINDER_X11_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.cc
new file mode 100644
index 0000000000..ff2d467e29
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.cc
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/window_list_utils.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <string.h>
+
+#include <algorithm>
+
+#include "modules/desktop_capture/linux/x11/x_error_trap.h"
+#include "modules/desktop_capture/linux/x11/x_window_property.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+namespace {
+
+class DeferXFree {
+ public:
+ explicit DeferXFree(void* data) : data_(data) {}
+ ~DeferXFree();
+
+ private:
+ void* const data_;
+};
+
+DeferXFree::~DeferXFree() {
+ if (data_)
+ XFree(data_);
+}
+
+// Iterates through `window` hierarchy to find first visible window, i.e. one
+// that has WM_STATE property set to NormalState.
+// See http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1 .
+::Window GetApplicationWindow(XAtomCache* cache, ::Window window) {
+ int32_t state = GetWindowState(cache, window);
+ if (state == NormalState) {
+ // Window has WM_STATE==NormalState. Return it.
+ return window;
+ } else if (state == IconicState) {
+ // Window is in minimized. Skip it.
+ return 0;
+ }
+
+ RTC_DCHECK_EQ(state, WithdrawnState);
+ // If the window is in WithdrawnState then look at all of its children.
+ ::Window root, parent;
+ ::Window* children;
+ unsigned int num_children;
+ if (!XQueryTree(cache->display(), window, &root, &parent, &children,
+ &num_children)) {
+ RTC_LOG(LS_ERROR) << "Failed to query for child windows although window"
+ "does not have a valid WM_STATE.";
+ return 0;
+ }
+ ::Window app_window = 0;
+ for (unsigned int i = 0; i < num_children; ++i) {
+ app_window = GetApplicationWindow(cache, children[i]);
+ if (app_window)
+ break;
+ }
+
+ if (children)
+ XFree(children);
+ return app_window;
+}
+
+// Returns true if the `window` is a desktop element.
+bool IsDesktopElement(XAtomCache* cache, ::Window window) {
+ RTC_DCHECK(cache);
+ if (window == 0)
+ return false;
+
+ // First look for _NET_WM_WINDOW_TYPE. The standard
+ // (http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#id2760306)
+ // says this hint *should* be present on all windows, and we use the existence
+ // of _NET_WM_WINDOW_TYPE_NORMAL in the property to indicate a window is not
+ // a desktop element (that is, only "normal" windows should be shareable).
+ XWindowProperty<uint32_t> window_type(cache->display(), window,
+ cache->WindowType());
+ if (window_type.is_valid() && window_type.size() > 0) {
+ uint32_t* end = window_type.data() + window_type.size();
+ bool is_normal =
+ (end != std::find(window_type.data(), end, cache->WindowTypeNormal()));
+ return !is_normal;
+ }
+
+ // Fall back on using the hint.
+ XClassHint class_hint;
+ Status status = XGetClassHint(cache->display(), window, &class_hint);
+ if (status == 0) {
+ // No hints, assume this is a normal application window.
+ return false;
+ }
+
+ DeferXFree free_res_name(class_hint.res_name);
+ DeferXFree free_res_class(class_hint.res_class);
+ return strcmp("gnome-panel", class_hint.res_name) == 0 ||
+ strcmp("desktop_window", class_hint.res_name) == 0;
+}
+
+} // namespace
+
+int32_t GetWindowState(XAtomCache* cache, ::Window window) {
+ // Get WM_STATE property of the window.
+ XWindowProperty<uint32_t> window_state(cache->display(), window,
+ cache->WmState());
+
+ // WM_STATE is considered to be set to WithdrawnState when it missing.
+ return window_state.is_valid() ? *window_state.data() : WithdrawnState;
+}
+
+bool GetWindowList(XAtomCache* cache,
+ rtc::FunctionView<bool(::Window)> on_window) {
+ RTC_DCHECK(cache);
+ RTC_DCHECK(on_window);
+ ::Display* const display = cache->display();
+
+ int failed_screens = 0;
+ const int num_screens = XScreenCount(display);
+ for (int screen = 0; screen < num_screens; screen++) {
+ ::Window root_window = XRootWindow(display, screen);
+ ::Window parent;
+ ::Window* children;
+ unsigned int num_children;
+ {
+ XErrorTrap error_trap(display);
+ if (XQueryTree(display, root_window, &root_window, &parent, &children,
+ &num_children) == 0 ||
+ error_trap.GetLastErrorAndDisable() != 0) {
+ failed_screens++;
+ RTC_LOG(LS_ERROR) << "Failed to query for child windows for screen "
+ << screen;
+ continue;
+ }
+ }
+
+ DeferXFree free_children(children);
+
+ for (unsigned int i = 0; i < num_children; i++) {
+ // Iterates in reverse order to return windows from front to back.
+ ::Window app_window =
+ GetApplicationWindow(cache, children[num_children - 1 - i]);
+ if (app_window && !IsDesktopElement(cache, app_window)) {
+ if (!on_window(app_window)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return failed_screens < num_screens;
+}
+
+bool GetWindowRect(::Display* display,
+ ::Window window,
+ DesktopRect* rect,
+ XWindowAttributes* attributes /* = nullptr */) {
+ XWindowAttributes local_attributes;
+ int offset_x;
+ int offset_y;
+ if (attributes == nullptr) {
+ attributes = &local_attributes;
+ }
+
+ {
+ XErrorTrap error_trap(display);
+ if (!XGetWindowAttributes(display, window, attributes) ||
+ error_trap.GetLastErrorAndDisable() != 0) {
+ return false;
+ }
+ }
+ *rect = DesktopRectFromXAttributes(*attributes);
+
+ {
+ XErrorTrap error_trap(display);
+ ::Window child;
+ if (!XTranslateCoordinates(display, window, attributes->root, -rect->left(),
+ -rect->top(), &offset_x, &offset_y, &child) ||
+ error_trap.GetLastErrorAndDisable() != 0) {
+ return false;
+ }
+ }
+ rect->Translate(offset_x, offset_y);
+ return true;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.h
new file mode 100644
index 0000000000..923842df14
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_LIST_UTILS_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_LIST_UTILS_H_
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <stdint.h>
+
+#include "api/function_view.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/linux/x11/x_atom_cache.h"
+
+namespace webrtc {
+
+// Synchronously iterates all on-screen windows in `cache`.display() in
+// decreasing z-order and sends them one-by-one to `on_window` function before
+// GetWindowList() returns. If `on_window` returns false, this function ignores
+// other windows and returns immediately. GetWindowList() returns false if
+// native APIs failed. If multiple screens are attached to the `display`, this
+// function returns false only when native APIs failed on all screens. Menus,
+// panels and minimized windows will be ignored.
+bool GetWindowList(XAtomCache* cache,
+ rtc::FunctionView<bool(::Window)> on_window);
+
+// Returns WM_STATE property of the `window`. This function returns
+// WithdrawnState if the `window` is missing.
+int32_t GetWindowState(XAtomCache* cache, ::Window window);
+
+// Returns the rectangle of the `window` in the coordinates of `display`. This
+// function returns false if native APIs failed. If `attributes` is provided, it
+// will be filled with the attributes of `window`. The `rect` is in system
+// coordinate, i.e. the primary monitor always starts from (0, 0).
+bool GetWindowRect(::Display* display,
+ ::Window window,
+ DesktopRect* rect,
+ XWindowAttributes* attributes = nullptr);
+
+// Creates a DesktopRect from `attributes`.
+template <typename T>
+DesktopRect DesktopRectFromXAttributes(const T& attributes) {
+ return DesktopRect::MakeXYWH(attributes.x, attributes.y, attributes.width,
+ attributes.height);
+}
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_LIST_UTILS_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.cc
new file mode 100644
index 0000000000..157ba8b8fd
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/x_atom_cache.h"
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+XAtomCache::XAtomCache(::Display* display) : display_(display) {
+ RTC_DCHECK(display_);
+}
+
+XAtomCache::~XAtomCache() = default;
+
+::Display* XAtomCache::display() const {
+ return display_;
+}
+
+Atom XAtomCache::WmState() {
+ return CreateIfNotExist(&wm_state_, "WM_STATE");
+}
+
+Atom XAtomCache::WindowType() {
+ return CreateIfNotExist(&window_type_, "_NET_WM_WINDOW_TYPE");
+}
+
+Atom XAtomCache::WindowTypeNormal() {
+ return CreateIfNotExist(&window_type_normal_, "_NET_WM_WINDOW_TYPE_NORMAL");
+}
+
+Atom XAtomCache::IccProfile() {
+ return CreateIfNotExist(&icc_profile_, "_ICC_PROFILE");
+}
+
+Atom XAtomCache::CreateIfNotExist(Atom* atom, const char* name) {
+ RTC_DCHECK(atom);
+ if (*atom == None) {
+ *atom = XInternAtom(display(), name, True);
+ }
+ return *atom;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.h
new file mode 100644
index 0000000000..39d957e98b
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_X_ATOM_CACHE_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_X_ATOM_CACHE_H_
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+
+namespace webrtc {
+
+// A cache of Atom. Each Atom object is created on demand.
+class XAtomCache final {
+ public:
+ explicit XAtomCache(::Display* display);
+ ~XAtomCache();
+
+ ::Display* display() const;
+
+ Atom WmState();
+ Atom WindowType();
+ Atom WindowTypeNormal();
+ Atom IccProfile();
+
+ private:
+ // If |*atom| is None, this function uses XInternAtom() to retrieve an Atom.
+ Atom CreateIfNotExist(Atom* atom, const char* name);
+
+ ::Display* const display_;
+ Atom wm_state_ = None;
+ Atom window_type_ = None;
+ Atom window_type_normal_ = None;
+ Atom icc_profile_ = None;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_X_ATOM_CACHE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.cc
new file mode 100644
index 0000000000..3314dd286c
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.cc
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/x_error_trap.h"
+
+#include <stddef.h>
+
+#include <limits>
+
+#include "rtc_base/checks.h"
+
+
+namespace webrtc {
+
+Bool XErrorTrap::XServerErrorHandler(Display* display, xReply* rep,
+ char* /* buf */, int /* len */,
+ XPointer data) {
+ XErrorTrap* self = reinterpret_cast<XErrorTrap*>(data);
+ if (rep->generic.type != X_Error ||
+ // Overflow-safe last_request_read <= last_ignored_request_ for skipping
+ // async replies from requests before XErrorTrap was created.
+ self->last_ignored_request_ - display->last_request_read <
+ std::numeric_limits<unsigned long>::max() >> 1)
+ return False;
+ self->last_xserver_error_code_ = rep->error.errorCode;
+ return True;
+}
+
+XErrorTrap::XErrorTrap(Display* display)
+ : display_(display),
+ last_xserver_error_code_(0),
+ enabled_(true) {
+ // Use async_handlers instead of XSetErrorHandler(). async_handlers can
+ // remain in place and then be safely removed at the right time even if a
+ // handler change happens concurrently on another thread. async_handlers
+ // are processed first and so can prevent errors reaching the global
+ // XSetErrorHandler handler. They also will not see errors from or affect
+ // handling of errors on other Displays, which may be processed on other
+ // threads.
+ LockDisplay(display);
+ async_handler_.next = display->async_handlers;
+ async_handler_.handler = XServerErrorHandler;
+ async_handler_.data = reinterpret_cast<XPointer>(this);
+ display->async_handlers = &async_handler_;
+ last_ignored_request_ = display->request;
+ UnlockDisplay(display);
+}
+
+int XErrorTrap::GetLastErrorAndDisable() {
+ assert(enabled_);
+ enabled_ = false;
+ LockDisplay(display_);
+ DeqAsyncHandler(display_, &async_handler_);
+ UnlockDisplay(display_);
+ return last_xserver_error_code_;
+}
+
+XErrorTrap::~XErrorTrap() {
+ if (enabled_)
+ GetLastErrorAndDisable();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.h
new file mode 100644
index 0000000000..df7e86bf03
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_X_ERROR_TRAP_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_X_ERROR_TRAP_H_
+
+#include <X11/Xlibint.h>
+#undef max // Xlibint.h defines this and it breaks std::max
+#undef min // Xlibint.h defines this and it breaks std::min
+
+namespace webrtc {
+
+// Helper class that registers X Window error handler. Caller can use
+// GetLastErrorAndDisable() to get the last error that was caught, if any.
+// An XErrorTrap may be constructed on any thread, but errors are collected
+// from all threads and so |display| should be used only on one thread.
+// Other Displays are unaffected.
+class XErrorTrap {
+ public:
+ explicit XErrorTrap(Display* display);
+ ~XErrorTrap();
+
+ XErrorTrap(const XErrorTrap&) = delete;
+ XErrorTrap& operator=(const XErrorTrap&) = delete;
+
+ // Returns last error and removes unregisters the error handler.
+ // Must not be called more than once.
+ int GetLastErrorAndDisable();
+
+ private:
+ static Bool XServerErrorHandler(Display* display, xReply* rep,
+ char* /* buf */, int /* len */,
+ XPointer data);
+
+ _XAsyncHandler async_handler_;
+ Display* display_;
+ unsigned long last_ignored_request_;
+ int last_xserver_error_code_;
+ bool enabled_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_X_ERROR_TRAP_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.cc
new file mode 100644
index 0000000000..e0b334dab1
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.cc
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/x_server_pixel_buffer.h"
+
+#include <X11/Xutil.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/linux/x11/window_list_utils.h"
+#include "modules/desktop_capture/linux/x11/x_error_trap.h"
+#include "modules/desktop_capture/linux/x11/x_window_property.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+namespace {
+
+// Returns the number of bits `mask` has to be shifted left so its last
+// (most-significant) bit set becomes the most-significant bit of the word.
+// When `mask` is 0 the function returns 31.
+uint32_t MaskToShift(uint32_t mask) {
+ int shift = 0;
+ if ((mask & 0xffff0000u) == 0) {
+ mask <<= 16;
+ shift += 16;
+ }
+ if ((mask & 0xff000000u) == 0) {
+ mask <<= 8;
+ shift += 8;
+ }
+ if ((mask & 0xf0000000u) == 0) {
+ mask <<= 4;
+ shift += 4;
+ }
+ if ((mask & 0xc0000000u) == 0) {
+ mask <<= 2;
+ shift += 2;
+ }
+ if ((mask & 0x80000000u) == 0)
+ shift += 1;
+
+ return shift;
+}
+
+// Returns true if `image` is in RGB format.
+bool IsXImageRGBFormat(XImage* image) {
+ return image->bits_per_pixel == 32 && image->red_mask == 0xff0000 &&
+ image->green_mask == 0xff00 && image->blue_mask == 0xff;
+}
+
+// We expose two forms of blitting to handle variations in the pixel format.
+// In FastBlit(), the operation is effectively a memcpy.
+void FastBlit(XImage* x_image,
+ uint8_t* src_pos,
+ const DesktopRect& rect,
+ DesktopFrame* frame) {
+ RTC_DCHECK_LE(frame->top_left().x(), rect.left());
+ RTC_DCHECK_LE(frame->top_left().y(), rect.top());
+
+ frame->CopyPixelsFrom(
+ src_pos, x_image->bytes_per_line,
+ DesktopRect::MakeXYWH(rect.left() - frame->top_left().x(),
+ rect.top() - frame->top_left().y(), rect.width(),
+ rect.height()));
+}
+
+void SlowBlit(XImage* x_image,
+ uint8_t* src_pos,
+ const DesktopRect& rect,
+ DesktopFrame* frame) {
+ RTC_DCHECK_LE(frame->top_left().x(), rect.left());
+ RTC_DCHECK_LE(frame->top_left().y(), rect.top());
+
+ int src_stride = x_image->bytes_per_line;
+ int dst_x = rect.left() - frame->top_left().x();
+ int dst_y = rect.top() - frame->top_left().y();
+ int width = rect.width(), height = rect.height();
+
+ uint32_t red_mask = x_image->red_mask;
+ uint32_t green_mask = x_image->red_mask;
+ uint32_t blue_mask = x_image->blue_mask;
+
+ uint32_t red_shift = MaskToShift(red_mask);
+ uint32_t green_shift = MaskToShift(green_mask);
+ uint32_t blue_shift = MaskToShift(blue_mask);
+
+ int bits_per_pixel = x_image->bits_per_pixel;
+
+ uint8_t* dst_pos = frame->data() + frame->stride() * dst_y;
+ dst_pos += dst_x * DesktopFrame::kBytesPerPixel;
+ // TODO(hclam): Optimize, perhaps using MMX code or by converting to
+ // YUV directly.
+ // TODO(sergeyu): This code doesn't handle XImage byte order properly and
+ // won't work with 24bpp images. Fix it.
+ for (int y = 0; y < height; y++) {
+ uint32_t* dst_pos_32 = reinterpret_cast<uint32_t*>(dst_pos);
+ uint32_t* src_pos_32 = reinterpret_cast<uint32_t*>(src_pos);
+ uint16_t* src_pos_16 = reinterpret_cast<uint16_t*>(src_pos);
+ for (int x = 0; x < width; x++) {
+ // Dereference through an appropriately-aligned pointer.
+ uint32_t pixel;
+ if (bits_per_pixel == 32) {
+ pixel = src_pos_32[x];
+ } else if (bits_per_pixel == 16) {
+ pixel = src_pos_16[x];
+ } else {
+ pixel = src_pos[x];
+ }
+ uint32_t r = (pixel & red_mask) << red_shift;
+ uint32_t g = (pixel & green_mask) << green_shift;
+ uint32_t b = (pixel & blue_mask) << blue_shift;
+ // Write as 32-bit RGB.
+ dst_pos_32[x] =
+ ((r >> 8) & 0xff0000) | ((g >> 16) & 0xff00) | ((b >> 24) & 0xff);
+ }
+ dst_pos += frame->stride();
+ src_pos += src_stride;
+ }
+}
+
+} // namespace
+
+XServerPixelBuffer::XServerPixelBuffer() {}
+
+XServerPixelBuffer::~XServerPixelBuffer() {
+ Release();
+}
+
+void XServerPixelBuffer::Release() {
+ if (x_image_) {
+ XDestroyImage(x_image_);
+ x_image_ = nullptr;
+ }
+ if (x_shm_image_) {
+ XDestroyImage(x_shm_image_);
+ x_shm_image_ = nullptr;
+ }
+ if (shm_pixmap_) {
+ XFreePixmap(display_, shm_pixmap_);
+ shm_pixmap_ = 0;
+ }
+ if (shm_gc_) {
+ XFreeGC(display_, shm_gc_);
+ shm_gc_ = nullptr;
+ }
+
+ ReleaseSharedMemorySegment();
+
+ window_ = 0;
+}
+
+void XServerPixelBuffer::ReleaseSharedMemorySegment() {
+ if (!shm_segment_info_)
+ return;
+ if (shm_segment_info_->shmaddr != nullptr)
+ shmdt(shm_segment_info_->shmaddr);
+ if (shm_segment_info_->shmid != -1)
+ shmctl(shm_segment_info_->shmid, IPC_RMID, 0);
+ delete shm_segment_info_;
+ shm_segment_info_ = nullptr;
+}
+
+bool XServerPixelBuffer::Init(XAtomCache* cache, Window window) {
+ Release();
+ display_ = cache->display();
+
+ XWindowAttributes attributes;
+ if (!GetWindowRect(display_, window, &window_rect_, &attributes)) {
+ return false;
+ }
+
+ if (cache->IccProfile() != None) {
+ // `window` is the root window when doing screen capture.
+ XWindowProperty<uint8_t> icc_profile_property(cache->display(), window,
+ cache->IccProfile());
+ if (icc_profile_property.is_valid() && icc_profile_property.size() > 0) {
+ icc_profile_ = std::vector<uint8_t>(
+ icc_profile_property.data(),
+ icc_profile_property.data() + icc_profile_property.size());
+ } else {
+ RTC_LOG(LS_WARNING) << "Failed to get icc profile";
+ }
+ }
+
+ window_ = window;
+ InitShm(attributes);
+
+ return true;
+}
+
+void XServerPixelBuffer::InitShm(const XWindowAttributes& attributes) {
+ Visual* default_visual = attributes.visual;
+ int default_depth = attributes.depth;
+
+ int major, minor;
+ Bool have_pixmaps;
+ if (!XShmQueryVersion(display_, &major, &minor, &have_pixmaps)) {
+ // Shared memory not supported. CaptureRect will use the XImage API instead.
+ return;
+ }
+
+ bool using_shm = false;
+ shm_segment_info_ = new XShmSegmentInfo;
+ shm_segment_info_->shmid = -1;
+ shm_segment_info_->shmaddr = nullptr;
+ shm_segment_info_->readOnly = False;
+ x_shm_image_ = XShmCreateImage(display_, default_visual, default_depth,
+ ZPixmap, 0, shm_segment_info_,
+ window_rect_.width(), window_rect_.height());
+ if (x_shm_image_) {
+ shm_segment_info_->shmid =
+ shmget(IPC_PRIVATE, x_shm_image_->bytes_per_line * x_shm_image_->height,
+ IPC_CREAT | 0600);
+ if (shm_segment_info_->shmid != -1) {
+ void* shmat_result = shmat(shm_segment_info_->shmid, 0, 0);
+ if (shmat_result != reinterpret_cast<void*>(-1)) {
+ shm_segment_info_->shmaddr = reinterpret_cast<char*>(shmat_result);
+ x_shm_image_->data = shm_segment_info_->shmaddr;
+
+ XErrorTrap error_trap(display_);
+ using_shm = XShmAttach(display_, shm_segment_info_);
+ XSync(display_, False);
+ if (error_trap.GetLastErrorAndDisable() != 0)
+ using_shm = false;
+ if (using_shm) {
+ RTC_LOG(LS_VERBOSE)
+ << "Using X shared memory segment " << shm_segment_info_->shmid;
+ }
+ }
+ } else {
+ RTC_LOG(LS_WARNING) << "Failed to get shared memory segment. "
+ "Performance may be degraded.";
+ }
+ }
+
+ if (!using_shm) {
+ RTC_LOG(LS_WARNING)
+ << "Not using shared memory. Performance may be degraded.";
+ ReleaseSharedMemorySegment();
+ return;
+ }
+
+ if (have_pixmaps)
+ have_pixmaps = InitPixmaps(default_depth);
+
+ shmctl(shm_segment_info_->shmid, IPC_RMID, 0);
+ shm_segment_info_->shmid = -1;
+
+ RTC_LOG(LS_VERBOSE) << "Using X shared memory extension v" << major << "."
+ << minor << " with" << (have_pixmaps ? "" : "out")
+ << " pixmaps.";
+}
+
+bool XServerPixelBuffer::InitPixmaps(int depth) {
+ if (XShmPixmapFormat(display_) != ZPixmap)
+ return false;
+
+ {
+ XErrorTrap error_trap(display_);
+ shm_pixmap_ = XShmCreatePixmap(
+ display_, window_, shm_segment_info_->shmaddr, shm_segment_info_,
+ window_rect_.width(), window_rect_.height(), depth);
+ XSync(display_, False);
+ if (error_trap.GetLastErrorAndDisable() != 0) {
+ // `shm_pixmap_` is not not valid because the request was not processed
+ // by the X Server, so zero it.
+ shm_pixmap_ = 0;
+ return false;
+ }
+ }
+
+ {
+ XErrorTrap error_trap(display_);
+ XGCValues shm_gc_values;
+ shm_gc_values.subwindow_mode = IncludeInferiors;
+ shm_gc_values.graphics_exposures = False;
+ shm_gc_ = XCreateGC(display_, window_,
+ GCSubwindowMode | GCGraphicsExposures, &shm_gc_values);
+ XSync(display_, False);
+ if (error_trap.GetLastErrorAndDisable() != 0) {
+ XFreePixmap(display_, shm_pixmap_);
+ shm_pixmap_ = 0;
+ shm_gc_ = 0; // See shm_pixmap_ comment above.
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool XServerPixelBuffer::IsWindowValid() const {
+ XWindowAttributes attributes;
+ {
+ XErrorTrap error_trap(display_);
+ if (!XGetWindowAttributes(display_, window_, &attributes) ||
+ error_trap.GetLastErrorAndDisable() != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void XServerPixelBuffer::Synchronize() {
+ if (shm_segment_info_ && !shm_pixmap_) {
+ // XShmGetImage can fail if the display is being reconfigured.
+ XErrorTrap error_trap(display_);
+ // XShmGetImage fails if the window is partially out of screen.
+ xshm_get_image_succeeded_ =
+ XShmGetImage(display_, window_, x_shm_image_, 0, 0, AllPlanes);
+ }
+}
+
+bool XServerPixelBuffer::CaptureRect(const DesktopRect& rect,
+ DesktopFrame* frame) {
+ RTC_DCHECK_LE(rect.right(), window_rect_.width());
+ RTC_DCHECK_LE(rect.bottom(), window_rect_.height());
+
+ XImage* image;
+ uint8_t* data;
+
+ if (shm_segment_info_ && (shm_pixmap_ || xshm_get_image_succeeded_)) {
+ if (shm_pixmap_) {
+ XCopyArea(display_, window_, shm_pixmap_, shm_gc_, rect.left(),
+ rect.top(), rect.width(), rect.height(), rect.left(),
+ rect.top());
+ XSync(display_, False);
+ }
+
+ image = x_shm_image_;
+ data = reinterpret_cast<uint8_t*>(image->data) +
+ rect.top() * image->bytes_per_line +
+ rect.left() * image->bits_per_pixel / 8;
+
+ } else {
+ if (x_image_)
+ XDestroyImage(x_image_);
+ x_image_ = XGetImage(display_, window_, rect.left(), rect.top(),
+ rect.width(), rect.height(), AllPlanes, ZPixmap);
+ if (!x_image_)
+ return false;
+
+ image = x_image_;
+ data = reinterpret_cast<uint8_t*>(image->data);
+ }
+
+ if (IsXImageRGBFormat(image)) {
+ FastBlit(image, data, rect, frame);
+ } else {
+ SlowBlit(image, data, rect, frame);
+ }
+
+ if (!icc_profile_.empty())
+ frame->set_icc_profile(icc_profile_);
+
+ return true;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.h
new file mode 100644
index 0000000000..38af3a3e76
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// Don't include this file in any .h files because it pulls in some X headers.
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_X_SERVER_PIXEL_BUFFER_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_X_SERVER_PIXEL_BUFFER_H_
+
+#include <X11/Xutil.h>
+#include <X11/extensions/XShm.h>
+
+#include <memory>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+
+namespace webrtc {
+
+class DesktopFrame;
+class XAtomCache;
+
+// A class to allow the X server's pixel buffer to be accessed as efficiently
+// as possible.
+class XServerPixelBuffer {
+ public:
+ XServerPixelBuffer();
+ ~XServerPixelBuffer();
+
+ XServerPixelBuffer(const XServerPixelBuffer&) = delete;
+ XServerPixelBuffer& operator=(const XServerPixelBuffer&) = delete;
+
+ void Release();
+
+ // Allocate (or reallocate) the pixel buffer for `window`. Returns false in
+ // case of an error (e.g. window doesn't exist).
+ bool Init(XAtomCache* cache, Window window);
+
+ bool is_initialized() { return window_ != 0; }
+
+ // Returns the size of the window the buffer was initialized for.
+ DesktopSize window_size() { return window_rect_.size(); }
+
+ // Returns the rectangle of the window the buffer was initialized for.
+ const DesktopRect& window_rect() { return window_rect_; }
+
+ // Returns true if the window can be found.
+ bool IsWindowValid() const;
+
+ // If shared memory is being used without pixmaps, synchronize this pixel
+ // buffer with the root window contents (otherwise, this is a no-op).
+ // This is to avoid doing a full-screen capture for each individual
+ // rectangle in the capture list, when it only needs to be done once at the
+ // beginning.
+ void Synchronize();
+
+ // Capture the specified rectangle and stores it in the `frame`. In the case
+ // where the full-screen data is captured by Synchronize(), this simply
+ // returns the pointer without doing any more work. The caller must ensure
+ // that `rect` is not larger than window_size().
+ bool CaptureRect(const DesktopRect& rect, DesktopFrame* frame);
+
+ private:
+ void ReleaseSharedMemorySegment();
+
+ void InitShm(const XWindowAttributes& attributes);
+ bool InitPixmaps(int depth);
+
+ Display* display_ = nullptr;
+ Window window_ = 0;
+ DesktopRect window_rect_;
+ XImage* x_image_ = nullptr;
+ XShmSegmentInfo* shm_segment_info_ = nullptr;
+ XImage* x_shm_image_ = nullptr;
+ Pixmap shm_pixmap_ = 0;
+ GC shm_gc_ = nullptr;
+ bool xshm_get_image_succeeded_ = false;
+ std::vector<uint8_t> icc_profile_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_X_SERVER_PIXEL_BUFFER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.cc
new file mode 100644
index 0000000000..5e16dac404
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.cc
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/x_window_property.h"
+
+namespace webrtc {
+
+XWindowPropertyBase::XWindowPropertyBase(Display* display,
+ Window window,
+ Atom property,
+ int expected_size) {
+ const int kBitsPerByte = 8;
+ Atom actual_type;
+ int actual_format;
+ unsigned long bytes_after; // NOLINT: type required by XGetWindowProperty
+ int status = XGetWindowProperty(display, window, property, 0L, ~0L, False,
+ AnyPropertyType, &actual_type, &actual_format,
+ &size_, &bytes_after, &data_);
+ if (status != Success) {
+ data_ = nullptr;
+ return;
+ }
+ if ((expected_size * kBitsPerByte) != actual_format) {
+ size_ = 0;
+ return;
+ }
+
+ is_valid_ = true;
+}
+
+XWindowPropertyBase::~XWindowPropertyBase() {
+ if (data_)
+ XFree(data_);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.h
new file mode 100644
index 0000000000..28dfb97311
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_X_WINDOW_PROPERTY_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_X_WINDOW_PROPERTY_H_
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+
+namespace webrtc {
+
+class XWindowPropertyBase {
+ public:
+ XWindowPropertyBase(Display* display,
+ Window window,
+ Atom property,
+ int expected_size);
+ virtual ~XWindowPropertyBase();
+
+ XWindowPropertyBase(const XWindowPropertyBase&) = delete;
+ XWindowPropertyBase& operator=(const XWindowPropertyBase&) = delete;
+
+ // True if we got properly value successfully.
+ bool is_valid() const { return is_valid_; }
+
+ // Size and value of the property.
+ size_t size() const { return size_; }
+
+ protected:
+ unsigned char* data_ = nullptr;
+
+ private:
+ bool is_valid_ = false;
+ unsigned long size_ = 0; // NOLINT: type required by XGetWindowProperty
+};
+
+// Convenience wrapper for XGetWindowProperty() results.
+template <class PropertyType>
+class XWindowProperty : public XWindowPropertyBase {
+ public:
+ XWindowProperty(Display* display, const Window window, const Atom property)
+ : XWindowPropertyBase(display, window, property, sizeof(PropertyType)) {}
+ ~XWindowProperty() override = default;
+
+ XWindowProperty(const XWindowProperty&) = delete;
+ XWindowProperty& operator=(const XWindowProperty&) = delete;
+
+ const PropertyType* data() const {
+ return reinterpret_cast<PropertyType*>(data_);
+ }
+ PropertyType* data() { return reinterpret_cast<PropertyType*>(data_); }
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_X_WINDOW_PROPERTY_H_