This CL has been generated with the following script: for m in PLOG \ LOG_TAG \ LOG_GLEM \ LOG_GLE_EX \ LOG_GLE \ LAST_SYSTEM_ERROR \ LOG_ERRNO_EX \ LOG_ERRNO \ LOG_ERR_EX \ LOG_ERR \ LOG_V \ LOG_F \ LOG_T_F \ LOG_E \ LOG_T \ LOG_CHECK_LEVEL_V \ LOG_CHECK_LEVEL \ LOG do git grep -l $m | xargs sed -i "s,\b$m\b,RTC_$m,g" done git checkout rtc_base/logging.h git cl format Bug: webrtc:8452 Change-Id: I1a53ef3e0a5ef6e244e62b2e012b864914784600 Reviewed-on: https://webrtc-review.googlesource.com/21325 Reviewed-by: Niels Moller <nisse@webrtc.org> Reviewed-by: Karl Wiberg <kwiberg@webrtc.org> Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org> Cr-Commit-Position: refs/heads/master@{#20617}
289 lines
9.2 KiB
C++
289 lines
9.2 KiB
C++
/*
|
|
* 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 <string.h>
|
|
|
|
#include <X11/extensions/Xcomposite.h>
|
|
#include <X11/extensions/Xrender.h>
|
|
#include <X11/Xutil.h>
|
|
|
|
#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/window_finder_x11.h"
|
|
#include "modules/desktop_capture/x11/shared_x_display.h"
|
|
#include "modules/desktop_capture/x11/window_list_utils.h"
|
|
#include "modules/desktop_capture/x11/x_atom_cache.h"
|
|
#include "modules/desktop_capture/x11/x_server_pixel_buffer.h"
|
|
#include "rtc_base/checks.h"
|
|
#include "rtc_base/constructormagic.h"
|
|
#include "rtc_base/logging.h"
|
|
#include "rtc_base/scoped_ref_ptr.h"
|
|
|
|
namespace webrtc {
|
|
|
|
namespace {
|
|
|
|
class WindowCapturerLinux : public DesktopCapturer,
|
|
public SharedXDisplay::XEventHandler {
|
|
public:
|
|
WindowCapturerLinux(const DesktopCaptureOptions& options);
|
|
~WindowCapturerLinux() override;
|
|
|
|
// 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);
|
|
|
|
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_;
|
|
|
|
RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerLinux);
|
|
};
|
|
|
|
WindowCapturerLinux::WindowCapturerLinux(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);
|
|
}
|
|
|
|
WindowCapturerLinux::~WindowCapturerLinux() {
|
|
x_display_->RemoveEventHandler(ConfigureNotify, this);
|
|
}
|
|
|
|
bool WindowCapturerLinux::GetSourceList(SourceList* sources) {
|
|
return GetWindowList(&atom_cache_,
|
|
[this, sources](::Window window) {
|
|
Source w;
|
|
w.id = window;
|
|
if (this->GetWindowTitle(window, &w.title)) {
|
|
sources->push_back(w);
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
|
|
bool WindowCapturerLinux::SelectSource(SourceId id) {
|
|
if (!x_server_pixel_buffer_.Init(display(), 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 WindowCapturerLinux::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 WindowCapturerLinux::Start(Callback* callback) {
|
|
RTC_DCHECK(!callback_);
|
|
RTC_DCHECK(callback);
|
|
|
|
callback_ = callback;
|
|
}
|
|
|
|
void WindowCapturerLinux::CaptureFrame() {
|
|
if (!x_server_pixel_buffer_.IsWindowValid()) {
|
|
RTC_LOG(LS_INFO) << "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_INFO) << "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())) {
|
|
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());
|
|
|
|
callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
|
|
}
|
|
|
|
bool WindowCapturerLinux::IsOccluded(const DesktopVector& pos) {
|
|
return window_finder_.GetWindowUnderPoint(pos) !=
|
|
static_cast<WindowId>(selected_window_);
|
|
}
|
|
|
|
bool WindowCapturerLinux::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(display(), 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 WindowCapturerLinux::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;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// static
|
|
std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer(
|
|
const DesktopCaptureOptions& options) {
|
|
if (!options.x_display())
|
|
return nullptr;
|
|
return std::unique_ptr<DesktopCapturer>(new WindowCapturerLinux(options));
|
|
}
|
|
|
|
} // namespace webrtc
|