From 53d726055275f98cccd9a48cac5175550899b235 Mon Sep 17 00:00:00 2001 From: Salman Malik Date: Fri, 15 Apr 2022 00:54:00 +0000 Subject: [PATCH] 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 Commit-Queue: Salman Malik Cr-Commit-Position: refs/heads/main@{#36557} --- modules/desktop_capture/BUILD.gn | 2 + .../linux/wayland/base_capturer_pipewire.h | 1 + .../linux/wayland/portal_request_response.h | 34 +++++ .../screen_capture_portal_interface.cc | 118 ++++++++++++++++ .../wayland/screen_capture_portal_interface.h | 46 +++++- .../linux/wayland/screencast_portal.cc | 31 +++-- .../linux/wayland/screencast_portal.h | 9 +- .../linux/wayland/xdg_desktop_portal_utils.h | 131 +----------------- 8 files changed, 221 insertions(+), 151 deletions(-) create mode 100644 modules/desktop_capture/linux/wayland/portal_request_response.h create mode 100644 modules/desktop_capture/linux/wayland/screen_capture_portal_interface.cc diff --git a/modules/desktop_capture/BUILD.gn b/modules/desktop_capture/BUILD.gn index b1d89497d7..b81ece62f4 100644 --- a/modules/desktop_capture/BUILD.gn +++ b/modules/desktop_capture/BUILD.gn @@ -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", diff --git a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h index da0ce95a01..b6f4ba8a1a 100644 --- a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h +++ b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h @@ -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" diff --git a/modules/desktop_capture/linux/wayland/portal_request_response.h b/modules/desktop_capture/linux/wayland/portal_request_response.h new file mode 100644 index 0000000000..dde9ac5eff --- /dev/null +++ b/modules/desktop_capture/linux/wayland/portal_request_response.h @@ -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_ diff --git a/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.cc b/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.cc new file mode 100644 index 0000000000..c76fa73dad --- /dev/null +++ b/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.cc @@ -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 + +#include "rtc_base/logging.h" + +namespace webrtc { +namespace xdg_portal { + +void ScreenCapturePortalInterface::RequestSessionUsingProxy( + GAsyncResult* result) { + Scoped 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 error; + Scoped 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 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 response_data; + g_variant_get(parameters, /*format_string=*/"(u@a{sv})", &portal_response, + response_data.receive()); + Scoped 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 error; + Scoped 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 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 diff --git a/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h b/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h index efb2ff2e2b..775ed1facc 100644 --- a/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h +++ b/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h @@ -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 + +#include + +#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 diff --git a/modules/desktop_capture/linux/wayland/screencast_portal.cc b/modules/desktop_capture/linux/wayland/screencast_portal.cc index eeb691ac3c..c70628bcf2 100644 --- a/modules/desktop_capture/linux/wayland/screencast_portal.cc +++ b/modules/desktop_capture/linux/wayland/screencast_portal.cc @@ -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( - static_cast(user_data), gobject, result); + static_cast(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(user_data), proxy, - result, user_data); + static_cast(user_data)->OnSessionRequestResult(proxy, + result); } // static @@ -155,9 +158,9 @@ void ScreenCastPortal::OnSessionRequestResponseSignal( gpointer user_data) { ScreenCastPortal* that = static_cast(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(user_data), proxy, - result); + static_cast(user_data)->OnStartRequestResult(proxy, + result); } // static diff --git a/modules/desktop_capture/linux/wayland/screencast_portal.h b/modules/desktop_capture/linux/wayland/screencast_portal.h index a1156947d6..73a6038c3c 100644 --- a/modules/desktop_capture/linux/wayland/screencast_portal.h +++ b/modules/desktop_capture/linux/wayland/screencast_portal.h @@ -15,6 +15,7 @@ #include +#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); diff --git a/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h b/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h index c4b3a58096..7f1ef0b8d5 100644 --- a/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h +++ b/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h @@ -17,6 +17,7 @@ #include #include +#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 -void RequestSessionUsingProxy(T* portal, - GObject* gobject, - GAsyncResult* result) { - RTC_DCHECK(portal); - Scoped 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 -void SessionRequestHandler(T* portal, - GDBusProxy* proxy, - GAsyncResult* result, - gpointer user_data) { - RTC_DCHECK(portal); - - Scoped error; - Scoped 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 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 -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 response_data; - g_variant_get(parameters, /*format_string=*/"(u@a{sv})", &portal_response, - response_data.receive()); - Scoped 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 -void StartRequestedHandler(T* portal, GDBusProxy* proxy, GAsyncResult* result) { - RTC_DCHECK(portal); - Scoped error; - Scoped 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 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