blob: 271e0844637fde2eba4608041d39b2bcc2ce8170 [file] [log] [blame] [edit]
/*
* 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/portal/xdg_desktop_portal_utils.h"
#include <string>
#include "absl/strings/string_view.h"
#include "modules/portal/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";
}
}
RequestResponse RequestResponseFromPortalResponse(uint32_t portal_response) {
// See:
// https://docs.flatpak.org/en/latest/portal-api-reference.html#gdbus-signal-org-freedesktop-portal-Request.Response
switch (portal_response) {
case 0:
return RequestResponse::kSuccess;
case 1:
return RequestResponse::kUserCancelled;
case 2:
return RequestResponse::kError;
default:
return RequestResponse::kUnknown;
}
}
std::string PrepareSignalHandle(absl::string_view 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(), "/",
std::string(token).c_str(), /*end of varargs*/ nullptr);
return handle;
}
uint32_t SetupRequestResponseSignal(absl::string_view object_path,
const GDBusSignalCallback callback,
gpointer user_data,
GDBusConnection* connection) {
return g_dbus_connection_signal_subscribe(
connection, kDesktopBusName, kRequestInterfaceName, "Response",
std::string(object_path).c_str(), /*arg0=*/nullptr,
G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, callback, user_data,
/*user_data_free_func=*/nullptr);
}
void RequestSessionProxy(absl::string_view 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, std::string(interface_name).c_str(),
cancellable,
reinterpret_cast<GAsyncReadyCallback>(proxy_request_callback), user_data);
}
void SetupSessionRequestHandlers(
absl::string_view 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", static_cast<int>(portal_prefix.size()),
portal_prefix.data(), 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", static_cast<int>(portal_prefix.size()),
portal_prefix.data(), 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(
absl::string_view prefix,
absl::string_view 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", static_cast<int>(prefix.size()), prefix.data(),
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})", std::string(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(absl::string_view session_handle,
GDBusProxy* proxy,
GCancellable* cancellable,
GDBusConnection* connection) {
if (!session_handle.empty()) {
Scoped<GDBusMessage> message(g_dbus_message_new_method_call(
kDesktopBusName, std::string(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