Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement XDG Desktop Portal backend #47

Draft
wants to merge 38 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
6d0c4c0
CMake: add xdg-desktop-portal backend and dependencies
Ferdi265 Oct 24, 2023
2d96f52
CMake: add libelogind and libbasu as fallback for libsystemd
Ferdi265 Oct 24, 2023
9553578
CMake: add option for specifying sd-bus provider
Ferdi265 Jan 3, 2023
a5a1f86
README: document xdg-desktop-portal and related dependencies
Ferdi265 Oct 24, 2023
a72bd18
README: document alternative dependencies for libsystemd
Ferdi265 Oct 24, 2023
01dba03
src/wayland: tolerate xdg_output_manager being sent after outputs
Ferdi265 Aug 6, 2024
4d0e55b
src/wayland: bind xdg-foreign protocol and export toplevel
Ferdi265 Oct 24, 2023
75eca94
src/options, src/mirror-xdg-portal: add (empty) xdg-portal backend
Ferdi265 Dec 21, 2022
3ff0a19
src/options: document xdg-desktop-portal backend
Ferdi265 Oct 24, 2023
d8751ae
src/mirror-xdg-portal: start adding DBus boilerplate
Ferdi265 Dec 22, 2022
7c55cae
src/mirror-xdg-portal: add close listener for session
Ferdi265 Dec 22, 2022
9ebde78
src/mirror-xdg-portal: start writing pipewire boilerplate
Ferdi265 Dec 26, 2022
aacbc92
src/mirror-xdg-portal: redo session handling
Ferdi265 Dec 26, 2022
b38644a
src/mirror-xdg-portal: fix pipewire fd getting closed behind our back
Ferdi265 Jan 3, 2023
005e673
src/mirror-xdg-portal: clean up event loop handling
Ferdi265 Jan 3, 2023
8fa8205
src/mirror-xdg-portal: clean up pw_core event handlers
Ferdi265 Jan 3, 2023
54ef9da
src/mirror-xdg-portal: remove unneessary renegotiate pipewire callback
Ferdi265 Jan 3, 2023
5e44350
src/mirror-xdg-portal: actually close sessions
Ferdi265 Jan 3, 2023
0719a95
src/mirror-xdg-portal: use asprintf for tokens, use pid and counter i…
Ferdi265 Jan 3, 2023
36cc2ec
src/mirror-xdg-portal: make missing xdg_exporter non-fatal
Ferdi265 Jan 3, 2023
b7d9bb8
src/mirror-xdg-portal: don't assume POLL and EPOLL constants are the …
Ferdi265 Jan 3, 2023
5bb2310
src/mirror-xdg-portal: correctly track whether a session is open
Ferdi265 Jan 3, 2023
073bb51
src/mirror-xdg-portal: connect stream and start handling stream data
Ferdi265 Mar 20, 2023
5a530b9
src/mirror-xdg-portal: request some params from pipewire
Ferdi265 Mar 20, 2023
0e51d58
src/mirror-xdg-portal: add some more debug prints
Ferdi265 Mar 20, 2023
6309076
src/mirror-xdg-portal: remember drm and gl formats
Ferdi265 Mar 20, 2023
c8a3497
src/mirror-xdg-portal: parse pipewire version and import dmabuf
Ferdi265 Mar 20, 2023
079bb8e
src/mirror-xdg-portal: fix cleanup sequence crashing
Ferdi265 Mar 21, 2023
480e9c4
src/egl: move viewport resizing into dmabuf import code
Ferdi265 Mar 21, 2023
7f8c38c
src/mirror-dmabuf, src/mirror-screencopy, src/mirror-xdg-portal: more…
Ferdi265 Jul 4, 2023
af25386
src/egl: add debug prints to dmabuf_to_texture
Ferdi265 Oct 27, 2023
ee6eed9
src/mirror-xdg-portal: add hardcoded list of offered formats
Ferdi265 Jul 5, 2023
b858df3
src/egl: add debug print to resize_viewport
Ferdi265 Oct 27, 2023
f83ead2
src/mirror-xdg-portal: conditionally update uniforms
Ferdi265 Oct 27, 2023
7a691a7
src/mirror-xdg-portal: fix heap buffer overflow in spa_pod_dynamic_bu…
Ferdi265 Nov 1, 2023
dfb9cae
src/mirror-xdg-portal: fix ADD_FORMAT not using passed builder
Ferdi265 Nov 1, 2023
133b2c1
src/mirror-xdg-portal: fix use after free by using separate builders …
Ferdi265 Nov 1, 2023
af76781
src/mirror-xdg-portal: don't require xdg_foreign
Ferdi265 Aug 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ project(wl-mirror C)
option(INSTALL_EXAMPLE_SCRIPTS "install wl-mirror example scripts" OFF)
option(INSTALL_DOCUMENTATION "install wl-mirror manual pages" OFF)
option(WITH_LIBDECOR "use libdecor for window decoration" OFF)
option(WITH_XDG_PORTAL_BACKEND "enable the xdg-desktop-portal / pipewire backend" OFF)
set(SD_BUS_PROVIDER AUTO CACHE STRING "provider library for sd-bus")
set(FORCE_WAYLAND_SCANNER_PATH "" CACHE STRING "provide a custom path for wayland-scanner")

# wayland protocols needed by wl-mirror
Expand All @@ -19,6 +21,7 @@ set(PROTOCOLS
"stable/viewporter/viewporter.xml"
"staging/fractional-scale/fractional-scale-v1.xml"
"unstable/xdg-output/xdg-output-unstable-v1.xml"
"unstable/xdg-foreign/xdg-foreign-unstable-v2.xml"
"unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml"
"unstable/wlr-export-dmabuf-unstable-v1.xml"
"unstable/wlr-screencopy-unstable-v1.xml"
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ backends:
- auto automatically try the backends in order and use the first that works (default)
- dmabuf use the wlr-export-dmabuf-unstable-v1 protocol to capture outputs
- screencopy use the wlr-screencopy-unstable-v1 protocol to capture outputs
- xdg-portal use xdg-desktop-portal and pipewire to capture outputs or windows (WIP)
- pipewire alias for 'xdg-portal' (WIP)

transforms:
transforms are specified as a dash-separated list of flips followed by a rotation
Expand Down Expand Up @@ -172,6 +174,8 @@ on (see issues [#16](https://github.com/Ferdi265/wl-mirror/issues/16) and
- `libGLESv2`
- `epoll-shim` (on systems that do not have `epoll`, e.g. FreeBSD)
- `libdecor` (see `WITH_LIBDECOR`)
- `libsystemd` or `libelogind` or `libbasu` (for xdg-desktop-portal backend, see `WITH_XDG_PORTAL_BACKEND`)
- `libpipewire-0.3` (for xdg-desktop-portal backend, see `WITH_XDG_PORTAL_BACKEND`)
- `wayland-scanner`
- `scdoc` (for manual pages, see `INSTALL_DOCUMENTATION`)

Expand All @@ -193,9 +197,12 @@ on (see issues [#16](https://github.com/Ferdi265/wl-mirror/issues/16) and
- `INSTALL_EXAMPLE_SCRIPTS`: also install example scripts (default `OFF`)
- `INSTALL_DOCUMENTATION`: also build and install manual pages (default `OFF`)
- `WITH_LIBDECOR`: build with libdecor for window decoration (default `OFF`)
- `WITH_XDG_PORTAL_BACKEND`: enable the xdg-desktop-portal and pipewire screen capture backend (default `OFF`)
- `SD_BUS_PROVIDER`: the library used to provide sd-bus (default `AUTO`)
- `FORCE_WAYLAND_SCANNER_PATH`: always use the provided path for wayland-scanner, do not use pkg-config (default empty)
- `FORCE_SYSTEM_WL_PROTOCOLS`: always use system-installed wayland-protocols, do not use submodules (default `OFF`)
- `FORCE_SYSTEM_WLR_PROTOCOLS`: always use system-installed wlr-protocols, do not use submodules (default `OFF`)
- `WITH_XDG_PORTAL_BACKEND`: enable the xdg-desktop-portal and pipewire screen capture backend (default `OFF`)
- `WL_PROTOCOL_DIR`: directory where system-installed wayland-protocols are located (default `/usr/share/wayland-protocols`)
- `WLR_PROTOCOL_DIR`: directory where system-installed wlr-protocols are located (default `/usr/share/wlr-protocols`)

Expand All @@ -208,6 +215,7 @@ on (see issues [#16](https://github.com/Ferdi265/wl-mirror/issues/16) and
- `src/mirror.c`: output mirroring code
- `src/mirror-dmabuf.c`: wlr-export-dmabuf-unstable-v1 backend code
- `src/mirror-screencopy.c`: wlr-screencopy-unstable-v1 backend code
- `src/mirror-xdg-portal.c`: xdg-desktop-portal and pipewire backend code (WIP)
- `src/transform.c`: matrix transformation code
- `src/event.c`: event loop
- `src/stream.c`: asynchronous option stream input
Expand Down
12 changes: 12 additions & 0 deletions deps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,15 @@ if(${WITH_LIBDECOR})
target_link_libraries(deps INTERFACE PkgConfig::LibDecor)
target_compile_definitions(deps INTERFACE WITH_LIBDECOR)
endif()

# find xdg-portal dependencies (dbus and pipewire)
if(${WITH_XDG_PORTAL_BACKEND})
if(${SD_BUS_PROVIDER} STREQUAL AUTO)
do_pkg_search_module(SDBus REQUIRED IMPORTED_TARGET "libsystemd" "libelogind" "basu")
else()
pkg_check_modules(SDBus REQUIRED IMPORTED_TARGET "${SD_BUS_PROVIDER}")
endif()
pkg_check_modules(PipeWire REQUIRED IMPORTED_TARGET "libpipewire-0.3")
target_link_libraries(deps INTERFACE PkgConfig::SDBus PkgConfig::PipeWire)
target_compile_definitions(deps INTERFACE WITH_XDG_PORTAL_BACKEND)
endif()
1 change: 1 addition & 0 deletions include/wlm/mirror-backends.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ typedef struct mirror_backend {

void wlm_mirror_dmabuf_init(struct ctx * ctx);
void wlm_mirror_screencopy_init(struct ctx * ctx);
void wlm_mirror_xdg_portal_init(struct ctx * ctx);

#endif
93 changes: 93 additions & 0 deletions include/wlm/mirror-xdg-portal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#ifndef WL_MIRROR_MIRROR_XDG_PORTAL_H_
#define WL_MIRROR_MIRROR_XDG_PORTAL_H_

#include <stdint.h>
#include <systemd/sd-bus.h>
#include <pipewire/pipewire.h>

#include <wlm/event.h>
#include <wlm/mirror.h>

struct xdg_portal_mirror_backend;

typedef enum {
SCREENCAST_MONITOR = 1,
SCREENCAST_WINDOW = 2,
SCREENCAST_VIRTUAL = 4
} screencast_source_types_t;

typedef enum {
SCREENCAST_HIDDEN = 1,
SCREENCAST_EMBEDDED = 2,
SCREENCAST_METADATA = 4
} screencast_cursor_modes_t;

typedef struct {
screencast_source_types_t source_types;
screencast_cursor_modes_t cursor_modes;
uint32_t version;
} screencast_properties_t;

typedef int (*request_ctx_handler_t)(struct ctx * ctx, struct xdg_portal_mirror_backend * backend, sd_bus_message * reply);

typedef struct {
struct ctx * ctx;
const char * name;
request_ctx_handler_t handler;
} request_ctx_t;

#define SCREENCAST_MIN_VERSION 2

typedef enum {
STATE_IDLE,
STATE_GET_PROPERTIES,
STATE_CREATE_SESSION,
STATE_SELECT_SOURCES,
STATE_START,
STATE_OPEN_PIPEWIRE_REMOTE,
STATE_PW_INIT,
STATE_PW_CREATE_STREAM,
STATE_RUNNING,
STATE_BROKEN
} xdg_portal_state_t;

typedef struct xdg_portal_mirror_backend {
mirror_backend_t header;

// general info
uint32_t x, y, w, h;
uint32_t gl_format;
uint32_t drm_format;
uint64_t drm_modifier;

// sd-bus state
screencast_properties_t screencast_properties;
char * request_handle;
char * session_handle;
bool session_open;

request_ctx_t rctx;
sd_bus_slot * call_slot;
sd_bus_slot * session_slot;
sd_bus * bus;
event_handler_t dbus_event_handler;

// pipewire state
int pw_fd;
uint32_t pw_node_id;
uint32_t pw_major;
uint32_t pw_minor;
uint32_t pw_patch;

struct pw_loop * pw_loop;
struct pw_context * pw_context;
struct pw_core * pw_core;
struct pw_stream * pw_stream;
struct spa_hook pw_core_listener;
struct spa_hook pw_stream_listener;
event_handler_t pw_event_handler;

xdg_portal_state_t state;
} xdg_portal_mirror_backend_t;

#endif
5 changes: 4 additions & 1 deletion include/wlm/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ typedef enum {
typedef enum {
BACKEND_AUTO,
BACKEND_DMABUF,
BACKEND_SCREENCOPY
BACKEND_SCREENCOPY,
#ifdef WITH_XDG_PORTAL_BACKEND
BACKEND_XDG_PORTAL,
#endif
} backend_t;

typedef struct ctx_opt {
Expand Down
7 changes: 7 additions & 0 deletions include/wlm/wayland.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <wlm/proto/fractional-scale-v1.h>
#include <wlm/proto/xdg-shell.h>
#include <wlm/proto/xdg-output-unstable-v1.h>
#include <wlm/proto/xdg-foreign-unstable-v2.h>
#include <wlm/proto/wlr-export-dmabuf-unstable-v1.h>
#include <wlm/proto/wlr-screencopy-unstable-v1.h>

Expand Down Expand Up @@ -66,6 +67,12 @@ typedef struct ctx_wl {
uint32_t shm_id;
uint32_t screencopy_manager_id;

// xdg portal backend objects
struct zxdg_exporter_v2 * xdg_exporter;
struct zxdg_exported_v2 * xdg_exported_surface;
const char * xdg_exported_handle;
uint32_t xdg_exporter_id;

// output list
output_list_node_t * outputs;
seat_list_node_t * seats;
Expand Down
26 changes: 26 additions & 0 deletions src/egl.c
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ void wlm_egl_resize_viewport(ctx_t * ctx) {
wlm_log_debug(ctx, "egl::resize_viewport(): view_width = %d, view_height = %d\n", view_width, view_height);

// updating GL viewport
wlm_log_debug(ctx, "egl::resize_viewport(): win = %dx%d, view = %dx%d, tex = %dx%d\n", win_width, win_height, view_width, view_height, tex_width, tex_height);
wlm_log_debug(ctx, "egl::resize_viewport(): viewport %d, %d, %d, %d\n",
(int32_t)(win_width - view_width) / 2, (int32_t)(win_height - view_height) / 2, view_width, view_height
);
Expand Down Expand Up @@ -504,6 +505,13 @@ bool wlm_egl_dmabuf_to_texture(ctx_t * ctx, dmabuf_t * dmabuf) {
image_attribs[i++] = dmabuf->height;
image_attribs[i++] = EGL_LINUX_DRM_FOURCC_EXT;
image_attribs[i++] = dmabuf->drm_format;
wlm_log_debug(ctx, "egl::dmabuf_to_texture(): w=%d h=%d drm_format=%c%c%c%c\n",
dmabuf->width, dmabuf->height,
((dmabuf->drm_format >> 0) & 0xFF),
((dmabuf->drm_format >> 8) & 0xFF),
((dmabuf->drm_format >> 16) & 0xFF),
((dmabuf->drm_format >> 24) & 0xFF)
);

for (size_t j = 0; j < dmabuf->planes; j++) {
image_attribs[i++] = fd_attribs[j];
Expand All @@ -516,10 +524,20 @@ bool wlm_egl_dmabuf_to_texture(ctx_t * ctx, dmabuf_t * dmabuf) {
image_attribs[i++] = (uint32_t)dmabuf->modifier;
image_attribs[i++] = modifier_high_attribs[j];
image_attribs[i++] = (uint32_t)(dmabuf->modifier >> 32);
wlm_log_debug(ctx, "egl::dmabuf_to_texture(): fd=% 3d offset=% 10d stride=% 10d modifier=%016lx\n",
dmabuf->fds[j], dmabuf->offsets[j], dmabuf->strides[j], dmabuf->modifier
);
}

image_attribs[i++] = EGL_NONE;

wlm_log_debug(ctx, "egl::dmabuf_to_texture(): image_attribs=");
if (ctx->opt.verbose) {
for (i = 0; image_attribs[i] != EGL_NONE; i++) {
fprintf(stderr, "%08lx%s", image_attribs[i], image_attribs[i + 1] != EGL_NONE ? "_" : "\n");
}
}

// create EGLImage from dmabuf with attribute array
EGLImage frame_image = eglCreateImage(ctx->egl.display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, image_attribs);
free(image_attribs);
Expand All @@ -532,10 +550,18 @@ bool wlm_egl_dmabuf_to_texture(ctx_t * ctx, dmabuf_t * dmabuf) {
// convert EGLImage to GL texture
glBindTexture(GL_TEXTURE_2D, ctx->egl.texture);
ctx->egl.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, frame_image);
ctx->egl.texture_initialized = true;

// destroy temporary image
eglDestroyImage(ctx->egl.display, frame_image);

// set texture size and aspect ratio only if changed
if (dmabuf->width != ctx->egl.width || dmabuf->height != ctx->egl.height) {
ctx->egl.width = dmabuf->width;
ctx->egl.height = dmabuf->height;
wlm_egl_resize_viewport(ctx);
}

return true;
}

Expand Down
17 changes: 7 additions & 10 deletions src/mirror-dmabuf.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,13 @@ static void on_frame(
backend->dmabuf.drm_format = format;
backend->dmabuf.planes = num_objects;

wlm_log_debug(ctx, "mirror-dmabuf::on_frame(): w=%d h=%d gl_format=%x drm_format=%08x drm_modifier=%016lx\n",
backend->dmabuf.width, backend->dmabuf.height, GL_RGB8_OES, backend->dmabuf.drm_format, backend->dmabuf.modifier
wlm_log_debug(ctx, "mirror-dmabuf::on_frame(): w=%d h=%d gl_format=%x drm_format=%c%c%c%c drm_modifier=%016lx\n",
backend->dmabuf.width, backend->dmabuf.height, GL_RGB8_OES,
(backend->dmabuf.drm_format >> 0) & 0xFF,
(backend->dmabuf.drm_format >> 8) & 0xFF,
(backend->dmabuf.drm_format >> 16) & 0xFF,
(backend->dmabuf.drm_format >> 24) & 0xFF,
backend->dmabuf.modifier
);

for (size_t i = 0; i < num_objects; i++) {
Expand Down Expand Up @@ -177,7 +182,6 @@ static void on_ready(

ctx->egl.format = GL_RGB8_OES; // FIXME: find out actual format
ctx->egl.texture_region_aware = false;
ctx->egl.texture_initialized = true;

// set buffer flags only if changed
bool invert_y = backend->buffer_flags & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT;
Expand All @@ -186,13 +190,6 @@ static void on_ready(
wlm_egl_update_uniforms(ctx);
}

// set texture size and aspect ratio only if changed
if (backend->dmabuf.width != ctx->egl.width || backend->dmabuf.height != ctx->egl.height) {
ctx->egl.width = backend->dmabuf.width;
ctx->egl.height = backend->dmabuf.height;
wlm_egl_resize_viewport(ctx);
}

dmabuf_frame_cleanup(backend);
backend->state = STATE_READY;
backend->header.fail_count = 0;
Expand Down
6 changes: 3 additions & 3 deletions src/mirror-screencopy.c
Original file line number Diff line number Diff line change
Expand Up @@ -292,10 +292,10 @@ static void on_ready(
wlm_log_debug(ctx, "mirror-screencopy::on_ready(): received ready event with width: %d, height: %d, stride: %d, format: %c%c%c%c\n",
backend->frame_width, backend->frame_height,
backend->frame_stride,
(backend->frame_format >> 24) & 0xff,
(backend->frame_format >> 16) & 0xff,
(backend->frame_format >> 0) & 0xff,
(backend->frame_format >> 8) & 0xff,
(backend->frame_format >> 0) & 0xff
(backend->frame_format >> 16) & 0xff,
(backend->frame_format >> 24) & 0xff
);
}

Expand Down
Loading