Compare commits

...

No commits in common. 'master' and 'ext-image-capture' have entirely different histories.

3
.gitignore vendored

@ -0,0 +1,3 @@
/out.*
/screencap
ext-*

@ -1,11 +0,0 @@
Copyright 2024 Tranquillity Codes
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -4,29 +4,27 @@ CC = gcc
CLIBS = -lwayland-client
CFLAGS = $(CLIBS)
DESTDIR ?= /usr/bin
SRC = main.c
OUT = screencap
WLR_PROTOCOL_IMPL = wlr-screencopy-protocol.c
WLR_PROTOCOL_HEADER = wlr-screencopy-client-protocol.h
SRC = main.c
OUT = screencopy
PROTOCOL_NAMES = $(foreach proto, $(wildcard protocols/*), $(notdir $(basename $(proto))))
PROTOCOL_IMPLS = $(foreach proto, $(PROTOCOL_NAMES), $(addsuffix .prot.c,$(proto)))
PROTOCOL_HEADERS = $(foreach proto, $(PROTOCOL_NAMES), $(addsuffix .prot.h,$(proto)))
PROTOCOLS = $(PROTOCOL_IMPLS) $(PROTOCOL_HEADERS)
%.prot.c: protocols/%.xml
wayland-scanner private-code $< $@
%.prot.h: protocols/%.xml
wayland-scanner client-header $< $@
all: $(OUT)
clean:
rm -f $(OUT)
rm -f $(WLR_PROTOCOL_IMPL)
rm -f $(WLR_PROTOCOL_HEADER)
install: all
cp $(OUT) $(DESTDIR)/
$(OUT): $(WLR_PROTOCOL_IMPL) $(WLR_PROTOCOL_HEADER) $(SRC)
$(CC) $(WLR_PROTOCOL_IMPL) $(SRC) $(CFLAGS) -o $(OUT)
rm -f $(PROTOCOLS)
$(WLR_PROTOCOL_IMPL):
wayland-scanner private-code ./wlr-screencopy-unstable-v1.xml $(WLR_PROTOCOL_IMPL)
$(OUT): $(PROTOCOLS) $(SRC)
$(CC) $(PROTOCOL_IMPLS) $(SRC) $(CFLAGS) -o $(OUT)
$(WLR_PROTOCOL_HEADER):
wayland-scanner client-header ./wlr-screencopy-unstable-v1.xml $(WLR_PROTOCOL_HEADER)

@ -1,4 +1,4 @@
A simple Wayland screenshotting utility that utilizes `wlr-screencopy`.
A simple Wayland screenshotting utility that utilizes the new `ext-image-capture-source-v1` & `ext-image-copy-capture-v1`.
Runtime Dependencies:
@ -9,3 +9,10 @@ Make Dependencies:
- `wayland-scanner`
- `make`
- `gcc`
Usage:
```sh
screencap # makes a full screenshot, saves it to out.rif
screencap '?' # prints all toplevels
screencap '5c261201ff47b0f28a1b55ce9f0e2e20' # captures the toplevel with the given identifier to out.rif
```

361
main.c

@ -1,15 +1,15 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <wayland-client-core.h>
#include <wayland-client-protocol.h>
#include <wayland-client.h>
#include "wlr-screencopy-client-protocol.h"
#include "ext-image-capture-source-v1.prot.h"
#include "ext-image-copy-capture-v1.prot.h"
#include "ext-foreign-toplevel-list-v1.prot.h"
#include "rif.h"
@ -24,28 +24,60 @@
// See memfd_create(2).
int memfd_create(const char* name, unsigned int flags);
struct wl_output* wl_output = NULL;
struct image_data {
uint32_t width;
uint32_t height;
uint32_t shm_format;
};
struct wl_output* stream_output = NULL;
struct wl_shm* wl_shm = NULL;
struct zwlr_screencopy_manager_v1* zwlr_manager = NULL;
struct wl_buffer* frame_buffer = NULL;
struct ext_output_image_capture_source_manager_v1* output_source_manager = NULL;
struct ext_image_copy_capture_manager_v1* capture_manager = NULL;
struct ext_foreign_toplevel_list_v1* toplevel_list = NULL;
struct ext_foreign_toplevel_image_capture_source_manager_v1* toplevel_source_manager = NULL;
int8_t ready = 0;
uint32_t buf_size = 0;
void* shm_data = NULL;
uint32_t image_width = 0;
uint32_t image_height = 0;
uint32_t image_format = 0;
void handle_global(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) {
struct image_data img_data;
uint8_t is_frame_ready = 0;
uint8_t is_capture_ready = 0;
uint8_t full_screenshot = 0;
uint32_t buf_size;
void handle_global(
void* data,
struct wl_registry* registry,
uint32_t name,
const char* interface,
uint32_t version) {
if(strcmp(interface, wl_output_interface.name) == 0) {
stream_output = wl_registry_bind(registry, name, &wl_output_interface, version);
}
if(strcmp(interface, wl_shm_interface.name) == 0) {
wl_shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
} else if(strcmp(interface, wl_output_interface.name) == 0) {
wl_output = wl_registry_bind(registry, name, &wl_output_interface, 3);
} else if(strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) {
zwlr_manager = wl_registry_bind(registry, name, &zwlr_screencopy_manager_v1_interface, 1);
wl_shm = wl_registry_bind(registry, name, &wl_shm_interface, version);
}
if(strcmp(interface, ext_output_image_capture_source_manager_v1_interface.name) == 0) {
output_source_manager = wl_registry_bind(registry, name, &ext_output_image_capture_source_manager_v1_interface, version);
}
if(strcmp(interface, ext_image_copy_capture_manager_v1_interface.name) == 0) {
capture_manager = wl_registry_bind(registry, name, &ext_image_copy_capture_manager_v1_interface, version);
}
if(strcmp(interface, ext_foreign_toplevel_list_v1_interface.name) == 0) {
toplevel_list = wl_registry_bind(registry, name, &ext_foreign_toplevel_list_v1_interface, version);
}
if(strcmp(interface, ext_foreign_toplevel_image_capture_source_manager_v1_interface.name) == 0) {
toplevel_source_manager = wl_registry_bind(registry, name, &ext_foreign_toplevel_image_capture_source_manager_v1_interface, version);
}
}
void handle_global_remove(void* data, struct wl_registry* registry, uint32_t name) {
void handle_global_remove(
void* data,
struct wl_registry* registry,
uint32_t name) {
// Who cares
}
@ -54,19 +86,46 @@ const struct wl_registry_listener reg_callbacks = {
.global_remove = handle_global_remove,
};
void frame_handle_buffer(void* data, struct zwlr_screencopy_frame_v1* frame, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
printf("Handling buffer.\n");
image_width = width;
image_height = height;
image_format = format;
buf_size = height * stride;
void buffer_size(void* data,
struct ext_image_copy_capture_session_v1* ext_image_copy_capture_session_v1,
uint32_t width,
uint32_t height) {
printf("x: %d, y: %d\n", width, height);
img_data.width = width;
img_data.height = height;
}
void shm_format(void* data,
struct ext_image_copy_capture_session_v1* ext_image_copy_capture_session_v1,
uint32_t format) {
img_data.shm_format = format;
ASSERT(format == WL_SHM_FORMAT_XBGR8888 || format == WL_SHM_FORMAT_ABGR8888, "Unsupported buffer format.");
}
void dmabuf_device(void* data,
struct ext_image_copy_capture_session_v1* ext_image_copy_capture_session_v1,
struct wl_array* device) {
}
void dmabuf_format(void* data,
struct ext_image_copy_capture_session_v1* ext_image_copy_capture_session_v1,
uint32_t format,
struct wl_array* modifiers) {
printf("Format: %010p\n", format);
ASSERT(format == WL_SHM_FORMAT_XBGR8888 || format == WL_SHM_FORMAT_ABGR8888, "Unsupported buffer format copied.");
}
int shm_fd = memfd_create("wl_shm data", 0);
void capture_done(void* data,
struct ext_image_copy_capture_session_v1* ext_image_copy_capture_session_v1) {
const int shm_fd = memfd_create("wl_shm data", 0);
ASSERT(shm_fd != -1, "Failed to create memfd.");
// Format is hardcoded to chan width 4
const uint32_t chan_width = 4;
const uint32_t stride = img_data.width* chan_width;
buf_size = img_data.height* stride;
ASSERT(ftruncate(shm_fd, buf_size) != -1, "Failed to ftruncate the memfd.");
shm_data = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
@ -75,34 +134,160 @@ void frame_handle_buffer(void* data, struct zwlr_screencopy_frame_v1* frame, ui
struct wl_shm_pool* pool = wl_shm_create_pool(wl_shm, shm_fd, buf_size);
ASSERT(pool != NULL, "Failed to create the wl_shm_pool.");
struct wl_buffer* buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, format);
ASSERT(buffer != NULL, "Failed to create the wl_buffer.");
frame_buffer = wl_shm_pool_create_buffer(pool, 0, img_data.width, img_data.height, stride, img_data.shm_format);
ASSERT(frame_buffer != NULL, "Failed to create the wl_buffer.");
is_frame_ready = 1;
}
void stopped(void* data,
struct ext_image_copy_capture_session_v1* ext_image_copy_capture_session_v1) {
zwlr_screencopy_frame_v1_copy(frame, buffer);
}
void frame_handle_flags(void* data, struct zwlr_screencopy_frame_v1* frame, uint32_t flags) {
// Ignored for now
const struct ext_image_copy_capture_session_v1_listener capture_listener = {
.buffer_size = buffer_size,
.shm_format = shm_format,
.dmabuf_device = dmabuf_device,
.dmabuf_format = dmabuf_format,
.done = capture_done,
.stopped = stopped
};
void transform(void* data,
struct ext_image_copy_capture_frame_v1* ext_image_copy_capture_frame_v1,
uint32_t transform) {
}
void damage(void* data,
struct ext_image_copy_capture_frame_v1* ext_image_copy_capture_frame_v1,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
void frame_handle_ready(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
printf("Ready.\n");
ready = 1;
}
void presentation_time(void* data,
struct ext_image_copy_capture_frame_v1* ext_image_copy_capture_frame_v1,
uint32_t tv_sec_hi,
uint32_t tv_sec_lo,
uint32_t tv_nsec) {
void frame_handle_failed(void* data, struct zwlr_screencopy_frame_v1* frame) {
fprintf(stderr, "Failed to copy output\n");
exit(EXIT_FAILURE);
}
void ready(void* data,
struct ext_image_copy_capture_frame_v1* ext_image_copy_capture_frame_v1) {
is_capture_ready = 1;
}
void failed(void* data,
struct ext_image_copy_capture_frame_v1* ext_image_copy_capture_frame_v1,
uint32_t reason) {
switch(reason) {
case 0:
printf("Capture failed. Reason: Unknown Runtime Error.");
break;
case 1:
printf("Capture failed. Reason: Buffer Constraints Mismatch.");
break;
case 2:
printf("Capture failed. Reason: Session Is No Longer Available.");
break;
}
}
const struct ext_image_copy_capture_frame_v1_listener frame_listener = {
.transform = transform,
.damage = damage,
.presentation_time = presentation_time,
.ready = ready,
.failed = failed
};
static const struct zwlr_screencopy_frame_v1_listener frame_listener = {
.buffer = frame_handle_buffer,
.flags = frame_handle_flags,
.ready = frame_handle_ready,
.failed = frame_handle_failed,
struct handle_data {
struct ext_foreign_toplevel_handle_v1* handle;
const char* title;
const char* app_id;
const char* identifier;
};
int main() {
struct handle_data*** handles;
size_t handles_len = 0;
void closed(void *data,
struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1) {
// TODO handle properly
}
void title(void *data,
struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1,
const char *title) {
struct handle_data* handle_data = (struct handle_data*)data;
size_t size = strlen(title)+1;
char* title_data = malloc(size);
memcpy(title_data, title, size);
handle_data->title = title_data;
}
void app_id(void *data,
struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1,
const char *app_id) {
struct handle_data* handle_data = (struct handle_data*)data;
size_t size = strlen(app_id)+1;
char* app_id_data = malloc(size);
memcpy(app_id_data, app_id, size);
handle_data->app_id = app_id_data;
}
void identifier(void *data,
struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1,
const char *identifier) {
struct handle_data* handle_data = (struct handle_data*)data;
size_t size = strlen(identifier)+1;
char* identifier_data = malloc(size);
memcpy(identifier_data, identifier, size);
handle_data->identifier = identifier_data;
}
void toplevel_done(void *data,
struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1) {
struct handle_data* handle_data = (struct handle_data*)data;
}
struct ext_foreign_toplevel_handle_v1_listener handle_listener = {
.closed = closed,
.done = toplevel_done,
.title = title,
.app_id = app_id,
.identifier = identifier,
};
void toplevel(void* data,
struct ext_foreign_toplevel_list_v1* ext_foreign_toplevel_list_v1,
struct ext_foreign_toplevel_handle_v1* toplevel) {
handles_len++;
*handles = realloc(*handles, handles_len*sizeof(void*));
struct handle_data* handle_data = malloc(sizeof(struct handle_data));
handle_data->handle = toplevel;
handle_data->title = NULL;
handle_data->app_id = NULL;
handle_data->identifier = NULL;
(*handles)[handles_len-1] = handle_data;
ext_foreign_toplevel_handle_v1_add_listener(toplevel, &handle_listener, handle_data);
}
void finished(void* data,
struct ext_foreign_toplevel_list_v1* ext_foreign_toplevel_list_v1) {
// TODO handle exploding properly
}
const struct ext_foreign_toplevel_list_v1_listener toplevel_listener = {
.toplevel = toplevel,
.finished = finished
};
int main(int argc, char* argv[]) {
struct wl_display* dpy = wl_display_connect(NULL);
ASSERT(dpy != NULL, "Unable to connect to Wayland");
@ -112,18 +297,86 @@ int main() {
ASSERT(wl_shm != NULL, "core.wl_shm not supported.");
ASSERT(zwlr_manager != NULL, "wlr-screencopy-unstable-v1.zwlr_screencopy_manager_v1 not supported.");
ASSERT(stream_output != NULL, "No outputs found!");
ASSERT(wl_output != NULL, "no core.wl_output found.");
int32_t with_cursor = 1;
ASSERT(output_source_manager != NULL, "ext-image-capture-source-v1.ext_output_image_capture_source_manager_v1 not supported.");
struct zwlr_screencopy_frame_v1* frame = zwlr_screencopy_manager_v1_capture_output(zwlr_manager, with_cursor, wl_output);
ASSERT(frame != NULL, "wlr-screencopy-unstable-v1.zwlr_screencopy_manager_v1::capture_output has failed");
struct handle_data* toplevel_handle = NULL;
if(argc == 2) {
ASSERT(toplevel_list != NULL, "ext-foreign-toplevel-list-v1.ext_foreign_toplevel_list_v1 not supported.");
zwlr_screencopy_frame_v1_add_listener(frame, &frame_listener, wl_output);
handles = malloc(sizeof(void*));
*handles = malloc(sizeof(void*));
handles_len = 0;
ext_foreign_toplevel_list_v1_add_listener(toplevel_list, &toplevel_listener, NULL);
if(strcmp(argv[1], "?") == 0) {
wl_display_roundtrip(dpy);
for(int i = 0; i < handles_len; i++) {
struct handle_data* handle_data = (*handles)[i];
printf("title: %s, app_id: %s, identifier: %s\n", handle_data->title, handle_data->app_id, handle_data->identifier);
}
return 0;
} else {
wl_display_roundtrip(dpy);
for(int i = 0; i < handles_len; i++) {
if(strcmp((*handles)[i]->identifier, argv[1]) == 0) {
toplevel_handle = (*handles)[i];
printf("Found handle: ");
printf("title: %s, app_id: %s, identifier: %s\n", toplevel_handle->title, toplevel_handle->app_id, toplevel_handle->identifier);
}
}
}
} else if(argc == 1) {
full_screenshot = 1;
wl_display_roundtrip(dpy);
} else {
ASSERT(argc >= 1, "Invalid exec!");
printf("Usage: %s [?|<identifier>]\n", argv[0]);
exit(-1);
}
ASSERT(capture_manager != NULL, "ext-image-copy-capture-v1.ext_image_copy_capture_manager_v1 not supported.");
struct ext_image_capture_source_v1* capture_source;
if(full_screenshot) {
ASSERT(output_source_manager != NULL, "ext-image-capture-source-v1.ext_output_image_capture_source_manager_v1 not supported.");
capture_source =
ext_output_image_capture_source_manager_v1_create_source(output_source_manager, stream_output);
ASSERT(capture_source != NULL, "Failed creating a source for the default output!")
} else {
ASSERT(toplevel_handle != NULL, "Invalid toplevel handle!");
ASSERT(toplevel_source_manager != NULL, "ext-image-capture-source-v1.ext_foreign_toplevel_image_capture_source_manager_v1 not supported.");
capture_source =
ext_foreign_toplevel_image_capture_source_manager_v1_create_source(toplevel_source_manager, toplevel_handle->handle);
ASSERT(capture_source != NULL, "Failed creating a source for the toplevel!")
}
struct ext_image_copy_capture_session_v1* capture_session =
ext_image_copy_capture_manager_v1_create_session(capture_manager, capture_source, EXT_IMAGE_COPY_CAPTURE_MANAGER_V1_OPTIONS_PAINT_CURSORS);
ASSERT(capture_session != NULL, "Failed creating a session for the capture!");
ext_image_copy_capture_session_v1_add_listener(capture_session, &capture_listener, NULL);
wl_display_roundtrip(dpy);
while(ready == 0 && wl_display_dispatch(dpy) != -1) {
while(is_frame_ready == 0 && wl_display_dispatch(dpy) != -1) {
// Empty
}
struct ext_image_copy_capture_frame_v1* frame = ext_image_copy_capture_session_v1_create_frame(capture_session);
ASSERT(frame != NULL, "Failed to create frame!");
ext_image_copy_capture_frame_v1_add_listener(frame, &frame_listener, NULL);
ext_image_copy_capture_frame_v1_attach_buffer(frame, frame_buffer);
ext_image_copy_capture_frame_v1_damage_buffer(frame, 0, 0, img_data.width, img_data.height);
ext_image_copy_capture_frame_v1_capture(frame);
wl_display_roundtrip(dpy);
while(is_capture_ready == 0 && wl_display_dispatch(dpy) != -1) {
// Empty
}
@ -131,7 +384,9 @@ int main() {
// void write_rif_little(char* path, uint32_t width, uint32_t size, uint8_t format, void* data) {
// Thanks to endianess shenanigans, ABGR/XBGR is RGBA.
write_rif_little("out.rif", image_width, buf_size, RIF_FORMAT_R8G8B8A8, shm_data);
write_rif_little("out.rif", img_data.width, buf_size, RIF_FORMAT_R8G8B8A8, shm_data);
ext_image_copy_capture_frame_v1_destroy(frame);
printf("Saved screenshot to ./out.rif.\n");
return 0;
}

@ -0,0 +1,219 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="ext_foreign_toplevel_list_v1">
<copyright>
Copyright © 2018 Ilia Bozhinov
Copyright © 2020 Isaac Freund
Copyright © 2022 wb9688
Copyright © 2023 i509VCB
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>
<description summary="list toplevels">
The purpose of this protocol is to provide protocol object handles for
toplevels, possibly originating from another client.
This protocol is intentionally minimalistic and expects additional
functionality (e.g. creating a screencopy source from a toplevel handle,
getting information about the state of the toplevel) to be implemented
in extension protocols.
The compositor may choose to restrict this protocol to a special client
launched by the compositor itself or expose it to all clients,
this is compositor policy.
The key words "must", "must not", "required", "shall", "shall not",
"should", "should not", "recommended", "may", and "optional" in this
document are to be interpreted as described in IETF RFC 2119.
Warning! The protocol described in this file is currently in the testing
phase. Backward compatible changes may be added together with the
corresponding interface version bump. Backward incompatible changes can
only be done by creating a new major version of the extension.
</description>
<interface name="ext_foreign_toplevel_list_v1" version="1">
<description summary="list toplevels">
A toplevel is defined as a surface with a role similar to xdg_toplevel.
XWayland surfaces may be treated like toplevels in this protocol.
After a client binds the ext_foreign_toplevel_list_v1, each mapped
toplevel window will be sent using the ext_foreign_toplevel_list_v1.toplevel
event.
Clients which only care about the current state can perform a roundtrip after
binding this global.
For each instance of ext_foreign_toplevel_list_v1, the compositor must
create a new ext_foreign_toplevel_handle_v1 object for each mapped toplevel.
If a compositor implementation sends the ext_foreign_toplevel_list_v1.finished
event after the global is bound, the compositor must not send any
ext_foreign_toplevel_list_v1.toplevel events.
</description>
<event name="toplevel">
<description summary="a toplevel has been created">
This event is emitted whenever a new toplevel window is created. It is
emitted for all toplevels, regardless of the app that has created them.
All initial properties of the toplevel (identifier, title, app_id) will be sent
immediately after this event using the corresponding events for
ext_foreign_toplevel_handle_v1. The compositor will use the
ext_foreign_toplevel_handle_v1.done event to indicate when all data has
been sent.
</description>
<arg name="toplevel" type="new_id" interface="ext_foreign_toplevel_handle_v1"/>
</event>
<event name="finished">
<description summary="the compositor has finished with the toplevel manager">
This event indicates that the compositor is done sending events
to this object. The client should destroy the object.
See ext_foreign_toplevel_list_v1.destroy for more information.
The compositor must not send any more toplevel events after this event.
</description>
</event>
<request name="stop">
<description summary="stop sending events">
This request indicates that the client no longer wishes to receive
events for new toplevels.
The Wayland protocol is asynchronous, meaning the compositor may send
further toplevel events until the stop request is processed.
The client should wait for a ext_foreign_toplevel_list_v1.finished
event before destroying this object.
</description>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the ext_foreign_toplevel_list_v1 object">
This request should be called either when the client will no longer
use the ext_foreign_toplevel_list_v1 or after the finished event
has been received to allow destruction of the object.
If a client wishes to destroy this object it should send a
ext_foreign_toplevel_list_v1.stop request and wait for a ext_foreign_toplevel_list_v1.finished
event, then destroy the handles and then this object.
</description>
</request>
</interface>
<interface name="ext_foreign_toplevel_handle_v1" version="1">
<description summary="a mapped toplevel">
A ext_foreign_toplevel_handle_v1 object represents a mapped toplevel
window. A single app may have multiple mapped toplevels.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the ext_foreign_toplevel_handle_v1 object">
This request should be used when the client will no longer use the handle
or after the closed event has been received to allow destruction of the
object.
When a handle is destroyed, a new handle may not be created by the server
until the toplevel is unmapped and then remapped. Destroying a toplevel handle
is not recommended unless the client is cleaning up child objects
before destroying the ext_foreign_toplevel_list_v1 object, the toplevel
was closed or the toplevel handle will not be used in the future.
Other protocols which extend the ext_foreign_toplevel_handle_v1
interface should require destructors for extension interfaces be
called before allowing the toplevel handle to be destroyed.
</description>
</request>
<event name="closed">
<description summary="the toplevel has been closed">
The server will emit no further events on the ext_foreign_toplevel_handle_v1
after this event. Any requests received aside from the destroy request must
be ignored. Upon receiving this event, the client should destroy the handle.
Other protocols which extend the ext_foreign_toplevel_handle_v1
interface must also ignore requests other than destructors.
</description>
</event>
<event name="done">
<description summary="all information about the toplevel has been sent">
This event is sent after all changes in the toplevel state have
been sent.
This allows changes to the ext_foreign_toplevel_handle_v1 properties
to be atomically applied. Other protocols which extend the
ext_foreign_toplevel_handle_v1 interface may use this event to also
atomically apply any pending state.
This event must not be sent after the ext_foreign_toplevel_handle_v1.closed
event.
</description>
</event>
<event name="title">
<description summary="title change">
The title of the toplevel has changed.
The configured state must not be applied immediately. See
ext_foreign_toplevel_handle_v1.done for details.
</description>
<arg name="title" type="string"/>
</event>
<event name="app_id">
<description summary="app_id change">
The app id of the toplevel has changed.
The configured state must not be applied immediately. See
ext_foreign_toplevel_handle_v1.done for details.
</description>
<arg name="app_id" type="string"/>
</event>
<event name="identifier">
<description summary="a stable identifier for a toplevel">
This identifier is used to check if two or more toplevel handles belong
to the same toplevel.
The identifier is useful for command line tools or privileged clients
which may need to reference an exact toplevel across processes or
instances of the ext_foreign_toplevel_list_v1 global.
The compositor must only send this event when the handle is created.
The identifier must be unique per toplevel and it's handles. Two different
toplevels must not have the same identifier. The identifier is only valid
as long as the toplevel is mapped. If the toplevel is unmapped the identifier
must not be reused. An identifier must not be reused by the compositor to
ensure there are no races when sharing identifiers between processes.
An identifier is a string that contains up to 32 printable ASCII bytes.
An identifier must not be an empty string. It is recommended that a
compositor includes an opaque generation value in identifiers. How the
generation value is used when generating the identifier is implementation
dependent.
</description>
<arg name="identifier" type="string"/>
</event>
</interface>
</protocol>

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="ext_image_capture_source_v1">
<copyright>
Copyright © 2022 Andri Yngvason
Copyright © 2024 Simon Ser
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="opaque image capture source objects">
This protocol serves as an intermediary between capturing protocols and
potential image capture sources such as outputs and toplevels.
This protocol may be extended to support more image capture sources in the
future, thereby adding those image capture sources to other protocols that
use the image capture source object without having to modify those
protocols.
Warning! The protocol described in this file is currently in the testing
phase. Backward compatible changes may be added together with the
corresponding interface version bump. Backward incompatible changes can
only be done by creating a new major version of the extension.
</description>
<interface name="ext_image_capture_source_v1" version="1">
<description summary="opaque image capture source object">
The image capture source object is an opaque descriptor for a capturable
resource. This resource may be any sort of entity from which an image
may be derived.
Note, because ext_image_capture_source_v1 objects are created from multiple
independent factory interfaces, the ext_image_capture_source_v1 interface is
frozen at version 1.
</description>
<request name="destroy" type="destructor">
<description summary="delete this object">
Destroys the image capture source. This request may be sent at any time
by the client.
</description>
</request>
</interface>
<interface name="ext_output_image_capture_source_manager_v1" version="1">
<description summary="image capture source manager for outputs">
A manager for creating image capture source objects for wl_output objects.
</description>
<request name="create_source">
<description summary="create source object for output">
Creates a source object for an output. Images captured from this source
will show the same content as the output. Some elements may be omitted,
such as cursors and overlays that have been marked as transparent to
capturing.
</description>
<arg name="source" type="new_id" interface="ext_image_capture_source_v1"/>
<arg name="output" type="object" interface="wl_output"/>
</request>
<request name="destroy" type="destructor">
<description summary="delete this object">
Destroys the manager. This request may be sent at any time by the client
and objects created by the manager will remain valid after its
destruction.
</description>
</request>
</interface>
<interface name="ext_foreign_toplevel_image_capture_source_manager_v1" version="1">
<description summary="image capture source manager for foreign toplevels">
A manager for creating image capture source objects for
ext_foreign_toplevel_handle_v1 objects.
</description>
<request name="create_source">
<description summary="create source object for foreign toplevel">
Creates a source object for a foreign toplevel handle. Images captured
from this source will show the same content as the toplevel.
</description>
<arg name="source" type="new_id" interface="ext_image_capture_source_v1"/>
<arg name="toplevel_handle" type="object" interface="ext_foreign_toplevel_handle_v1"/>
</request>
<request name="destroy" type="destructor">
<description summary="delete this object">
Destroys the manager. This request may be sent at any time by the client
and objects created by the manager will remain valid after its
destruction.
</description>
</request>
</interface>
</protocol>

@ -0,0 +1,456 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="ext_image_copy_capture_v1">
<copyright>
Copyright © 2021-2023 Andri Yngvason
Copyright © 2024 Simon Ser
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="image capturing into client buffers">
This protocol allows clients to ask the compositor to capture image sources
such as outputs and toplevels into user submitted buffers.
Warning! The protocol described in this file is currently in the testing
phase. Backward compatible changes may be added together with the
corresponding interface version bump. Backward incompatible changes can
only be done by creating a new major version of the extension.
</description>
<interface name="ext_image_copy_capture_manager_v1" version="1">
<description summary="manager to inform clients and begin capturing">
This object is a manager which offers requests to start capturing from a
source.
</description>
<enum name="error">
<entry name="invalid_option" value="1" summary="invalid option flag"/>
</enum>
<enum name="options" bitfield="true">
<entry name="paint_cursors" value="1" summary="paint cursors onto captured frames"/>
</enum>
<request name="create_session">
<description summary="capture an image capture source">
Create a capturing session for an image capture source.
If the paint_cursors option is set, cursors shall be composited onto
the captured frame. The cursor must not be composited onto the frame
if this flag is not set.
If the options bitfield is invalid, the invalid_option protocol error
is sent.
</description>
<arg name="session" type="new_id" interface="ext_image_copy_capture_session_v1"/>
<arg name="source" type="object" interface="ext_image_capture_source_v1"/>
<arg name="options" type="uint" enum="options"/>
</request>
<request name="create_pointer_cursor_session">
<description summary="capture the pointer cursor of an image capture source">
Create a cursor capturing session for the pointer of an image capture
source.
</description>
<arg name="session" type="new_id" interface="ext_image_copy_capture_cursor_session_v1"/>
<arg name="source" type="object" interface="ext_image_capture_source_v1"/>
<arg name="pointer" type="object" interface="wl_pointer"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the manager">
Destroy the manager object.
Other objects created via this interface are unaffected.
</description>
</request>
</interface>
<interface name="ext_image_copy_capture_session_v1" version="1">
<description summary="image copy capture session">
This object represents an active image copy capture session.
After a capture session is created, buffer constraint events will be
emitted from the compositor to tell the client which buffer types and
formats are supported for reading from the session. The compositor may
re-send buffer constraint events whenever they change.
The advertise buffer constraints, the compositor must send in no
particular order: zero or more shm_format and dmabuf_format events, zero
or one dmabuf_device event, and exactly one buffer_size event. Then the
compositor must send a done event.
When the client has received all the buffer constraints, it can create a
buffer accordingly, attach it to the capture session using the
attach_buffer request, set the buffer damage using the damage_buffer
request and then send the capture request.
</description>
<enum name="error">
<entry name="duplicate_frame" value="1"
summary="create_frame sent before destroying previous frame"/>
</enum>
<event name="buffer_size">
<description summary="image capture source dimensions">
Provides the dimensions of the source image in buffer pixel coordinates.
The client must attach buffers that match this size.
</description>
<arg name="width" type="uint" summary="buffer width"/>
<arg name="height" type="uint" summary="buffer height"/>
</event>
<event name="shm_format">
<description summary="shm buffer format">
Provides the format that must be used for shared-memory buffers.
This event may be emitted multiple times, in which case the client may
choose any given format.
</description>
<arg name="format" type="uint" enum="wl_shm.format" summary="shm format"/>
</event>
<event name="dmabuf_device">
<description summary="dma-buf device">
This event advertises the device buffers must be allocated on for
dma-buf buffers.
In general the device is a DRM node. The DRM node type (primary vs.
render) is unspecified. Clients must not rely on the compositor sending
a particular node type. Clients cannot check two devices for equality
by comparing the dev_t value.
</description>
<arg name="device" type="array" summary="device dev_t value"/>
</event>
<event name="dmabuf_format">
<description summary="dma-buf format">
Provides the format that must be used for dma-buf buffers.
The client may choose any of the modifiers advertised in the array of
64-bit unsigned integers.
This event may be emitted multiple times, in which case the client may
choose any given format.
</description>
<arg name="format" type="uint" summary="drm format code"/>
<arg name="modifiers" type="array" summary="drm format modifiers"/>
</event>
<event name="done">
<description summary="all constraints have been sent">
This event is sent once when all buffer constraint events have been
sent.
The compositor must always end a batch of buffer constraint events with
this event, regardless of whether it sends the initial constraints or
an update.
</description>
</event>
<event name="stopped">
<description summary="session is no longer available">
This event indicates that the capture session has stopped and is no
longer available. This can happen in a number of cases, e.g. when the
underlying source is destroyed, if the user decides to end the image
capture, or if an unrecoverable runtime error has occurred.
The client should destroy the session after receiving this event.
</description>
</event>
<request name="create_frame">
<description summary="create a frame">
Create a capture frame for this session.
At most one frame object can exist for a given session at any time. If
a client sends a create_frame request before a previous frame object
has been destroyed, the duplicate_frame protocol error is raised.
</description>
<arg name="frame" type="new_id" interface="ext_image_copy_capture_frame_v1"/>
</request>
<request name="destroy" type="destructor">
<description summary="delete this object">
Destroys the session. This request can be sent at any time by the
client.
This request doesn't affect ext_image_copy_capture_frame_v1 objects created by
this object.
</description>
</request>
</interface>
<interface name="ext_image_copy_capture_frame_v1" version="1">
<description summary="image capture frame">
This object represents an image capture frame.
The client should attach a buffer, damage the buffer, and then send a
capture request.
If the capture is successful, the compositor must send the frame metadata
(transform, damage, presentation_time in any order) followed by the ready
event.
If the capture fails, the compositor must send the failed event.
</description>
<enum name="error">
<entry name="no_buffer" value="1" summary="capture sent without attach_buffer"/>
<entry name="invalid_buffer_damage" value="2" summary="invalid buffer damage"/>
<entry name="already_captured" value="3" summary="capture request has been sent"/>
</enum>
<request name="destroy" type="destructor">
<description summary="destroy this object">
Destroys the session. This request can be sent at any time by the
client.
</description>
</request>
<request name="attach_buffer">
<description summary="attach buffer to session">
Attach a buffer to the session.
The wl_buffer.release request is unused.
The new buffer replaces any previously attached buffer.
This request must not be sent after capture, or else the
already_captured protocol error is raised.
</description>
<arg name="buffer" type="object" interface="wl_buffer"/>
</request>
<request name="damage_buffer">
<description summary="damage buffer">
Apply damage to the buffer which is to be captured next. This request
may be sent multiple times to describe a region.
The client indicates the accumulated damage since this wl_buffer was
last captured. During capture, the compositor will update the buffer
with at least the union of the region passed by the client and the
region advertised by ext_image_copy_capture_frame_v1.damage.
When a wl_buffer is captured for the first time, or when the client
doesn't track damage, the client must damage the whole buffer.
This is for optimisation purposes. The compositor may use this
information to reduce copying.
These coordinates originate from the upper left corner of the buffer.
If x or y are strictly negative, or if width or height are negative or
zero, the invalid_buffer_damage protocol error is raised.
This request must not be sent after capture, or else the
already_captured protocol error is raised.
</description>
<arg name="x" type="int" summary="region x coordinate"/>
<arg name="y" type="int" summary="region y coordinate"/>
<arg name="width" type="int" summary="region width"/>
<arg name="height" type="int" summary="region height"/>
</request>
<request name="capture">
<description summary="capture a frame">
Capture a frame.
Unless this is the first successful captured frame performed in this
session, the compositor may wait an indefinite amount of time for the
source content to change before performing the copy.
This request may only be sent once, or else the already_captured
protocol error is raised. A buffer must be attached before this request
is sent, or else the no_buffer protocol error is raised.
</description>
</request>
<event name="transform">
<description summary="buffer transform">
This event is sent before the ready event and holds the transform that
the compositor has applied to the buffer contents.
</description>
<arg name="transform" type="uint" enum="wl_output.transform"/>
</event>
<event name="damage">
<description summary="buffer damaged region">
This event is sent before the ready event. It may be generated multiple
times to describe a region.
The first captured frame in a session will always carry full damage.
Subsequent frames' damaged regions describe which parts of the buffer
have changed since the last ready event.
These coordinates originate in the upper left corner of the buffer.
</description>
<arg name="x" type="int" summary="damage x coordinate"/>
<arg name="y" type="int" summary="damage y coordinate"/>
<arg name="width" type="int" summary="damage width"/>
<arg name="height" type="int" summary="damage height"/>
</event>
<event name="presentation_time">
<description summary="presentation time of the frame">
This event indicates the time at which the frame is presented to the
output in system monotonic time. This event is sent before the ready
event.
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].
</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>
<event name="ready">
<description summary="frame is available for reading">
Called as soon as the frame is copied, indicating it is available
for reading.
The buffer may be re-used by the client after this event.
After receiving this event, the client must destroy the object.
</description>
</event>
<enum name="failure_reason">
<entry name="unknown" value="0">
<description summary="unknown runtime error">
An unspecified runtime error has occurred. The client may retry.
</description>
</entry>
<entry name="buffer_constraints" value="1">
<description summary="buffer constraints mismatch">
The buffer submitted by the client doesn't match the latest session
constraints. The client should re-allocate its buffers and retry.
</description>
</entry>
<entry name="stopped" value="2">
<description summary="session is no longer available">
The session has stopped. See ext_image_copy_capture_session_v1.stopped.
</description>
</entry>
</enum>
<event name="failed">
<description summary="capture failed">
This event indicates that the attempted frame copy has failed.
After receiving this event, the client must destroy the object.
</description>
<arg name="reason" type="uint" enum="failure_reason"/>
</event>
</interface>
<interface name="ext_image_copy_capture_cursor_session_v1" version="1">
<description summary="cursor capture session">
This object represents a cursor capture session. It extends the base
capture session with cursor-specific metadata.
</description>
<enum name="error">
<entry name="duplicate_session" value="1" summary="get_captuerer_session sent twice"/>
</enum>
<request name="destroy" type="destructor">
<description summary="delete this object">
Destroys the session. This request can be sent at any time by the
client.
This request doesn't affect ext_image_copy_capture_frame_v1 objects created by
this object.
</description>
</request>
<request name="get_capture_session">
<description summary="get image copy captuerer session">
Gets the image copy capture session for this cursor session.
The session will produce frames of the cursor image. The compositor may
pause the session when the cursor leaves the captured area.
This request must not be sent more than once, or else the
duplicate_session protocol error is raised.
</description>
<arg name="session" type="new_id" interface="ext_image_copy_capture_session_v1"/>
</request>
<event name="enter">
<description summary="cursor entered captured area">
Sent when a cursor enters the captured area. It shall be generated
before the "position" and "hotspot" events when and only when a cursor
enters the area.
The cursor enters the captured area when the cursor image intersects
with the captured area. Note, this is different from e.g.
wl_pointer.enter.
</description>
</event>
<event name="leave">
<description summary="cursor left captured area">
Sent when a cursor leaves the captured area. No "position" or "hotspot"
event is generated for the cursor until the cursor enters the captured
area again.
</description>
</event>
<event name="position">
<description summary="position changed">
Cursors outside the image capture source do not get captured and no
event will be generated for them.
The given position is the position of the cursor's hotspot and it is
relative to the main buffer's top left corner in transformed buffer
pixel coordinates. The coordinates may be negative or greater than the
main buffer size.
</description>
<arg name="x" type="int" summary="position x coordinates"/>
<arg name="y" type="int" summary="position y coordinates"/>
</event>
<event name="hotspot">
<description summary="hotspot changed">
The hotspot describes the offset between the cursor image and the
position of the input device.
The given coordinates are the hotspot's offset from the origin in
buffer coordinates.
Clients should not apply the hotspot immediately: the hotspot becomes
effective when the next ext_image_copy_capture_frame_v1.ready event is received.
Compositors may delay this event until the client captures a new frame.
</description>
<arg name="x" type="int" summary="hotspot x coordinates"/>
<arg name="y" type="int" summary="hotspot y coordinates"/>
</event>
</interface>
</protocol>

14
rif.h

@ -13,14 +13,18 @@ typedef struct rif {
#define RIF_FORMAT_R8G8B8 0
#define RIF_FORMAT_R8G8B8A8 1
// TODO detect endianess - this breaks on big endian systems.
void write_rif_little(char* path, uint32_t width, uint32_t size, uint8_t format, void* data) {
#define IMG_SIZE 17 + size // 17 is the size of raw_img excluding the data field
rif_t* img = malloc(IMG_SIZE);
// TODO is this better than just hardcoding 17?
const uint32_t header_size = sizeof(rif_t) - sizeof(uint8_t*);
const uint64_t img_size = header_size + size;
rif_t* img = malloc(img_size);
char magic[] = RIF_MAGIC_LITTLE;
memcpy(((char*)img)+0, magic, 8);
memcpy(&img->magic_num, magic, 8);
img->width = width;
img->format = format;
memcpy(((char*)img)+17, data, size);
memcpy(&img->data, data, size);
FILE* output_file = fopen(path, "w");
fwrite(img, 1, IMG_SIZE, output_file);
fwrite(img, 1, img_size, output_file);
free(img);
}

@ -1,232 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_screencopy_unstable_v1">
<copyright>
Copyright © 2018 Simon Ser
Copyright © 2019 Andri Yngvason
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="screen content capturing on client buffers">
This protocol allows clients to ask the compositor to copy part of the
screen content to a client buffer.
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_screencopy_manager_v1" version="3">
<description summary="manager to inform clients and begin capturing">
This object is a manager which offers requests to start capturing from a
source.
</description>
<request name="capture_output">
<description summary="capture an output">
Capture the next frame of an entire output.
</description>
<arg name="frame" type="new_id" interface="zwlr_screencopy_frame_v1"/>
<arg name="overlay_cursor" type="int"
summary="composite cursor onto the frame"/>
<arg name="output" type="object" interface="wl_output"/>
</request>
<request name="capture_output_region">
<description summary="capture an output's region">
Capture the next frame of an output's region.
The region is given in output logical coordinates, see
xdg_output.logical_size. The region will be clipped to the output's
extents.
</description>
<arg name="frame" type="new_id" interface="zwlr_screencopy_frame_v1"/>
<arg name="overlay_cursor" type="int"
summary="composite cursor onto the frame"/>
<arg name="output" type="object" interface="wl_output"/>
<arg name="x" type="int"/>
<arg name="y" type="int"/>
<arg name="width" type="int"/>
<arg name="height" type="int"/>
</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_screencopy_frame_v1" version="3">
<description summary="a frame ready for copy">
This object represents a single frame.
When created, a series of buffer events will be sent, each representing a
supported buffer type. The "buffer_done" event is sent afterwards to
indicate that all supported buffer types have been enumerated. The client
will then be able to send a "copy" request. If the capture is successful,
the compositor will send a "flags" followed by a "ready" event.
For objects version 2 or lower, wl_shm buffers are always supported, ie.
the "buffer" event is guaranteed to be sent.
If the capture failed, the "failed" event is sent. This can happen anytime
before the "ready" event.
Once either a "ready" or a "failed" event is received, the client should
destroy the frame.
</description>
<event name="buffer">
<description summary="wl_shm buffer information">
Provides information about wl_shm buffer parameters that need to be
used for this frame. This event is sent once after the frame is created
if wl_shm buffers are supported.
</description>
<arg name="format" type="uint" enum="wl_shm.format" summary="buffer format"/>
<arg name="width" type="uint" summary="buffer width"/>
<arg name="height" type="uint" summary="buffer height"/>
<arg name="stride" type="uint" summary="buffer stride"/>
</event>
<request name="copy">
<description summary="copy the frame">
Copy the frame to the supplied buffer. The buffer must have a the
correct size, see zwlr_screencopy_frame_v1.buffer and
zwlr_screencopy_frame_v1.linux_dmabuf. The buffer needs to have a
supported format.
If the frame is successfully copied, a "flags" and a "ready" events are
sent. Otherwise, a "failed" event is sent.
</description>
<arg name="buffer" type="object" interface="wl_buffer"/>
</request>
<enum name="error">
<entry name="already_used" value="0"
summary="the object has already been used to copy a wl_buffer"/>
<entry name="invalid_buffer" value="1"
summary="buffer attributes are invalid"/>
</enum>
<enum name="flags" bitfield="true">
<entry name="y_invert" value="1" summary="contents are y-inverted"/>
</enum>
<event name="flags">
<description summary="frame flags">
Provides flags about the frame. This event is sent once before the
"ready" event.
</description>
<arg name="flags" type="uint" enum="flags" summary="frame flags"/>
</event>
<event name="ready">
<description summary="indicates frame is available for reading">
Called as soon as the frame is copied, 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 the 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>
<event name="failed">
<description summary="frame copy failed">
This event indicates that the attempted frame copy has failed.
After receiving this event, the client should destroy the object.
</description>
</event>
<request name="destroy" type="destructor">
<description summary="delete this object, used or not">
Destroys the frame. This request can be sent at any time by the client.
</description>
</request>
<!-- Version 2 additions -->
<request name="copy_with_damage" since="2">
<description summary="copy the frame when it's damaged">
Same as copy, except it waits until there is damage to copy.
</description>
<arg name="buffer" type="object" interface="wl_buffer"/>
</request>
<event name="damage" since="2">
<description summary="carries the coordinates of the damaged region">
This event is sent right before the ready event when copy_with_damage is
requested. It may be generated multiple times for each copy_with_damage
request.
The arguments describe a box around an area that has changed since the
last copy request that was derived from the current screencopy manager
instance.
The union of all regions received between the call to copy_with_damage
and a ready event is the total damage since the prior ready event.
</description>
<arg name="x" type="uint" summary="damaged x coordinates"/>
<arg name="y" type="uint" summary="damaged y coordinates"/>
<arg name="width" type="uint" summary="current width"/>
<arg name="height" type="uint" summary="current height"/>
</event>
<!-- Version 3 additions -->
<event name="linux_dmabuf" since="3">
<description summary="linux-dmabuf buffer information">
Provides information about linux-dmabuf buffer parameters that need to
be used for this frame. This event is sent once after the frame is
created if linux-dmabuf buffers are supported.
</description>
<arg name="format" type="uint" summary="fourcc pixel format"/>
<arg name="width" type="uint" summary="buffer width"/>
<arg name="height" type="uint" summary="buffer height"/>
</event>
<event name="buffer_done" since="3">
<description summary="all buffer types reported">
This event is sent once after all buffer events have been sent.
The client should proceed to create a buffer of one of the supported
types, and send a "copy" request.
</description>
</event>
</interface>
</protocol>
Loading…
Cancel
Save