Merge branch 'xdg-cutouts-v1-proto' into 'master'

Draft: xdg-cutouts-v1: New protocol implementation

See merge request wlroots/wlroots!5217
This commit is contained in:
Guido Günther 2026-02-03 18:21:43 +00:00
commit 13b496f728
5 changed files with 693 additions and 0 deletions

View file

@ -0,0 +1,82 @@
#ifndef WLR_TYPES_WLR_XDG_CUTOUTS_V1
#define WLR_TYPES_WLR_XDG_CUTOUTS_V1
#include <wayland-server-core.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/edges.h>
enum wlr_cutouts_type {
WLR_CUTOUTS_TYPE_CUTOUT = 0,
WLR_CUTOUTS_TYPE_NOTCH = 1 << 0,
WLR_CUTOUTS_TYPE_WATERFALL = 1 << 1,
};
struct wlr_xdg_cutouts_manager_v1 {
struct wl_global *global;
struct wl_list cutouts; // wlr_xdg_cutouts_v1.link
struct {
struct wl_signal new_cutouts; // struct wlr_xdg_cutouts
struct wl_signal destroy;
} events;
void *data;
struct {
uint32_t next_id;
struct wl_listener display_destroy;
} WLR_PRIVATE;
};
struct wlr_xdg_cutouts_v1_configure {
struct wl_list link; // wlr_xdg_cutouts.configure_list
struct wlr_surface_configure *surface_configure;
// Ids valid in this configure sequence
struct wl_array valid_ids;
};
struct wlr_xdg_cutouts_v1_state {
struct wl_array unhandled;
};
/**
* Cutouts interface.
*
* Emits `send_coutouts` when cutout information should be sent for the associated toplevel.
* Emits `unhandled_updated` when the list of unhandled ids got updated.
*/
struct wlr_xdg_cutouts_v1 {
struct wl_resource *resource;
struct wlr_xdg_toplevel *toplevel;
struct wlr_xdg_cutouts_manager_v1 *manager;
struct wl_list link; // wlr_xdg_cutouts_manager_v1.link
struct wlr_xdg_cutouts_v1_state current, pending;
struct {
struct wl_signal destroy;
struct wl_signal send_cutouts;
struct wl_signal unhandled_updated;
} events;
void *data;
struct {
// ids sent in the current configure sequence
struct wl_list configure_list; // wlr_xdg_cutouts_v1_configure.link
struct wl_array sent_ids; // uint32_t
struct wl_listener toplevel_destroy;
struct wl_listener surface_configure;
struct wl_listener surface_ack_configure;
} WLR_PRIVATE;
};
struct wlr_xdg_cutouts_manager_v1 *wlr_xdg_cutouts_manager_v1_create(struct wl_display *display);
void wlr_xdg_cutouts_v1_send_cutout(struct wlr_xdg_cutouts_v1 *cutouts, const struct wlr_box *box,
enum wlr_cutouts_type type, uint32_t id);
void wlr_xdg_cutouts_v1_send_corner(struct wlr_xdg_cutouts_v1 *cutouts, enum wlr_edges position,
uint32_t radius, uint32_t id);
void wlr_xdg_cutouts_v1_send_cutouts_done(struct wlr_xdg_cutouts_v1 *cutouts);
#endif

View file

@ -77,6 +77,9 @@ protocols = {
'wlr-output-power-management-unstable-v1': 'wlr-output-power-management-unstable-v1.xml',
'wlr-screencopy-unstable-v1': 'wlr-screencopy-unstable-v1.xml',
'wlr-virtual-pointer-unstable-v1': 'wlr-virtual-pointer-unstable-v1.xml',
# Until it gets merge in wayland protocols:
'xdg-cutouts-v1': 'xdg-cutouts-v1.xml',
}
protocols_code = {}

227
protocol/xdg-cutouts-v1.xml Normal file
View file

@ -0,0 +1,227 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="xdg_cutouts_unstable_v1">
<copyright>
Copyright © 2025 Phosh.mobi e.V.
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="Protocol to describe cut out surface regions">
This protocol describes the areas of a toplevel that are cut out
of the available surface area by hardware elements present in the
physical display. This allows clients to avoid placing user interface
elements in those areas.
Typical cutout areas are notches (i.e. embedding a camera) or
"waterfall" display edges. In the case of a notch the compositor
would usually supply the bounding box of the notch or an
approximation by multiple rectangles. Thus a single physical
element in the display can correspond to multiple cutout events in
the protocol.
The protocol currently supports xdg_toplevel surfaces but is meant
to be extended to other surfaces (like layer surfaces) in the
future.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible
changes may be added together with the corresponding interface
version bump. Backward incompatible changes can only be done by
creating a new major version of the extension.
</description>
<interface name="xdg_cutouts_manager_v1" version="1">
<description summary="Display cutouts area manager">
This interface allows a compositor to announce support for
supplying cutout information to the client.
</description>
<enum name="error">
<entry name="invalid_role" value="0" summary="given wl_surface has incorrect role"/>
<entry name="defunct_cutouts_object" value="1"
summary="wl_surface or surface role was destroyed before the cutouts object"/>
</enum>
<request name="destroy" type="destructor">
<description summary="destroy the xdg_cutouts_manager object">
Using this request a client can tell the server that it is not
going to use the xdg_cutouts_manger object anymore.
Any objects already created through this instance are not affected.
</description>
</request>
<request name="get_cutouts">
<description summary="create a cutout notifier from a xdg toplevel">
This creates a new xdg_cutouts object for the given
surface. The role of the surface must be xdg_toplevel
otherwise an invalid_role protocol error will be raised. Later
versions of this protocol might allow for other surface roles.
</description>
<arg name="id" type="new_id" interface="xdg_cutouts_v1"/>
<arg name="surface" type="object" interface="wl_surface"/>
</request>
</interface>
<interface name="xdg_cutouts_v1" version="1">
<description summary="cutout regions information">
An xdg_cutouts describes the areas currently "cut out" of a
toplevel.
Each cutout event carries an id that identifies the
physical element. If the compositor describes an element by
multiple cutout events these should use the same element
id. A typical example is a curved notch that is approximated
by several cutout_box elements. Using the same element
id allows the client to identify that these belong to the
same physical object. Ids are only valid during one configure
sequence. No guarantee is given that the same id identifies
the same element in different configure sequences.
Typically compositors would only send cutout information when
the toplevel enters fullscreen or maxmized state (as specified
in the xdg_shell protocol).
The xdg_cutouts_v1 object must be destroyed before its
underlying xdg_toplevel and wl_surface. Otherwise the
defunct_cutouts_object protocol error will be send.
</description>
<enum name="type">
<description summary="Cutout type">
These values indicate the type of cutout. The information is
meant to help clients to decide whether they can possibly
ignore the element.
</description>
<entry name="cutout" value="0">
<description summary="A generic cutout">
This element type can be used by the compositor if it
doesn't want to provide a more specific type.
</description>
</entry>
<entry name="notch" value="1">
<description summary="Small functional cutout area">
A functional, irregular shape on one of the device's
edges. It often contains a camera.
</description>
</entry>
<entry name="waterfall" value="2">
<description summary="A curved display edge">
A curved display edge intended to make the device appear
like not having any bezel.
</description>
</entry>
</enum>
<enum name="corner_position">
<description summary="Corner position">
The position of a corner on a surface
</description>
<entry name="top_left" value="0"/>
<entry name="top_right" value="1"/>
<entry name="bottom_right" value="2"/>
<entry name="bottom_left" value="3"/>
</enum>
<enum name="error">
<entry name="invalid_element_id" value="0"
summary="Invalid element id in a set_unhandled request"/>
</enum>
<request name="destroy" type="destructor">
<description summary="destroy the xdg_cutouts object">
Using this request a client can tell the server that it is not
going to use the xdg_cutouts object anymore.
</description>
</request>
<event name="cutout_box">
<description summary="A rectangular cutout region">
The cutout_box event describes a rectangular cutout area in
surface-local coordinates.
This can be an approximation of e.g. a circular camera notch.
</description>
<arg name="x" type="int"
summary="x coordinate of the box's top left corner"/>
<arg name="y" type="int"
summary="y coordinate of the box's top left corner"/>
<arg name="width" type="int"/>
<arg name="height" type="int"/>
<arg name="type" type="uint" enum="type" summary="The type of cutout"/>
<arg name="id" type="uint" summary="An identifier identifying the physical element"/>
</event>
<event name="cutout_corner">
<description summary="A cutout corner">
The cutout_corner event describes a rounded corner in
surface-local coordinates. The area towards the screen edge is
the cutout corner part.
</description>
<arg name="position" type="uint" enum="corner_position" summary="The position of the described corner"/>
<arg name="radius" type="uint" summary="The corner's radius"/>
<arg name="id" type="uint" summary="An identifier identifying the physical element"/>
</event>
<event name="configure">
<description summary="notify cutout changes">
The configure event marks the end of a configure sequence. A
configure sequence is a set of zero or more cutout events and
the final xdg_cutout.configure event.
In the case of a xdg_toplevel clients should arrange their
surface for the new cutouts, and then send an
xdg_surface.ack_configure request at some point before
committing the new surface. See xdg_surface.configure and
xdg_surface.ack_configure in the xdg_shell protocol for
details.
If the cutout sequence consists of only a configure event and
contains no cutout events this indicates that the surface
isn't overlapping with any cutouts.
If the client receives multiple configure events before it can
respond to one, it is free to discard all but the last event
it received.
</description>
</event>
<request name="set_unhandled">
<description summary="Notify about unhandled cutouts">
If a client doesn't handle one or more cutouts in the to be
acked sequence, it can add their element's id to the
unhandled array. The compositor might then try to reposition
the surface in a way that avoids these elements in a future
configure sequence.
The request (if used) must be sent before acking the configure
sequence. State set with this request is double-buffered. It
will get applied on the next ack_configure and stay valid
until the next configure event.
</description>
<arg name="unhandled" type="array" summary="array of unhandled element ids"/>
</request>
</interface>
</protocol>

View file

@ -98,6 +98,7 @@ wlr_files += files(
'wlr_virtual_pointer_v1.c',
'wlr_xcursor_manager.c',
'wlr_xdg_activation_v1.c',
'wlr_xdg_cutouts_v1.c',
'wlr_xdg_decoration_v1.c',
'wlr_xdg_dialog_v1.c',
'wlr_xdg_foreign_v1.c',

380
types/wlr_xdg_cutouts_v1.c Normal file
View file

@ -0,0 +1,380 @@
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <wlr/types/wlr_xdg_cutouts_v1.h>
#include <wlr/util/log.h>
#include "xdg-cutouts-v1-protocol.h"
#define CUTOUTS_MANAGER_VERSION 1
static const struct xdg_cutouts_v1_interface cutouts_impl;
static struct wlr_xdg_cutouts_v1 *cutouts_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource, &xdg_cutouts_v1_interface, &cutouts_impl));
return wl_resource_get_user_data(resource);
}
static void cutouts_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void cutouts_handle_set_unhandled(struct wl_client *client,
struct wl_resource *resource,
struct wl_array *unhandled) {
struct wlr_xdg_cutouts_v1 *cutouts =
cutouts_from_resource(resource);
wl_array_release(&cutouts->pending.unhandled);
wl_array_init(&cutouts->pending.unhandled);
wl_array_copy(&cutouts->pending.unhandled, unhandled);
}
static const struct xdg_cutouts_v1_interface cutouts_impl = {
.destroy = cutouts_handle_destroy,
.set_unhandled = cutouts_handle_set_unhandled,
};
static void
add_sent_id(struct wlr_xdg_cutouts_v1 *cutouts, uint32_t id)
{
uint32_t *new_id = wl_array_add (&cutouts->sent_ids, sizeof(uint32_t));
*new_id = id;
}
static void
array_move(struct wl_array *dst, struct wl_array *src)
{
assert(dst->size == 0);
wl_array_copy(dst, src);
wl_array_release (src);
wl_array_init(src);
}
static bool
array_equal(struct wl_array *a1, struct wl_array *a2)
{
if (a1->size != a2->size)
return false;
return memcmp(a1, a2, a1->size) == 0;
}
static bool
array_contains(struct wl_array *haystack, uint32_t needle)
{
uint32_t *id;
wl_array_for_each(id, haystack) {
if (needle == *id)
return true;
}
return false;
}
void wlr_xdg_cutouts_v1_send_cutout(struct wlr_xdg_cutouts_v1 *cutouts, const struct wlr_box *box,
enum wlr_cutouts_type type, uint32_t id) {
enum xdg_cutouts_v1_type cutouts_v1_type;
assert(!wl_list_empty(&cutouts->configure_list));
switch (type) {
case WLR_CUTOUTS_TYPE_CUTOUT:
cutouts_v1_type = XDG_CUTOUTS_V1_TYPE_CUTOUT;
break;
case WLR_CUTOUTS_TYPE_NOTCH:
cutouts_v1_type = XDG_CUTOUTS_V1_TYPE_NOTCH;
break;
case WLR_CUTOUTS_TYPE_WATERFALL:
cutouts_v1_type = XDG_CUTOUTS_V1_TYPE_WATERFALL;
break;
default:
assert(false);
}
xdg_cutouts_v1_send_cutout_box (cutouts->resource, box->x, box->y, box->width, box->height,
cutouts_v1_type, id);
add_sent_id (cutouts, id);
}
void wlr_xdg_cutouts_v1_send_corner(struct wlr_xdg_cutouts_v1 *cutouts,
enum wlr_edges position, uint32_t radius, uint32_t id) {
enum xdg_cutouts_v1_corner_position corner;
assert(!wl_list_empty(&cutouts->configure_list));
switch ((uint32_t)position) {
case WLR_EDGE_LEFT | WLR_EDGE_TOP:
corner = XDG_CUTOUTS_V1_CORNER_POSITION_TOP_LEFT;
break;
case WLR_EDGE_RIGHT | WLR_EDGE_TOP:
corner = XDG_CUTOUTS_V1_CORNER_POSITION_TOP_RIGHT;
break;
case WLR_EDGE_RIGHT | WLR_EDGE_BOTTOM:
corner = XDG_CUTOUTS_V1_CORNER_POSITION_BOTTOM_RIGHT;
break;
case WLR_EDGE_LEFT | WLR_EDGE_BOTTOM:
corner = XDG_CUTOUTS_V1_CORNER_POSITION_BOTTOM_LEFT;
break;
default:
assert(false);
}
xdg_cutouts_v1_send_cutout_corner (cutouts->resource, corner, radius, id);
add_sent_id (cutouts, id);
}
void wlr_xdg_cutouts_v1_send_cutouts_done(struct wlr_xdg_cutouts_v1 *cutouts) {
// The xdg_surface.configure from the current configure sequence needs to be present and
// the first element in the list
assert(!wl_list_empty(&cutouts->configure_list));
struct wlr_xdg_cutouts_v1_configure *configure =
wl_container_of((&cutouts->configure_list)->next, configure, link);
array_move(&configure->valid_ids, &cutouts->sent_ids);
xdg_cutouts_v1_send_configure (cutouts->resource);
}
static void cutouts_handle_resource_destroy(struct wl_resource *resource) {
struct wlr_xdg_cutouts_v1 *cutouts = cutouts_from_resource(resource);
wl_signal_emit_mutable(&cutouts->events.destroy, NULL);
assert(wl_list_empty(&cutouts->events.destroy.listener_list));
assert(wl_list_empty(&cutouts->events.unhandled_updated.listener_list));
assert(wl_list_empty(&cutouts->events.send_cutouts.listener_list));
wl_list_remove(&cutouts->toplevel_destroy.link);
wl_list_remove(&cutouts->surface_configure.link);
wl_list_remove(&cutouts->surface_ack_configure.link);
struct wlr_xdg_cutouts_v1_configure *configure, *tmp;
wl_list_for_each_safe(configure, tmp, &cutouts->configure_list, link) {
free(configure);
}
wl_list_remove(&cutouts->link);
wl_array_release(&cutouts->sent_ids);
wl_array_release(&cutouts->pending.unhandled);
wl_array_release(&cutouts->current.unhandled);
free(cutouts);
}
static void cutouts_handle_toplevel_destroy(
struct wl_listener *listener, void *data) {
struct wlr_xdg_cutouts_v1 *cutouts =
wl_container_of(listener, cutouts, toplevel_destroy);
wl_resource_post_error(cutouts->resource,
XDG_CUTOUTS_MANAGER_V1_ERROR_DEFUNCT_CUTOUTS_OBJECT,
"xdg_toplevel destroyed before xdg_cutouts");
wl_resource_destroy(cutouts->resource);
}
static void cutouts_handle_surface_configure(
struct wl_listener *listener, void *data) {
struct wlr_xdg_cutouts_v1 *cutouts =
wl_container_of(listener, cutouts, surface_configure);
struct wlr_surface_configure *surface_configure = data;
struct wlr_xdg_cutouts_v1_configure *configure = calloc(1, sizeof(*configure));
if (configure == NULL) {
return;
}
wl_array_init (&configure->valid_ids);
configure->surface_configure = surface_configure;
wl_list_insert(cutouts->configure_list.prev, &configure->link);
wl_signal_emit_mutable(&cutouts->events.send_cutouts, NULL);
}
static void
cutouts_configure_destroy(struct wlr_xdg_cutouts_v1_configure *configure) {
wl_list_remove(&configure->link);
wl_array_release (&configure->valid_ids);
free(configure);
}
static void cutouts_handle_surface_ack_configure(
struct wl_listener *listener, void *data) {
struct wlr_xdg_cutouts_v1 *cutouts =
wl_container_of(listener, cutouts, surface_ack_configure);
struct wlr_surface_configure *surface_configure = data;
// First find the ack'ed configure
bool found = false;
struct wlr_xdg_cutouts_v1_configure *configure, *tmp;
wl_list_for_each(configure, &cutouts->configure_list, link) {
if (configure->surface_configure == surface_configure) {
found = true;
break;
}
}
if (!found) {
return;
}
bool needs_update = !array_equal(&cutouts->current.unhandled, &cutouts->pending.unhandled);
if (needs_update) {
uint32_t *id;
wl_array_for_each (id, &cutouts->pending.unhandled) {
if (!array_contains(&configure->valid_ids, *id)) {
wl_resource_post_error(cutouts->resource,
XDG_CUTOUTS_V1_ERROR_INVALID_ELEMENT_ID,
"Invalid element id %d", *id);
}
}
}
// Then remove old configures from the list
wl_list_for_each_safe(configure, tmp, &cutouts->configure_list, link) {
if (configure->surface_configure == surface_configure) {
break;
}
cutouts_configure_destroy (configure);
}
if (needs_update) {
wl_array_release(&cutouts->current.unhandled);
wl_array_init(&cutouts->current.unhandled);
array_move(&cutouts->current.unhandled, &cutouts->pending.unhandled);
wl_signal_emit_mutable(&cutouts->events.unhandled_updated, NULL);
}
cutouts_configure_destroy (configure);
}
static const struct xdg_cutouts_manager_v1_interface cutouts_manager_impl;
static struct wlr_xdg_cutouts_manager_v1 *
cutouts_manager_from_resource(struct wl_resource *resource) {
assert(wl_resource_instance_of(resource, &xdg_cutouts_manager_v1_interface,
&cutouts_manager_impl));
return wl_resource_get_user_data(resource);
}
static void cutouts_manager_handle_destroy(
struct wl_client *client, struct wl_resource *manager_resource) {
wl_resource_destroy(manager_resource);
}
static void cutouts_manager_handle_get_xdg_cutouts(
struct wl_client *client, struct wl_resource *manager_resource,
uint32_t id, struct wl_resource *surface_resource) {
struct wlr_xdg_cutouts_manager_v1 *manager = cutouts_manager_from_resource(manager_resource);
struct wlr_surface *surface = wlr_surface_from_resource (surface_resource);
struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_try_from_wlr_surface (surface);
if (!toplevel) {
wl_resource_post_error(manager_resource,
XDG_CUTOUTS_MANAGER_V1_ERROR_INVALID_ROLE,
"xdg_cutouts_v1 must be a xdg_toplevel");
return;
}
struct wlr_xdg_cutouts_v1 *cutouts = calloc(1, sizeof(*cutouts));
if (cutouts == NULL) {
wl_client_post_no_memory(client);
return;
}
cutouts->manager = manager;
cutouts->toplevel = toplevel;
uint32_t version = wl_resource_get_version(manager_resource);
cutouts->resource = wl_resource_create(client, &xdg_cutouts_v1_interface, version, id);
if (cutouts->resource == NULL) {
free(cutouts);
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(cutouts->resource, &cutouts_impl, cutouts,
cutouts_handle_resource_destroy);
wlr_log(WLR_DEBUG, "new xdg_cutouts %p (res %p)", cutouts,
cutouts->resource);
wl_list_init(&cutouts->configure_list);
wl_array_init(&cutouts->pending.unhandled);
wl_array_init(&cutouts->current.unhandled);
wl_array_init(&cutouts->sent_ids);
wl_signal_init(&cutouts->events.send_cutouts);
wl_signal_init(&cutouts->events.unhandled_updated);
wl_signal_init(&cutouts->events.destroy);
wl_signal_add(&toplevel->events.destroy, &cutouts->toplevel_destroy);
cutouts->toplevel_destroy.notify = cutouts_handle_toplevel_destroy;
wl_signal_add(&toplevel->base->events.configure, &cutouts->surface_configure);
cutouts->surface_configure.notify = cutouts_handle_surface_configure;
wl_signal_add(&toplevel->base->events.ack_configure, &cutouts->surface_ack_configure);
cutouts->surface_ack_configure.notify = cutouts_handle_surface_ack_configure;
wl_list_insert(&manager->cutouts, &cutouts->link);
wl_signal_emit_mutable(&manager->events.new_cutouts, cutouts);
// Schedule a configure to emit cutouts information
wlr_xdg_surface_schedule_configure(cutouts->toplevel->base);
}
static const struct xdg_cutouts_manager_v1_interface cutouts_manager_impl = {
.destroy = cutouts_manager_handle_destroy,
.get_cutouts = cutouts_manager_handle_get_xdg_cutouts,
};
static void cutouts_manager_bind(struct wl_client *client, void *data,
uint32_t version, uint32_t id) {
struct wlr_xdg_cutouts_manager_v1 *manager = data;
struct wl_resource *resource = wl_resource_create(client,
&xdg_cutouts_manager_v1_interface, version, id);
if (resource == NULL) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &cutouts_manager_impl,
manager, NULL);
}
static void handle_display_destroy(struct wl_listener *listener, void *data) {
struct wlr_xdg_cutouts_manager_v1 *manager =
wl_container_of(listener, manager, display_destroy);
wl_signal_emit_mutable(&manager->events.destroy, NULL);
assert(wl_list_empty(&manager->events.new_cutouts.listener_list));
assert(wl_list_empty(&manager->events.destroy.listener_list));
wl_list_remove(&manager->display_destroy.link);
wl_global_destroy(manager->global);
free(manager);
}
struct wlr_xdg_cutouts_manager_v1 *
wlr_xdg_cutouts_manager_v1_create(struct wl_display *display) {
struct wlr_xdg_cutouts_manager_v1 *manager = calloc(1, sizeof(*manager));
if (manager == NULL) {
return NULL;
}
manager->global = wl_global_create(display, &xdg_cutouts_manager_v1_interface,
CUTOUTS_MANAGER_VERSION, manager, cutouts_manager_bind);
if (manager->global == NULL) {
free(manager);
return NULL;
}
wl_list_init(&manager->cutouts);
manager->next_id = 1;
wl_signal_init(&manager->events.new_cutouts);
wl_signal_init(&manager->events.destroy);
manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &manager->display_destroy);
return manager;
}