feat(src/window/switcher/app-capture): add app-capture implementation
This commit is contained in:
11 files changed
+2322
No files matched your search
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0"?>
|
||||
<repository version="1.2"
|
||||
xmlns="http://www.gtk.org/introspection/core/1.0"
|
||||
xmlns:c="http://www.gtk.org/introspection/c/1.0"
|
||||
xmlns:glib="http://www.gtk.org/introspection/glib/1.0">
|
||||
|
||||
<include name="GObject" version="2.0"/>
|
||||
<include name="GLib" version="2.0"/>
|
||||
|
||||
<package name="appcapture"/>
|
||||
<c:include name="app-capture.h"/>
|
||||
|
||||
<namespace name="AppCapture"
|
||||
version="1.0"
|
||||
shared-library="libappcapture.so"
|
||||
c:identifier-prefixes="App"
|
||||
c:symbol-prefixes="app">
|
||||
|
||||
<class name="Capture"
|
||||
c:symbol-prefix="capture"
|
||||
c:type="AppCapture"
|
||||
parent="GObject.Object"
|
||||
glib:type-name="AppCapture"
|
||||
glib:get-type="app_capture_get_type"
|
||||
glib:type-struct="CaptureClass">
|
||||
|
||||
<constructor name="new" c:identifier="app_capture_new">
|
||||
<return-value transfer-ownership="full">
|
||||
<type name="Capture" c:type="AppCapture*"/>
|
||||
</return-value>
|
||||
</constructor>
|
||||
|
||||
<!-- address accepts "0x564f60266bd0" or "564f60266bd0" -->
|
||||
<method name="capture_by_handle" c:identifier="app_capture_capture_by_handle">
|
||||
<parameters>
|
||||
<instance-parameter name="self" transfer-ownership="none">
|
||||
<type name="Capture" c:type="AppCapture*"/>
|
||||
</instance-parameter>
|
||||
<parameter name="address" transfer-ownership="none">
|
||||
<type name="utf8" c:type="const gchar*"/>
|
||||
</parameter>
|
||||
</parameters>
|
||||
<return-value transfer-ownership="none">
|
||||
<type name="none" c:type="void"/>
|
||||
</return-value>
|
||||
</method>
|
||||
|
||||
<glib:signal name="frame-ready" when="last">
|
||||
<return-value transfer-ownership="none">
|
||||
<type name="none" c:type="void"/>
|
||||
</return-value>
|
||||
<parameters>
|
||||
<parameter name="bytes" transfer-ownership="none">
|
||||
<type name="GLib.Bytes" c:type="GBytes*"/>
|
||||
</parameter>
|
||||
<parameter name="width" transfer-ownership="none">
|
||||
<type name="gint" c:type="gint"/>
|
||||
</parameter>
|
||||
<parameter name="height" transfer-ownership="none">
|
||||
<type name="gint" c:type="gint"/>
|
||||
</parameter>
|
||||
<parameter name="stride" transfer-ownership="none">
|
||||
<type name="gint" c:type="gint"/>
|
||||
</parameter>
|
||||
</parameters>
|
||||
</glib:signal>
|
||||
|
||||
<glib:signal name="frame-failed" when="last">
|
||||
<return-value transfer-ownership="none">
|
||||
<type name="none" c:type="void"/>
|
||||
</return-value>
|
||||
<parameters>
|
||||
<parameter name="reason" transfer-ownership="none">
|
||||
<type name="utf8" c:type="gchararray"/>
|
||||
</parameter>
|
||||
</parameters>
|
||||
</glib:signal>
|
||||
|
||||
</class>
|
||||
|
||||
<record name="CaptureClass"
|
||||
c:type="AppCaptureClass"
|
||||
glib:is-gtype-struct-for="Capture">
|
||||
<field name="parent_class">
|
||||
<type name="GObject.ObjectClass" c:type="GObjectClass"/>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</namespace>
|
||||
</repository>
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,672 @@
|
||||
#define _GNU_SOURCE
|
||||
#include "app-capture.h"
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <gdk/gdkwayland.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "hyprland-toplevel-export-v1.h"
|
||||
#include "wlr-foreign-toplevel-management-unstable-v1.h"
|
||||
#include "hyprland-toplevel-mapping-v1.h"
|
||||
|
||||
/* =========================================================================
|
||||
* Signals
|
||||
* ========================================================================= */
|
||||
|
||||
enum {
|
||||
SIGNAL_FRAME_READY,
|
||||
SIGNAL_FRAME_FAILED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
/* How long capture_by_handle() will wait for an unknown address's wlr handle
|
||||
* to arrive before giving up and emitting frame-failed. */
|
||||
#define PENDING_CAPTURE_TIMEOUT_MS 1500
|
||||
|
||||
/* =========================================================================
|
||||
* Toplevel entry — one per live wlr toplevel
|
||||
* =========================================================================
|
||||
* We build a table of these at init time by:
|
||||
* 1. Binding zwlr_foreign_toplevel_manager_v1 and receiving toplevel events
|
||||
* 2. For each handle, requesting a hyprland_toplevel_mapping to get the
|
||||
* full 64-bit Hyprland address (split as hi + lo uint32s)
|
||||
* This lets capture_by_handle() look up the wlr object for any address.
|
||||
* ========================================================================= */
|
||||
|
||||
typedef struct {
|
||||
struct zwlr_foreign_toplevel_handle_v1 *wlr_handle;
|
||||
char address[32]; /* hex string, e.g. "564f60266bd0", no "0x" prefix */
|
||||
gboolean mapped; /* false until window_address event arrives */
|
||||
} ToplevelEntry;
|
||||
|
||||
/* =========================================================================
|
||||
* Pending capture — capture_by_handle() called before mapping arrived
|
||||
* ========================================================================= */
|
||||
|
||||
typedef struct {
|
||||
AppCapture *self; /* unowned; pending queue is owned by self */
|
||||
char *address; /* owned, stripped of "0x" */
|
||||
guint timeout_id; /* 0 once consumed */
|
||||
} PendingCapture;
|
||||
|
||||
/* =========================================================================
|
||||
* Object struct
|
||||
* ========================================================================= */
|
||||
|
||||
struct _AppCapture {
|
||||
GObject parent_instance;
|
||||
|
||||
/* Wayland globals */
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
struct wl_shm *shm;
|
||||
struct hyprland_toplevel_export_manager_v1 *export_manager;
|
||||
struct zwlr_foreign_toplevel_manager_v1 *wlr_manager;
|
||||
struct hyprland_toplevel_mapping_manager_v1 *mapping_manager;
|
||||
|
||||
/* Address → wlr_handle table */
|
||||
GPtrArray *toplevels; /* element-type: ToplevelEntry* */
|
||||
|
||||
/* Captures waiting on a not-yet-mapped address */
|
||||
GPtrArray *pending_captures; /* element-type: PendingCapture* */
|
||||
|
||||
/* Per-frame state — reset before every capture */
|
||||
int shm_fd;
|
||||
unsigned char *pixel_data;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t stride;
|
||||
uint32_t format;
|
||||
size_t shm_size;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(AppCapture, app_capture, G_TYPE_OBJECT)
|
||||
|
||||
/* =========================================================================
|
||||
* Forward declarations
|
||||
* ========================================================================= */
|
||||
|
||||
static void cleanup_shm(AppCapture *self);
|
||||
static void request_mapping(AppCapture *self, ToplevelEntry *entry);
|
||||
static void emit_frame_failed(AppCapture *self, const char *reason);
|
||||
static void do_capture(AppCapture *self,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *wlr_handle);
|
||||
static struct zwlr_foreign_toplevel_handle_v1 *find_wlr_handle(AppCapture *self,
|
||||
const char *addr);
|
||||
static void flush_pending_for_address(AppCapture *self, const char *address);
|
||||
static gboolean pending_capture_timeout_cb(gpointer data);
|
||||
static void pending_capture_free(gpointer data);
|
||||
|
||||
/* =========================================================================
|
||||
* Hyprland toplevel mapping listener
|
||||
* Fires once per ToplevelEntry with the full 64-bit address.
|
||||
* ========================================================================= */
|
||||
|
||||
typedef struct {
|
||||
AppCapture *self;
|
||||
ToplevelEntry *entry;
|
||||
} MappingContext;
|
||||
|
||||
static void mapping_handle_window_address(void *data,
|
||||
struct hyprland_toplevel_window_mapping_handle_v1 *handle,
|
||||
uint32_t address_hi, uint32_t address)
|
||||
{
|
||||
MappingContext *ctx = data;
|
||||
ToplevelEntry *entry = ctx->entry;
|
||||
AppCapture *self = ctx->self;
|
||||
|
||||
uint64_t full = ((uint64_t)address_hi << 32) | (uint64_t)address;
|
||||
snprintf(entry->address, sizeof(entry->address), "%" PRIx64, full);
|
||||
entry->mapped = TRUE;
|
||||
|
||||
/* Dispatch any capture that was waiting for this address */
|
||||
flush_pending_for_address(self, entry->address);
|
||||
|
||||
hyprland_toplevel_window_mapping_handle_v1_destroy(handle);
|
||||
g_free(ctx);
|
||||
}
|
||||
|
||||
static void mapping_handle_failed(void *data,
|
||||
struct hyprland_toplevel_window_mapping_handle_v1 *handle)
|
||||
{
|
||||
MappingContext *ctx = data;
|
||||
/* Address stays empty — this entry won't be matchable, which is fine.
|
||||
* Any pending capture for that address will time out via its own timer. */
|
||||
hyprland_toplevel_window_mapping_handle_v1_destroy(handle);
|
||||
g_free(ctx);
|
||||
}
|
||||
|
||||
static const struct hyprland_toplevel_window_mapping_handle_v1_listener mapping_listener = {
|
||||
.window_address = mapping_handle_window_address,
|
||||
.failed = mapping_handle_failed,
|
||||
};
|
||||
|
||||
/* =========================================================================
|
||||
* wlr foreign toplevel listeners
|
||||
* We only need enough to know when a window appears or disappears.
|
||||
* ========================================================================= */
|
||||
|
||||
static void wlr_handle_title(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *handle, const char *title)
|
||||
{ (void)data; (void)handle; (void)title; }
|
||||
|
||||
static void wlr_handle_app_id(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *handle, const char *app_id)
|
||||
{ (void)data; (void)handle; (void)app_id; }
|
||||
|
||||
static void wlr_handle_output_enter(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_output *output)
|
||||
{ (void)data; (void)handle; (void)output; }
|
||||
|
||||
static void wlr_handle_output_leave(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_output *output)
|
||||
{ (void)data; (void)handle; (void)output; }
|
||||
|
||||
static void wlr_handle_state(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_array *state)
|
||||
{ (void)data; (void)handle; (void)state; }
|
||||
|
||||
static void wlr_handle_done(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *handle)
|
||||
{ (void)data; (void)handle; }
|
||||
|
||||
static void wlr_handle_closed(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *handle)
|
||||
{
|
||||
ToplevelEntry *entry = data;
|
||||
/* Mark as unmapped so capture_by_handle skips it */
|
||||
entry->mapped = FALSE;
|
||||
zwlr_foreign_toplevel_handle_v1_destroy(handle);
|
||||
entry->wlr_handle = NULL;
|
||||
}
|
||||
|
||||
static void wlr_handle_parent(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *handle,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *parent)
|
||||
{ (void)data; (void)handle; (void)parent; }
|
||||
|
||||
static const struct zwlr_foreign_toplevel_handle_v1_listener wlr_handle_listener = {
|
||||
.title = wlr_handle_title,
|
||||
.app_id = wlr_handle_app_id,
|
||||
.output_enter = wlr_handle_output_enter,
|
||||
.output_leave = wlr_handle_output_leave,
|
||||
.state = wlr_handle_state,
|
||||
.done = wlr_handle_done,
|
||||
.closed = wlr_handle_closed,
|
||||
.parent = wlr_handle_parent,
|
||||
};
|
||||
|
||||
/* =========================================================================
|
||||
* wlr foreign toplevel manager listener
|
||||
* ========================================================================= */
|
||||
|
||||
static void wlr_manager_handle_toplevel(void *data,
|
||||
struct zwlr_foreign_toplevel_manager_v1 *manager,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *handle)
|
||||
{
|
||||
(void)manager;
|
||||
AppCapture *self = APP_CAPTURE(data);
|
||||
ToplevelEntry *entry = g_new0(ToplevelEntry, 1);
|
||||
entry->wlr_handle = handle;
|
||||
entry->mapped = FALSE;
|
||||
|
||||
zwlr_foreign_toplevel_handle_v1_add_listener(handle,
|
||||
&wlr_handle_listener, entry);
|
||||
|
||||
g_ptr_array_add(self->toplevels, entry);
|
||||
|
||||
/* Immediately request the Hyprland address mapping for this handle */
|
||||
if (self->mapping_manager)
|
||||
request_mapping(self, entry);
|
||||
}
|
||||
|
||||
static void wlr_manager_handle_finished(void *data,
|
||||
struct zwlr_foreign_toplevel_manager_v1 *manager)
|
||||
{ (void)data; (void)manager; }
|
||||
|
||||
static const struct zwlr_foreign_toplevel_manager_v1_listener wlr_manager_listener = {
|
||||
.toplevel = wlr_manager_handle_toplevel,
|
||||
.finished = wlr_manager_handle_finished,
|
||||
};
|
||||
|
||||
/* =========================================================================
|
||||
* Mapping helper — ask Hyprland for the address of a wlr handle
|
||||
* ========================================================================= */
|
||||
|
||||
static void request_mapping(AppCapture *self, ToplevelEntry *entry)
|
||||
{
|
||||
MappingContext *ctx = g_new0(MappingContext, 1);
|
||||
ctx->self = self;
|
||||
ctx->entry = entry;
|
||||
|
||||
struct hyprland_toplevel_window_mapping_handle_v1 *mh =
|
||||
hyprland_toplevel_mapping_manager_v1_get_window_for_toplevel_wlr(
|
||||
self->mapping_manager, entry->wlr_handle);
|
||||
|
||||
hyprland_toplevel_window_mapping_handle_v1_add_listener(mh,
|
||||
&mapping_listener, ctx);
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* Registry listener
|
||||
* ========================================================================= */
|
||||
|
||||
static void registry_handle_global(void *data, struct wl_registry *registry,
|
||||
uint32_t name, const char *interface, uint32_t version)
|
||||
{
|
||||
(void)version;
|
||||
AppCapture *self = APP_CAPTURE(data);
|
||||
|
||||
if (g_strcmp0(interface,
|
||||
hyprland_toplevel_export_manager_v1_interface.name) == 0) {
|
||||
self->export_manager = wl_registry_bind(registry, name,
|
||||
&hyprland_toplevel_export_manager_v1_interface, 2);
|
||||
}
|
||||
else if (g_strcmp0(interface, wl_shm_interface.name) == 0) {
|
||||
self->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
|
||||
}
|
||||
else if (g_strcmp0(interface,
|
||||
zwlr_foreign_toplevel_manager_v1_interface.name) == 0) {
|
||||
self->wlr_manager = wl_registry_bind(registry, name,
|
||||
&zwlr_foreign_toplevel_manager_v1_interface, 3);
|
||||
zwlr_foreign_toplevel_manager_v1_add_listener(self->wlr_manager,
|
||||
&wlr_manager_listener, self);
|
||||
}
|
||||
else if (g_strcmp0(interface,
|
||||
hyprland_toplevel_mapping_manager_v1_interface.name) == 0) {
|
||||
self->mapping_manager = wl_registry_bind(registry, name,
|
||||
&hyprland_toplevel_mapping_manager_v1_interface, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void registry_handle_global_remove(void *data,
|
||||
struct wl_registry *registry, uint32_t name)
|
||||
{ (void)data; (void)registry; (void)name; }
|
||||
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
.global = registry_handle_global,
|
||||
.global_remove = registry_handle_global_remove,
|
||||
};
|
||||
|
||||
/* =========================================================================
|
||||
* Frame listener
|
||||
*
|
||||
* Event order: buffer → (linux_dmabuf) → buffer_done → [copy()] → flags → ready|failed
|
||||
* ========================================================================= */
|
||||
|
||||
static void frame_handle_buffer(void *data,
|
||||
struct hyprland_toplevel_export_frame_v1 *frame,
|
||||
uint32_t format, uint32_t width, uint32_t height, uint32_t stride)
|
||||
{
|
||||
(void)frame;
|
||||
AppCapture *self = APP_CAPTURE(data);
|
||||
self->format = format;
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
self->stride = stride;
|
||||
}
|
||||
|
||||
static void frame_handle_linux_dmabuf(void *data,
|
||||
struct hyprland_toplevel_export_frame_v1 *frame,
|
||||
uint32_t format, uint32_t width, uint32_t height)
|
||||
{ (void)data; (void)frame; (void)format; (void)width; (void)height; }
|
||||
|
||||
static void frame_handle_buffer_done(void *data,
|
||||
struct hyprland_toplevel_export_frame_v1 *frame)
|
||||
{
|
||||
AppCapture *self = APP_CAPTURE(data);
|
||||
|
||||
if (self->width == 0 || self->height == 0 || self->stride == 0) {
|
||||
g_warning("AppCapture: buffer_done with zero dimensions");
|
||||
hyprland_toplevel_export_frame_v1_destroy(frame);
|
||||
emit_frame_failed(self, "buffer_invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
self->shm_size = (size_t)self->stride * self->height;
|
||||
|
||||
self->shm_fd = memfd_create("app-capture-buffer", 0);
|
||||
if (self->shm_fd < 0) {
|
||||
g_warning("AppCapture: memfd_create failed");
|
||||
hyprland_toplevel_export_frame_v1_destroy(frame);
|
||||
emit_frame_failed(self, "alloc_failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ftruncate(self->shm_fd, (off_t)self->shm_size) < 0) {
|
||||
g_warning("AppCapture: ftruncate failed");
|
||||
cleanup_shm(self);
|
||||
hyprland_toplevel_export_frame_v1_destroy(frame);
|
||||
emit_frame_failed(self, "alloc_failed");
|
||||
return;
|
||||
}
|
||||
|
||||
self->pixel_data = mmap(NULL, self->shm_size,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||
self->shm_fd, 0);
|
||||
if (self->pixel_data == MAP_FAILED) {
|
||||
g_warning("AppCapture: mmap failed");
|
||||
self->pixel_data = NULL;
|
||||
cleanup_shm(self);
|
||||
hyprland_toplevel_export_frame_v1_destroy(frame);
|
||||
emit_frame_failed(self, "alloc_failed");
|
||||
return;
|
||||
}
|
||||
|
||||
struct wl_shm_pool *pool = wl_shm_create_pool(self->shm, self->shm_fd,
|
||||
(int32_t)self->shm_size);
|
||||
struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool,
|
||||
0, (int32_t)self->width, (int32_t)self->height,
|
||||
(int32_t)self->stride, self->format);
|
||||
wl_shm_pool_destroy(pool);
|
||||
|
||||
hyprland_toplevel_export_frame_v1_copy(frame, buffer, 1);
|
||||
wl_buffer_destroy(buffer);
|
||||
}
|
||||
|
||||
static void frame_handle_flags(void *data,
|
||||
struct hyprland_toplevel_export_frame_v1 *frame, uint32_t flags)
|
||||
{ (void)data; (void)frame; (void)flags; }
|
||||
|
||||
static void frame_handle_ready(void *data,
|
||||
struct hyprland_toplevel_export_frame_v1 *frame,
|
||||
uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec)
|
||||
{
|
||||
(void)tv_sec_hi; (void)tv_sec_lo; (void)tv_nsec;
|
||||
AppCapture *self = APP_CAPTURE(data);
|
||||
|
||||
if (!self->pixel_data) {
|
||||
g_warning("AppCapture: ready fired but pixel_data is NULL");
|
||||
hyprland_toplevel_export_frame_v1_destroy(frame);
|
||||
emit_frame_failed(self, "internal");
|
||||
return;
|
||||
}
|
||||
|
||||
GBytes *bytes = g_bytes_new(self->pixel_data, self->shm_size);
|
||||
g_signal_emit(self, signals[SIGNAL_FRAME_READY], 0,
|
||||
bytes, (gint)self->width, (gint)self->height, (gint)self->stride);
|
||||
g_bytes_unref(bytes);
|
||||
|
||||
cleanup_shm(self);
|
||||
hyprland_toplevel_export_frame_v1_destroy(frame);
|
||||
}
|
||||
|
||||
static void frame_handle_failed(void *data,
|
||||
struct hyprland_toplevel_export_frame_v1 *frame)
|
||||
{
|
||||
AppCapture *self = APP_CAPTURE(data);
|
||||
g_warning("AppCapture: frame capture failed");
|
||||
cleanup_shm(self);
|
||||
hyprland_toplevel_export_frame_v1_destroy(frame);
|
||||
emit_frame_failed(self, "frame_failed");
|
||||
}
|
||||
|
||||
static void frame_handle_damage(void *data,
|
||||
struct hyprland_toplevel_export_frame_v1 *frame,
|
||||
uint32_t x, uint32_t y, uint32_t width, uint32_t height)
|
||||
{ (void)data; (void)frame; (void)x; (void)y; (void)width; (void)height; }
|
||||
|
||||
static const struct hyprland_toplevel_export_frame_v1_listener frame_listener = {
|
||||
.buffer = frame_handle_buffer,
|
||||
.linux_dmabuf = frame_handle_linux_dmabuf,
|
||||
.buffer_done = frame_handle_buffer_done,
|
||||
.flags = frame_handle_flags,
|
||||
.ready = frame_handle_ready,
|
||||
.failed = frame_handle_failed,
|
||||
.damage = frame_handle_damage,
|
||||
};
|
||||
|
||||
/* =========================================================================
|
||||
* Capture helpers
|
||||
* ========================================================================= */
|
||||
|
||||
static void emit_frame_failed(AppCapture *self, const char *reason)
|
||||
{
|
||||
g_signal_emit(self, signals[SIGNAL_FRAME_FAILED], 0, reason);
|
||||
}
|
||||
|
||||
static struct zwlr_foreign_toplevel_handle_v1 *find_wlr_handle(AppCapture *self,
|
||||
const char *addr)
|
||||
{
|
||||
for (guint i = 0; i < self->toplevels->len; i++) {
|
||||
ToplevelEntry *entry = g_ptr_array_index(self->toplevels, i);
|
||||
if (entry->mapped && entry->wlr_handle &&
|
||||
g_strcmp0(entry->address, addr) == 0)
|
||||
return entry->wlr_handle;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void do_capture(AppCapture *self,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *wlr_handle)
|
||||
{
|
||||
self->width = self->height = self->stride = self->format = 0;
|
||||
|
||||
struct hyprland_toplevel_export_frame_v1 *frame =
|
||||
hyprland_toplevel_export_manager_v1_capture_toplevel_with_wlr_toplevel_handle(
|
||||
self->export_manager,
|
||||
0, /* overlay_cursor */
|
||||
wlr_handle
|
||||
);
|
||||
|
||||
hyprland_toplevel_export_frame_v1_add_listener(frame, &frame_listener, self);
|
||||
wl_display_flush(self->display);
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* Pending capture queue
|
||||
*
|
||||
* When capture_by_handle() is called for an address whose wlr handle hasn't
|
||||
* been mapped yet (race between Hyprland IPC and wlr foreign-toplevel), we
|
||||
* park a PendingCapture and wait for either:
|
||||
* - mapping_handle_window_address → flush_pending_for_address → do_capture
|
||||
* - PENDING_CAPTURE_TIMEOUT_MS elapses → emit frame-failed
|
||||
*
|
||||
* In practice the JS layer is single-flight so this queue holds ≤ 1 entry.
|
||||
* ========================================================================= */
|
||||
|
||||
static void pending_capture_free(gpointer data)
|
||||
{
|
||||
PendingCapture *pc = data;
|
||||
if (pc->timeout_id != 0) {
|
||||
g_source_remove(pc->timeout_id);
|
||||
pc->timeout_id = 0;
|
||||
}
|
||||
g_free(pc->address);
|
||||
g_free(pc);
|
||||
}
|
||||
|
||||
static gboolean pending_capture_timeout_cb(gpointer data)
|
||||
{
|
||||
PendingCapture *pc = data;
|
||||
AppCapture *self = pc->self;
|
||||
|
||||
g_warning("AppCapture: timeout waiting for wlr handle '%s' "
|
||||
"(toplevel table has %u entries)",
|
||||
pc->address, self->toplevels->len);
|
||||
|
||||
/* Mark consumed so pending_capture_free doesn't double-remove */
|
||||
pc->timeout_id = 0;
|
||||
|
||||
/* Remove first (frees pc), THEN emit — emit may re-enter capture_by_handle */
|
||||
g_ptr_array_remove(self->pending_captures, pc);
|
||||
emit_frame_failed(self, "no_handle_timeout");
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void flush_pending_for_address(AppCapture *self, const char *address)
|
||||
{
|
||||
if (self->pending_captures->len == 0) return;
|
||||
|
||||
struct zwlr_foreign_toplevel_handle_v1 *wlr_handle =
|
||||
find_wlr_handle(self, address);
|
||||
if (!wlr_handle) return;
|
||||
|
||||
/* Iterate backwards so removals don't shift indices we're about to visit */
|
||||
for (guint i = self->pending_captures->len; i > 0; i--) {
|
||||
PendingCapture *pc = g_ptr_array_index(self->pending_captures, i - 1);
|
||||
if (g_strcmp0(pc->address, address) != 0) continue;
|
||||
|
||||
do_capture(self, wlr_handle);
|
||||
g_ptr_array_remove_index(self->pending_captures, i - 1);
|
||||
/* JS side is single-flight — at most one match expected.
|
||||
* Break to avoid issuing two captures against the same shm state. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* SHM helpers
|
||||
* ========================================================================= */
|
||||
|
||||
static void cleanup_shm(AppCapture *self)
|
||||
{
|
||||
if (self->pixel_data && self->pixel_data != MAP_FAILED) {
|
||||
munmap(self->pixel_data, self->shm_size);
|
||||
self->pixel_data = NULL;
|
||||
}
|
||||
if (self->shm_fd >= 0) {
|
||||
close(self->shm_fd);
|
||||
self->shm_fd = -1;
|
||||
}
|
||||
self->shm_size = 0;
|
||||
}
|
||||
|
||||
static void toplevel_entry_free(gpointer data)
|
||||
{
|
||||
ToplevelEntry *entry = data;
|
||||
if (entry->wlr_handle)
|
||||
zwlr_foreign_toplevel_handle_v1_destroy(entry->wlr_handle);
|
||||
g_free(entry);
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* GObject lifecycle
|
||||
* ========================================================================= */
|
||||
|
||||
static void app_capture_finalize(GObject *object)
|
||||
{
|
||||
AppCapture *self = APP_CAPTURE(object);
|
||||
cleanup_shm(self);
|
||||
g_ptr_array_unref(self->pending_captures);
|
||||
g_ptr_array_unref(self->toplevels);
|
||||
if (self->export_manager)
|
||||
hyprland_toplevel_export_manager_v1_destroy(self->export_manager);
|
||||
if (self->mapping_manager)
|
||||
hyprland_toplevel_mapping_manager_v1_destroy(self->mapping_manager);
|
||||
if (self->wlr_manager)
|
||||
zwlr_foreign_toplevel_manager_v1_destroy(self->wlr_manager);
|
||||
if (self->shm)
|
||||
wl_shm_destroy(self->shm);
|
||||
if (self->registry)
|
||||
wl_registry_destroy(self->registry);
|
||||
G_OBJECT_CLASS(app_capture_parent_class)->finalize(object);
|
||||
}
|
||||
|
||||
static void app_capture_class_init(AppCaptureClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
||||
object_class->finalize = app_capture_finalize;
|
||||
|
||||
signals[SIGNAL_FRAME_READY] = g_signal_new(
|
||||
"frame-ready",
|
||||
G_TYPE_FROM_CLASS(klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0, NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 4,
|
||||
G_TYPE_BYTES, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT
|
||||
);
|
||||
|
||||
signals[SIGNAL_FRAME_FAILED] = g_signal_new(
|
||||
"frame-failed",
|
||||
G_TYPE_FROM_CLASS(klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0, NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 1,
|
||||
G_TYPE_STRING
|
||||
);
|
||||
}
|
||||
|
||||
static void app_capture_init(AppCapture *self)
|
||||
{
|
||||
self->shm_fd = -1;
|
||||
self->pixel_data = NULL;
|
||||
self->shm_size = 0;
|
||||
self->toplevels = g_ptr_array_new_with_free_func(toplevel_entry_free);
|
||||
self->pending_captures = g_ptr_array_new_with_free_func(pending_capture_free);
|
||||
|
||||
GdkDisplay *gdk_display = gdk_display_get_default();
|
||||
self->display = gdk_wayland_display_get_wl_display(gdk_display);
|
||||
|
||||
self->registry = wl_display_get_registry(self->display);
|
||||
wl_registry_add_listener(self->registry, ®istry_listener, self);
|
||||
|
||||
/* First roundtrip: compositor advertises globals, we bind them all */
|
||||
wl_display_roundtrip(self->display);
|
||||
|
||||
/* Second roundtrip: wlr_manager emits toplevel events for existing
|
||||
* windows, and mapping requests are sent for each one */
|
||||
wl_display_roundtrip(self->display);
|
||||
|
||||
/* Third roundtrip: mapping responses (window_address events) arrive */
|
||||
wl_display_roundtrip(self->display);
|
||||
|
||||
if (!self->export_manager)
|
||||
g_warning("AppCapture: hyprland_toplevel_export_manager_v1 not found");
|
||||
if (!self->wlr_manager)
|
||||
g_warning("AppCapture: zwlr_foreign_toplevel_manager_v1 not found");
|
||||
if (!self->mapping_manager)
|
||||
g_warning("AppCapture: hyprland_toplevel_mapping_manager_v1 not found");
|
||||
if (!self->shm)
|
||||
g_warning("AppCapture: wl_shm not found");
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* Public API
|
||||
* ========================================================================= */
|
||||
|
||||
AppCapture *app_capture_new(void)
|
||||
{
|
||||
return g_object_new(APP_TYPE_CAPTURE, NULL);
|
||||
}
|
||||
|
||||
void app_capture_capture_by_handle(AppCapture *self, const gchar *address)
|
||||
{
|
||||
g_return_if_fail(APP_IS_CAPTURE(self));
|
||||
g_return_if_fail(address != NULL);
|
||||
|
||||
if (!self->export_manager) {
|
||||
g_warning("AppCapture: export_manager not available");
|
||||
emit_frame_failed(self, "no_export_manager");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Strip optional "0x" prefix so matching works regardless of input */
|
||||
const gchar *addr = address;
|
||||
if (g_str_has_prefix(addr, "0x") || g_str_has_prefix(addr, "0X"))
|
||||
addr += 2;
|
||||
|
||||
/* Fast path: handle already known and mapped */
|
||||
struct zwlr_foreign_toplevel_handle_v1 *wlr_handle =
|
||||
find_wlr_handle(self, addr);
|
||||
if (wlr_handle) {
|
||||
do_capture(self, wlr_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Slow path: park and wait up to PENDING_CAPTURE_TIMEOUT_MS for the
|
||||
* wlr_manager toplevel/mapping events to arrive. Race window is
|
||||
* typically <100 ms in practice but can spike under load. */
|
||||
PendingCapture *pc = g_new0(PendingCapture, 1);
|
||||
pc->self = self;
|
||||
pc->address = g_strdup(addr);
|
||||
pc->timeout_id = g_timeout_add(PENDING_CAPTURE_TIMEOUT_MS,
|
||||
pending_capture_timeout_cb, pc);
|
||||
g_ptr_array_add(self->pending_captures, pc);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define APP_TYPE_CAPTURE (app_capture_get_type())
|
||||
G_DECLARE_FINAL_TYPE(AppCapture, app_capture, APP, CAPTURE, GObject)
|
||||
|
||||
/**
|
||||
* app_capture_new:
|
||||
*
|
||||
* Returns: (transfer full): a new #AppCapture.
|
||||
* Binds hyprland_toplevel_export_manager_v1, zwlr_foreign_toplevel_manager_v1,
|
||||
* and hyprland_toplevel_mapping_manager_v1, then does three roundtrips to
|
||||
* enumerate all live windows and map their addresses.
|
||||
*/
|
||||
AppCapture *app_capture_new(void);
|
||||
|
||||
/**
|
||||
* app_capture_capture_by_handle:
|
||||
* @self: an #AppCapture
|
||||
* @address: (transfer none): the Hyprland window address as a string.
|
||||
* Accepts both "0x564f60266bd0" and "564f60266bd0" (with or without 0x).
|
||||
*
|
||||
* Captures one frame of the window. Every call eventually emits exactly one of:
|
||||
* - #AppCapture::frame-ready (GBytes, gint, gint, gint) on success
|
||||
* - #AppCapture::frame-failed (gchararray reason) on failure
|
||||
*
|
||||
* If the wlr handle for @address has not yet been announced/mapped (race
|
||||
* between Hyprland IPC and the wlr foreign-toplevel protocol), the request
|
||||
* is queued internally for up to ~1.5 s waiting for the mapping to arrive.
|
||||
*
|
||||
* Failure reasons emitted via frame-failed:
|
||||
* "no_export_manager" — wayland init never bound the export protocol
|
||||
* "no_handle_timeout" — wlr handle never appeared for this address
|
||||
* "frame_failed" — compositor refused the export (unmapped, hidden, …)
|
||||
* "buffer_invalid" — buffer event reported zero dimensions
|
||||
* "alloc_failed" — memfd_create / ftruncate / mmap failed
|
||||
* "internal" — defensive fallback (should never fire)
|
||||
*/
|
||||
void app_capture_capture_by_handle(AppCapture *self, const gchar *address);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -0,0 +1,110 @@
|
||||
project('app-capture', 'c',
|
||||
version: '1.0',
|
||||
default_options: ['c_std=c11', 'warning_level=1']
|
||||
)
|
||||
|
||||
# ─── Dependencies ─────────────────────────────────────────────────────────────
|
||||
wayland_client = dependency('wayland-client')
|
||||
wayland_scanner_dep = dependency('wayland-scanner', native: true)
|
||||
gobject = dependency('gobject-2.0')
|
||||
gtk3 = dependency('gtk+-3.0')
|
||||
gir = dependency('gobject-introspection-1.0', required: true)
|
||||
|
||||
wayland_scanner = find_program('wayland-scanner')
|
||||
|
||||
# ─── Protocol XML files ───────────────────────────────────────────────────────
|
||||
# All must exist under protocols/ in the source tree:
|
||||
#
|
||||
# hyprland-toplevel-export-v1.xml (hyprwm/hyprland-protocols)
|
||||
# wlr-foreign-toplevel-management-unstable-v1.xml (swaywm/wlr-protocols)
|
||||
# hyprland-toplevel-mapping-v1.xml (hyprwm/hyprland-protocols)
|
||||
# ext-foreign-toplevel-list-v1.xml (wayland-protocols staging)
|
||||
#
|
||||
hypr_export_xml = files('protocols/hyprland-toplevel-export-v1.xml')
|
||||
wlr_toplevel_xml = files('protocols/wlr-foreign-toplevel-management-unstable-v1.xml')
|
||||
hypr_mapping_xml = files('protocols/hyprland-toplevel-mapping-v1.xml')
|
||||
ext_toplevel_xml = files('protocols/ext-foreign-toplevel-list-v1.xml')
|
||||
|
||||
# ─── Generate C bindings ──────────────────────────────────────────────────────
|
||||
|
||||
# hyprland-toplevel-export: the capture protocol we call directly
|
||||
hypr_export_client_h = custom_target('hypr-export-client-h',
|
||||
input: hypr_export_xml, output: 'hyprland-toplevel-export-v1.h',
|
||||
command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
hypr_export_private_c = custom_target('hypr-export-private-c',
|
||||
input: hypr_export_xml, output: 'hyprland-toplevel-export-v1.c',
|
||||
command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
|
||||
# wlr-foreign-toplevel: used to enumerate live windows and pass handles to the
|
||||
# export protocol's v2 request; also provides zwlr_foreign_toplevel_handle_v1_interface
|
||||
# which hyprland-toplevel-export-v1.c references at link time.
|
||||
wlr_toplevel_client_h = custom_target('wlr-toplevel-client-h',
|
||||
input: wlr_toplevel_xml, output: 'wlr-foreign-toplevel-management-unstable-v1.h',
|
||||
command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
wlr_toplevel_private_c = custom_target('wlr-toplevel-private-c',
|
||||
input: wlr_toplevel_xml, output: 'wlr-foreign-toplevel-management-unstable-v1.c',
|
||||
command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
|
||||
# hyprland-toplevel-mapping: maps wlr handles → full 64-bit Hyprland addresses.
|
||||
# Its generated .c references ext_foreign_toplevel_handle_v1_interface (below).
|
||||
hypr_mapping_client_h = custom_target('hypr-mapping-client-h',
|
||||
input: hypr_mapping_xml, output: 'hyprland-toplevel-mapping-v1.h',
|
||||
command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
hypr_mapping_private_c = custom_target('hypr-mapping-private-c',
|
||||
input: hypr_mapping_xml, output: 'hyprland-toplevel-mapping-v1.c',
|
||||
command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
|
||||
# ext-foreign-toplevel-list: we never call it directly, but its private-code
|
||||
# provides ext_foreign_toplevel_handle_v1_interface which the mapping protocol's
|
||||
# generated .c references at link time.
|
||||
ext_toplevel_private_c = custom_target('ext-toplevel-private-c',
|
||||
input: ext_toplevel_xml, output: 'ext-foreign-toplevel-list-v1.c',
|
||||
command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
|
||||
# ─── Shared library ───────────────────────────────────────────────────────────
|
||||
lib = shared_library('appcapture',
|
||||
sources: [
|
||||
'app-capture.c',
|
||||
hypr_export_private_c,
|
||||
hypr_export_client_h,
|
||||
wlr_toplevel_private_c,
|
||||
wlr_toplevel_client_h,
|
||||
hypr_mapping_private_c,
|
||||
hypr_mapping_client_h,
|
||||
ext_toplevel_private_c, # link-time symbol only, no header needed in our code
|
||||
],
|
||||
include_directories: [
|
||||
include_directories('.'),
|
||||
],
|
||||
dependencies: [
|
||||
wayland_client,
|
||||
gobject,
|
||||
gtk3,
|
||||
],
|
||||
gnu_symbol_visibility: 'default',
|
||||
install: true,
|
||||
)
|
||||
|
||||
# ─── GObject Introspection ────────────────────────────────────────────────────
|
||||
gir_file = files('AppCapture-1.0.gir')
|
||||
|
||||
install_data(gir_file,
|
||||
install_dir: get_option('datadir') / 'gir-1.0',
|
||||
)
|
||||
|
||||
gir_compiler = find_program('g-ir-compiler')
|
||||
|
||||
custom_target('appcapture-typelib',
|
||||
input: gir_file,
|
||||
output: 'AppCapture-1.0.typelib',
|
||||
command: [gir_compiler, '--output=@OUTPUT@', '@INPUT@'],
|
||||
install: true,
|
||||
install_dir: get_option('libdir') / 'girepository-1.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 its 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.
|
||||
|
||||
To 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 frame. 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_capture_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 capturer 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>
|
||||
@@ -0,0 +1,228 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="hyprland_toplevel_export_v1">
|
||||
<copyright>
|
||||
Copyright © 2022 Vaxry
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
</copyright>
|
||||
|
||||
<description summary="capturing the contents of toplevel windows">
|
||||
This protocol allows clients to ask for exporting another toplevel's
|
||||
surface(s) to a buffer.
|
||||
|
||||
Particularly useful for sharing a single window.
|
||||
</description>
|
||||
|
||||
<interface name="hyprland_toplevel_export_manager_v1" version="2">
|
||||
<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_toplevel">
|
||||
<description summary="capture a toplevel">
|
||||
Capture the next frame of a toplevel. (window)
|
||||
|
||||
The captured frame will not contain any server-side decorations and will
|
||||
ignore the compositor-set geometry, like e.g. rounded corners.
|
||||
|
||||
It will contain all the subsurfaces and popups, however the latter will be clipped
|
||||
to the geometry of the base surface.
|
||||
|
||||
The handle parameter refers to the address of the window as seen in `hyprctl clients`.
|
||||
For example, for d161e7b0 it would be 3512854448.
|
||||
</description>
|
||||
<arg name="frame" type="new_id" interface="hyprland_toplevel_export_frame_v1"/>
|
||||
<arg name="overlay_cursor" type="int"
|
||||
summary="composite cursor onto the frame"/>
|
||||
<arg name="handle" type="uint" summary="the handle of the toplevel (window) to be captured"/>
|
||||
</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>
|
||||
|
||||
<!-- Version 2 -->
|
||||
<request name="capture_toplevel_with_wlr_toplevel_handle" since="2">
|
||||
<description summary="capture a toplevel">
|
||||
Same as capture_toplevel, but with a zwlr_foreign_toplevel_handle_v1 handle.
|
||||
</description>
|
||||
<arg name="frame" type="new_id" interface="hyprland_toplevel_export_frame_v1"/>
|
||||
<arg name="overlay_cursor" type="int"
|
||||
summary="composite cursor onto the frame"/>
|
||||
<arg name="handle" type="object" interface="zwlr_foreign_toplevel_handle_v1" allow-null="false" summary="the zwlr_foreign_toplevel_handle_v1 handle of the toplevel to be captured"/>
|
||||
</request>
|
||||
<!-- End Version 2 -->
|
||||
</interface>
|
||||
|
||||
<interface name="hyprland_toplevel_export_frame_v1" version="2">
|
||||
<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.
|
||||
|
||||
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 the
|
||||
correct size, see hyprland_toplevel_export_frame_v1.buffer and
|
||||
hyprland_toplevel_export_frame_v1.linux_dmabuf. The buffer needs to have a
|
||||
supported format.
|
||||
|
||||
If the frame is successfully copied, a "flags" and a "ready" event is
|
||||
sent. Otherwise, a "failed" event is sent.
|
||||
|
||||
This event will wait for appropriate damage to be copied, unless the ignore_damage
|
||||
arg is set to a non-zero value.
|
||||
</description>
|
||||
<arg name="buffer" type="object" interface="wl_buffer"/>
|
||||
<arg name="ignore_damage" type="int"/>
|
||||
</request>
|
||||
|
||||
<event name="damage">
|
||||
<description summary="carries the coordinates of the damaged region">
|
||||
This event is sent right before the ready event when ignore_damage was
|
||||
not set. It may be generated multiple times for each copy
|
||||
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
|
||||
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>
|
||||
|
||||
<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>
|
||||
|
||||
<event name="linux_dmabuf">
|
||||
<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">
|
||||
<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>
|
||||
@@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="hyprland_toplevel_mapping_v1">
|
||||
<copyright>
|
||||
Copyright © 2025 WhySoBad
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
</copyright>
|
||||
|
||||
<description summary="mapping of toplevels to windows">
|
||||
This protocol allows clients to retrieve the mapping of toplevels to hyprland window addresses.
|
||||
</description>
|
||||
|
||||
<interface name="hyprland_toplevel_mapping_manager_v1" version="1">
|
||||
<description summary="manager to request toplevel mappings">
|
||||
This object is a manager which offers requests to retrieve a window address
|
||||
for a toplevel.
|
||||
</description>
|
||||
|
||||
<request name="get_window_for_toplevel">
|
||||
<description summary="get window address for toplevel">
|
||||
Get the window address for a toplevel.
|
||||
</description>
|
||||
<arg
|
||||
name="handle"
|
||||
type="new_id"
|
||||
interface="hyprland_toplevel_window_mapping_handle_v1"
|
||||
/>
|
||||
<arg
|
||||
name="toplevel"
|
||||
type="object"
|
||||
interface="ext_foreign_toplevel_handle_v1"
|
||||
summary="toplevel to get the window address for"
|
||||
/>
|
||||
</request>
|
||||
|
||||
<request name="get_window_for_toplevel_wlr">
|
||||
<description summary="get window address for wlr toplevel">
|
||||
Get the window address for a wlr toplevel.
|
||||
</description>
|
||||
<arg
|
||||
name="handle"
|
||||
type="new_id"
|
||||
interface="hyprland_toplevel_window_mapping_handle_v1"
|
||||
/>
|
||||
<arg
|
||||
name="toplevel"
|
||||
type="object"
|
||||
interface="zwlr_foreign_toplevel_handle_v1"
|
||||
summary="wlr toplevel to get the window address for"
|
||||
/>
|
||||
</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="hyprland_toplevel_window_mapping_handle_v1" version="1">
|
||||
<description summary="toplevel to window mapping">
|
||||
This object represents a mapping of a (wlr) toplevel to a window address.
|
||||
|
||||
Once created, the `window_address` event will be sent containing the address of the window
|
||||
associated with the toplevel.
|
||||
Should the mapping fail, the `failed` event will be sent.
|
||||
</description>
|
||||
|
||||
<event name="window_address">
|
||||
<description summary="address of the window">
|
||||
The full 64bit window address. The `address` field contains the lower 32 bits whilst the
|
||||
`address_hi` contains the upper 32 bits
|
||||
</description>
|
||||
<arg
|
||||
name="address_hi"
|
||||
type="uint"
|
||||
summary="upper 32 bits of the window address"
|
||||
/>
|
||||
<arg
|
||||
name="address"
|
||||
type="uint"
|
||||
summary="lower 32 bits of the window address"
|
||||
/>
|
||||
</event>
|
||||
|
||||
<event name="failed">
|
||||
<description summary="mapping failed">
|
||||
The mapping of the toplevel to a window address failed. Most likely the window does not
|
||||
exist (anymore).
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy handle">
|
||||
Destroy the handle. This request can be sent at any time by the client.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
</protocol>
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="wlr_foreign_toplevel_management_unstable_v1">
|
||||
<copyright>
|
||||
Copyright © 2018 Ilia Bozhinov
|
||||
|
||||
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>
|
||||
|
||||
<interface name="zwlr_foreign_toplevel_manager_v1" version="3">
|
||||
<description summary="list and control opened apps">
|
||||
The purpose of this protocol is to enable the creation of taskbars
|
||||
and docks by providing them with a list of opened applications and
|
||||
letting them request certain actions on them, like maximizing, etc.
|
||||
|
||||
After a client binds the zwlr_foreign_toplevel_manager_v1, each opened
|
||||
toplevel window will be sent via the toplevel event
|
||||
</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 details of the toplevel(title, app_id, states, etc.) will
|
||||
be sent immediately after this event via the corresponding events in
|
||||
zwlr_foreign_toplevel_handle_v1.
|
||||
</description>
|
||||
<arg name="toplevel" type="new_id" interface="zwlr_foreign_toplevel_handle_v1"/>
|
||||
</event>
|
||||
|
||||
<request name="stop">
|
||||
<description summary="stop sending events">
|
||||
Indicates the client no longer wishes to receive events for new toplevels.
|
||||
However the compositor may emit further toplevel_created events, until
|
||||
the finished event is emitted.
|
||||
|
||||
The client must not send any more requests after this one.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="finished" type="destructor">
|
||||
<description summary="the compositor has finished with the toplevel manager">
|
||||
This event indicates that the compositor is done sending events to the
|
||||
zwlr_foreign_toplevel_manager_v1. The server will destroy the object
|
||||
immediately after sending this request, so it will become invalid and
|
||||
the client should free any resources associated with it.
|
||||
</description>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_foreign_toplevel_handle_v1" version="3">
|
||||
<description summary="an opened toplevel">
|
||||
A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
|
||||
window. Each app may have multiple opened toplevels.
|
||||
|
||||
Each toplevel has a list of outputs it is visible on, conveyed to the
|
||||
client with the output_enter and output_leave events.
|
||||
</description>
|
||||
|
||||
<event name="title">
|
||||
<description summary="title change">
|
||||
This event is emitted whenever the title of the toplevel changes.
|
||||
</description>
|
||||
<arg name="title" type="string"/>
|
||||
</event>
|
||||
|
||||
<event name="app_id">
|
||||
<description summary="app-id change">
|
||||
This event is emitted whenever the app-id of the toplevel changes.
|
||||
</description>
|
||||
<arg name="app_id" type="string"/>
|
||||
</event>
|
||||
|
||||
<event name="output_enter">
|
||||
<description summary="toplevel entered an output">
|
||||
This event is emitted whenever the toplevel becomes visible on
|
||||
the given output. A toplevel may be visible on multiple outputs.
|
||||
</description>
|
||||
<arg name="output" type="object" interface="wl_output"/>
|
||||
</event>
|
||||
|
||||
<event name="output_leave">
|
||||
<description summary="toplevel left an output">
|
||||
This event is emitted whenever the toplevel stops being visible on
|
||||
the given output. It is guaranteed that an entered-output event
|
||||
with the same output has been emitted before this event.
|
||||
</description>
|
||||
<arg name="output" type="object" interface="wl_output"/>
|
||||
</event>
|
||||
|
||||
<request name="set_maximized">
|
||||
<description summary="requests that the toplevel be maximized">
|
||||
Requests that the toplevel be maximized. If the maximized state actually
|
||||
changes, this will be indicated by the state event.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="unset_maximized">
|
||||
<description summary="requests that the toplevel be unmaximized">
|
||||
Requests that the toplevel be unmaximized. If the maximized state actually
|
||||
changes, this will be indicated by the state event.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="set_minimized">
|
||||
<description summary="requests that the toplevel be minimized">
|
||||
Requests that the toplevel be minimized. If the minimized state actually
|
||||
changes, this will be indicated by the state event.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="unset_minimized">
|
||||
<description summary="requests that the toplevel be unminimized">
|
||||
Requests that the toplevel be unminimized. If the minimized state actually
|
||||
changes, this will be indicated by the state event.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="activate">
|
||||
<description summary="activate the toplevel">
|
||||
Request that this toplevel be activated on the given seat.
|
||||
There is no guarantee the toplevel will be actually activated.
|
||||
</description>
|
||||
<arg name="seat" type="object" interface="wl_seat"/>
|
||||
</request>
|
||||
|
||||
<enum name="state">
|
||||
<description summary="types of states on the toplevel">
|
||||
The different states that a toplevel can have. These have the same meaning
|
||||
as the states with the same names defined in xdg-toplevel
|
||||
</description>
|
||||
|
||||
<entry name="maximized" value="0" summary="the toplevel is maximized"/>
|
||||
<entry name="minimized" value="1" summary="the toplevel is minimized"/>
|
||||
<entry name="activated" value="2" summary="the toplevel is active"/>
|
||||
<entry name="fullscreen" value="3" summary="the toplevel is fullscreen" since="2"/>
|
||||
</enum>
|
||||
|
||||
<event name="state">
|
||||
<description summary="the toplevel state changed">
|
||||
This event is emitted immediately after the zlw_foreign_toplevel_handle_v1
|
||||
is created and each time the toplevel state changes, either because of a
|
||||
compositor action or because of a request in this protocol.
|
||||
</description>
|
||||
|
||||
<arg name="state" type="array"/>
|
||||
</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 zwlr_foreign_toplevel_handle_v1 properties
|
||||
to be seen as atomic, even if they happen via multiple events.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<request name="close">
|
||||
<description summary="request that the toplevel be closed">
|
||||
Send a request to the toplevel to close itself. The compositor would
|
||||
typically use a shell-specific method to carry out this request, for
|
||||
example by sending the xdg_toplevel.close event. However, this gives
|
||||
no guarantees the toplevel will actually be destroyed. If and when
|
||||
this happens, the zwlr_foreign_toplevel_handle_v1.closed event will
|
||||
be emitted.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="set_rectangle">
|
||||
<description summary="the rectangle which represents the toplevel">
|
||||
The rectangle of the surface specified in this request corresponds to
|
||||
the place where the app using this protocol represents the given toplevel.
|
||||
It can be used by the compositor as a hint for some operations, e.g
|
||||
minimizing. The client is however not required to set this, in which
|
||||
case the compositor is free to decide some default value.
|
||||
|
||||
If the client specifies more than one rectangle, only the last one is
|
||||
considered.
|
||||
|
||||
The dimensions are given in surface-local coordinates.
|
||||
Setting width=height=0 removes the already-set rectangle.
|
||||
</description>
|
||||
|
||||
<arg name="surface" type="object" interface="wl_surface"/>
|
||||
<arg name="x" type="int"/>
|
||||
<arg name="y" type="int"/>
|
||||
<arg name="width" type="int"/>
|
||||
<arg name="height" type="int"/>
|
||||
</request>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="invalid_rectangle" value="0"
|
||||
summary="the provided rectangle is invalid"/>
|
||||
</enum>
|
||||
|
||||
<event name="closed">
|
||||
<description summary="this toplevel has been destroyed">
|
||||
This event means the toplevel has been destroyed. It is guaranteed there
|
||||
won't be any more events for this zwlr_foreign_toplevel_handle_v1. The
|
||||
toplevel itself becomes inert so any requests will be ignored except the
|
||||
destroy request.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the zwlr_foreign_toplevel_handle_v1 object">
|
||||
Destroys the zwlr_foreign_toplevel_handle_v1 object.
|
||||
|
||||
This request should be called either when the client does not want to
|
||||
use the toplevel anymore or after the closed event to finalize the
|
||||
destruction of the object.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<!-- Version 2 additions -->
|
||||
|
||||
<request name="set_fullscreen" since="2">
|
||||
<description summary="request that the toplevel be fullscreened">
|
||||
Requests that the toplevel be fullscreened on the given output. If the
|
||||
fullscreen state and/or the outputs the toplevel is visible on actually
|
||||
change, this will be indicated by the state and output_enter/leave
|
||||
events.
|
||||
|
||||
The output parameter is only a hint to the compositor. Also, if output
|
||||
is NULL, the compositor should decide which output the toplevel will be
|
||||
fullscreened on, if at all.
|
||||
</description>
|
||||
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
|
||||
</request>
|
||||
|
||||
<request name="unset_fullscreen" since="2">
|
||||
<description summary="request that the toplevel be unfullscreened">
|
||||
Requests that the toplevel be unfullscreened. If the fullscreen state
|
||||
actually changes, this will be indicated by the state event.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<!-- Version 3 additions -->
|
||||
|
||||
<event name="parent" since="3">
|
||||
<description summary="parent change">
|
||||
This event is emitted whenever the parent of the toplevel changes.
|
||||
|
||||
No event is emitted when the parent handle is destroyed by the client.
|
||||
</description>
|
||||
<arg name="parent" type="object" interface="zwlr_foreign_toplevel_handle_v1" allow-null="true"/>
|
||||
</event>
|
||||
</interface>
|
||||
</protocol>
|
||||
Reference in new issue
Block a user