blob: 4fdf54abce0d829e3486c217f68649d8c6ac92f8 [file] [log] [blame]
/*
* 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/xdg_desktop_portal_utils.h"
#include "modules/desktop_capture/linux/wayland/scoped_glib.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace xdg_portal {
std::string RequestResponseToString(RequestResponse request) {
switch (request) {
case RequestResponse::kUnknown:
return "kUnknown";
case RequestResponse::kSuccess:
return "kSuccess";
case RequestResponse::kUserCancelled:
return "kUserCancelled";
case RequestResponse::kError:
return "kError";
default:
return "Uknown";
}
}
std::string PrepareSignalHandle(const char* token,
GDBusConnection* connection) {
Scoped<char> sender(
g_strdup(g_dbus_connection_get_unique_name(connection) + 1));
for (int i = 0; sender.get()[i]; ++i) {
if (sender.get()[i] == '.') {
sender.get()[i] = '_';
}
}
const char* handle = g_strconcat(kDesktopRequestObjectPath, "/", sender.get(),
"/", token, /*end of varargs*/ nullptr);
return handle;
}
uint32_t SetupRequestResponseSignal(const char* object_path,
const GDBusSignalCallback callback,
gpointer user_data,
GDBusConnection* connection) {
return g_dbus_connection_signal_subscribe(
connection, kDesktopBusName, kRequestInterfaceName, "Response",
object_path, /*arg0=*/nullptr, G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
callback, user_data, /*user_data_free_func=*/nullptr);
}
void RequestSessionProxy(const char* interface_name,
const ProxyRequestCallback proxy_request_callback,
GCancellable* cancellable,
gpointer user_data) {
g_dbus_proxy_new_for_bus(
G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, /*info=*/nullptr,
kDesktopBusName, kDesktopObjectPath, interface_name, cancellable,
reinterpret_cast<GAsyncReadyCallback>(proxy_request_callback), user_data);
}
void SetupSessionRequestHandlers(
const std::string& portal_prefix,
const SessionRequestCallback session_request_callback,
const SessionRequestResponseSignalHandler request_response_signale_handler,
GDBusConnection* connection,
GDBusProxy* proxy,
GCancellable* cancellable,
std::string& portal_handle,
guint& session_request_signal_id,
gpointer user_data) {
GVariantBuilder builder;
Scoped<char> variant_string;
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
variant_string = g_strdup_printf("%s_session%d", portal_prefix.c_str(),
g_random_int_range(0, G_MAXINT));
g_variant_builder_add(&builder, "{sv}", "session_handle_token",
g_variant_new_string(variant_string.get()));
variant_string = g_strdup_printf("%s_%d", portal_prefix.c_str(),
g_random_int_range(0, G_MAXINT));
g_variant_builder_add(&builder, "{sv}", "handle_token",
g_variant_new_string(variant_string.get()));
portal_handle = PrepareSignalHandle(variant_string.get(), connection);
session_request_signal_id = SetupRequestResponseSignal(
portal_handle.c_str(), request_response_signale_handler, user_data,
connection);
RTC_LOG(LS_INFO) << "Desktop session requested.";
g_dbus_proxy_call(
proxy, "CreateSession", g_variant_new("(a{sv})", &builder),
G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable,
reinterpret_cast<GAsyncReadyCallback>(session_request_callback),
user_data);
}
void StartSessionRequest(
const std::string& prefix,
const std::string session_handle,
const StartRequestResponseSignalHandler signal_handler,
const SessionStartRequestedHandler session_started_handler,
GDBusProxy* proxy,
GDBusConnection* connection,
GCancellable* cancellable,
guint& start_request_signal_id,
std::string& start_handle,
gpointer user_data) {
GVariantBuilder builder;
Scoped<char> variant_string;
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
variant_string =
g_strdup_printf("%s%d", prefix.c_str(), g_random_int_range(0, G_MAXINT));
g_variant_builder_add(&builder, "{sv}", "handle_token",
g_variant_new_string(variant_string.get()));
start_handle = PrepareSignalHandle(variant_string.get(), connection);
start_request_signal_id = SetupRequestResponseSignal(
start_handle.c_str(), signal_handler, user_data, connection);
// "Identifier for the application window", this is Wayland, so not "x11:...".
const char parent_window[] = "";
RTC_LOG(LS_INFO) << "Starting the portal session.";
g_dbus_proxy_call(
proxy, "Start",
g_variant_new("(osa{sv})", session_handle.c_str(), parent_window,
&builder),
G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable,
reinterpret_cast<GAsyncReadyCallback>(session_started_handler),
user_data);
}
void TearDownSession(std::string session_handle,
GDBusProxy* proxy,
GCancellable* cancellable,
GDBusConnection* connection) {
if (!session_handle.empty()) {
Scoped<GDBusMessage> message(
g_dbus_message_new_method_call(kDesktopBusName, session_handle.c_str(),
kSessionInterfaceName, "Close"));
if (message.get()) {
Scoped<GError> error;
g_dbus_connection_send_message(connection, message.get(),
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
/*out_serial=*/nullptr, error.receive());
if (error.get()) {
RTC_LOG(LS_ERROR) << "Failed to close the session: " << error->message;
}
}
}
if (cancellable) {
g_cancellable_cancel(cancellable);
g_object_unref(cancellable);
}
if (proxy) {
g_object_unref(proxy);
}
}
} // namespace xdg_portal
} // namespace webrtc