From 55f873dac4ae6da8b96e6de57609ccb8cd2160c3 Mon Sep 17 00:00:00 2001 From: danshick Date: Thu, 16 Apr 2020 04:21:55 -0400 Subject: [PATCH] Screencast session support (#22) * Initial session support WIP Remove libdrm dependency Remove display from context, add dbus properties Use random names for shm and pw_stream, init the stream only for new cast instances Separate cast initialized flag from refcount, cleanup names and comments * Refactor and stability improvements Properly report xdp screencast implementation version --- .build.yml | 1 - include/logger.h | 4 - include/pipewire_screencast.h | 13 +- include/screencast.h | 13 +- include/screencast_common.h | 85 +++++++---- include/wlr_screencast.h | 29 +--- include/xdpw.h | 26 ++-- meson.build | 2 - src/core/logger.c | 5 + src/core/main.c | 18 ++- src/core/request.c | 15 +- src/core/session.c | 45 ++++-- src/screencast/pipewire_screencast.c | 136 ++++++++++------- src/screencast/screencast.c | 219 +++++++++++++++++++++------ src/screencast/screencast_common.c | 24 ++- src/screencast/wlr_screencast.c | 196 +++++++++++++++--------- src/screenshot/screenshot.c | 4 +- 17 files changed, 544 insertions(+), 291 deletions(-) diff --git a/.build.yml b/.build.yml index ca20d86..5e8bff3 100644 --- a/.build.yml +++ b/.build.yml @@ -6,7 +6,6 @@ packages: - wayland - wayland-protocols - pipewire - - libdrm sources: - https://github.com/emersion/xdg-desktop-portal-wlr tasks: diff --git a/include/logger.h b/include/logger.h index 3b6c17d..3a7e13f 100644 --- a/include/logger.h +++ b/include/logger.h @@ -1,11 +1,7 @@ #ifndef LOGGER_H #define LOGGER_H -#include #include -#include -#include -#include enum LOGLEVEL { QUIET, ERROR, WARN, INFO, DEBUG, TRACE }; diff --git a/include/pipewire_screencast.h b/include/pipewire_screencast.h index 5612ab8..a90409a 100644 --- a/include/pipewire_screencast.h +++ b/include/pipewire_screencast.h @@ -1,20 +1,13 @@ #ifndef PIPEWIRE_SCREENCAST_H #define PIPEWIRE_SCREENCAST_H -#include -#include -#include -#include -#include -#include - -#include "wlr_screencast.h" #include "screencast_common.h" -#include "xdpw.h" #define BUFFERS 1 #define ALIGN 16 -void *pwr_start(struct xdpw_state *state); +void xdpw_pwr_stream_init(struct xdpw_screencast_instance *cast); +int xdpw_pwr_core_connect(struct xdpw_state *state); +void xdpw_pwr_stream_destroy(struct xdpw_screencast_instance *cast); #endif diff --git a/include/screencast.h b/include/screencast.h index 5f6a8eb..2e9a993 100644 --- a/include/screencast.h +++ b/include/screencast.h @@ -1,17 +1,8 @@ #ifndef SCREENCAST_H #define SCREENCAST_H -#include -#include -#include -#include -#include -#include -#include - -#include "pipewire_screencast.h" #include "screencast_common.h" -#include "wlr_screencast.h" -#include "xdpw.h" + +void xdpw_screencast_instance_destroy(struct xdpw_screencast_instance *cast); #endif diff --git a/include/screencast_common.h b/include/screencast_common.h index ce78b6e..a129a46 100644 --- a/include/screencast_common.h +++ b/include/screencast_common.h @@ -1,21 +1,33 @@ #ifndef SCREENCAST_COMMON_H #define SCREENCAST_COMMON_H -#include #include #include -#include #include -#include "logger.h" -struct damage { +// 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 2 + +enum cursor_modes { + HIDDEN = 1, + EMBEDDED = 2, + METADATA = 4, +}; + +enum source_types { + MONITOR = 1, + WINDOW = 2, +}; + +struct xdpw_frame_damage { uint32_t x; uint32_t y; uint32_t width; uint32_t height; }; -struct simple_frame { +struct xdpw_frame { uint32_t width; uint32_t height; uint32_t size; @@ -24,53 +36,65 @@ struct simple_frame { uint64_t tv_sec; uint32_t tv_nsec; enum wl_shm_format format; - struct damage *damage; + struct xdpw_frame_damage damage; struct wl_buffer *buffer; void *data; }; -struct screencast_context { +struct xdpw_screencast_context { + + // xdpw + struct xdpw_state *state; + // pipewire struct pw_context *pwr_context; struct pw_core *core; - struct spa_source *event; - struct pw_stream *stream; - struct spa_hook stream_listener; - struct spa_video_info_raw pwr_format; - uint32_t seq; - uint32_t node_id; - bool stream_state; // wlroots - struct wl_display *display; struct wl_list output_list; struct wl_registry *registry; struct zwlr_screencopy_manager_v1 *screencopy_manager; struct zxdg_output_manager_v1* xdg_output_manager; struct wl_shm *shm; - // main frame callback - struct zwlr_screencopy_frame_v1 *frame_callback; - - // target output - struct wayland_output *target_output; - uint32_t framerate; - bool with_cursor; - - // frame - struct zwlr_screencopy_frame_v1 *wlr_frame; - struct simple_frame simple_frame; - // cli options const char *output_name; const char *forced_pixelformat; - // if something happens during capture + // sessions + struct wl_list screencast_instances; +}; + +struct xdpw_screencast_instance { + // list + struct wl_list link; + + // xdpw + uint32_t refcount; + struct xdpw_screencast_context *ctx; + bool initialized; + + // pipewire + struct spa_source *event; + struct pw_stream *stream; + struct spa_hook stream_listener; + struct spa_video_info_raw pwr_format; + uint32_t seq; + uint32_t node_id; + bool pwr_stream_state; + + // wlroots + struct zwlr_screencopy_frame_v1 *frame_callback; + struct xdpw_wlr_output *target_output; + uint32_t framerate; + struct zwlr_screencopy_frame_v1 *wlr_frame; + struct xdpw_frame simple_frame; + bool with_cursor; int err; bool quit; }; -struct wayland_output { +struct xdpw_wlr_output { struct wl_list link; uint32_t id; struct wl_output *output; @@ -83,6 +107,7 @@ struct wayland_output { float framerate; }; -uint32_t pipewire_from_wl_shm(void *data); +void randname(char *buf); +uint32_t xdpw_format_pw_from_wl_shm(void *data); #endif /* SCREENCAST_COMMON_H */ diff --git a/include/wlr_screencast.h b/include/wlr_screencast.h index c80ea66..0f711aa 100644 --- a/include/wlr_screencast.h +++ b/include/wlr_screencast.h @@ -1,37 +1,22 @@ #ifndef WLR_SCREENCAST_H #define WLR_SCREENCAST_H -#include "wlr-screencopy-unstable-v1-client-protocol.h" -#include "xdg-output-unstable-v1-client-protocol.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pipewire_screencast.h" #include "screencast_common.h" #define SC_MANAGER_VERSION 2 struct xdpw_state; -void wlr_frame_free(struct xdpw_state *state); -int wlr_screencopy_init(struct xdpw_state *state); -void wlr_screencopy_uninit(struct screencast_context *ctx); +int xdpw_wlr_screencopy_init(struct xdpw_state *state); +void xdpw_wlr_screencopy_finish(struct xdpw_screencast_context *ctx); -struct wayland_output *wlr_output_find_by_name(struct wl_list *output_list, +struct xdpw_wlr_output *xdpw_wlr_output_find_by_name(struct wl_list *output_list, const char *name); -struct wayland_output *wlr_output_find(struct screencast_context *ctx, +struct xdpw_wlr_output *xdpw_wlr_output_first(struct wl_list *output_list); +struct xdpw_wlr_output *xdpw_wlr_output_find(struct xdpw_screencast_context *ctx, struct wl_output *out, uint32_t id); -struct wayland_output *wlr_output_first(struct wl_list *output_list); -void wlr_register_cb(struct xdpw_state *state); +void xdpw_wlr_frame_free(struct xdpw_screencast_instance *cast); +void xdpw_wlr_register_cb(struct xdpw_screencast_instance *cast); #endif diff --git a/include/xdpw.h b/include/xdpw.h index 786d240..52aa9cc 100644 --- a/include/xdpw.h +++ b/include/xdpw.h @@ -7,15 +7,18 @@ #elif HAVE_ELOGIND #include #endif -#include "logger.h" -#include "screencast.h" + +#include "screencast_common.h" struct xdpw_state { + struct wl_list xdpw_sessions; sd_bus *bus; struct wl_display *wl_display; struct pw_loop *pw_loop; - - struct screencast_context screencast; + struct xdpw_screencast_context screencast; + uint32_t screencast_source_types; // bitfield of enum source_types + uint32_t screencast_cursor_modes; // bitfield of enum cursor_modes + uint32_t screencast_version; }; struct xdpw_request { @@ -23,7 +26,10 @@ struct xdpw_request { }; struct xdpw_session { + struct wl_list link; sd_bus_slot *slot; + char *session_handle; + struct xdpw_screencast_instance *screencast_instance; }; enum { @@ -32,14 +38,14 @@ enum { PORTAL_RESPONSE_ENDED = 2 }; -int init_screenshot(struct xdpw_state *state); -int init_screencast(struct xdpw_state *state, const char *output_name, +int xdpw_screenshot_init(struct xdpw_state *state); +int xdpw_screencast_init(struct xdpw_state *state, const char *output_name, const char *forced_pixelformat); -struct xdpw_request *request_create(sd_bus *bus, const char *object_path); -void request_destroy(struct xdpw_request *req); +struct xdpw_request *xdpw_request_create(sd_bus *bus, const char *object_path); +void xdpw_request_destroy(struct xdpw_request *req); -struct xdpw_session *session_create(sd_bus *bus, const char *object_path); -void session_destroy(struct xdpw_session *req); +struct xdpw_session *xdpw_session_create(struct xdpw_state *state, sd_bus *bus, char *object_path); +void xdpw_session_destroy(struct xdpw_session *req); #endif diff --git a/meson.build b/meson.build index 451db0d..fc41b13 100644 --- a/meson.build +++ b/meson.build @@ -25,7 +25,6 @@ rt = cc.find_library('rt') pipewire = dependency('libpipewire-0.3', version: '>= 0.2.9') wayland_client = dependency('wayland-client') wayland_protos = dependency('wayland-protocols', version: '>=1.14') -drm = dependency('libdrm').partial_dependency(includes: true) logind = dependency('libsystemd', required: false) if logind.found() @@ -56,7 +55,6 @@ executable( logind, pipewire, rt, - drm ], include_directories: [inc], install: true, diff --git a/src/core/logger.c b/src/core/logger.c index 2f4bdfe..fa0d7b1 100644 --- a/src/core/logger.c +++ b/src/core/logger.c @@ -1,5 +1,10 @@ #include "logger.h" +#include +#include +#include +#include + static int NUM_LEVELS = 6; static const char *loglevels[] = { diff --git a/src/core/main.c b/src/core/main.c index 3b66a39..30ae243 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -6,6 +6,7 @@ #include #include #include "xdpw.h" +#include "logger.h" enum event_loop_fd { EVENT_LOOP_DBUS, @@ -77,12 +78,14 @@ int main(int argc, char *argv[]) { logprint(ERROR, "dbus: failed to connect to user bus: %s", strerror(-ret)); goto error; } + logprint(TRACE, "dbus: connected"); struct wl_display *wl_display = wl_display_connect(NULL); if (!wl_display) { logprint(ERROR, "wayland: failed to connect to display"); goto error; } + logprint(TRACE, "wlroots: wl_display connected"); pw_init(NULL, NULL); struct pw_loop *pw_loop = pw_loop_new(NULL); @@ -90,15 +93,25 @@ int main(int argc, char *argv[]) { logprint(ERROR, "pipewire: failed to create loop"); goto error; } + logprint(TRACE, "pipewire: pw_loop created"); struct xdpw_state state = { .bus = bus, .wl_display = wl_display, .pw_loop = pw_loop, + .screencast_source_types = MONITOR, + .screencast_cursor_modes = HIDDEN | EMBEDDED, + .screencast_version = XDP_CAST_PROTO_VER, }; - init_screenshot(&state); - init_screencast(&state, output_name, forced_pixelformat); + wl_list_init(&state.xdpw_sessions); + + xdpw_screenshot_init(&state); + ret = xdpw_screencast_init(&state, output_name, forced_pixelformat); + if (ret < 0) { + logprint(ERROR, "xdpw: failed to initialize screencast"); + goto error; + } ret = sd_bus_request_name(bus, service_name, 0); if (ret < 0) { @@ -173,5 +186,6 @@ error: sd_bus_unref(bus); pw_loop_leave(state.pw_loop); pw_loop_destroy(state.pw_loop); + wl_display_disconnect(state.wl_display); return EXIT_FAILURE; } diff --git a/src/core/request.c b/src/core/request.c index ba7edea..064e9d9 100644 --- a/src/core/request.c +++ b/src/core/request.c @@ -3,15 +3,14 @@ #include #include #include "xdpw.h" +#include "logger.h" static const char interface_name[] = "org.freedesktop.impl.portal.Request"; static int method_close(sd_bus_message *msg, void *data, sd_bus_error *ret_error) { - - int ret = 0; - // struct xdpw_request *req = data; - // TODO + struct xdpw_request *req = data; + int ret = 0; logprint(INFO, "dbus: request closed"); sd_bus_message *reply = NULL; @@ -25,7 +24,9 @@ static int method_close(sd_bus_message *msg, void *data, return ret; } - sd_bus_message_unref(reply); + sd_bus_message_unref(reply); + + xdpw_request_destroy(req); return 0; } @@ -36,7 +37,7 @@ static const sd_bus_vtable request_vtable[] = { SD_BUS_VTABLE_END }; -struct xdpw_request *request_create(sd_bus *bus, const char *object_path) { +struct xdpw_request *xdpw_request_create(sd_bus *bus, const char *object_path) { struct xdpw_request *req = calloc(1, sizeof(struct xdpw_request)); if (sd_bus_add_object_vtable(bus, &req->slot, object_path, interface_name, @@ -50,7 +51,7 @@ struct xdpw_request *request_create(sd_bus *bus, const char *object_path) { return req; } -void request_destroy(struct xdpw_request *req) { +void xdpw_request_destroy(struct xdpw_request *req) { if (req == NULL) { return; } diff --git a/src/core/session.c b/src/core/session.c index 6c2b4c4..4c27896 100644 --- a/src/core/session.c +++ b/src/core/session.c @@ -2,16 +2,17 @@ #include #include #include +#include #include "xdpw.h" +#include "screencast.h" +#include "logger.h" static const char interface_name[] = "org.freedesktop.impl.portal.Session"; static int method_close(sd_bus_message *msg, void *data, sd_bus_error *ret_error) { - int ret = 0; - // struct xdpw_session *session = data; - // TODO + struct xdpw_session *sess = data; logprint(INFO, "dbus: session closed"); sd_bus_message *reply = NULL; @@ -27,6 +28,8 @@ static int method_close(sd_bus_message *msg, void *data, sd_bus_message_unref(reply); + xdpw_session_destroy(sess); + return 0; } @@ -36,24 +39,40 @@ static const sd_bus_vtable session_vtable[] = { SD_BUS_VTABLE_END }; -struct xdpw_session *session_create(sd_bus *bus, const char *object_path) { - struct xdpw_session *req = calloc(1, sizeof(struct xdpw_session)); +struct xdpw_session *xdpw_session_create(struct xdpw_state *state, sd_bus *bus, char *object_path) { + struct xdpw_session *sess = calloc(1, sizeof(struct xdpw_session)); - if (sd_bus_add_object_vtable(bus, &req->slot, object_path, interface_name, - session_vtable, NULL) < 0) { - free(req); + sess->session_handle = object_path; + + if (sd_bus_add_object_vtable(bus, &sess->slot, object_path, interface_name, + session_vtable, sess) < 0) { + free(sess); logprint(ERROR, "dbus: sd_bus_add_object_vtable failed: %s", strerror(-errno)); return NULL; } - return req; + wl_list_insert(&state->xdpw_sessions, &sess->link); + return sess; } -void session_destroy(struct xdpw_session *req) { - if (req == NULL) { +void xdpw_session_destroy(struct xdpw_session *sess) { + logprint(TRACE, "dbus: destroying session"); + if (!sess) { return; } - sd_bus_slot_unref(req->slot); - free(req); + struct xdpw_screencast_instance *cast = sess->screencast_instance; + if (cast) { + assert(cast->refcount > 0); + --cast->refcount; + logprint(INFO, "xdpw: screencast instance %p has %d references", cast, cast->refcount); + if (cast->refcount < 1) { + cast->quit = true; + } + } + + sd_bus_slot_unref(sess->slot); + wl_list_remove(&sess->link); + free(sess->session_handle); + free(sess); } diff --git a/src/screencast/pipewire_screencast.c b/src/screencast/pipewire_screencast.c index b288f79..7ab688d 100644 --- a/src/screencast/pipewire_screencast.c +++ b/src/screencast/pipewire_screencast.c @@ -1,5 +1,15 @@ #include "pipewire_screencast.h" +#include +#include +#include +#include +#include + +#include "wlr_screencast.h" +#include "xdpw.h" +#include "logger.h" + static void writeFrameData(void *pwFramePointer, void *wlrFramePointer, uint32_t height, uint32_t stride, bool inverted) { if (!inverted) { @@ -17,8 +27,7 @@ static void writeFrameData(void *pwFramePointer, void *wlrFramePointer, } static void pwr_on_event(void *data, uint64_t expirations) { - struct xdpw_state *state = data; - struct screencast_context *ctx = &state->screencast; + struct xdpw_screencast_instance *cast = data; struct pw_buffer *pw_buf; struct spa_buffer *spa_buf; struct spa_meta_header *h; @@ -27,7 +36,7 @@ static void pwr_on_event(void *data, uint64_t expirations) { logprint(TRACE, "********************"); logprint(TRACE, "pipewire: event fired"); - if ((pw_buf = pw_stream_dequeue_buffer(ctx->stream)) == NULL) { + if ((pw_buf = pw_stream_dequeue_buffer(cast->stream)) == NULL) { logprint(WARN, "pipewire: out of buffers"); return; } @@ -41,60 +50,58 @@ static void pwr_on_event(void *data, uint64_t expirations) { if ((h = spa_buffer_find_meta_data(spa_buf, SPA_META_Header, sizeof(*h)))) { h->pts = -1; h->flags = 0; - h->seq = ctx->seq++; + h->seq = cast->seq++; h->dts_offset = 0; } d[0].type = SPA_DATA_MemPtr; - d[0].maxsize = ctx->simple_frame.size; + d[0].maxsize = cast->simple_frame.size; d[0].mapoffset = 0; - d[0].chunk->size = ctx->simple_frame.size; - d[0].chunk->stride = ctx->simple_frame.stride; + d[0].chunk->size = cast->simple_frame.size; + d[0].chunk->stride = cast->simple_frame.stride; d[0].chunk->offset = 0; d[0].flags = 0; d[0].fd = -1; - writeFrameData(d[0].data, ctx->simple_frame.data, ctx->simple_frame.height, - ctx->simple_frame.stride, ctx->simple_frame.y_invert); + writeFrameData(d[0].data, cast->simple_frame.data, cast->simple_frame.height, + cast->simple_frame.stride, cast->simple_frame.y_invert); logprint(TRACE, "pipewire: pointer %p", d[0].data); logprint(TRACE, "pipewire: size %d", d[0].maxsize); logprint(TRACE, "pipewire: stride %d", d[0].chunk->stride); - logprint(TRACE, "pipewire: width %d", ctx->simple_frame.width); - logprint(TRACE, "pipewire: height %d", ctx->simple_frame.height); - logprint(TRACE, "pipewire: y_invert %d", ctx->simple_frame.y_invert); + logprint(TRACE, "pipewire: width %d", cast->simple_frame.width); + logprint(TRACE, "pipewire: height %d", cast->simple_frame.height); + logprint(TRACE, "pipewire: y_invert %d", cast->simple_frame.y_invert); logprint(TRACE, "********************"); - pw_stream_queue_buffer(ctx->stream, pw_buf); + pw_stream_queue_buffer(cast->stream, pw_buf); - wlr_frame_free(state); + xdpw_wlr_frame_free(cast); } static void pwr_handle_stream_state_changed(void *data, enum pw_stream_state old, enum pw_stream_state state, const char *error) { - struct xdpw_state *xdpw_state = data; - struct screencast_context *ctx = &xdpw_state->screencast; - ctx->node_id = pw_stream_get_node_id(ctx->stream); + struct xdpw_screencast_instance *cast = data; + cast->node_id = pw_stream_get_node_id(cast->stream); logprint(INFO, "pipewire: stream state changed to \"%s\"", pw_stream_state_as_string(state)); - logprint(INFO, "pipewire: node id is %d", ctx->node_id); + logprint(INFO, "pipewire: node id is %d", cast->node_id); switch (state) { case PW_STREAM_STATE_STREAMING: - ctx->stream_state = true; + cast->pwr_stream_state = true; break; default: - ctx->stream_state = false; + cast->pwr_stream_state = false; break; } } static void pwr_handle_stream_param_changed(void *data, uint32_t id, const struct spa_pod *param) { - struct xdpw_state *xdpw_state = data; - struct screencast_context *ctx = &xdpw_state->screencast; - struct pw_stream *stream = ctx->stream; + struct xdpw_screencast_instance *cast = data; + struct pw_stream *stream = cast->stream; uint8_t params_buffer[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer)); @@ -104,14 +111,14 @@ static void pwr_handle_stream_param_changed(void *data, uint32_t id, return; } - spa_format_video_raw_parse(param, &ctx->pwr_format); + spa_format_video_raw_parse(param, &cast->pwr_format); params[0] = spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(BUFFERS, 1, 32), SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), - SPA_PARAM_BUFFERS_size, SPA_POD_Int(ctx->simple_frame.size), - SPA_PARAM_BUFFERS_stride, SPA_POD_Int(ctx->simple_frame.stride), + SPA_PARAM_BUFFERS_size, SPA_POD_Int(cast->simple_frame.size), + SPA_PARAM_BUFFERS_stride, SPA_POD_Int(cast->simple_frame.stride), SPA_PARAM_BUFFERS_align, SPA_POD_Int(ALIGN)); params[1] = spa_pod_builder_add_object(&b, @@ -128,66 +135,89 @@ static const struct pw_stream_events pwr_stream_events = { .param_changed = pwr_handle_stream_param_changed, }; -void *pwr_start(struct xdpw_state *state) { - struct screencast_context *ctx = &state->screencast; +void xdpw_pwr_stream_init(struct xdpw_screencast_instance *cast) { + struct xdpw_screencast_context *ctx = cast->ctx; + struct xdpw_state *state = ctx->state; + pw_loop_enter(state->pw_loop); const struct spa_pod *params[1]; uint8_t buffer[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); - ctx->pwr_context = pw_context_new(state->pw_loop, NULL, 0); - if (!ctx->pwr_context) { - logprint(ERROR, "pipewire: failed to create context"); - abort(); - } - - ctx->core = pw_context_connect(ctx->pwr_context, NULL, 0); - if (!ctx->core) { - logprint(ERROR, "pipewire: couldn't connect to context"); - abort(); - } - - ctx->stream = pw_stream_new(ctx->core, "xdg-desktop-portal-wlr", + char name[] = "xdpw-stream-XXXXXX"; + randname(name + strlen(name) - 6); + cast->stream = pw_stream_new(ctx->core, name, pw_properties_new( PW_KEY_MEDIA_CLASS, "Video/Source", NULL)); - if (!ctx->stream) { + if (!cast->stream) { logprint(ERROR, "pipewire: failed to create stream"); abort(); } - ctx->stream_state = false; + cast->pwr_stream_state = false; /* make an event to signal frame ready */ - ctx->event = - pw_loop_add_event(state->pw_loop, pwr_on_event, state); + cast->event = + pw_loop_add_event(state->pw_loop, pwr_on_event, cast); + logprint(TRACE, "pipewire: registered event %p", cast->event); params[0] = spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), - SPA_FORMAT_VIDEO_format, SPA_POD_Id(pipewire_from_wl_shm(ctx)), + SPA_FORMAT_VIDEO_format, SPA_POD_Id(xdpw_format_pw_from_wl_shm(cast)), SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle( - &SPA_RECTANGLE(ctx->simple_frame.width, ctx->simple_frame.height), + &SPA_RECTANGLE(cast->simple_frame.width, cast->simple_frame.height), &SPA_RECTANGLE(1, 1), &SPA_RECTANGLE(4096, 4096)), // variable framerate SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&SPA_FRACTION(0, 1)), SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_CHOICE_RANGE_Fraction( - &SPA_FRACTION(ctx->framerate, 1), + &SPA_FRACTION(cast->framerate, 1), &SPA_FRACTION(1, 1), - &SPA_FRACTION(ctx->framerate, 1))); + &SPA_FRACTION(cast->framerate, 1))); - pw_stream_add_listener (ctx->stream, &ctx->stream_listener, - &pwr_stream_events, state); + pw_stream_add_listener(cast->stream, &cast->stream_listener, + &pwr_stream_events, cast); - pw_stream_connect(ctx->stream, + pw_stream_connect(cast->stream, PW_DIRECTION_OUTPUT, PW_ID_ANY, (PW_STREAM_FLAG_DRIVER | PW_STREAM_FLAG_MAP_BUFFERS), params, 1); - return NULL; +} + +int xdpw_pwr_core_connect(struct xdpw_state *state) { + struct xdpw_screencast_context *ctx = &state->screencast; + + logprint(TRACE, "pipewire: establishing connection to core"); + + if (!ctx->pwr_context) { + ctx->pwr_context = pw_context_new(state->pw_loop, NULL, 0); + if (!ctx->pwr_context) { + logprint(ERROR, "pipewire: failed to create context"); + return -1; + } + } + + if (!ctx->core) { + ctx->core = pw_context_connect(ctx->pwr_context, NULL, 0); + if (!ctx->core) { + logprint(ERROR, "pipewire: couldn't connect to context"); + return -1; + } + } + return 0; +} + +void xdpw_pwr_stream_destroy(struct xdpw_screencast_instance *cast) { + logprint(TRACE, "pipewire: destroying stream"); + pw_stream_flush(cast->stream, false); + pw_stream_disconnect(cast->stream); + pw_stream_destroy(cast->stream); + cast->stream = NULL; } diff --git a/src/screencast/screencast.c b/src/screencast/screencast.c index c7fd8e4..4af613b 100644 --- a/src/screencast/screencast.c +++ b/src/screencast/screencast.c @@ -1,61 +1,115 @@ +#define _POSIX_C_SOURCE 200809L + #include "screencast.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pipewire_screencast.h" +#include "wlr_screencast.h" +#include "xdpw.h" +#include "logger.h" + static const char object_path[] = "/org/freedesktop/portal/desktop"; static const char interface_name[] = "org.freedesktop.impl.portal.ScreenCast"; -int setup_outputs(struct xdpw_state *state) { +void xdpw_screencast_instance_init(struct xdpw_screencast_context *ctx, + struct xdpw_screencast_instance *cast, struct xdpw_wlr_output *out, bool with_cursor) { + cast->ctx = ctx; + cast->target_output = out; + cast->framerate = out->framerate; + cast->with_cursor = with_cursor; + cast->refcount = 1; + logprint(INFO, "xdpw: screencast instance %p has %d references", cast, cast->refcount); + wl_list_insert(&ctx->screencast_instances, &cast->link); + logprint(INFO, "xdpw: %d active screencast instances", + wl_list_length(&ctx->screencast_instances)); +} - struct screencast_context *ctx = &state->screencast; +void xdpw_screencast_instance_destroy(struct xdpw_screencast_instance *cast) { + assert(cast->refcount == 0); + logprint(TRACE, "xdpw: destroying cast instance"); + wl_list_remove(&cast->link); + xdpw_pwr_stream_destroy(cast); + free(cast); +} - struct wayland_output *output, *tmp_o; +int setup_outputs(struct xdpw_screencast_context *ctx, struct xdpw_session *sess, bool with_cursor) { + + struct xdpw_wlr_output *output, *tmp_o; 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", output->make, output->model, output->id, output->name); } - struct wayland_output *out; + struct xdpw_wlr_output *out; if (ctx->output_name) { - out = wlr_output_find_by_name(&ctx->output_list, ctx->output_name); + out = xdpw_wlr_output_find_by_name(&ctx->output_list, ctx->output_name); if (!out) { logprint(ERROR, "wlroots: no such output"); abort(); } } else { - out = wlr_output_first(&ctx->output_list); + out = xdpw_wlr_output_first(&ctx->output_list); if (!out) { logprint(ERROR, "wlroots: no output found"); abort(); } } - ctx->target_output = out; - ctx->framerate = out->framerate; - ctx->with_cursor = 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", + cast->target_output->id, + cast->with_cursor ? "with" : "without"); - logprint(INFO, "wlroots: output: %s", ctx->target_output->name); - logprint(INFO, "wlroots: wl_display fd: %d", wl_display_get_fd(state->wl_display)); + if (cast->target_output->id == out->id && cast->with_cursor == with_cursor) { + sess->screencast_instance = cast; + ++cast->refcount; + logprint(INFO, "xdpw: screencast instance %p has %d references", cast, cast->refcount); + } + } + + if (!sess->screencast_instance) { + sess->screencast_instance = calloc(1, sizeof(struct xdpw_screencast_instance)); + xdpw_screencast_instance_init(ctx, sess->screencast_instance, + out, with_cursor); + } + logprint(INFO, "wlroots: output: %s", + sess->screencast_instance->target_output->name); return 0; } -void *start_screencast(void *data) { - struct xdpw_state *state = data; +static int start_screencast(void *data) { + struct xdpw_screencast_instance *cast = data; + + xdpw_wlr_register_cb(cast); - wlr_register_cb(state); // process at least one frame so that we know // some of the metadata required for the pipewire // remote state connected event - wl_display_dispatch(state->wl_display); - wl_display_roundtrip(state->wl_display); + wl_display_dispatch(cast->ctx->state->wl_display); + wl_display_roundtrip(cast->ctx->state->wl_display); - pwr_start(state); + xdpw_pwr_stream_init(cast); - return NULL; + cast->initialized = true; + return 0; } static int method_screencast_create_session(sd_bus_message *msg, void *data, sd_bus_error *ret_error) { + struct xdpw_state *state = data; + int ret = 0; logprint(INFO, "dbus: create session method invoked"); @@ -106,16 +160,14 @@ static int method_screencast_create_session(sd_bus_message *msg, void *data, return ret; } - // TODO: cleanup this struct xdpw_request *req = - request_create(sd_bus_message_get_bus(msg), request_handle); + xdpw_request_create(sd_bus_message_get_bus(msg), request_handle); if (req == NULL) { return -ENOMEM; } - // TODO: cleanup this struct xdpw_session *sess = - session_create(sd_bus_message_get_bus(msg), session_handle); + xdpw_session_create(state, sd_bus_message_get_bus(msg), strdup(session_handle)); if (sess == NULL) { return -ENOMEM; } @@ -138,16 +190,19 @@ static int method_screencast_create_session(sd_bus_message *msg, void *data, return 0; } - static int method_screencast_select_sources(sd_bus_message *msg, void *data, sd_bus_error *ret_error) { struct xdpw_state *state = data; + struct xdpw_screencast_context *ctx = &state->screencast; int ret = 0; + struct xdpw_session *sess, *tmp_s; + sd_bus_message *reply = NULL; logprint(INFO, "dbus: select sources method invoked"); - setup_outputs(state); + // 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); @@ -178,7 +233,22 @@ static int method_screencast_select_sources(sd_bus_message *msg, void *data, } else if (strcmp(key, "types") == 0) { uint32_t mask; sd_bus_message_read(msg, "v", "u", &mask); + if (mask & (1<xdpw_sessions, link) { + if (strcmp(sess->session_handle, session_handle) == 0) { + logprint(TRACE, "dbus: select sources: found matching session %s", sess->session_handle); + ret = setup_outputs(ctx, sess, cursor_embedded); + } + } + if (ret < 0) { + return ret; + } - sd_bus_message *reply = NULL; ret = sd_bus_message_new_method_return(msg, &reply); if (ret < 0) { return ret; @@ -211,22 +290,41 @@ static int method_screencast_select_sources(sd_bus_message *msg, void *data, if (ret < 0) { return ret; } - sd_bus_message_unref(reply); 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(TRACE, "dbus: select sources error: destroying matching session %s", sess->session_handle); + xdpw_session_destroy(sess); + } + } + + ret = sd_bus_message_new_method_return(msg, &reply); + if (ret < 0) { + return ret; + } + ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_CANCELLED, 0); + if (ret < 0) { + return ret; + } + ret = sd_bus_send(NULL, reply, NULL); + if (ret < 0) { + return ret; + } + sd_bus_message_unref(reply); + return -1; } static int method_screencast_start(sd_bus_message *msg, void *data, sd_bus_error *ret_error) { struct xdpw_state *state = data; - struct screencast_context *ctx = &state->screencast; int ret = 0; logprint(INFO, "dbus: start method invoked"); - start_screencast(state); - char *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) { @@ -249,10 +347,8 @@ static int method_screencast_start(sd_bus_message *msg, void *data, if (innerRet < 0) { return innerRet; } - logprint(WARN, "dbus: unknown option: %s", key); sd_bus_message_skip(msg, "v"); - innerRet = sd_bus_message_exit_container(msg); if (innerRet < 0) { return innerRet; @@ -266,24 +362,40 @@ static int method_screencast_start(sd_bus_message *msg, void *data, return ret; } + struct xdpw_screencast_instance *cast = NULL; + struct xdpw_session *sess, *tmp_s; + wl_list_for_each_reverse_safe(sess, tmp_s, &state->xdpw_sessions, link) { + if (strcmp(sess->session_handle, session_handle) == 0) { + logprint(TRACE, "dbus: start: found matching session %s", sess->session_handle); + cast = sess->screencast_instance; + } + } + if (!cast) { + return -1; + } + + if (!cast->initialized) { + start_screencast(cast); + } + + while (cast->node_id == 0) { + int ret = pw_loop_iterate(state->pw_loop, 0); + if (ret != 0) { + logprint(ERROR, "pipewire_loop_iterate failed: %s", spa_strerror(ret)); + } + } + sd_bus_message *reply = NULL; ret = sd_bus_message_new_method_return(msg, &reply); if (ret < 0) { return ret; } - while (ctx->node_id == 0) { - int ret = pw_loop_iterate(state->pw_loop, 0); - if (ret < 0) { - logprint(ERROR, "pipewire_loop_iterate failed: %s", spa_strerror(ret)); - } - } - ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_SUCCESS, 1, "streams", "a(ua{sv})", 1, - ctx->node_id, 2, + cast->node_id, 2, "position", "(ii)", 0, 0, - "size", "(ii)", ctx->simple_frame.width, ctx->simple_frame.height); + "size", "(ii)", cast->simple_frame.width, cast->simple_frame.height); if (ret < 0) { return ret; @@ -306,20 +418,33 @@ static const sd_bus_vtable screencast_vtable[] = { method_screencast_select_sources, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Start", "oossa{sv}", "ua{sv}", method_screencast_start, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_PROPERTY("AvailableSourceTypes", "u", NULL, + offsetof(struct xdpw_state, screencast_source_types), + SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("AvailableCursorModes", "u", NULL, + offsetof(struct xdpw_state, screencast_cursor_modes), + SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("version", "u", NULL, + offsetof(struct xdpw_state, screencast_version), + SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_VTABLE_END }; -int init_screencast(struct xdpw_state *state, const char *output_name, const char *forced_pixelformat) { - // TODO: cleanup +int xdpw_screencast_init(struct xdpw_state *state, const char *output_name, const char *forced_pixelformat) { sd_bus_slot *slot = NULL; - state->screencast = (struct screencast_context) { 0 }; + state->screencast = (struct xdpw_screencast_context) { 0 }; + state->screencast.state = state; state->screencast.forced_pixelformat = forced_pixelformat; state->screencast.output_name = output_name; - state->screencast.simple_frame = (struct simple_frame) { 0 }; - state->screencast.simple_frame.damage = &(struct damage) { 0 }; - int err = wlr_screencopy_init(state); + int err; + err = xdpw_pwr_core_connect(state); + if (err) { + goto end; + } + + err = xdpw_wlr_screencopy_init(state); if (err) { goto end; } @@ -328,6 +453,6 @@ int init_screencast(struct xdpw_state *state, const char *output_name, const cha screencast_vtable, state); end: - wlr_screencopy_uninit(&state->screencast); + xdpw_wlr_screencopy_finish(&state->screencast); return err; } diff --git a/src/screencast/screencast_common.c b/src/screencast/screencast_common.c index e7306a2..f80e77e 100644 --- a/src/screencast/screencast_common.c +++ b/src/screencast/screencast_common.c @@ -1,18 +1,30 @@ #include "screencast_common.h" +#include -enum spa_video_format pipewire_from_wl_shm(void *data) { - struct screencast_context *ctx = data; +void randname(char *buf) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + long r = ts.tv_nsec; + for (int i = 0; i < 6; ++i) { + assert(buf[i] == 'X'); + buf[i] = 'A'+(r&15)+(r&16)*2; + r >>= 5; + } +} - if (ctx->forced_pixelformat) { - if (strcmp(ctx->forced_pixelformat, "BGRx") == 0) { +enum spa_video_format xdpw_format_pw_from_wl_shm(void *data) { + struct xdpw_screencast_instance *cast = data; + + if (cast->ctx->forced_pixelformat) { + if (strcmp(cast->ctx->forced_pixelformat, "BGRx") == 0) { return SPA_VIDEO_FORMAT_BGRx; } - if (strcmp(ctx->forced_pixelformat, "RGBx") == 0) { + if (strcmp(cast->ctx->forced_pixelformat, "RGBx") == 0) { return SPA_VIDEO_FORMAT_RGBx; } } - switch (ctx->simple_frame.format) { + switch (cast->simple_frame.format) { case WL_SHM_FORMAT_ARGB8888: return SPA_VIDEO_FORMAT_BGRA; case WL_SHM_FORMAT_XRGB8888: diff --git a/src/screencast/wlr_screencast.c b/src/screencast/wlr_screencast.c index 7b80847..2718160 100644 --- a/src/screencast/wlr_screencast.c +++ b/src/screencast/wlr_screencast.c @@ -1,31 +1,74 @@ #define _POSIX_C_SOURCE 200809L #include "wlr_screencast.h" -#include "xdpw.h" -void wlr_frame_free(struct xdpw_state *state) { - zwlr_screencopy_frame_v1_destroy(state->screencast.wlr_frame); - munmap(state->screencast.simple_frame.data, state->screencast.simple_frame.size); - wl_buffer_destroy(state->screencast.simple_frame.buffer); +#include "wlr-screencopy-unstable-v1-client-protocol.h" +#include "xdg-output-unstable-v1-client-protocol.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "screencast.h" +#include "pipewire_screencast.h" +#include "xdpw.h" +#include "logger.h" + +void xdpw_wlr_frame_free(struct xdpw_screencast_instance *cast) { + zwlr_screencopy_frame_v1_destroy(cast->wlr_frame); + cast->wlr_frame = NULL; + munmap(cast->simple_frame.data, cast->simple_frame.size); + cast->simple_frame.data = NULL; + // TODO: reuse this buffer unless we quit or error out + wl_buffer_destroy(cast->simple_frame.buffer); + cast->simple_frame.buffer = NULL; logprint(TRACE, "wlroots: frame destroyed"); - if (!state->screencast.quit && !state->screencast.err) { - wlr_register_cb(state); + if (cast->quit || cast->err) { + xdpw_screencast_instance_destroy(cast); + return ; } + + xdpw_wlr_register_cb(cast); } -static struct wl_buffer *create_shm_buffer(struct screencast_context *ctx, +static int anonymous_shm_open(void) { + char name[] = "/xdpw-shm-XXXXXX"; + int retries = 100; + + do { + randname(name + strlen(name) - 6); + + --retries; + // shm_open guarantees that O_CLOEXEC is set + int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + if (fd >= 0) { + shm_unlink(name); + return fd; + } + } while (retries > 0 && errno == EEXIST); + + return -1; +} + +static struct wl_buffer *create_shm_buffer(struct xdpw_screencast_instance *cast, enum wl_shm_format fmt, int width, int height, int stride, void **data_out) { + struct xdpw_screencast_context *ctx = cast->ctx; int size = stride * height; - const char shm_name[] = "/wlroots-screencopy"; - int fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + int fd = anonymous_shm_open(); if (fd < 0) { logprint(ERROR, "wlroots: shm_open failed"); return NULL; } - shm_unlink(shm_name); int ret; while ((ret = ftruncate(fd, size)) == EINTR); @@ -55,77 +98,72 @@ static struct wl_buffer *create_shm_buffer(struct screencast_context *ctx, static void wlr_frame_buffer(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) { - struct xdpw_state *state = data; - struct screencast_context *ctx = &state->screencast; + struct xdpw_screencast_instance *cast = data; logprint(TRACE, "wlroots: buffer event handler"); - ctx->wlr_frame = frame; - ctx->simple_frame.width = width; - ctx->simple_frame.height = height; - ctx->simple_frame.stride = stride; - ctx->simple_frame.size = stride * height; - ctx->simple_frame.format = format; - ctx->simple_frame.buffer = create_shm_buffer(ctx, format, width, height, - stride, &ctx->simple_frame.data); + cast->wlr_frame = frame; + cast->simple_frame.width = width; + cast->simple_frame.height = height; + cast->simple_frame.stride = stride; + cast->simple_frame.size = stride * height; + cast->simple_frame.format = format; + cast->simple_frame.buffer = create_shm_buffer(cast, format, width, height, + stride, &cast->simple_frame.data); - if (ctx->simple_frame.buffer == NULL) { + if (cast->simple_frame.buffer == NULL) { logprint(ERROR, "wlroots: failed to create buffer"); abort(); } - zwlr_screencopy_frame_v1_copy_with_damage(frame, ctx->simple_frame.buffer); + zwlr_screencopy_frame_v1_copy_with_damage(frame, cast->simple_frame.buffer); logprint(TRACE, "wlroots: frame copied"); } static void wlr_frame_flags(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t flags) { - struct xdpw_state *state = data; - struct screencast_context *ctx = &state->screencast; + struct xdpw_screencast_instance *cast = data; logprint(TRACE, "wlroots: flags event handler"); - ctx->simple_frame.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT; + cast->simple_frame.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT; } static void wlr_frame_ready(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { - struct xdpw_state *state = data; - struct screencast_context *ctx = &state->screencast; + struct xdpw_screencast_instance *cast = data; logprint(TRACE, "wlroots: ready event handler"); - ctx->simple_frame.tv_sec = ((((uint64_t)tv_sec_hi) << 32) | tv_sec_lo); - ctx->simple_frame.tv_nsec = tv_nsec; + cast->simple_frame.tv_sec = ((((uint64_t)tv_sec_hi) << 32) | tv_sec_lo); + cast->simple_frame.tv_nsec = tv_nsec; - if (!ctx->quit && !ctx->err && ctx->stream_state) { - pw_loop_signal_event(state->pw_loop, ctx->event); + if (!cast->quit && !cast->err && cast->pwr_stream_state) { + pw_loop_signal_event(cast->ctx->state->pw_loop, cast->event); return ; } - wlr_frame_free(state); + xdpw_wlr_frame_free(cast); } static void wlr_frame_failed(void *data, struct zwlr_screencopy_frame_v1 *frame) { - struct xdpw_state *state = data; - struct screencast_context *ctx = &state->screencast; + struct xdpw_screencast_instance *cast = data; logprint(TRACE, "wlroots: failed event handler"); - ctx->err = true; + cast->err = true; - wlr_frame_free(state); + xdpw_wlr_frame_free(cast); } static void wlr_frame_damage(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t x, uint32_t y, uint32_t width, uint32_t height) { - struct xdpw_state *state = data; - struct screencast_context *ctx = &state->screencast; + struct xdpw_screencast_instance *cast = data; logprint(TRACE, "wlroots: damage event handler"); - ctx->simple_frame.damage->x = x; - ctx->simple_frame.damage->y = y; - ctx->simple_frame.damage->width = width; - ctx->simple_frame.damage->height = height; + cast->simple_frame.damage.x = x; + cast->simple_frame.damage.y = y; + cast->simple_frame.damage.width = width; + cast->simple_frame.damage.height = height; } static const struct zwlr_screencopy_frame_v1_listener wlr_frame_listener = { @@ -136,21 +174,20 @@ static const struct zwlr_screencopy_frame_v1_listener wlr_frame_listener = { .damage = wlr_frame_damage, }; -void wlr_register_cb(struct xdpw_state *state) { - struct screencast_context *ctx = &state->screencast; +void xdpw_wlr_register_cb(struct xdpw_screencast_instance *cast) { - ctx->frame_callback = zwlr_screencopy_manager_v1_capture_output( - ctx->screencopy_manager, ctx->with_cursor, ctx->target_output->output); + cast->frame_callback = zwlr_screencopy_manager_v1_capture_output( + cast->ctx->screencopy_manager, cast->with_cursor, cast->target_output->output); - zwlr_screencopy_frame_v1_add_listener(ctx->frame_callback, - &wlr_frame_listener, state); + zwlr_screencopy_frame_v1_add_listener(cast->frame_callback, + &wlr_frame_listener, cast); logprint(TRACE, "wlroots: callbacks registered"); } static void wlr_output_handle_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t phys_width, int32_t phys_height, int32_t subpixel, const char *make, const char *model, int32_t transform) { - struct wayland_output *output = data; + struct xdpw_wlr_output *output = data; output->make = strdup(make); output->model = strdup(model); } @@ -158,7 +195,7 @@ static void wlr_output_handle_geometry(void *data, struct wl_output *wl_output, static void wlr_output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { if (flags & WL_OUTPUT_MODE_CURRENT) { - struct wayland_output *output = data; + struct xdpw_wlr_output *output = data; output->framerate = (float)refresh/1000; } } @@ -181,7 +218,7 @@ static const struct wl_output_listener wlr_output_listener = { static void wlr_xdg_output_name(void* data, struct zxdg_output_v1* xdg_output, const char* name) { - struct wayland_output *output = data; + struct xdpw_wlr_output *output = data; output->name = strdup(name); }; @@ -198,34 +235,34 @@ static const struct zxdg_output_v1_listener wlr_xdg_output_listener = { .name = wlr_xdg_output_name, }; -void wlr_add_xdg_output_listener(struct wayland_output *output, +static void wlr_add_xdg_output_listener(struct xdpw_wlr_output *output, struct zxdg_output_v1* xdg_output) { output->xdg_output = xdg_output; zxdg_output_v1_add_listener(output->xdg_output, &wlr_xdg_output_listener, output); } -static void wlr_init_xdg_outputs(struct screencast_context *ctx) { - struct wayland_output *output, *tmp; +static void wlr_init_xdg_outputs(struct xdpw_screencast_context *ctx) { + struct xdpw_wlr_output *output, *tmp; wl_list_for_each_safe(output, tmp, &ctx->output_list, link) { struct zxdg_output_v1 *xdg_output = - zxdg_output_manager_v1_get_xdg_output( ctx->xdg_output_manager, + zxdg_output_manager_v1_get_xdg_output(ctx->xdg_output_manager, output->output); wlr_add_xdg_output_listener(output, xdg_output); } } -struct wayland_output *wlr_output_first(struct wl_list *output_list) { - struct wayland_output *output, *tmp; +struct xdpw_wlr_output *xdpw_wlr_output_first(struct wl_list *output_list) { + struct xdpw_wlr_output *output, *tmp; wl_list_for_each_safe(output, tmp, output_list, link) { return output; } return NULL; } -struct wayland_output *wlr_output_find_by_name(struct wl_list *output_list, +struct xdpw_wlr_output *xdpw_wlr_output_find_by_name(struct wl_list *output_list, const char* name) { - struct wayland_output *output, *tmp; + struct xdpw_wlr_output *output, *tmp; wl_list_for_each_safe(output, tmp, output_list, link) { if (strcmp(output->name, name) == 0) { return output; @@ -234,9 +271,9 @@ struct wayland_output *wlr_output_find_by_name(struct wl_list *output_list, return NULL; } -struct wayland_output *wlr_output_find(struct screencast_context *ctx, +struct xdpw_wlr_output *xdpw_wlr_output_find(struct xdpw_screencast_context *ctx, struct wl_output *out, uint32_t id) { - struct wayland_output *output, *tmp; + struct xdpw_wlr_output *output, *tmp; wl_list_for_each_safe(output, tmp, &ctx->output_list, link) { if ((output->output == out) || (output->id == id)) { return output; @@ -245,16 +282,16 @@ struct wayland_output *wlr_output_find(struct screencast_context *ctx, return NULL; } -static void wlr_remove_output(struct wayland_output *out) { +static void wlr_remove_output(struct xdpw_wlr_output *out) { wl_list_remove(&out->link); } static void wlr_registry_handle_add(void *data, struct wl_registry *reg, uint32_t id, const char *interface, uint32_t ver) { - struct screencast_context *ctx = data; + struct xdpw_screencast_context *ctx = data; if (!strcmp(interface, wl_output_interface.name)) { - struct wayland_output *output = malloc(sizeof(*output)); + struct xdpw_wlr_output *output = malloc(sizeof(*output)); output->id = id; output->output = wl_registry_bind(reg, id, &wl_output_interface, 1); @@ -281,7 +318,7 @@ static void wlr_registry_handle_add(void *data, struct wl_registry *reg, static void wlr_registry_handle_remove(void *data, struct wl_registry *reg, uint32_t id) { wlr_remove_output( - wlr_output_find((struct screencast_context *)data, NULL, id)); + xdpw_wlr_output_find((struct xdpw_screencast_context *)data, NULL, id)); } static const struct wl_registry_listener wlr_registry_listener = { @@ -289,12 +326,15 @@ static const struct wl_registry_listener wlr_registry_listener = { .global_remove = wlr_registry_handle_remove, }; -int wlr_screencopy_init(struct xdpw_state *state) { - struct screencast_context *ctx = &state->screencast; +int xdpw_wlr_screencopy_init(struct xdpw_state *state) { + struct xdpw_screencast_context *ctx = &state->screencast; - // retrieve list of outputs + // initialize a list of outputs wl_list_init(&ctx->output_list); + // initialize a list of active screencast instances + wl_list_init(&ctx->screencast_instances); + // retrieve registry ctx->registry = wl_display_get_registry(state->wl_display); wl_registry_add_listener(ctx->registry, &wlr_registry_listener, ctx); @@ -327,15 +367,29 @@ int wlr_screencopy_init(struct xdpw_state *state) { return 0; } -void wlr_screencopy_uninit(struct screencast_context *ctx) { - struct wayland_output *output, *tmp_o; +void xdpw_wlr_screencopy_finish(struct xdpw_screencast_context *ctx) { + struct xdpw_wlr_output *output, *tmp_o; wl_list_for_each_safe(output, tmp_o, &ctx->output_list, link) { wl_list_remove(&output->link); zxdg_output_v1_destroy(output->xdg_output); wl_output_destroy(output->output); } + struct xdpw_screencast_instance *cast, *tmp_c; + wl_list_for_each_safe(cast, tmp_c, &ctx->screencast_instances, link) { + cast->quit = true; + } + if (ctx->screencopy_manager) { zwlr_screencopy_manager_v1_destroy(ctx->screencopy_manager); } + if (ctx->shm) { + wl_shm_destroy(ctx->shm); + } + if (ctx->xdg_output_manager) { + zxdg_output_manager_v1_destroy(ctx->xdg_output_manager); + } + if (ctx->registry) { + wl_registry_destroy(ctx->registry); + } } diff --git a/src/screenshot/screenshot.c b/src/screenshot/screenshot.c index b504e21..94fe83c 100644 --- a/src/screenshot/screenshot.c +++ b/src/screenshot/screenshot.c @@ -49,7 +49,7 @@ static int method_screenshot(sd_bus_message *msg, void *data, // TODO: cleanup this struct xdpw_request *req = - request_create(sd_bus_message_get_bus(msg), handle); + xdpw_request_create(sd_bus_message_get_bus(msg), handle); if (req == NULL) { return -ENOMEM; } @@ -90,7 +90,7 @@ static const sd_bus_vtable screenshot_vtable[] = { SD_BUS_VTABLE_END }; -int init_screenshot(struct xdpw_state *state) { +int xdpw_screenshot_init(struct xdpw_state *state) { // TODO: cleanup sd_bus_slot *slot = NULL; return sd_bus_add_object_vtable(state->bus, &slot, object_path, interface_name,