mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-02-15 04:28:00 -05:00
xdg-cutouts-v1: New protocol implementation
See https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/372 The compositor implemention can connect to the `new_cutouts` signal which is invoked whenever a client binds the protocol. It then listens for the `send_cutouts` signal on the received `cutouts` object and when received sends the cutout information via any number of `wlr_xdg_cutouts_v1_send_{cutout,corner}` calls followed by a `wlr_xdg_cutouts_v1_send_cutouts_done` call. The ids used in these calls are up to the compositor implementation. The `unhandled_update` signal is invoked when the client reports any sent ids as unhandled. Signed-off-by: Guido Günther <agx@sigxcpu.org>
This commit is contained in:
parent
a962d58727
commit
dc597cec78
5 changed files with 693 additions and 0 deletions
82
include/wlr/types/wlr_xdg_cutouts_v1.h
Normal file
82
include/wlr/types/wlr_xdg_cutouts_v1.h
Normal 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
|
||||
|
|
@ -76,6 +76,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
227
protocol/xdg-cutouts-v1.xml
Normal 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>
|
||||
|
|
@ -97,6 +97,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
380
types/wlr_xdg_cutouts_v1.c
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue