wayland: Leverage ScreenCapturePortalInterface in helpers

XDG desktop portal utils implicitly rely on few portal interfaces.
This change makes those interfaces explicit.

Bug: chromium:1291247
Change-Id: I3c300f33d04bfa8857ac9f1f9d634dbfc077e591
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/258720
Reviewed-by: Alexander Cooper <alcooper@chromium.org>
Commit-Queue: Salman Malik <salmanmalik@google.com>
Cr-Commit-Position: refs/heads/main@{#36557}
This commit is contained in:
Salman Malik 2022-04-15 00:54:00 +00:00 committed by WebRTC LUCI CQ
parent 4a1e32f06e
commit 53d7260552
8 changed files with 221 additions and 151 deletions

View File

@ -592,8 +592,10 @@ rtc_library("desktop_capture_generic") {
"linux/wayland/egl_dmabuf.h",
"linux/wayland/mouse_cursor_monitor_pipewire.cc",
"linux/wayland/mouse_cursor_monitor_pipewire.h",
"linux/wayland/portal_request_response.h",
"linux/wayland/scoped_glib.cc",
"linux/wayland/scoped_glib.h",
"linux/wayland/screen_capture_portal_interface.cc",
"linux/wayland/screen_capture_portal_interface.h",
"linux/wayland/screencast_portal.cc",
"linux/wayland/screencast_portal.h",

View File

@ -13,6 +13,7 @@
#include "modules/desktop_capture/desktop_capture_options.h"
#include "modules/desktop_capture/desktop_capturer.h"
#include "modules/desktop_capture/linux/wayland/portal_request_response.h"
#include "modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h"
#include "modules/desktop_capture/linux/wayland/screencast_portal.h"
#include "modules/desktop_capture/linux/wayland/shared_screencast_stream.h"

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2022 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_WAYLAND_PORTAL_REQUEST_RESPONSE_H_
#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_PORTAL_REQUEST_RESPONSE_H_
namespace webrtc {
namespace xdg_portal {
// Contains type of responses that can be observed when making a request to
// a desktop portal interface.
enum class RequestResponse {
// Unknown, the initialized status.
kUnknown,
// Success, the request is carried out.
kSuccess,
// The user cancelled the interaction.
kUserCancelled,
// The user interaction was ended in some other way.
kError,
kMaxValue = kError,
};
} // namespace xdg_portal
} // namespace webrtc
#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_PORTAL_REQUEST_RESPONSE_H_

View File

@ -0,0 +1,118 @@
/*
* Copyright 2022 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/wayland/screen_capture_portal_interface.h"
#include <string>
#include "rtc_base/logging.h"
namespace webrtc {
namespace xdg_portal {
void ScreenCapturePortalInterface::RequestSessionUsingProxy(
GAsyncResult* result) {
Scoped<GError> error;
GDBusProxy* proxy = g_dbus_proxy_new_finish(result, error.receive());
if (!proxy) {
// Ignore the error caused by user cancelling the request via `cancellable_`
if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
RTC_LOG(LS_ERROR) << "Failed to get a proxy for the portal: "
<< error->message;
OnPortalDone(RequestResponse::kError);
return;
}
RTC_LOG(LS_INFO) << "Successfully created proxy for the portal.";
RequestSession(proxy);
}
void ScreenCapturePortalInterface::OnSessionRequestResult(
GDBusProxy* proxy,
GAsyncResult* result) {
Scoped<GError> error;
Scoped<GVariant> variant(
g_dbus_proxy_call_finish(proxy, result, error.receive()));
if (!variant) {
// Ignore the error caused by user cancelling the request via `cancellable_`
if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
RTC_LOG(LS_ERROR) << "Failed to request session: " << error->message;
OnPortalDone(RequestResponse::kError);
return;
}
RTC_LOG(LS_INFO) << "Initializing the session.";
Scoped<char> handle;
g_variant_get_child(variant.get(), /*index=*/0, /*format_string=*/"o",
&handle);
if (!handle) {
RTC_LOG(LS_ERROR) << "Failed to initialize the session.";
OnPortalDone(RequestResponse::kError);
return;
}
}
void ScreenCapturePortalInterface::RegisterSessionClosedSignalHandler(
const SessionClosedSignalHandler session_close_signal_handler,
GVariant* parameters,
GDBusConnection* connection,
std::string& session_handle,
guint& session_closed_signal_id) {
uint32_t portal_response;
Scoped<GVariant> response_data;
g_variant_get(parameters, /*format_string=*/"(u@a{sv})", &portal_response,
response_data.receive());
Scoped<GVariant> g_session_handle(
g_variant_lookup_value(response_data.get(), /*key=*/"session_handle",
/*expected_type=*/nullptr));
session_handle = g_variant_dup_string(
/*value=*/g_session_handle.get(), /*length=*/nullptr);
if (session_handle.empty() || portal_response) {
RTC_LOG(LS_ERROR) << "Failed to request the session subscription.";
OnPortalDone(RequestResponse::kError);
return;
}
session_closed_signal_id = g_dbus_connection_signal_subscribe(
connection, kDesktopBusName, kSessionInterfaceName, /*member=*/"Closed",
session_handle.c_str(), /*arg0=*/nullptr, G_DBUS_SIGNAL_FLAGS_NONE,
session_close_signal_handler, this, /*user_data_free_func=*/nullptr);
}
void ScreenCapturePortalInterface::OnStartRequestResult(GDBusProxy* proxy,
GAsyncResult* result) {
Scoped<GError> error;
Scoped<GVariant> variant(
g_dbus_proxy_call_finish(proxy, result, error.receive()));
if (!variant) {
if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
RTC_LOG(LS_ERROR) << "Failed to start the portal session: "
<< error->message;
OnPortalDone(RequestResponse::kError);
return;
}
Scoped<char> handle;
g_variant_get_child(variant.get(), 0, "o", handle.receive());
if (!handle) {
RTC_LOG(LS_ERROR) << "Failed to initialize the start portal session.";
OnPortalDone(RequestResponse::kError);
return;
}
RTC_LOG(LS_INFO) << "Subscribed to the start signal.";
}
} // namespace xdg_portal
} // namespace webrtc

View File

@ -11,19 +11,59 @@
#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREEN_CAPTURE_PORTAL_INTERFACE_H_
#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREEN_CAPTURE_PORTAL_INTERFACE_H_
#include <gio/gio.h>
#include <string>
#include "modules/desktop_capture/linux/wayland/portal_request_response.h"
#include "modules/desktop_capture/linux/wayland/scoped_glib.h"
#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h"
#include "modules/desktop_capture/linux/wayland/xdg_session_details.h"
namespace webrtc {
namespace xdg_portal {
// An interface for XDG desktop portals that can capture desktop/screen.
using SessionClosedSignalHandler = void (*)(GDBusConnection*,
const char*,
const char*,
const char*,
const char*,
GVariant*,
gpointer);
// A base class for XDG desktop portals that can capture desktop/screen.
// Note: downstream clients inherit from this class so it is advisable to
// provide a default implementation of any new virtual methods that may be added
// to this class.
class ScreenCapturePortalInterface {
public:
virtual ~ScreenCapturePortalInterface() {}
// Gets details about the session such as session handle.
virtual xdg_portal::SessionDetails GetSessionDetails() = 0;
virtual xdg_portal::SessionDetails GetSessionDetails() { return {}; }
// Starts the portal setup.
virtual void Start() = 0;
virtual void Start() {}
// Notifies observers about the success/fail state of the portal
// request/response.
virtual void OnPortalDone(xdg_portal::RequestResponse result) {}
// Sends a create session request to the portal.
virtual void RequestSession(GDBusProxy* proxy) {}
// Following methods should not be made virtual as they share a common
// implementation between portals.
// Requests portal session using the proxy object.
void RequestSessionUsingProxy(GAsyncResult* result);
// Handles the session request result.
void OnSessionRequestResult(GDBusProxy* proxy, GAsyncResult* result);
// Subscribes to session close signal and sets up a handler for it.
void RegisterSessionClosedSignalHandler(
const SessionClosedSignalHandler session_close_signal_handler,
GVariant* parameters,
GDBusConnection* connection,
std::string& session_handle,
guint& session_closed_signal_id);
// Handles the result of session start request.
void OnStartRequestResult(GDBusProxy* proxy, GAsyncResult* result);
};
} // namespace xdg_portal

View File

@ -25,12 +25,8 @@ using xdg_portal::kScreenCastInterfaceName;
using xdg_portal::PrepareSignalHandle;
using xdg_portal::RequestResponse;
using xdg_portal::RequestSessionProxy;
using xdg_portal::RequestSessionUsingProxy;
using xdg_portal::SessionRequestHandler;
using xdg_portal::SessionRequestResponseSignalHelper;
using xdg_portal::SetupRequestResponseSignal;
using xdg_portal::SetupSessionRequestHandlers;
using xdg_portal::StartRequestedHandler;
using xdg_portal::StartSessionRequest;
using xdg_portal::TearDownSession;
@ -59,9 +55,14 @@ ScreenCastPortal::ScreenCastPortal(
user_data_(user_data) {}
ScreenCastPortal::~ScreenCastPortal() {
Cleanup();
}
void ScreenCastPortal::Cleanup() {
UnsubscribeSignalHandlers();
TearDownSession(std::move(session_handle_), proxy_, cancellable_,
connection_);
session_handle_ = "";
cancellable_ = nullptr;
proxy_ = nullptr;
@ -118,17 +119,19 @@ xdg_portal::SessionDetails ScreenCastPortal::GetSessionDetails() {
void ScreenCastPortal::OnPortalDone(RequestResponse result) {
notifier_->OnScreenCastRequestResult(result, pw_stream_node_id_, pw_fd_);
if (result != RequestResponse::kSuccess) {
Cleanup();
}
}
// static
void ScreenCastPortal::OnProxyRequested(GObject* gobject,
GAsyncResult* result,
gpointer user_data) {
RequestSessionUsingProxy<ScreenCastPortal>(
static_cast<ScreenCastPortal*>(user_data), gobject, result);
static_cast<ScreenCastPortal*>(user_data)->RequestSessionUsingProxy(result);
}
void ScreenCastPortal::SessionRequest(GDBusProxy* proxy) {
void ScreenCastPortal::RequestSession(GDBusProxy* proxy) {
proxy_ = proxy;
connection_ = g_dbus_proxy_get_connection(proxy_);
SetupSessionRequestHandlers(
@ -140,8 +143,8 @@ void ScreenCastPortal::SessionRequest(GDBusProxy* proxy) {
void ScreenCastPortal::OnSessionRequested(GDBusProxy* proxy,
GAsyncResult* result,
gpointer user_data) {
SessionRequestHandler(static_cast<ScreenCastPortal*>(user_data), proxy,
result, user_data);
static_cast<ScreenCastPortal*>(user_data)->OnSessionRequestResult(proxy,
result);
}
// static
@ -155,9 +158,9 @@ void ScreenCastPortal::OnSessionRequestResponseSignal(
gpointer user_data) {
ScreenCastPortal* that = static_cast<ScreenCastPortal*>(user_data);
RTC_DCHECK(that);
SessionRequestResponseSignalHelper(
OnSessionClosedSignal, that, that->connection_, that->session_handle_,
parameters, that->session_closed_signal_id_);
that->RegisterSessionClosedSignalHandler(
OnSessionClosedSignal, parameters, that->connection_,
that->session_handle_, that->session_closed_signal_id_);
that->SourcesRequest();
}
@ -298,8 +301,8 @@ void ScreenCastPortal::StartRequest() {
void ScreenCastPortal::OnStartRequested(GDBusProxy* proxy,
GAsyncResult* result,
gpointer user_data) {
StartRequestedHandler(static_cast<ScreenCastPortal*>(user_data), proxy,
result);
static_cast<ScreenCastPortal*>(user_data)->OnStartRequestResult(proxy,
result);
}
// static

View File

@ -15,6 +15,7 @@
#include <string>
#include "modules/desktop_capture/linux/wayland/portal_request_response.h"
#include "modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h"
#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h"
#include "modules/desktop_capture/linux/wayland/xdg_session_details.h"
@ -92,12 +93,11 @@ class ScreenCastPortal : public xdg_portal::ScreenCapturePortalInterface {
xdg_portal::SessionDetails GetSessionDetails() override;
// Method to notify the reason for failure of a portal request.
void OnPortalDone(xdg_portal::RequestResponse result);
void OnPortalDone(xdg_portal::RequestResponse result) override;
// Sends a create session request to the portal.
void SessionRequest(GDBusProxy* proxy);
void UnsubscribeSignalHandlers();
void RequestSession(GDBusProxy* proxy) override;
void Cleanup();
// Set of methods leveraged by remote desktop portal to setup a common session
// with screen cast portal.
@ -135,6 +135,7 @@ class ScreenCastPortal : public xdg_portal::ScreenCapturePortalInterface {
guint start_request_signal_id_ = 0;
guint session_closed_signal_id_ = 0;
void UnsubscribeSignalHandlers();
static void OnProxyRequested(GObject* object,
GAsyncResult* result,
gpointer user_data);

View File

@ -17,6 +17,7 @@
#include <string>
#include <vector>
#include "modules/desktop_capture/linux/wayland/portal_request_response.h"
#include "modules/desktop_capture/linux/wayland/scoped_glib.h"
#include "modules/desktop_capture/linux/wayland/xdg_session_details.h"
#include "rtc_base/checks.h"
@ -43,13 +44,6 @@ using SessionRequestResponseSignalHandler = void (*)(GDBusConnection*,
GVariant*,
gpointer);
using SessionRequestResponseSignalCallback = void (*)(std::string);
using SessionClosedSignalHandler = void (*)(GDBusConnection*,
const char*,
const char*,
const char*,
const char*,
GVariant*,
gpointer);
using StartRequestResponseSignalHandler = void (*)(GDBusConnection*,
const char*,
const char*,
@ -61,21 +55,6 @@ using SessionStartRequestedHandler = void (*)(GDBusProxy*,
GAsyncResult*,
gpointer);
// Contains type of responses that can be observed when making a request to
// a desktop portal interface.
enum class RequestResponse {
// Unknown, the initialized status.
kUnknown,
// Success, the request is carried out.
kSuccess,
// The user cancelled the interaction.
kUserCancelled,
// The user interaction was ended in some other way.
kError,
kMaxValue = kError,
};
std::string RequestResponseToString(RequestResponse request);
// Returns a string path for signal handle based on the provided connection and
@ -123,114 +102,6 @@ void TearDownSession(std::string session_handle,
GCancellable* cancellable,
GDBusConnection* connection);
template <typename T>
void RequestSessionUsingProxy(T* portal,
GObject* gobject,
GAsyncResult* result) {
RTC_DCHECK(portal);
Scoped<GError> error;
GDBusProxy* proxy = g_dbus_proxy_new_finish(result, error.receive());
if (!proxy) {
if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
RTC_LOG(LS_ERROR) << "Failed to get a proxy for the portal: "
<< error->message;
portal->OnPortalDone(RequestResponse::kError);
return;
}
RTC_LOG(LS_INFO) << "Successfully created proxy for the portal.";
portal->SessionRequest(proxy);
}
template <typename T>
void SessionRequestHandler(T* portal,
GDBusProxy* proxy,
GAsyncResult* result,
gpointer user_data) {
RTC_DCHECK(portal);
Scoped<GError> error;
Scoped<GVariant> variant(
g_dbus_proxy_call_finish(proxy, result, error.receive()));
if (!variant) {
if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
RTC_LOG(LS_ERROR) << "Failed to session: " << error->message;
portal->OnPortalDone(RequestResponse::kError);
return;
}
RTC_LOG(LS_INFO) << "Initializing the session.";
Scoped<char> handle;
g_variant_get_child(variant.get(), /*index=*/0, /*format_string=*/"o",
&handle);
if (!handle) {
RTC_LOG(LS_ERROR) << "Failed to initialize the session.";
portal->UnsubscribeSignalHandlers();
portal->OnPortalDone(RequestResponse::kError);
return;
}
}
template <typename T>
void SessionRequestResponseSignalHelper(
const SessionClosedSignalHandler session_close_signal_handler,
T* portal,
GDBusConnection* connection,
std::string& session_handle,
GVariant* parameters,
guint& session_closed_signal_id) {
uint32_t portal_response;
Scoped<GVariant> response_data;
g_variant_get(parameters, /*format_string=*/"(u@a{sv})", &portal_response,
response_data.receive());
Scoped<GVariant> g_session_handle(
g_variant_lookup_value(response_data.get(), /*key=*/"session_handle",
/*expected_type=*/nullptr));
session_handle = g_variant_dup_string(
/*value=*/g_session_handle.get(), /*length=*/nullptr);
if (session_handle.empty() || portal_response) {
RTC_LOG(LS_ERROR) << "Failed to request the session subscription.";
portal->OnPortalDone(RequestResponse::kError);
return;
}
session_closed_signal_id = g_dbus_connection_signal_subscribe(
connection, kDesktopBusName, kSessionInterfaceName, /*member=*/"Closed",
session_handle.c_str(), /*arg0=*/nullptr, G_DBUS_SIGNAL_FLAGS_NONE,
session_close_signal_handler, portal, /*user_data_free_func=*/nullptr);
}
template <typename T>
void StartRequestedHandler(T* portal, GDBusProxy* proxy, GAsyncResult* result) {
RTC_DCHECK(portal);
Scoped<GError> error;
Scoped<GVariant> variant(
g_dbus_proxy_call_finish(proxy, result, error.receive()));
if (!variant) {
if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
RTC_LOG(LS_ERROR) << "Failed to start the portal session: "
<< error->message;
portal->OnPortalDone(RequestResponse::kError);
return;
}
Scoped<char> handle;
g_variant_get_child(variant.get(), 0, "o", handle.receive());
if (!handle) {
RTC_LOG(LS_ERROR) << "Failed to initialize the start portal session.";
portal->UnsubscribeSignalHandlers();
portal->OnPortalDone(RequestResponse::kError);
return;
}
RTC_LOG(LS_INFO) << "Subscribed to the start signal.";
}
} // namespace xdg_portal
} // namespace webrtc