diff --git a/include/screencast_common.h b/include/screencast_common.h index c56f25be..978034c9 100644 --- a/include/screencast_common.h +++ b/include/screencast_common.h @@ -9,7 +9,8 @@ // this seems to be right based on // https://github.com/flatpak/xdg-desktop-portal/blob/309a1fc0cf2fb32cceb91dbc666d20cf0a3202c2/src/screen-cast.c#L955 -#define XDP_CAST_PROTO_VER 3 +#define XDP_CAST_PROTO_VER 4 +#define XDP_CAST_DATA_VER 1 enum cursor_modes { HIDDEN = 1, @@ -121,6 +122,14 @@ struct xdpw_screencast_instance { struct fps_limit_state fps_limit; }; +struct xdpw_screencast_session_data { + uint32_t version; + struct xdpw_screencast_instance *screencast_instance; + char *output_name; + uint32_t cursor_mode; + uint32_t persist_mode; +}; + struct xdpw_wlr_output { struct wl_list link; uint32_t id; diff --git a/include/xdpw.h b/include/xdpw.h index 105877dc..baf1dde1 100644 --- a/include/xdpw.h +++ b/include/xdpw.h @@ -36,7 +36,7 @@ struct xdpw_session { struct wl_list link; sd_bus_slot *slot; char *session_handle; - struct xdpw_screencast_instance *screencast_instance; + struct xdpw_screencast_session_data screencast_data; }; typedef void (*xdpw_event_loop_timer_func_t)(void *data); diff --git a/src/core/session.c b/src/core/session.c index 0a3884d9..ef40ebac 100644 --- a/src/core/session.c +++ b/src/core/session.c @@ -61,7 +61,7 @@ void xdpw_session_destroy(struct xdpw_session *sess) { if (!sess) { return; } - struct xdpw_screencast_instance *cast = sess->screencast_instance; + struct xdpw_screencast_instance *cast = sess->screencast_data.screencast_instance; if (cast) { assert(cast->refcount > 0); --cast->refcount; @@ -71,6 +71,7 @@ void xdpw_session_destroy(struct xdpw_session *sess) { cast->quit = true; } } + free(sess->screencast_data.output_name); sd_bus_slot_unref(sess->slot); wl_list_remove(&sess->link); diff --git a/src/screencast/screencast.c b/src/screencast/screencast.c index e700c04e..ebcda8d6 100644 --- a/src/screencast/screencast.c +++ b/src/screencast/screencast.c @@ -94,7 +94,7 @@ void xdpw_screencast_instance_destroy(struct xdpw_screencast_instance *cast) { free(cast); } -bool setup_outputs(struct xdpw_screencast_context *ctx, struct xdpw_session *sess, bool with_cursor) { +bool setup_outputs(struct xdpw_screencast_context *ctx, struct xdpw_session *sess) { struct xdpw_wlr_output *output, *tmp_o; wl_list_for_each_reverse_safe(output, tmp_o, &ctx->output_list, link) { @@ -102,13 +102,21 @@ bool setup_outputs(struct xdpw_screencast_context *ctx, struct xdpw_session *ses output->make, output->model, output->id, output->name); } - struct xdpw_wlr_output *out; - out = xdpw_wlr_output_chooser(ctx); + struct xdpw_wlr_output *out = NULL; + if (sess->screencast_data.output_name) { + out = xdpw_wlr_output_find_by_name(&ctx->output_list, sess->screencast_data.output_name); + } + if (!out) { + out = xdpw_wlr_output_chooser(ctx); + } if (!out) { logprint(ERROR, "wlroots: no output found"); return false; } + // default to embedded cursor mode if not specified + bool with_cursor = sess->screencast_data.cursor_mode & HIDDEN ? false : true; + struct xdpw_screencast_instance *cast, *tmp_c; wl_list_for_each_reverse_safe(cast, tmp_c, &ctx->screencast_instances, link) { logprint(INFO, "xdpw: existing screencast instance: %d %s cursor", @@ -122,7 +130,7 @@ bool setup_outputs(struct xdpw_screencast_context *ctx, struct xdpw_session *ses "but is already scheduled for destruction, skipping"); } else { - sess->screencast_instance = cast; + sess->screencast_data.screencast_instance = cast; ++cast->refcount; } logprint(INFO, "xdpw: screencast instance %p now has %d references", @@ -130,13 +138,13 @@ bool setup_outputs(struct xdpw_screencast_context *ctx, struct xdpw_session *ses } } - if (!sess->screencast_instance) { - sess->screencast_instance = calloc(1, sizeof(struct xdpw_screencast_instance)); - xdpw_screencast_instance_init(ctx, sess->screencast_instance, + if (!sess->screencast_data.screencast_instance) { + sess->screencast_data.screencast_instance = calloc(1, sizeof(struct xdpw_screencast_instance)); + xdpw_screencast_instance_init(ctx, sess->screencast_data.screencast_instance, out, with_cursor); } - logprint(INFO, "wlroots: output: %s", - sess->screencast_instance->target_output->name); + sess->screencast_data.output_name = strdup(sess->screencast_data.screencast_instance->target_output->name); + logprint(INFO, "wlroots: output: %s", sess->screencast_data.output_name); return true; @@ -252,9 +260,6 @@ static int method_screencast_select_sources(sd_bus_message *msg, void *data, logprint(INFO, "dbus: select sources method invoked"); - // default to embedded cursor mode if not specified - bool cursor_embedded = true; - char *request_handle, *session_handle, *app_id; ret = sd_bus_message_read(msg, "oos", &request_handle, &session_handle, &app_id); if (ret < 0) { @@ -269,6 +274,19 @@ static int method_screencast_select_sources(sd_bus_message *msg, void *data, logprint(INFO, "dbus: session_handle: %s", session_handle); logprint(INFO, "dbus: app_id: %s", app_id); + sess = NULL; + wl_list_for_each_reverse_safe(sess, tmp_s, &state->xdpw_sessions, link) { + if (strcmp(sess->session_handle, session_handle) == 0) { + logprint(DEBUG, "dbus: select sources: found matching session %s", sess->session_handle); + break; + } + } + + if (!sess) { + logprint(WARN, "dbus: select sources: no matching session %s found", sess->session_handle); + goto error; + } + char *key; int innerRet = 0; while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { @@ -290,16 +308,59 @@ static int method_screencast_select_sources(sd_bus_message *msg, void *data, } logprint(INFO, "dbus: option types:%x", mask); } else if (strcmp(key, "cursor_mode") == 0) { - uint32_t cursor_mode; - sd_bus_message_read(msg, "v", "u", &cursor_mode); - if (cursor_mode & HIDDEN) { - cursor_embedded = false; - } - if (cursor_mode & METADATA) { + sd_bus_message_read(msg, "v", "u", &sess->screencast_data.cursor_mode); + if (sess->screencast_data.cursor_mode & METADATA) { logprint(ERROR, "dbus: unsupported cursor mode requested, cancelling"); goto error; } - logprint(INFO, "dbus: option cursor_mode:%x", cursor_mode); + logprint(INFO, "dbus: option cursor_mode:%x", sess->screencast_data.cursor_mode); + } else if (strcmp(key, "restore_data") == 0) { + logprint(INFO, "dbus: restore data available"); + char *portal_vendor; + uint32_t restore_data_version; + innerRet = sd_bus_message_enter_container(msg, 'v', "(suv)"); + if (innerRet < 0) { + return innerRet; + } + if (strcmp(portal_vendor, "wlroots") != 0) { + sd_bus_message_read(msg, "v", "su", &portal_vendor, &restore_data_version); + logprint(INFO, "dbus: skipping restore_data from another vendor (%s)", portal_vendor); + continue; + } + innerRet = sd_bus_message_enter_container(msg, 'v', "a{sv}"); + if (innerRet < 0) { + return innerRet; + } + innerRet = sd_bus_message_enter_container(msg, 'a', "{sv}"); + if (innerRet < 0) { + return innerRet; + } + logprint(INFO, "dbus: restoring session from data"); + int rdRet; + char *rdKey; + while ((innerRet = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { + rdRet = sd_bus_message_read(msg, "s", &rdKey); + if (rdRet < 0) { + return rdRet; + } + if (strcmp(rdKey, "output_name") == 0) { + char *output_name; + sd_bus_message_read(msg, "v", "s", &output_name); + if (output_name) { + sess->screencast_data.output_name = strdup(output_name); + } + logprint(INFO, "dbus: option restore_data.output_name:%s", sess->screencast_data.output_name); + } else { + logprint(WARN, "dbus: unknown option %s", rdKey); + sd_bus_message_skip(msg, "v"); + } + } + if (innerRet < 0) { + return innerRet; + } + } else if (strcmp(key, "persist_mode") == 0) { + sd_bus_message_read(msg, "v", "u", &sess->screencast_data.persist_mode); + logprint(INFO, "dbus: option persist_mode:%u", sess->screencast_data.persist_mode); } else { logprint(WARN, "dbus: unknown option %s", key); sd_bus_message_skip(msg, "v"); @@ -319,12 +380,7 @@ static int method_screencast_select_sources(sd_bus_message *msg, void *data, } bool output_selection_canceled = 1; - wl_list_for_each_reverse_safe(sess, tmp_s, &state->xdpw_sessions, link) { - if (strcmp(sess->session_handle, session_handle) == 0) { - logprint(DEBUG, "dbus: select sources: found matching session %s", sess->session_handle); - output_selection_canceled = !setup_outputs(ctx, sess, cursor_embedded); - } - } + output_selection_canceled = !setup_outputs(ctx, sess); ret = sd_bus_message_new_method_return(msg, &reply); if (ret < 0) { @@ -346,11 +402,8 @@ static int method_screencast_select_sources(sd_bus_message *msg, void *data, return 0; error: - wl_list_for_each_reverse_safe(sess, tmp_s, &state->xdpw_sessions, link) { - if (strcmp(sess->session_handle, session_handle) == 0) { - logprint(DEBUG, "dbus: select sources error: destroying matching session %s", sess->session_handle); - xdpw_session_destroy(sess); - } + if (sess) { + xdpw_session_destroy(sess); } ret = sd_bus_message_new_method_return(msg, &reply); @@ -419,14 +472,15 @@ static int method_screencast_start(sd_bus_message *msg, void *data, wl_list_for_each_reverse_safe(sess, tmp_s, &state->xdpw_sessions, link) { if (strcmp(sess->session_handle, session_handle) == 0) { logprint(DEBUG, "dbus: start: found matching session %s", sess->session_handle); - cast = sess->screencast_instance; + cast = sess->screencast_data.screencast_instance; + break; } } if (!cast) { return -1; } - if (!cast->initialized) { + start_screencast(cast); } @@ -444,12 +498,32 @@ static int method_screencast_start(sd_bus_message *msg, void *data, } logprint(DEBUG, "dbus: start: returning node %d", (int)cast->node_id); - ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_SUCCESS, 1, - "streams", "a(ua{sv})", 1, - cast->node_id, 3, - "position", "(ii)", 0, 0, - "size", "(ii)", cast->screencopy_frame.width, cast->screencopy_frame.height, - "source_type", "u", 1 << MONITOR); + logprint(ERROR, "dbus: start: returning output (%s)", sess->screencast_data.output_name); + logprint(ERROR, "dbus: start: returning output (%s)", cast->target_output->name); + switch (sess->screencast_data.persist_mode) { + case 1: + case 2: + logprint(DEBUG, "Persistmode"); + ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_SUCCESS, 3, + "streams", "a(ua{sv})", 1, + cast->node_id, 3, + "position", "(ii)", 0, 0, + "size", "(ii)", cast->screencopy_frame.width, cast->screencopy_frame.height, + "source_type", "u", 1 << MONITOR, + "persist_mode", "u", sess->screencast_data.persist_mode, + "restore_data", "(suv)", + "wlroots", XDP_CAST_DATA_VER, + "a{sv}", 1, "output_name", "s", sess->screencast_data.output_name); + break; + default: + logprint(DEBUG, "No persistmode"); + ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_SUCCESS, 1, + "streams", "a(ua{sv})", 1, + cast->node_id, 3, + "position", "(ii)", 0, 0, + "size", "(ii)", cast->screencopy_frame.width, cast->screencopy_frame.height, + "source_type", "u", 1 << MONITOR); + } if (ret < 0) { return ret;