47#include <gio/gunixfdlist.h>
48#include <glib-object.h>
50#include <glib/gtypes.h>
51#include <initializer_list>
58#define MOD_NAME "[dbus] "
61struct request_path_t {
65 static request_path_t create(
const std::string &
name) {
68 auto token = std::string(
"uv") + std::to_string(token_counter);
69 request_path_t result = {
71 .path = std::string(
"/org/freedesktop/portal/desktop/request/") +
name +
"/" + token
79 static unsigned int token_counter;
82unsigned int request_path_t::token_counter = 0;
84struct session_path_t {
88 static session_path_t create(
const std::string &
name) {
91 auto token = std::string(
"uv") + std::to_string(token_counter);
94 .path = std::string(
"/org/freedesktop/portal/desktop/session/") +
name +
"/" + token
99 static unsigned int token_counter;
102unsigned int session_path_t::token_counter = 0;
104struct GVariant_deleter {
void operator()(GVariant *a) { g_variant_unref(a); } };
105using GVariant_uniq = std::unique_ptr<GVariant, GVariant_deleter>;
112 template<auto callback>
114 std::initializer_list<GVariant*> arguments,
115 GVariantBuilder ¶ms_builder);
121 static void started(uint32_t
response, GVariant *results,
void *user_data);
192 GError *error =
nullptr;
194 dbus_loop = g_main_loop_new(
nullptr,
false);
195 connection = g_bus_get_sync(G_BUS_TYPE_SESSION,
nullptr, &error);
196 g_assert_no_error(error);
203 "org.freedesktop.portal.Desktop",
204 "/org/freedesktop/portal/desktop",
205 "org.freedesktop.portal.ScreenCast",
nullptr, &error);
206 g_assert_no_error(error);
215 "org.freedesktop.portal.Desktop",
217 "org.freedesktop.portal.Session",
218 "Close",
nullptr,
nullptr,
219 G_DBUS_CALL_FLAGS_NONE, -1,
nullptr,
nullptr,
229 const gchar *sender_name,
230 const gchar *object_path,
231 const gchar *interface_name,
232 const gchar *signal_name,
233 GVariant *parameters,
237 (void) interface_name;
241 GVariant_uniq results;
242 g_variant_get(parameters,
"(u@a{sv})", &
response,
out_ptr(results).
operator GVariant**());
246 g_dbus_connection_call(connection,
"org.freedesktop.portal.Desktop",
247 object_path,
"org.freedesktop.portal.Request",
"Close",
248 nullptr,
nullptr, G_DBUS_CALL_FLAGS_NONE, -1,
nullptr,
nullptr,
nullptr);
252template<auto callback>
254 std::initializer_list<GVariant*> arguments,
255 GVariantBuilder ¶ms_builder)
257 assert(method_name !=
nullptr);
258 request_path_t request_path = request_path_t::create(
sender_name());
261 g_dbus_connection_signal_subscribe(
connection,
"org.freedesktop.portal.Desktop",
262 "org.freedesktop.portal.Request",
264 request_path.path.c_str(),
266 G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
271 auto call_finished = [](GObject *source_object, GAsyncResult *result, gpointer user_data) {
273 GError *error =
nullptr;
274 GVariant_uniq result_finished(g_dbus_proxy_call_finish(G_DBUS_PROXY(source_object), result, &error));
276 if(error !=
nullptr){
277 s->promise.set_value(error->message);
281 const char *path =
nullptr;
282 g_variant_get(result_finished.get(),
"(o)", &path);
287 g_variant_builder_add(¶ms_builder,
"{sv}",
"handle_token", g_variant_new_string(request_path.token.c_str()));
289 GVariantBuilder args_builder;
290 g_variant_builder_init(&args_builder, G_VARIANT_TYPE_TUPLE);
291 for(GVariant* arg : arguments){
292 g_variant_builder_add_value(&args_builder, arg);
294 g_variant_builder_add_value(&args_builder, g_variant_builder_end(¶ms_builder));
296 g_dbus_proxy_call(
screencast_proxy, method_name, g_variant_builder_end(&args_builder), G_DBUS_CALL_FLAGS_NONE, -1,
nullptr, call_finished,
this);
300 [[maybe_unused]]
const gchar *sender_name,
301 [[maybe_unused]]
const gchar *object_path,
302 [[maybe_unused]]
const gchar *interface_name,
303 [[maybe_unused]]
const gchar *signal_name,
304 [[maybe_unused]] GVariant *parameters,
305 [[maybe_unused]] gpointer user_data)
316 GError *error =
nullptr;
317 GUnixFDList *fd_list =
nullptr;
319 GVariant_uniq result(g_dbus_proxy_call_with_unix_fd_list_finish(G_DBUS_PROXY(
source), &fd_list, res, &error));
320 g_assert_no_error(error);
323 g_variant_get(result.get(),
"(h)", &handle);
326 s->pw_fd = g_unix_fd_list_get(fd_list, handle, &error);
327 g_assert_no_error(error);
329 s->promise.set_value(
"");
337 s->promise.set_value(
"failed to start (dialog cancelled by user)");
340 s->promise.set_value(
"failed to start (unknown reason)");
344 const char *restore_token =
nullptr;
345 if (g_variant_lookup(results,
"restore_token",
"s", &restore_token)){
346 std::ofstream file(s->restore_file);
350 GVariant *streams = g_variant_lookup_value(results,
"streams", G_VARIANT_TYPE_ARRAY);
351 GVariant *stream_properties;
353 g_variant_iter_init(&iter, streams);
354 assert(g_variant_iter_n_children(&iter) == 1);
355 bool got_item = g_variant_iter_loop(&iter,
"(u@a{sv})", &s->pw_node, &stream_properties);
358 GVariantBuilder builder;
359 g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
360 g_dbus_proxy_call_with_unix_fd_list(s->proxy(),
"OpenPipeWireRemote",
361 g_variant_new(
"(oa{sv})", s->session_path().c_str(), &builder),
362 G_DBUS_CALL_FLAGS_NONE, -1,
364 g_variant_builder_clear(&builder);
368 gchar *pretty = g_variant_print(results,
true);
370 g_free((gpointer) pretty);
373 s->
promise.set_value(
"Failed to select sources");
379 g_variant_builder_init(&
options, G_VARIANT_TYPE_VARDICT);
386 s->
promise.set_value(
"Failed to create session");
390 const char *session_handle =
nullptr;
391 g_variant_lookup(results,
"session_handle",
"s", &session_handle);
398 g_variant_builder_init(&
params, G_VARIANT_TYPE_VARDICT);
399 g_variant_builder_add(&
params,
"{sv}",
"types", g_variant_new_uint32(3));
400 g_variant_builder_add(&
params,
"{sv}",
"multiple", g_variant_new_boolean(
false));
402 g_variant_builder_add(&
params,
"{sv}",
"cursor_mode", g_variant_new_uint32(2));
409 std::ostringstream ss;
415 g_variant_builder_add(&
params,
"{sv}",
"persist_mode", g_variant_new_uint32(2));
417 g_variant_builder_add(&
params,
"{sv}",
"restore_token", g_variant_new_string(token.c_str()));
430 "org.freedesktop.portal.Session",
434 G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
440 g_variant_builder_init(&
params, G_VARIANT_TYPE_VARDICT);
441 g_variant_builder_add(&
params,
"{sv}",
"session_handle_token", g_variant_new_string(
session_token().c_str()));
448ScreenCastPortal::ScreenCastPortal():
454ScreenCastPortal::~ScreenCastPortal(){
455 if(thread.joinable()){
461ScreenCastPortalResult ScreenCastPortal::run(std::string restore_file,
bool show_cursor){
462 auto future = impl->get_future();
465 restore_file, show_cursor);
467 auto error = future.get();
470 return {-1, UINT32_MAX};
473 return {impl->get_pw_fd(), impl->get_pw_node()};
GDBusConnection * dbus_connection() const
static void pipewire_opened(GObject *source, GAsyncResult *res, void *user_data)
void call_with_request(const char *method_name, std::initializer_list< GVariant * > arguments, GVariantBuilder ¶ms_builder)
static void session_created(uint32_t response, GVariant *results, ScreenCastPortal_impl *s)
const std::string & session_token() const
GDBusConnection * connection
static constexpr uint32_t REQUEST_RESPONSE_CANCELLED_BY_USER
static void sources_selected(uint32_t response, GVariant *results, ScreenCastPortal_impl *s)
void run(std::string restore_file, bool show_cursor)
const std::string & session_path() const
static constexpr uint32_t REQUEST_RESPONSE_OTHER_ERROR
const std::string & sender_name() const
static constexpr uint32_t REQUEST_RESPONSE_OK
static void started(uint32_t response, GVariant *results, void *user_data)
GDBusProxy * screencast_proxy
std::future< std::string > get_future()
std::promise< std::string > promise
static void on_portal_session_closed(GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data)
void CallbackWrapper(GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data)
void log_msg(int level, const char *format,...)
#define LOG_LEVEL_INFO
normal reporting
#define LOG_LEVEL_ERROR
general errors
#define LOG_LEVEL_VERBOSE
static struct @19 params[100]
HRESULT __stdcall(* func)()