mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-17 23:57:59 +01:00

Currently, when users want to use the screen sharing and are using the Wayland display server (the default on Fedora distribution), then it doesn't work, because the WebRTC only includes the X11 implementation. This change adds the support by using the PipeWire multimedia server. The PipeWire implementation in WebRTC stays in screen-capturer-pipewire.c and is guarded by the rtc_use_pipewire build flag that is automatically enabled on Linux. More information are included in the relevant commit messages. Tested on the current Chromium master and Firefox. The sysroot changes are requested in: https://chromium-review.googlesource.com/c/chromium/src/+/1258174 Co-authored-by: Jan Grulich <grulja@gmail.com> Co-authored-by: Eike Rathke <erathke@redhat.com> Change-Id: I212074a4bc437b99a77bf383266026c5bfae7c4a BUG=chromium:682122 Change-Id: I212074a4bc437b99a77bf383266026c5bfae7c4a Reviewed-on: https://webrtc-review.googlesource.com/c/103504 Commit-Queue: Patrik Höglund <phoglund@webrtc.org> Reviewed-by: Patrik Höglund <phoglund@webrtc.org> Reviewed-by: Brave Yao <braveyao@webrtc.org> Cr-Commit-Position: refs/heads/master@{#25461}
246 lines
7.3 KiB
C++
246 lines
7.3 KiB
C++
/*
|
|
* 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/window_list_utils.h"
|
|
|
|
#include <X11/Xatom.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <string.h>
|
|
|
|
#include <algorithm>
|
|
|
|
#include "modules/desktop_capture/linux/x_error_trap.h"
|
|
#include "rtc_base/checks.h"
|
|
#include "rtc_base/constructormagic.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_);
|
|
}
|
|
|
|
// Convenience wrapper for XGetWindowProperty() results.
|
|
template <class PropertyType>
|
|
class XWindowProperty {
|
|
public:
|
|
XWindowProperty(Display* display, Window window, Atom property) {
|
|
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 (sizeof(PropertyType) * kBitsPerByte != actual_format) {
|
|
size_ = 0;
|
|
return;
|
|
}
|
|
|
|
is_valid_ = true;
|
|
}
|
|
|
|
~XWindowProperty() {
|
|
if (data_)
|
|
XFree(data_);
|
|
}
|
|
|
|
// 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_; }
|
|
const PropertyType* data() const {
|
|
return reinterpret_cast<PropertyType*>(data_);
|
|
}
|
|
PropertyType* data() { return reinterpret_cast<PropertyType*>(data_); }
|
|
|
|
private:
|
|
bool is_valid_ = false;
|
|
unsigned long size_ = 0; // NOLINT: type required by XGetWindowProperty
|
|
unsigned char* data_ = nullptr;
|
|
|
|
RTC_DISALLOW_COPY_AND_ASSIGN(XWindowProperty);
|
|
};
|
|
|
|
// 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
|