diff options
Diffstat (limited to '')
-rw-r--r-- | third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.cc | 257 |
1 files changed, 257 insertions, 0 deletions
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 |