Bind toplevel protos once before first screencast

Also style.
This commit is contained in:
vaxerski 2023-02-01 17:49:29 +00:00
parent b03b1c2f27
commit ee73fca9cc
3 changed files with 1232 additions and 1235 deletions

View file

@ -6,134 +6,134 @@
#include <spa/param/video/format-utils.h> #include <spa/param/video/format-utils.h>
#include <wayland-client-protocol.h> #include <wayland-client-protocol.h>
#include <xf86drm.h> #include <xf86drm.h>
#include "hyprland-toplevel-export-v1-client-protocol.h"
#include "fps_limit.h" #include "fps_limit.h"
#include "hyprland-toplevel-export-v1-client-protocol.h"
// this seems to be right based on // this seems to be right based on
// https://github.com/flatpak/xdg-desktop-portal/blob/309a1fc0cf2fb32cceb91dbc666d20cf0a3202c2/src/screen-cast.c#L955 // https://github.com/flatpak/xdg-desktop-portal/blob/309a1fc0cf2fb32cceb91dbc666d20cf0a3202c2/src/screen-cast.c#L955
#define XDP_CAST_PROTO_VER 2 #define XDP_CAST_PROTO_VER 2
enum cursor_modes { enum cursor_modes {
HIDDEN = 1, HIDDEN = 1,
EMBEDDED = 2, EMBEDDED = 2,
METADATA = 4, METADATA = 4,
}; };
enum source_types { enum source_types {
MONITOR = 1, MONITOR = 1,
WINDOW = 2, WINDOW = 2,
}; };
enum buffer_type { enum buffer_type {
WL_SHM = 0, WL_SHM = 0,
DMABUF = 1, DMABUF = 1,
}; };
enum xdpw_chooser_types { enum xdpw_chooser_types {
XDPW_CHOOSER_DEFAULT, XDPW_CHOOSER_DEFAULT,
XDPW_CHOOSER_NONE, XDPW_CHOOSER_NONE,
XDPW_CHOOSER_SIMPLE, XDPW_CHOOSER_SIMPLE,
XDPW_CHOOSER_DMENU, XDPW_CHOOSER_DMENU,
}; };
enum xdpw_frame_state { enum xdpw_frame_state {
XDPW_FRAME_STATE_NONE, XDPW_FRAME_STATE_NONE,
XDPW_FRAME_STATE_STARTED, XDPW_FRAME_STATE_STARTED,
XDPW_FRAME_STATE_RENEG, XDPW_FRAME_STATE_RENEG,
XDPW_FRAME_STATE_FAILED, XDPW_FRAME_STATE_FAILED,
XDPW_FRAME_STATE_SUCCESS, XDPW_FRAME_STATE_SUCCESS,
}; };
struct xdpw_output_chooser { struct xdpw_output_chooser {
enum xdpw_chooser_types type; enum xdpw_chooser_types type;
char *cmd; char *cmd;
}; };
struct xdpw_frame_damage { struct xdpw_frame_damage {
uint32_t x; uint32_t x;
uint32_t y; uint32_t y;
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
}; };
struct xdpw_frame { struct xdpw_frame {
bool y_invert; bool y_invert;
uint64_t tv_sec; uint64_t tv_sec;
uint32_t tv_nsec; uint32_t tv_nsec;
struct xdpw_frame_damage damage; struct xdpw_frame_damage damage;
struct xdpw_buffer *xdpw_buffer; struct xdpw_buffer *xdpw_buffer;
struct pw_buffer *pw_buffer; struct pw_buffer *pw_buffer;
}; };
struct xdpw_screencopy_frame_info { struct xdpw_screencopy_frame_info {
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
uint32_t size; uint32_t size;
uint32_t stride; uint32_t stride;
uint32_t format; uint32_t format;
}; };
struct xdpw_buffer { struct xdpw_buffer {
struct wl_list link; struct wl_list link;
enum buffer_type buffer_type; enum buffer_type buffer_type;
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
uint32_t format; uint32_t format;
int plane_count; int plane_count;
int fd[4]; int fd[4];
uint32_t size[4]; uint32_t size[4];
uint32_t stride[4]; uint32_t stride[4];
uint32_t offset[4]; uint32_t offset[4];
struct gbm_bo *bo; struct gbm_bo *bo;
struct wl_buffer *buffer; struct wl_buffer *buffer;
}; };
struct xdpw_format_modifier_pair { struct xdpw_format_modifier_pair {
uint32_t fourcc; uint32_t fourcc;
uint64_t modifier; uint64_t modifier;
}; };
struct xdpw_dmabuf_feedback_data { struct xdpw_dmabuf_feedback_data {
void *format_table_data; void *format_table_data;
uint32_t format_table_size; uint32_t format_table_size;
bool device_used; bool device_used;
}; };
struct xdpw_screencast_context { struct xdpw_screencast_context {
// xdpw
struct xdpw_state *state;
// xdpw // pipewire
struct xdpw_state *state; struct pw_context *pwr_context;
struct pw_core *core;
// pipewire // wlroots
struct pw_context *pwr_context; struct wl_list output_list;
struct pw_core *core; struct wl_registry *registry;
struct zwlr_screencopy_manager_v1 *screencopy_manager;
struct zxdg_output_manager_v1 *xdg_output_manager;
struct wl_shm *shm;
struct zwp_linux_dmabuf_v1 *linux_dmabuf;
struct zwp_linux_dmabuf_feedback_v1 *linux_dmabuf_feedback;
struct xdpw_dmabuf_feedback_data feedback_data;
struct wl_array format_modifier_pairs;
// wlroots // hyprland
struct wl_list output_list; struct hyprland_toplevel_export_manager_v1 *hyprland_toplevel_manager;
struct wl_registry *registry; struct zwlr_foreign_toplevel_manager_v1 *wlroots_toplevel_manager;
struct zwlr_screencopy_manager_v1 *screencopy_manager; struct wl_list toplevel_resource_list;
struct zxdg_output_manager_v1 *xdg_output_manager; int toplevel_mgr_bind;
struct wl_shm *shm;
struct zwp_linux_dmabuf_v1 *linux_dmabuf;
struct zwp_linux_dmabuf_feedback_v1 *linux_dmabuf_feedback;
struct xdpw_dmabuf_feedback_data feedback_data;
struct wl_array format_modifier_pairs;
// hyprland // gbm
struct hyprland_toplevel_export_manager_v1 *hyprland_toplevel_manager; struct gbm_device *gbm;
struct zwlr_foreign_toplevel_manager_v1 *wlroots_toplevel_manager;
struct wl_list toplevel_resource_list;
// gbm // sessions
struct gbm_device *gbm; struct wl_list screencast_instances;
// sessions
struct wl_list screencast_instances;
}; };
struct xdpw_wlr_output { struct xdpw_wlr_output {
@ -155,58 +155,58 @@ struct xdpw_share {
int y; int y;
int w; int w;
int h; int h;
int window_handle; int window_handle;
}; };
struct xdpw_screencast_instance { struct xdpw_screencast_instance {
// list // list
struct wl_list link; struct wl_list link;
// xdpw // xdpw
uint32_t refcount; uint32_t refcount;
struct xdpw_screencast_context *ctx; struct xdpw_screencast_context *ctx;
bool initialized; bool initialized;
struct xdpw_frame current_frame; struct xdpw_frame current_frame;
enum xdpw_frame_state frame_state; enum xdpw_frame_state frame_state;
struct wl_list buffer_list; struct wl_list buffer_list;
bool avoid_dmabufs; bool avoid_dmabufs;
// pipewire // pipewire
struct pw_stream *stream; struct pw_stream *stream;
struct spa_hook stream_listener; struct spa_hook stream_listener;
struct spa_video_info_raw pwr_format; struct spa_video_info_raw pwr_format;
uint32_t seq; uint32_t seq;
uint32_t node_id; uint32_t node_id;
bool pwr_stream_state; bool pwr_stream_state;
uint32_t framerate; uint32_t framerate;
// wlroots // wlroots
struct zwlr_screencopy_frame_v1 *frame_callback; struct zwlr_screencopy_frame_v1 *frame_callback;
struct xdpw_share target; struct xdpw_share target;
uint32_t max_framerate; uint32_t max_framerate;
struct zwlr_screencopy_frame_v1 *wlr_frame; struct zwlr_screencopy_frame_v1 *wlr_frame;
struct xdpw_screencopy_frame_info screencopy_frame_info[2]; struct xdpw_screencopy_frame_info screencopy_frame_info[2];
bool with_cursor; bool with_cursor;
int err; int err;
bool quit; bool quit;
bool teardown; bool teardown;
enum buffer_type buffer_type; enum buffer_type buffer_type;
// hyprland // hyprland
struct hyprland_toplevel_export_frame_v1 *frame_callback_hyprland; struct hyprland_toplevel_export_frame_v1 *frame_callback_hyprland;
struct hyprland_toplevel_export_frame_v1 *hyprland_frame; struct hyprland_toplevel_export_frame_v1 *hyprland_frame;
// fps limit // fps limit
struct fps_limit_state fps_limit; struct fps_limit_state fps_limit;
}; };
void randname(char *buf); void randname(char *buf);
struct gbm_device *xdpw_gbm_device_create(drmDevice *device); struct gbm_device *xdpw_gbm_device_create(drmDevice *device);
struct xdpw_buffer *xdpw_buffer_create(struct xdpw_screencast_instance *cast, struct xdpw_buffer *xdpw_buffer_create(struct xdpw_screencast_instance *cast,
enum buffer_type buffer_type, struct xdpw_screencopy_frame_info *frame_info); enum buffer_type buffer_type, struct xdpw_screencopy_frame_info *frame_info);
void xdpw_buffer_destroy(struct xdpw_buffer *buffer); void xdpw_buffer_destroy(struct xdpw_buffer *buffer);
bool wlr_query_dmabuf_modifiers(struct xdpw_screencast_context *ctx, uint32_t drm_format, bool wlr_query_dmabuf_modifiers(struct xdpw_screencast_context *ctx, uint32_t drm_format,
uint32_t num_modifiers, uint64_t *modifiers, uint32_t *max_modifiers); uint32_t num_modifiers, uint64_t *modifiers, uint32_t *max_modifiers);
enum wl_shm_format xdpw_format_wl_shm_from_drm_fourcc(uint32_t format); enum wl_shm_format xdpw_format_wl_shm_from_drm_fourcc(uint32_t format);
uint32_t xdpw_format_drm_fourcc_from_wl_shm(enum wl_shm_format format); uint32_t xdpw_format_drm_fourcc_from_wl_shm(enum wl_shm_format format);
enum spa_video_format xdpw_format_pw_from_drm_fourcc(uint32_t format); enum spa_video_format xdpw_format_pw_from_drm_fourcc(uint32_t format);

View file

@ -1,135 +1,132 @@
#include "screencast.h" #include "screencast.h"
#include <assert.h>
#include <drm_fourcc.h>
#include <errno.h> #include <errno.h>
#include <spa/utils/result.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <drm_fourcc.h>
#include <sys/wait.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <spa/utils/result.h> #include <sys/wait.h>
#include <unistd.h>
#include "logger.h"
#include "pipewire_screencast.h" #include "pipewire_screencast.h"
#include "wlr_screencast.h" #include "wlr_screencast.h"
#include "xdpw.h" #include "xdpw.h"
#include "logger.h"
static const char object_path[] = "/org/freedesktop/portal/desktop"; static const char object_path[] = "/org/freedesktop/portal/desktop";
static const char interface_name[] = "org.freedesktop.impl.portal.ScreenCast"; static const char interface_name[] = "org.freedesktop.impl.portal.ScreenCast";
void exec_with_shell(char *command) { void exec_with_shell(char *command) {
pid_t pid1 = fork(); pid_t pid1 = fork();
if (pid1 < 0) { if (pid1 < 0) {
perror("fork"); perror("fork");
return; return;
} else if (pid1 == 0) { } else if (pid1 == 0) {
pid_t pid2 = fork(); pid_t pid2 = fork();
if (pid2 < 0) { if (pid2 < 0) {
perror("fork"); perror("fork");
} else if (pid2 == 0) { } else if (pid2 == 0) {
char *const argv[] = { char *const argv[] = {
"sh", "sh",
"-c", "-c",
command, command,
NULL, NULL,
}; };
execvp("sh", argv); execvp("sh", argv);
perror("execvp"); perror("execvp");
_exit(127); _exit(127);
} }
_exit(0); _exit(0);
} }
int stat; int stat;
if (waitpid(pid1, &stat, 0) < 0) { if (waitpid(pid1, &stat, 0) < 0) {
perror("waitpid"); perror("waitpid");
} }
} }
void xdpw_screencast_instance_init(struct xdpw_screencast_context *ctx, void xdpw_screencast_instance_init(struct xdpw_screencast_context *ctx,
struct xdpw_screencast_instance *cast, struct xdpw_share out, bool with_cursor) { struct xdpw_screencast_instance *cast, struct xdpw_share out, bool with_cursor) {
// only run exec_before if there's no other instance running that already ran it
if (wl_list_empty(&ctx->screencast_instances)) {
char *exec_before = ctx->state->config->screencast_conf.exec_before;
if (exec_before) {
logprint(INFO, "xdpw: executing %s before screencast", exec_before);
exec_with_shell(exec_before);
}
}
// only run exec_before if there's no other instance running that already ran it cast->ctx = ctx;
if (wl_list_empty(&ctx->screencast_instances)) { cast->target = out;
char *exec_before = ctx->state->config->screencast_conf.exec_before; if (out.output == NULL) {
if (exec_before) {
logprint(INFO, "xdpw: executing %s before screencast", exec_before);
exec_with_shell(exec_before);
}
}
cast->ctx = ctx;
cast->target = out;
if (out.output == NULL) {
cast->max_framerate = 60; // dirty cast->max_framerate = 60; // dirty
} else { } else {
if (ctx->state->config->screencast_conf.max_fps > 0) { if (ctx->state->config->screencast_conf.max_fps > 0) {
cast->max_framerate = ctx->state->config->screencast_conf.max_fps < (uint32_t)out.output->framerate ? cast->max_framerate = ctx->state->config->screencast_conf.max_fps < (uint32_t)out.output->framerate ? ctx->state->config->screencast_conf.max_fps : (uint32_t)out.output->framerate;
ctx->state->config->screencast_conf.max_fps : (uint32_t)out.output->framerate; } else {
} else { cast->max_framerate = (uint32_t)out.output->framerate;
cast->max_framerate = (uint32_t)out.output->framerate; }
} }
}
cast->framerate = cast->max_framerate;
cast->framerate = cast->max_framerate; cast->with_cursor = with_cursor;
cast->with_cursor = with_cursor; cast->refcount = 1;
cast->refcount = 1; cast->node_id = SPA_ID_INVALID;
cast->node_id = SPA_ID_INVALID; cast->avoid_dmabufs = false;
cast->avoid_dmabufs = false; cast->teardown = false;
cast->teardown = false; wl_list_init(&cast->buffer_list);
wl_list_init(&cast->buffer_list); logprint(INFO, "xdpw: screencast instance %p has %d references", cast, cast->refcount);
logprint(INFO, "xdpw: screencast instance %p has %d references", cast, cast->refcount); wl_list_insert(&ctx->screencast_instances, &cast->link);
wl_list_insert(&ctx->screencast_instances, &cast->link); logprint(INFO, "xdpw: %d active screencast instances",
logprint(INFO, "xdpw: %d active screencast instances", wl_list_length(&ctx->screencast_instances));
wl_list_length(&ctx->screencast_instances));
} }
void xdpw_screencast_instance_destroy(struct xdpw_screencast_instance *cast) { void xdpw_screencast_instance_destroy(struct xdpw_screencast_instance *cast) {
assert(cast->refcount == 0); // Fails assert if called by screencast_finish assert(cast->refcount == 0); // Fails assert if called by screencast_finish
logprint(DEBUG, "xdpw: destroying cast instance"); logprint(DEBUG, "xdpw: destroying cast instance");
// make sure this is the last running instance that is being destroyed // make sure this is the last running instance that is being destroyed
if (wl_list_length(&cast->link) == 1) { if (wl_list_length(&cast->link) == 1) {
char *exec_after = cast->ctx->state->config->screencast_conf.exec_after; char *exec_after = cast->ctx->state->config->screencast_conf.exec_after;
if (exec_after) { if (exec_after) {
logprint(INFO, "xdpw: executing %s after screencast", exec_after); logprint(INFO, "xdpw: executing %s after screencast", exec_after);
exec_with_shell(exec_after); exec_with_shell(exec_after);
} }
} }
wl_list_remove(&cast->link); wl_list_remove(&cast->link);
xdpw_pwr_stream_destroy(cast); xdpw_pwr_stream_destroy(cast);
assert(wl_list_length(&cast->buffer_list) == 0); assert(wl_list_length(&cast->buffer_list) == 0);
free(cast); free(cast);
} }
void xdpw_screencast_instance_teardown(struct xdpw_screencast_instance *cast) { void xdpw_screencast_instance_teardown(struct xdpw_screencast_instance *cast) {
struct xdpw_session *sess, *tmp; struct xdpw_session *sess, *tmp;
wl_list_for_each_safe(sess, tmp, &cast->ctx->state->xdpw_sessions, link) { wl_list_for_each_safe(sess, tmp, &cast->ctx->state->xdpw_sessions, link) {
if (sess->screencast_instance == cast) { if (sess->screencast_instance == cast) {
xdpw_session_destroy(sess); xdpw_session_destroy(sess);
} }
} }
} }
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, bool with_cursor) {
struct xdpw_wlr_output *output, *tmp_o;
struct xdpw_wlr_output *output, *tmp_o; wl_list_for_each_reverse_safe(output, tmp_o, &ctx->output_list, link) {
wl_list_for_each_reverse_safe(output, tmp_o, &ctx->output_list, link) { logprint(INFO, "wlroots: capturable output: %s model: %s: id: %i name: %s",
logprint(INFO, "wlroots: capturable output: %s model: %s: id: %i name: %s", output->make, output->model, output->id, output->name);
output->make, output->model, output->id, output->name); }
}
struct xdpw_share out; struct xdpw_share out;
out = xdpw_wlr_chooser(ctx); out = xdpw_wlr_chooser(ctx);
if (!out.output && out.window_handle == -1) { if (!out.output && out.window_handle == -1) {
logprint(ERROR, "wlroots: no output / window found"); logprint(ERROR, "wlroots: no output / window found");
return false; return false;
} }
// Disable screencast sharing to avoid sharing between dmabuf and shm capable clients // Disable screencast sharing to avoid sharing between dmabuf and shm capable clients
/* /*
struct xdpw_screencast_instance *cast, *tmp_c; struct xdpw_screencast_instance *cast, *tmp_c;
wl_list_for_each_reverse_safe(cast, tmp_c, &ctx->screencast_instances, link) { wl_list_for_each_reverse_safe(cast, tmp_c, &ctx->screencast_instances, link) {
logprint(INFO, "xdpw: existing screencast instance: %d %s cursor", logprint(INFO, "xdpw: existing screencast instance: %d %s cursor",
@ -152,401 +149,400 @@ bool setup_outputs(struct xdpw_screencast_context *ctx, struct xdpw_session *ses
} }
*/ */
if (!sess->screencast_instance) { if (!sess->screencast_instance) {
sess->screencast_instance = calloc(1, sizeof(struct xdpw_screencast_instance)); sess->screencast_instance = calloc(1, sizeof(struct xdpw_screencast_instance));
xdpw_screencast_instance_init(ctx, sess->screencast_instance, xdpw_screencast_instance_init(ctx, sess->screencast_instance,
out, with_cursor); out, with_cursor);
} }
if (out.output) { if (out.output) {
logprint(INFO, "wlroots: output: %s", logprint(INFO, "wlroots: output: %s",
sess->screencast_instance->target.output->name); sess->screencast_instance->target.output->name);
} else { } else {
logprint(INFO, "hyprland: window handle %d", sess->screencast_instance->target.window_handle); logprint(INFO, "hyprland: window handle %d", sess->screencast_instance->target.window_handle);
} }
return true;
return true;
} }
static int start_screencast(struct xdpw_screencast_instance *cast) { static int start_screencast(struct xdpw_screencast_instance *cast) {
xdpw_wlr_register_cb(cast); xdpw_wlr_register_cb(cast);
// process at least one frame so that we know // process at least one frame so that we know
// some of the metadata required for the pipewire // some of the metadata required for the pipewire
// remote state connected event // remote state connected event
wl_display_dispatch(cast->ctx->state->wl_display); cast->ctx->toplevel_mgr_bind = 1;
wl_display_roundtrip(cast->ctx->state->wl_display); wl_display_dispatch(cast->ctx->state->wl_display);
wl_display_roundtrip(cast->ctx->state->wl_display);
if (cast->screencopy_frame_info[WL_SHM].format == DRM_FORMAT_INVALID || if (cast->screencopy_frame_info[WL_SHM].format == DRM_FORMAT_INVALID ||
(cast->ctx->state->screencast_version >= 3 && (cast->ctx->state->screencast_version >= 3 &&
cast->screencopy_frame_info[DMABUF].format == DRM_FORMAT_INVALID)) { cast->screencopy_frame_info[DMABUF].format == DRM_FORMAT_INVALID)) {
logprint(INFO, "wlroots: unable to receive a valid format from wlr_screencopy"); logprint(INFO, "wlroots: unable to receive a valid format from wlr_screencopy");
return -1; return -1;
} }
xdpw_pwr_stream_create(cast); xdpw_pwr_stream_create(cast);
cast->initialized = true; cast->initialized = true;
return 0; return 0;
} }
static int method_screencast_create_session(sd_bus_message *msg, void *data, static int method_screencast_create_session(sd_bus_message *msg, void *data,
sd_bus_error *ret_error) { sd_bus_error *ret_error) {
struct xdpw_state *state = data; struct xdpw_state *state = data;
int ret = 0; int ret = 0;
logprint(INFO, "dbus: create session method invoked"); logprint(INFO, "dbus: create session method invoked");
char *request_handle, *session_handle, *app_id; char *request_handle, *session_handle, *app_id;
ret = sd_bus_message_read(msg, "oos", &request_handle, &session_handle, &app_id); ret = sd_bus_message_read(msg, "oos", &request_handle, &session_handle, &app_id);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
ret = sd_bus_message_enter_container(msg, 'a', "{sv}"); ret = sd_bus_message_enter_container(msg, 'a', "{sv}");
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
logprint(INFO, "dbus: request_handle: %s", request_handle); logprint(INFO, "dbus: request_handle: %s", request_handle);
logprint(INFO, "dbus: session_handle: %s", session_handle); logprint(INFO, "dbus: session_handle: %s", session_handle);
logprint(INFO, "dbus: app_id: %s", app_id); logprint(INFO, "dbus: app_id: %s", app_id);
char *key; char *key;
int innerRet = 0; int innerRet = 0;
while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) {
innerRet = sd_bus_message_read(msg, "s", &key); innerRet = sd_bus_message_read(msg, "s", &key);
if (innerRet < 0) { if (innerRet < 0) {
return innerRet; return innerRet;
} }
if (strcmp(key, "session_handle_token") == 0) { if (strcmp(key, "session_handle_token") == 0) {
char *token; char *token;
sd_bus_message_read(msg, "v", "s", &token); sd_bus_message_read(msg, "v", "s", &token);
logprint(INFO, "dbus: option token: %s", token); logprint(INFO, "dbus: option token: %s", token);
} else { } else {
logprint(WARN, "dbus: unknown option: %s", key); logprint(WARN, "dbus: unknown option: %s", key);
sd_bus_message_skip(msg, "v"); sd_bus_message_skip(msg, "v");
} }
innerRet = sd_bus_message_exit_container(msg); innerRet = sd_bus_message_exit_container(msg);
if (innerRet < 0) { if (innerRet < 0) {
return innerRet; return innerRet;
} }
} }
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
ret = sd_bus_message_exit_container(msg); ret = sd_bus_message_exit_container(msg);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
struct xdpw_request *req = struct xdpw_request *req =
xdpw_request_create(sd_bus_message_get_bus(msg), request_handle); xdpw_request_create(sd_bus_message_get_bus(msg), request_handle);
if (req == NULL) { if (req == NULL) {
return -ENOMEM; return -ENOMEM;
} }
struct xdpw_session *sess = struct xdpw_session *sess =
xdpw_session_create(state, sd_bus_message_get_bus(msg), strdup(session_handle)); xdpw_session_create(state, sd_bus_message_get_bus(msg), strdup(session_handle));
if (sess == NULL) { if (sess == NULL) {
return -ENOMEM; return -ENOMEM;
} }
sd_bus_message *reply = NULL; sd_bus_message *reply = NULL;
ret = sd_bus_message_new_method_return(msg, &reply); ret = sd_bus_message_new_method_return(msg, &reply);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_SUCCESS, 0); ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_SUCCESS, 0);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
ret = sd_bus_send(NULL, reply, NULL); ret = sd_bus_send(NULL, reply, NULL);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
sd_bus_message_unref(reply); sd_bus_message_unref(reply);
return 0; return 0;
} }
static int method_screencast_select_sources(sd_bus_message *msg, void *data, static int method_screencast_select_sources(sd_bus_message *msg, void *data,
sd_bus_error *ret_error) { sd_bus_error *ret_error) {
struct xdpw_state *state = data; struct xdpw_state *state = data;
struct xdpw_screencast_context *ctx = &state->screencast; struct xdpw_screencast_context *ctx = &state->screencast;
int ret = 0; int ret = 0;
struct xdpw_session *sess, *tmp_s; struct xdpw_session *sess, *tmp_s;
sd_bus_message *reply = NULL; sd_bus_message *reply = NULL;
logprint(INFO, "dbus: select sources method invoked"); logprint(INFO, "dbus: select sources method invoked");
// default to embedded cursor mode if not specified // default to embedded cursor mode if not specified
bool cursor_embedded = true; bool cursor_embedded = true;
char *request_handle, *session_handle, *app_id; char *request_handle, *session_handle, *app_id;
ret = sd_bus_message_read(msg, "oos", &request_handle, &session_handle, &app_id); ret = sd_bus_message_read(msg, "oos", &request_handle, &session_handle, &app_id);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
ret = sd_bus_message_enter_container(msg, 'a', "{sv}"); ret = sd_bus_message_enter_container(msg, 'a', "{sv}");
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
logprint(INFO, "dbus: request_handle: %s", request_handle); logprint(INFO, "dbus: request_handle: %s", request_handle);
logprint(INFO, "dbus: session_handle: %s", session_handle); logprint(INFO, "dbus: session_handle: %s", session_handle);
logprint(INFO, "dbus: app_id: %s", app_id); logprint(INFO, "dbus: app_id: %s", app_id);
char *key; char *key;
int innerRet = 0; int innerRet = 0;
while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) {
innerRet = sd_bus_message_read(msg, "s", &key); innerRet = sd_bus_message_read(msg, "s", &key);
if (innerRet < 0) { if (innerRet < 0) {
return innerRet; return innerRet;
} }
if (strcmp(key, "multiple") == 0) { if (strcmp(key, "multiple") == 0) {
int multiple; int multiple;
sd_bus_message_read(msg, "v", "b", &multiple); sd_bus_message_read(msg, "v", "b", &multiple);
logprint(INFO, "dbus: option multiple: %d", multiple); logprint(INFO, "dbus: option multiple: %d", multiple);
} else if (strcmp(key, "types") == 0) { } else if (strcmp(key, "types") == 0) {
uint32_t mask; uint32_t mask;
sd_bus_message_read(msg, "v", "u", &mask); sd_bus_message_read(msg, "v", "u", &mask);
if (mask & (1<<WINDOW)) { if (mask & (1 << WINDOW)) {
logprint(INFO, "dbus: non-monitor cast requested, not replying"); logprint(INFO, "dbus: non-monitor cast requested, not replying");
return -1; return -1;
} }
logprint(INFO, "dbus: option types:%x", mask); logprint(INFO, "dbus: option types:%x", mask);
} else if (strcmp(key, "cursor_mode") == 0) { } else if (strcmp(key, "cursor_mode") == 0) {
uint32_t cursor_mode; uint32_t cursor_mode;
sd_bus_message_read(msg, "v", "u", &cursor_mode); sd_bus_message_read(msg, "v", "u", &cursor_mode);
if (cursor_mode & HIDDEN) { if (cursor_mode & HIDDEN) {
cursor_embedded = false; cursor_embedded = false;
} }
if (cursor_mode & METADATA) { if (cursor_mode & METADATA) {
logprint(ERROR, "dbus: unsupported cursor mode requested, cancelling"); logprint(ERROR, "dbus: unsupported cursor mode requested, cancelling");
goto error; goto error;
} }
logprint(INFO, "dbus: option cursor_mode:%x", cursor_mode); logprint(INFO, "dbus: option cursor_mode:%x", cursor_mode);
} else { } else {
logprint(WARN, "dbus: unknown option %s", key); logprint(WARN, "dbus: unknown option %s", key);
sd_bus_message_skip(msg, "v"); sd_bus_message_skip(msg, "v");
} }
innerRet = sd_bus_message_exit_container(msg); innerRet = sd_bus_message_exit_container(msg);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
} }
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
ret = sd_bus_message_exit_container(msg); ret = sd_bus_message_exit_container(msg);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
bool output_selection_canceled = 1; bool output_selection_canceled = 1;
wl_list_for_each_reverse_safe(sess, tmp_s, &state->xdpw_sessions, link) { wl_list_for_each_reverse_safe(sess, tmp_s, &state->xdpw_sessions, link) {
if (strcmp(sess->session_handle, session_handle) == 0) { if (strcmp(sess->session_handle, session_handle) == 0) {
logprint(DEBUG, "dbus: select sources: found matching session %s", sess->session_handle); 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, cursor_embedded);
} }
} }
ret = sd_bus_message_new_method_return(msg, &reply); ret = sd_bus_message_new_method_return(msg, &reply);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
if (output_selection_canceled) { if (output_selection_canceled) {
ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_CANCELLED, 0); ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_CANCELLED, 0);
} else { } else {
ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_SUCCESS, 0); ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_SUCCESS, 0);
} }
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
ret = sd_bus_send(NULL, reply, NULL); ret = sd_bus_send(NULL, reply, NULL);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
sd_bus_message_unref(reply); sd_bus_message_unref(reply);
return 0; return 0;
error: error:
wl_list_for_each_reverse_safe(sess, tmp_s, &state->xdpw_sessions, link) { wl_list_for_each_reverse_safe(sess, tmp_s, &state->xdpw_sessions, link) {
if (strcmp(sess->session_handle, session_handle) == 0) { if (strcmp(sess->session_handle, session_handle) == 0) {
logprint(DEBUG, "dbus: select sources error: destroying matching session %s", sess->session_handle); logprint(DEBUG, "dbus: select sources error: destroying matching session %s", sess->session_handle);
xdpw_session_destroy(sess); xdpw_session_destroy(sess);
} }
} }
ret = sd_bus_message_new_method_return(msg, &reply); ret = sd_bus_message_new_method_return(msg, &reply);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_CANCELLED, 0); ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_CANCELLED, 0);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
ret = sd_bus_send(NULL, reply, NULL); ret = sd_bus_send(NULL, reply, NULL);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
sd_bus_message_unref(reply); sd_bus_message_unref(reply);
return -1; return -1;
} }
static int method_screencast_start(sd_bus_message *msg, void *data, static int method_screencast_start(sd_bus_message *msg, void *data,
sd_bus_error *ret_error) { sd_bus_error *ret_error) {
struct xdpw_state *state = data; struct xdpw_state *state = data;
int ret = 0; int ret = 0;
logprint(INFO, "dbus: start method invoked"); logprint(INFO, "dbus: start method invoked");
char *request_handle, *session_handle, *app_id, *parent_window; char *request_handle, *session_handle, *app_id, *parent_window;
ret = sd_bus_message_read(msg, "ooss", &request_handle, &session_handle, &app_id, &parent_window); ret = sd_bus_message_read(msg, "ooss", &request_handle, &session_handle, &app_id, &parent_window);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
ret = sd_bus_message_enter_container(msg, 'a', "{sv}"); ret = sd_bus_message_enter_container(msg, 'a', "{sv}");
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
logprint(INFO, "dbus: request_handle: %s", request_handle); logprint(INFO, "dbus: request_handle: %s", request_handle);
logprint(INFO, "dbus: session_handle: %s", session_handle); logprint(INFO, "dbus: session_handle: %s", session_handle);
logprint(INFO, "dbus: app_id: %s", app_id); logprint(INFO, "dbus: app_id: %s", app_id);
logprint(INFO, "dbus: parent_window: %s", parent_window); logprint(INFO, "dbus: parent_window: %s", parent_window);
char *key; char *key;
int innerRet = 0; int innerRet = 0;
while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) {
innerRet = sd_bus_message_read(msg, "s", &key); innerRet = sd_bus_message_read(msg, "s", &key);
if (innerRet < 0) { if (innerRet < 0) {
return innerRet; return innerRet;
} }
logprint(WARN, "dbus: unknown option: %s", key); logprint(WARN, "dbus: unknown option: %s", key);
sd_bus_message_skip(msg, "v"); sd_bus_message_skip(msg, "v");
innerRet = sd_bus_message_exit_container(msg); innerRet = sd_bus_message_exit_container(msg);
if (innerRet < 0) { if (innerRet < 0) {
return innerRet; return innerRet;
} }
} }
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
ret = sd_bus_message_exit_container(msg); ret = sd_bus_message_exit_container(msg);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
struct xdpw_screencast_instance *cast = NULL; struct xdpw_screencast_instance *cast = NULL;
struct xdpw_session *sess, *tmp_s; struct xdpw_session *sess, *tmp_s;
wl_list_for_each_reverse_safe(sess, tmp_s, &state->xdpw_sessions, link) { wl_list_for_each_reverse_safe(sess, tmp_s, &state->xdpw_sessions, link) {
if (strcmp(sess->session_handle, session_handle) == 0) { if (strcmp(sess->session_handle, session_handle) == 0) {
logprint(DEBUG, "dbus: start: found matching session %s", sess->session_handle); logprint(DEBUG, "dbus: start: found matching session %s", sess->session_handle);
cast = sess->screencast_instance; cast = sess->screencast_instance;
} }
} }
if (!cast) { if (!cast) {
return -1; return -1;
} }
if (!cast->initialized) { if (!cast->initialized) {
ret = start_screencast(cast); ret = start_screencast(cast);
} }
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
while (cast->node_id == SPA_ID_INVALID) { while (cast->node_id == SPA_ID_INVALID) {
int ret = pw_loop_iterate(state->pw_loop, 0); int ret = pw_loop_iterate(state->pw_loop, 0);
if (ret < 0) { if (ret < 0) {
logprint(ERROR, "pipewire_loop_iterate failed: %s", spa_strerror(ret)); logprint(ERROR, "pipewire_loop_iterate failed: %s", spa_strerror(ret));
return ret; return ret;
} }
} }
sd_bus_message *reply = NULL; sd_bus_message *reply = NULL;
ret = sd_bus_message_new_method_return(msg, &reply); ret = sd_bus_message_new_method_return(msg, &reply);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
logprint(DEBUG, "dbus: start: returning node %d", (int)cast->node_id); logprint(DEBUG, "dbus: start: returning node %d", (int)cast->node_id);
ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_SUCCESS, 1, ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_SUCCESS, 1,
"streams", "a(ua{sv})", 1, "streams", "a(ua{sv})", 1,
cast->node_id, 2, cast->node_id, 2,
"position", "(ii)", 0, 0, "position", "(ii)", 0, 0,
"size", "(ii)", cast->screencopy_frame_info[WL_SHM].width, cast->screencopy_frame_info[WL_SHM].height); "size", "(ii)", cast->screencopy_frame_info[WL_SHM].width, cast->screencopy_frame_info[WL_SHM].height);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
ret = sd_bus_send(NULL, reply, NULL); ret = sd_bus_send(NULL, reply, NULL);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
sd_bus_message_unref(reply); sd_bus_message_unref(reply);
return 0; return 0;
} }
static const sd_bus_vtable screencast_vtable[] = { static const sd_bus_vtable screencast_vtable[] = {
SD_BUS_VTABLE_START(0), SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("CreateSession", "oosa{sv}", "ua{sv}", SD_BUS_METHOD("CreateSession", "oosa{sv}", "ua{sv}",
method_screencast_create_session, SD_BUS_VTABLE_UNPRIVILEGED), method_screencast_create_session, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SelectSources", "oosa{sv}", "ua{sv}", SD_BUS_METHOD("SelectSources", "oosa{sv}", "ua{sv}",
method_screencast_select_sources, SD_BUS_VTABLE_UNPRIVILEGED), method_screencast_select_sources, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Start", "oossa{sv}", "ua{sv}", SD_BUS_METHOD("Start", "oossa{sv}", "ua{sv}",
method_screencast_start, SD_BUS_VTABLE_UNPRIVILEGED), method_screencast_start, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_PROPERTY("AvailableSourceTypes", "u", NULL, SD_BUS_PROPERTY("AvailableSourceTypes", "u", NULL,
offsetof(struct xdpw_state, screencast_source_types), offsetof(struct xdpw_state, screencast_source_types),
SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("AvailableCursorModes", "u", NULL, SD_BUS_PROPERTY("AvailableCursorModes", "u", NULL,
offsetof(struct xdpw_state, screencast_cursor_modes), offsetof(struct xdpw_state, screencast_cursor_modes),
SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("version", "u", NULL, SD_BUS_PROPERTY("version", "u", NULL,
offsetof(struct xdpw_state, screencast_version), offsetof(struct xdpw_state, screencast_version),
SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END SD_BUS_VTABLE_END};
};
int xdpw_screencast_init(struct xdpw_state *state) { int xdpw_screencast_init(struct xdpw_state *state) {
sd_bus_slot *slot = NULL; sd_bus_slot *slot = NULL;
state->screencast = (struct xdpw_screencast_context) { 0 }; state->screencast = (struct xdpw_screencast_context){0};
state->screencast.state = state; state->screencast.state = state;
state->screencast.hyprland_toplevel_manager = NULL; state->screencast.hyprland_toplevel_manager = NULL;
state->screencast.toplevel_mgr_bind = 0;
int err; int err;
err = xdpw_pwr_context_create(state); err = xdpw_pwr_context_create(state);
if (err) { if (err) {
goto fail_pipewire; goto fail_pipewire;
} }
err = xdpw_wlr_screencopy_init(state); err = xdpw_wlr_screencopy_init(state);
if (err) { if (err) {
goto fail_screencopy; goto fail_screencopy;
} }
return sd_bus_add_object_vtable(state->bus, &slot, object_path, interface_name, return sd_bus_add_object_vtable(state->bus, &slot, object_path, interface_name,
screencast_vtable, state); screencast_vtable, state);
fail_screencopy: fail_screencopy:
xdpw_wlr_screencopy_finish(&state->screencast); xdpw_wlr_screencopy_finish(&state->screencast);
fail_pipewire: fail_pipewire:
xdpw_pwr_context_destroy(state); xdpw_pwr_context_destroy(state);
return err; return err;
} }

File diff suppressed because it is too large Load diff