diff options
author | progandy <code@progandy> | 2020-04-30 20:25:25 +0200 |
---|---|---|
committer | progandy <code@progandy> | 2020-04-30 20:25:25 +0200 |
commit | 260cd732767185a2781173905b16454140005fea (patch) | |
tree | e57b26d2fd24550a70ff285b1c88c59ce7e2ceaf |
Create an output mirror prototype for wlroots
To mirror an output its dmabuf is exported and then submitted to a new
xdg surface which can be placed on another output.
-rw-r--r-- | LICENSE | 19 | ||||
-rw-r--r-- | main.c | 606 | ||||
-rw-r--r-- | meson.build | 42 | ||||
-rw-r--r-- | protocol/meson.build | 49 | ||||
-rw-r--r-- | protocol/wlr-export-dmabuf-unstable-v1.xml | 203 |
5 files changed, 919 insertions, 0 deletions
@@ -0,0 +1,19 @@ +Copyright (c) 2020 ProgAndy + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. @@ -0,0 +1,606 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) 2020 ProgAndy */ + +#define _POSIX_C_SOURCE 200809L +#include <assert.h> +#include <errno.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wayland-client.h> +#include "wlr-export-dmabuf-unstable-v1-client-protocol.h" +#include "linux-dmabuf-unstable-v1-client-protocol.h" +#include "xdg-shell-client-protocol.h" + +struct wayland_output { + struct wl_list link; + uint32_t id; + struct wl_output *output; + char *make; + char *model; + int width; + int height; + int framerate; +}; + +struct frame_object { + uint32_t index; + int32_t fd; + uint32_t size; + uint32_t offset; + uint32_t stride; + uint32_t plane_index; +}; + +struct frame { + struct zwlr_export_dmabuf_frame_v1 *frame; + struct wl_buffer *buffer; + uint32_t width; + uint32_t height; + uint32_t offset_x; + uint32_t offset_y; + uint32_t buffer_flags; + uint32_t flags; + uint32_t format; + uint32_t mod_high; + uint32_t mod_low; + uint32_t num_objects; + struct frame_object objects[]; +}; + +struct window { + struct wl_surface *surface; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + bool init; + int width, height; +}; + +struct mirror_context { + struct wl_display *display; + struct wl_registry *registry; + struct zwlr_export_dmabuf_manager_v1 *export_manager; + struct wl_compositor *compositor; + struct xdg_wm_base *xdg_wm_base; + struct zwp_linux_dmabuf_v1 *dmabuf; + + struct wl_list output_list; + + struct window *window; + /* Target */ + struct wl_output *source_output; + int w; + int h; + bool with_cursor; + + /* Main frame callback */ + struct zwlr_export_dmabuf_frame_v1 *frame_callback; + + /* If something happens during capture */ + int err; + bool quit; + + /* dmabuf frames */ + struct frame *incomplete_frame; + struct frame *next_frame; + + +}; +struct mirror_context *q_ctx = NULL; + +void window_destroy(struct window *window); +int window_create(struct mirror_context *ctx); + +static void frame_free(struct frame *f) { + + if (f) { + if (f->buffer) + wl_buffer_destroy(f->buffer); + for (uint32_t i = 0; i < f->num_objects; ++i) { + close(f->objects[i].fd); + } + if (f->frame) + zwlr_export_dmabuf_frame_v1_destroy(f->frame); + free(f); + } + +} + +static void 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; + output->make = strdup(make); + output->model = strdup(model); +} + +static void 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; + output->width = width; + output->height = height; + output->framerate = refresh; + } +} + +static void output_handle_done(void* data, struct wl_output *wl_output) { + /* Nothing to do */ +} + +static void output_handle_scale(void* data, struct wl_output *wl_output, + int32_t factor) { + /* Nothing to do */ +} + +static const struct wl_output_listener output_listener = { + .geometry = output_handle_geometry, + .mode = output_handle_mode, + .done = output_handle_done, + .scale = output_handle_scale, +}; + +static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial) +{ + xdg_wm_base_pong(xdg_wm_base, serial); +} + +static const struct xdg_wm_base_listener xdg_wm_base_listener = { + xdg_wm_base_ping, +}; + + +static void registry_handle_add(void *data, struct wl_registry *reg, + uint32_t id, const char *interface, uint32_t ver) { + struct mirror_context *ctx = data; + + if (!strcmp(interface, wl_output_interface.name)) { + struct wayland_output *output = calloc(1,sizeof(*output)); + + output->id = id; + output->output = wl_registry_bind(reg, id, &wl_output_interface, 1); + + wl_output_add_listener(output->output, &output_listener, output); + wl_list_insert(&ctx->output_list, &output->link); + } + + if (!strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name)) { + ctx->export_manager = wl_registry_bind(reg, id, + &zwlr_export_dmabuf_manager_v1_interface, 1); + } + + if (!strcmp(interface, wl_compositor_interface.name)) { + ctx->compositor = wl_registry_bind(reg, id, &wl_compositor_interface, 1); + } + + if (!strcmp(interface, xdg_wm_base_interface.name)) { + ctx->xdg_wm_base = wl_registry_bind(reg, id, &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(ctx->xdg_wm_base, &xdg_wm_base_listener, ctx); + } + + if (!strcmp(interface, zwp_linux_dmabuf_v1_interface.name)) { + if (ver < 3) { + return; + } + ctx->dmabuf = wl_registry_bind(reg, id, &zwp_linux_dmabuf_v1_interface, 3); + /* assume we get a compatible format from dmabuf export */ + } +} + +static void remove_output(struct wayland_output *out) { + wl_list_remove(&out->link); + free(out->make); + free(out->model); + free(out); +} + +static struct wayland_output *find_output(struct mirror_context *ctx, + struct wl_output *out, uint32_t id) { + struct wayland_output *output, *tmp; + wl_list_for_each_safe(output, tmp, &ctx->output_list, link) { + if ((output->output == out) || (output->id == id)) { + return output; + } + } + return NULL; +} + +static void registry_handle_remove(void *data, struct wl_registry *reg, + uint32_t id) { + remove_output(find_output((struct mirror_context *)data, NULL, id)); +} + +static const struct wl_registry_listener registry_listener = { + .global = registry_handle_add, + .global_remove = registry_handle_remove, +}; + +static void frame_start(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, + uint32_t width, uint32_t height, uint32_t offset_x, uint32_t offset_y, + uint32_t buffer_flags, uint32_t flags, uint32_t format, + uint32_t mod_high, uint32_t mod_low, uint32_t num_objects) { + struct mirror_context *ctx = data; + int err = 0; + + /* Allocate DRM specific struct */ + struct frame *f = calloc(1,sizeof(*f)+num_objects*sizeof(struct frame_object)); + if (!f) { + err = ENOMEM; + goto fail; + } + // suppress y-invert flag. + flags &= ~ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT; + f->frame = frame; + f->width = width; + f->height = height; + f->offset_x = offset_x; + f->offset_y = offset_y; + f->buffer_flags = buffer_flags; + f->flags = flags; + f->format = format; + f->mod_high = mod_high; + f->mod_low = mod_low; + f->num_objects = num_objects; + + f->width = width; + f->height = height; + + ctx->incomplete_frame = f; + + return; + +fail: + ctx->err = err; + if (f) frame_free(f); +} + +static void frame_object(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, + uint32_t index, int32_t fd, uint32_t size, uint32_t offset, + uint32_t stride, uint32_t plane_index) { + struct mirror_context *ctx = data; + struct frame *f = ctx->incomplete_frame; + f->objects[index].index = index; + f->objects[index].fd = fd; + f->objects[index].size = size; + f->objects[index].offset = offset; + f->objects[index].stride = stride; + f->objects[index].plane_index = plane_index; + +} + +static void +buffer_release(void *data, struct wl_buffer *buffer) +{ + struct frame *f = data; + + frame_free(f); +} + +static const struct wl_buffer_listener buffer_listener = { + buffer_release +}; + + +static void display_frame(struct mirror_context *ctx); +static void request_frame(struct mirror_context *ctx); + +static void frame_ready(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, + uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { + struct mirror_context *ctx = data; + struct frame *f = ctx->incomplete_frame; + ctx->incomplete_frame = NULL; + struct zwp_linux_buffer_params_v1 *params; + params = zwp_linux_dmabuf_v1_create_params(ctx->dmabuf); + + for (uint32_t i = 0; i < f->num_objects; ++i) { + zwp_linux_buffer_params_v1_add(params, + f->objects[i].fd, + f->objects[i].plane_index, + f->objects[i].offset, + f->objects[i].stride, + f->mod_high, + f->mod_low); + } + + + // TODO: handle failed creation + f->buffer = + zwp_linux_buffer_params_v1_create_immed(params, + f->width, + f->height, + f->format, + f->flags); + zwp_linux_buffer_params_v1_destroy(params); + wl_buffer_add_listener(f->buffer, &buffer_listener, f); + + if (ctx->next_frame) { + frame_free(ctx->next_frame); + } + ctx->next_frame = f; + + + //* Frames will not be requested in the render loop + if (!ctx->quit && !ctx->err) { + if (ctx->window && ctx->window->init) { + display_frame(ctx); + } + request_frame(ctx); + } + return; +} + +static void frame_cancel(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, + uint32_t reason) { + struct mirror_context *ctx = data; + frame_free(ctx->incomplete_frame); + if (reason == ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERMANENT) { + /* Permanent error. Exit! */ + ctx->err = true; + } else { + request_frame(ctx); + } +} + +static const struct zwlr_export_dmabuf_frame_v1_listener frame_listener = { + .frame = frame_start, + .object = frame_object, + .ready = frame_ready, + .cancel = frame_cancel, +}; + +static void request_frame(struct mirror_context *ctx) { + ctx->frame_callback = zwlr_export_dmabuf_manager_v1_capture_output( + ctx->export_manager, ctx->with_cursor, ctx->source_output); + + zwlr_export_dmabuf_frame_v1_add_listener(ctx->frame_callback, + &frame_listener, ctx); +} + + + + +static void xdg_surface_handle_configure(void *data, + struct xdg_surface *surface, uint32_t serial) { + struct mirror_context *ctx = data; + + xdg_surface_ack_configure(surface, serial); + /* mark window ready */ + ctx->window->init = true; +} + +static const struct xdg_surface_listener xdg_surface_listener = { + xdg_surface_handle_configure, +}; + +static void xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *toplevel, + int32_t width, int32_t height, struct wl_array *states) { + /* Empty */ +} + +static void +xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel) +{ + struct mirror_context *ctx = data; + ctx->quit = true; +} + +static const struct xdg_toplevel_listener xdg_toplevel_listener = { + xdg_toplevel_handle_configure, + xdg_toplevel_handle_close, +}; + + +static void on_quit_signal(int signo) { + if (q_ctx) { + q_ctx->quit = true; + } +} + + +static int main_loop(struct mirror_context *ctx) { + + q_ctx = ctx; + + if (signal(SIGINT, on_quit_signal) == SIG_ERR) { + /*av_log(ctx, AV_LOG_ERROR, "Unable to install signal handler!\n");*/ + return EINVAL; + } + + request_frame(ctx); + + while (wl_display_dispatch(ctx->display) != -1 && !ctx->err && !ctx->quit); + + return ctx->err; +} + +static int init(struct mirror_context *ctx) { + wl_list_init(&ctx->output_list); + + ctx->display = wl_display_connect(NULL); + if (!ctx->display) { + puts("Failed to connect to display"); + return EINVAL; + } + + + ctx->registry = wl_display_get_registry(ctx->display); + wl_registry_add_listener(ctx->registry, ®istry_listener, ctx); + + wl_display_roundtrip(ctx->display); + wl_display_dispatch(ctx->display); + assert(ctx->compositor); + + if (!ctx->export_manager) { + printf("Compositor doesn't support %s!\n", + zwlr_export_dmabuf_manager_v1_interface.name); + return -1; + } + if (!ctx->dmabuf) { + printf("Compositor doesn't support %s!\n", + zwp_linux_dmabuf_v1_interface.name); + return -1; + } + if (!ctx->xdg_wm_base) { + printf("Compositor doesn't support %s!\n", + xdg_wm_base_interface.name); + return -1; + } + + return 0; +} + + +void window_destroy(struct window *window) { + if (window->xdg_toplevel) { + xdg_toplevel_destroy(window->xdg_toplevel); + } + if (window->xdg_surface) { + xdg_surface_destroy(window->xdg_surface); + } + if (window->surface) { + wl_surface_destroy(window->surface); + } + free(window); +} + +int window_create(struct mirror_context *ctx) { + struct window *window; + int err = 0; + + assert(!ctx->window); + + window = calloc(1,sizeof(*window)); + if (!window) { + return 1; + } + + window->width = ctx->w; + window->height = ctx->h; + + window->surface = wl_compositor_create_surface(ctx->compositor); + + window->xdg_surface = xdg_wm_base_get_xdg_surface(ctx->xdg_wm_base, window->surface); + xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, ctx); + + window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface); + xdg_toplevel_add_listener(window->xdg_toplevel, &xdg_toplevel_listener, ctx); + + xdg_toplevel_set_title(window->xdg_toplevel, "wlr dmabuf output mirror"); + + wl_surface_commit(window->surface); + + ctx->window = window; + + return err; +} + + +static void display_frame(struct mirror_context *ctx) { + + struct frame *next = ctx->next_frame; + ctx->next_frame = NULL; + if (!next) return; + + + wl_surface_attach(ctx->window->surface, next->buffer, 0, 0); + wl_surface_damage(ctx->window->surface, 0, 0, ctx->window->width, ctx->window->height); + + wl_surface_commit(ctx->window->surface); +} + + +static void uninit(struct mirror_context *ctx); + +int main(int argc, char *argv[]) { + int err; + struct mirror_context ctx = { 0 }; + struct wayland_output *o, *tmp_o; + + + err = init(&ctx); + if (err) { + puts("Could not initialize wayland"); + goto end; + } + + if (argc != 2 || !strcmp(argv[1], "-h")) { + printf("wdomirror SOURCE_ID\n" + "Mirror wlroots output with dmabuf protocols.\n\n"); + wl_list_for_each_reverse_safe(o, tmp_o, &ctx.output_list, link) { + printf("Mirrorable output: %s Model: %s: ID: %i\n", + o->make, o->model, o->id); + } + err = 1; + goto end; + } + + const int o_id = strtol(argv[1], NULL, 10); + o = find_output(&ctx, NULL, o_id); + if (!o) { + printf("Unable to find output with ID %i!\n", o_id); + err = 1; + goto end; + } + printf("Mirroring output: %s Model: %s: ID: %i\n", + o->make, o->model, o->id); + + ctx.source_output = o->output; + ctx.w = o->width; + ctx.h = o->height; + ctx.with_cursor = true; + + + err = window_create(&ctx); + if (err) { + goto end; + } + + err = main_loop(&ctx); + if (err) { + goto end; + } + +end: + uninit(&ctx); + return err; +} + +static void uninit(struct mirror_context *ctx) { + struct wayland_output *output, *tmp_o; + wl_list_for_each_safe(output, tmp_o, &ctx->output_list, link) { + remove_output(output); + } + if (ctx->window) + window_destroy(ctx->window); + + if (ctx->export_manager) { + zwlr_export_dmabuf_manager_v1_destroy(ctx->export_manager); + } + if (ctx->dmabuf) { + zwp_linux_dmabuf_v1_destroy(ctx->dmabuf); + } + if (ctx->xdg_wm_base) { + xdg_wm_base_destroy(ctx->xdg_wm_base); + } + if (ctx->compositor) { + wl_compositor_destroy(ctx->compositor); + } + if (ctx->registry) { + wl_registry_destroy(ctx->registry); + } + if (ctx->display) { + wl_display_flush(ctx->display); + wl_display_disconnect(ctx->display); + } + + if (ctx->next_frame) { + frame_free(ctx->next_frame); + } + +} + +/* vim: set ts=2 sw=2 et: */ diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..52b01ec --- /dev/null +++ b/meson.build @@ -0,0 +1,42 @@ +project( + 'wdomirror', + 'c', + version: '0.1.0', + license: 'MIT', + meson_version: '>=0.54.0', + default_options: [ + 'c_std=c11', + 'warning_level=2', + 'werror=true', + ], +) + +cc = meson.get_compiler('c') + +add_project_arguments(cc.get_supported_arguments([ + '-Wno-missing-braces', + '-Wno-unused-parameter', +]), language: 'c') + +wayland_client = dependency('wayland-client') +wayland_protos = dependency('wayland-protocols', version: '>=1.17') + +subdir('protocol') + +mirror_files = [ 'main.c' ] +mirror_deps = [ + wayland_client, + wayland_protos, + client_protos, +] + + +proto_inc = include_directories('protocol') + +executable( + meson.project_name(), + mirror_files, + dependencies: mirror_deps, + include_directories: proto_inc, + install: true +) diff --git a/protocol/meson.build b/protocol/meson.build new file mode 100644 index 0000000..2b274a2 --- /dev/null +++ b/protocol/meson.build @@ -0,0 +1,49 @@ +wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') + +wayland_scanner = find_program('wayland-scanner') + +# should check wayland_scanner's version, but it is hard to get +if wayland_client.version().version_compare('>=1.14.91') + code_type = 'private-code' +else + code_type = 'code' +endif + +wayland_scanner_code = generator( + wayland_scanner, + output: '@BASENAME@-protocol.c', + arguments: [code_type, '@INPUT@', '@OUTPUT@'], +) + +wayland_scanner_client = generator( + wayland_scanner, + output: '@BASENAME@-client-protocol.h', + arguments: ['client-header', '@INPUT@', '@OUTPUT@'], +) + +client_protocols = [ + [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], + [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], + 'wlr-export-dmabuf-unstable-v1.xml', +] + + +client_protos_src = [] +client_protos_headers = [] + +foreach p : client_protocols + xml = join_paths(p) + client_protos_src += wayland_scanner_code.process(xml) + client_protos_headers += wayland_scanner_client.process(xml) +endforeach + +lib_client_protos = static_library( + 'client_protos', + client_protos_src + client_protos_headers, + dependencies: [wayland_client] +) # for the include directory + +client_protos = declare_dependency( + link_with: lib_client_protos, + sources: client_protos_headers, +) diff --git a/protocol/wlr-export-dmabuf-unstable-v1.xml b/protocol/wlr-export-dmabuf-unstable-v1.xml new file mode 100644 index 0000000..751f7ef --- /dev/null +++ b/protocol/wlr-export-dmabuf-unstable-v1.xml @@ -0,0 +1,203 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="wlr_export_dmabuf_unstable_v1"> + <copyright> + Copyright © 2018 Rostislav Pehlivanov + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + </copyright> + + <description summary="a protocol for low overhead screen content capturing"> + An interface to capture surfaces in an efficient way by exporting DMA-BUFs. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + </description> + + <interface name="zwlr_export_dmabuf_manager_v1" version="1"> + <description summary="manager to inform clients and begin capturing"> + This object is a manager with which to start capturing from sources. + </description> + + <request name="capture_output"> + <description summary="capture a frame from an output"> + Capture the next frame of a an entire output. + </description> + <arg name="frame" type="new_id" interface="zwlr_export_dmabuf_frame_v1"/> + <arg name="overlay_cursor" type="int" + summary="include custom client hardware cursor on top of the frame"/> + <arg name="output" type="object" interface="wl_output"/> + </request> + + <request name="destroy" type="destructor"> + <description summary="destroy the manager"> + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + </description> + </request> + </interface> + + <interface name="zwlr_export_dmabuf_frame_v1" version="1"> + <description summary="a DMA-BUF frame"> + This object represents a single DMA-BUF frame. + + If the capture is successful, the compositor will first send a "frame" + event, followed by one or several "object". When the frame is available + for readout, the "ready" event is sent. + + If the capture failed, the "cancel" event is sent. This can happen anytime + before the "ready" event. + + Once either a "ready" or a "cancel" event is received, the client should + destroy the frame. Once an "object" event is received, the client is + responsible for closing the associated file descriptor. + + All frames are read-only and may not be written into or altered. + </description> + + <enum name="flags"> + <description summary="frame flags"> + Special flags that should be respected by the client. + </description> + <entry name="transient" value="0x1" + summary="clients should copy frame before processing"/> + </enum> + + <event name="frame"> + <description summary="a frame description"> + Main event supplying the client with information about the frame. If the + capture didn't fail, this event is always emitted first before any other + events. + + This event is followed by a number of "object" as specified by the + "num_objects" argument. + </description> + <arg name="width" type="uint" + summary="frame width in pixels"/> + <arg name="height" type="uint" + summary="frame height in pixels"/> + <arg name="offset_x" type="uint" + summary="crop offset for the x axis"/> + <arg name="offset_y" type="uint" + summary="crop offset for the y axis"/> + <arg name="buffer_flags" type="uint" + summary="flags which indicate properties (invert, interlacing), + has the same values as zwp_linux_buffer_params_v1:flags"/> + <arg name="flags" type="uint" enum="flags" + summary="indicates special frame features"/> + <arg name="format" type="uint" + summary="format of the frame (DRM_FORMAT_*)"/> + <arg name="mod_high" type="uint" + summary="drm format modifier, high"/> + <arg name="mod_low" type="uint" + summary="drm format modifier, low"/> + <arg name="num_objects" type="uint" + summary="indicates how many objects (FDs) the frame has (max 4)"/> + </event> + + <event name="object"> + <description summary="an object description"> + Event which serves to supply the client with the file descriptors + containing the data for each object. + + After receiving this event, the client must always close the file + descriptor as soon as they're done with it and even if the frame fails. + </description> + <arg name="index" type="uint" + summary="index of the current object"/> + <arg name="fd" type="fd" + summary="fd of the current object"/> + <arg name="size" type="uint" + summary="size in bytes for the current object"/> + <arg name="offset" type="uint" + summary="starting point for the data in the object's fd"/> + <arg name="stride" type="uint" + summary="line size in bytes"/> + <arg name="plane_index" type="uint" + summary="index of the the plane the data in the object applies to"/> + </event> + + <event name="ready"> + <description summary="indicates frame is available for reading"> + This event is sent as soon as the frame is presented, indicating it is + available for reading. This event includes the time at which + presentation happened at. + + The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, + each component being an unsigned 32-bit value. Whole seconds are in + tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, + and the additional fractional part in tv_nsec as nanoseconds. Hence, + for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part + may have an arbitrary offset at start. + + After receiving this event, the client should destroy this object. + </description> + <arg name="tv_sec_hi" type="uint" + summary="high 32 bits of the seconds part of the timestamp"/> + <arg name="tv_sec_lo" type="uint" + summary="low 32 bits of the seconds part of the timestamp"/> + <arg name="tv_nsec" type="uint" + summary="nanoseconds part of the timestamp"/> + </event> + + <enum name="cancel_reason"> + <description summary="cancel reason"> + Indicates reason for cancelling the frame. + </description> + <entry name="temporary" value="0" + summary="temporary error, source will produce more frames"/> + <entry name="permanent" value="1" + summary="fatal error, source will not produce frames"/> + <entry name="resizing" value="2" + summary="temporary error, source will produce more frames"/> + </enum> + + <event name="cancel"> + <description summary="indicates the frame is no longer valid"> + If the capture failed or if the frame is no longer valid after the + "frame" event has been emitted, this event will be used to inform the + client to scrap the frame. + + If the failure is temporary, the client may capture again the same + source. If the failure is permanent, any further attempts to capture the + same source will fail again. + + After receiving this event, the client should destroy this object. + </description> + <arg name="reason" type="uint" enum="cancel_reason" + summary="indicates a reason for cancelling this frame capture"/> + </event> + + <request name="destroy" type="destructor"> + <description summary="delete this object, used or not"> + Unreferences the frame. This request must be called as soon as its no + longer used. + + It can be called at any time by the client. The client will still have + to close any FDs it has been given. + </description> + </request> + </interface> +</protocol> |