Merge branch 'feature/wlr_foreign_new_states' into 'master'

Draft: wlr-foreign-toplevel: add 4 new states

See merge request wlroots/wlroots!4942
This commit is contained in:
Consolatis 2026-01-22 20:30:14 +01:00
commit 3c0662933b
3 changed files with 239 additions and 13 deletions

View file

@ -30,10 +30,14 @@ struct wlr_foreign_toplevel_manager_v1 {
};
enum wlr_foreign_toplevel_handle_v1_state {
WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED = (1 << 0),
WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED = (1 << 1),
WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED = (1 << 2),
WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN = (1 << 3),
WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED = (1 << 0),
WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED = (1 << 1),
WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED = (1 << 2),
WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN = (1 << 3),
WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ALWAYS_ON_TOP = (1 << 4),
WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ON_ALL_WORKSPACES = (1 << 5),
WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ROLLED_UP = (1 << 6),
WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_URGENT = (1 << 7),
};
struct wlr_foreign_toplevel_handle_v1_output {
@ -69,6 +73,9 @@ struct wlr_foreign_toplevel_handle_v1 {
// struct wlr_foreign_toplevel_handle_v1_fullscreen_event
struct wl_signal request_fullscreen;
struct wl_signal request_close;
struct wl_signal request_always_on_top;
struct wl_signal request_on_all_workspaces;
struct wl_signal request_roll_up;
// struct wlr_foreign_toplevel_handle_v1_set_rectangle_event
struct wl_signal set_rectangle;
@ -99,6 +106,21 @@ struct wlr_foreign_toplevel_handle_v1_fullscreen_event {
struct wlr_output *output;
};
struct wlr_foreign_toplevel_handle_v1_always_on_top_event {
struct wlr_foreign_toplevel_handle_v1 *toplevel;
bool always_on_top;
};
struct wlr_foreign_toplevel_handle_v1_on_all_workspaces_event {
struct wlr_foreign_toplevel_handle_v1 *toplevel;
bool on_all_workspaces;
};
struct wlr_foreign_toplevel_handle_v1_roll_up_event {
struct wlr_foreign_toplevel_handle_v1 *toplevel;
bool roll_up;
};
struct wlr_foreign_toplevel_handle_v1_set_rectangle_event {
struct wlr_foreign_toplevel_handle_v1 *toplevel;
struct wlr_surface *surface;
@ -139,6 +161,14 @@ void wlr_foreign_toplevel_handle_v1_set_activated(
struct wlr_foreign_toplevel_handle_v1 *toplevel, bool activated);
void wlr_foreign_toplevel_handle_v1_set_fullscreen(
struct wlr_foreign_toplevel_handle_v1* toplevel, bool fullscreen);
void wlr_foreign_toplevel_handle_v1_set_always_on_top(
struct wlr_foreign_toplevel_handle_v1* toplevel, bool always_on_top);
void wlr_foreign_toplevel_handle_v1_set_on_all_workspaces(
struct wlr_foreign_toplevel_handle_v1* toplevel, bool on_all_workspaces);
void wlr_foreign_toplevel_handle_v1_set_rolled_up(
struct wlr_foreign_toplevel_handle_v1* toplevel, bool rolled_up);
void wlr_foreign_toplevel_handle_v1_set_urgent(
struct wlr_foreign_toplevel_handle_v1* toplevel, bool urgent);
/**
* Set the parent of a toplevel. If the parent changed from its previous

View file

@ -25,7 +25,7 @@
THIS SOFTWARE.
</copyright>
<interface name="zwlr_foreign_toplevel_manager_v1" version="3">
<interface name="zwlr_foreign_toplevel_manager_v1" version="4">
<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
@ -68,7 +68,7 @@
</event>
</interface>
<interface name="zwlr_foreign_toplevel_handle_v1" version="3">
<interface name="zwlr_foreign_toplevel_handle_v1" version="4">
<description summary="an opened toplevel">
A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
window. Each app may have multiple opened toplevels.
@ -146,14 +146,45 @@
<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
The different states that a toplevel can have.
A compositor may not support all states in which case they will never be set.
</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"/>
<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"/>
<entry name="always_on_top" value="4" since="4">
<description summary="always on top of the stacking order">
The exact policy in regards to what "stacking order" means is compositor policy.
In a auto tiling compositor for example this could mean that toplevels with this
state set are always positioned in the most prominent tile. In a more traditional
stacking compositor this usually means that applicatios with this state set are
rendered top-most on a given workspace. In X11 terms this is known as 'above'.
</description>
</entry>
<entry name="on_all_workspaces" value="5" since="4">
<description summary="called 'sticky' in X11 terms">
Toplevels with this state set should show up on all workspaces. What exactly this
means is compositor policy. For example in a tag based compositor a toplevel with
this state set could show up on any tag. In X11 terms this is known as 'sticky'.
</description>
</entry>
<entry name="rolled_up" value="6" since="4">
<description summary="called 'shaded' in X11 terms">
Toplevel with this state set are "rolled up" so that only the titlebar is visible.
In X11 terms this is known as 'shade'.
</description>
</entry>
<entry name="urgent" value="7" since="4">
<description summary="the toplevel is requesting attention">
Toplevel with this state set are requesting attention. This state can only be set
by the compositor. The circumstances when this state is enabled and disabled is
compositor policy. A compositor may for example use the xdg-activation protocol
to treat activation requests with invalid tokens as a request for urgency and
disable this state again once the toplevel receives focus.
</description>
</entry>
</enum>
<event name="state">
@ -266,5 +297,50 @@
</description>
<arg name="parent" type="object" interface="zwlr_foreign_toplevel_handle_v1" allow-null="true"/>
</event>
<!-- Version 4 additions -->
<request name="set_always_on_top" since="4">
<description summary="requests that the toplevel be set to always on top">
Requests that the toplevel be set to always on top.
If the state actually changes, this will be indicated by the state event.
</description>
</request>
<request name="unset_always_on_top" since="4">
<description summary="Requests that the toplevel stops being set to always on top">
Requests that the toplevel stops being set to always on top.
If the state actually changes, this will be indicated by the state event.
</description>
</request>
<request name="set_visible_on_all_workspaces" since="4">
<description summary="Requests that the toplevel be shown on all workspaces">
Requests that the toplevel be shown on all workspaces.
If the state actually changes, this will be indicated by the state event.
</description>
</request>
<request name="unset_visible_on_all_workspaces" since="4">
<description summary="Requests that the toplevel is not shown on all workspaces">
Requests that the toplevel is not shown on all workspaces.
If the state actually changes, this will be indicated by the state event.
</description>
</request>
<request name="set_rolled_up" since="4">
<description summary="Requests that the toplevel be rolled up (shaded)">
Requests that the toplevel be rolled up (shaded).
If the state actually changes, this will be indicated by the state event.
</description>
</request>
<request name="unset_rolled_up" since="4">
<description summary="Requests that the toplevel be rolled down (unshaded)">
Requests that the toplevel be rolled down (unshaded).
If the state actually changes, this will be indicated by the state event.
</description>
</request>
</interface>
</protocol>

View file

@ -7,7 +7,7 @@
#include <wlr/util/log.h>
#include "wlr-foreign-toplevel-management-unstable-v1-protocol.h"
#define FOREIGN_TOPLEVEL_MANAGEMENT_V1_VERSION 3
#define FOREIGN_TOPLEVEL_MANAGEMENT_V1_VERSION 4
#define FOREIGN_TOPLEVEL_HANDLE_V1_STATE_COUNT 32
@ -70,6 +70,79 @@ static void foreign_toplevel_handle_unset_minimized(struct wl_client *client,
toplevel_send_minimized_event(resource, false);
}
static void toplevel_send_always_on_top_event(struct wl_resource *resource,
bool state) {
struct wlr_foreign_toplevel_handle_v1 *toplevel =
toplevel_handle_from_resource(resource);
if (!toplevel) {
return;
}
struct wlr_foreign_toplevel_handle_v1_always_on_top_event event = {
.toplevel = toplevel,
.always_on_top = state,
};
wl_signal_emit_mutable(&toplevel->events.request_always_on_top, &event);
}
static void foreign_toplevel_handle_set_always_on_top(struct wl_client *client,
struct wl_resource *resource) {
toplevel_send_always_on_top_event(resource, true);
}
static void foreign_toplevel_handle_unset_always_on_top(struct wl_client *client,
struct wl_resource *resource) {
toplevel_send_always_on_top_event(resource, false);
}
static void toplevel_send_on_all_workspaces_event(struct wl_resource *resource,
bool state) {
struct wlr_foreign_toplevel_handle_v1 *toplevel =
toplevel_handle_from_resource(resource);
if (!toplevel) {
return;
}
struct wlr_foreign_toplevel_handle_v1_on_all_workspaces_event event = {
.toplevel = toplevel,
.on_all_workspaces = state,
};
wl_signal_emit_mutable(&toplevel->events.request_on_all_workspaces, &event);
}
static void foreign_toplevel_handle_set_on_all_workspaces(struct wl_client *client,
struct wl_resource *resource) {
toplevel_send_on_all_workspaces_event(resource, true);
}
static void foreign_toplevel_handle_unset_on_all_workspaces(struct wl_client *client,
struct wl_resource *resource) {
toplevel_send_on_all_workspaces_event(resource, false);
}
static void toplevel_send_roll_up_event(struct wl_resource *resource,
bool state) {
struct wlr_foreign_toplevel_handle_v1 *toplevel =
toplevel_handle_from_resource(resource);
if (!toplevel) {
return;
}
struct wlr_foreign_toplevel_handle_v1_roll_up_event event = {
.toplevel = toplevel,
.roll_up = state,
};
wl_signal_emit_mutable(&toplevel->events.request_roll_up, &event);
}
static void foreign_toplevel_handle_set_roll_up(struct wl_client *client,
struct wl_resource *resource) {
toplevel_send_roll_up_event(resource, true);
}
static void foreign_toplevel_handle_unset_roll_up(struct wl_client *client,
struct wl_resource *resource) {
toplevel_send_roll_up_event(resource, false);
}
static void toplevel_send_fullscreen_event(struct wl_resource *resource,
bool state, struct wl_resource *output_resource) {
struct wlr_foreign_toplevel_handle_v1 *toplevel =
@ -172,6 +245,12 @@ static const struct zwlr_foreign_toplevel_handle_v1_interface toplevel_handle_im
.destroy = foreign_toplevel_handle_destroy,
.set_fullscreen = foreign_toplevel_handle_set_fullscreen,
.unset_fullscreen = foreign_toplevel_handle_unset_fullscreen,
.set_always_on_top = foreign_toplevel_handle_set_always_on_top,
.unset_always_on_top = foreign_toplevel_handle_unset_always_on_top,
.set_visible_on_all_workspaces = foreign_toplevel_handle_set_on_all_workspaces,
.unset_visible_on_all_workspaces = foreign_toplevel_handle_unset_on_all_workspaces,
.set_rolled_up = foreign_toplevel_handle_set_roll_up,
.unset_rolled_up = foreign_toplevel_handle_unset_roll_up,
};
static void toplevel_idle_send_done(void *data) {
@ -355,6 +434,22 @@ static void fill_array_from_toplevel_state(struct wl_array *states,
&& (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN)) {
data[nstates++] = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN;
}
if (version >= ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ALWAYS_ON_TOP_SINCE_VERSION
&& (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ALWAYS_ON_TOP)) {
data[nstates++] = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ALWAYS_ON_TOP;
}
if (version >= ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ON_ALL_WORKSPACES_SINCE_VERSION
&& (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ON_ALL_WORKSPACES)) {
data[nstates++] = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ON_ALL_WORKSPACES;
}
if (version >= ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ROLLED_UP_SINCE_VERSION
&& (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ROLLED_UP)) {
data[nstates++] = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ROLLED_UP;
}
if (version >= ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_URGENT_SINCE_VERSION
&& (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_URGENT)) {
data[nstates++] = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_URGENT;
}
assert(nstates <= FOREIGN_TOPLEVEL_HANDLE_V1_STATE_COUNT);
*states = (struct wl_array){
@ -409,6 +504,28 @@ void wlr_foreign_toplevel_handle_v1_set_fullscreen(
set_state(toplevel, fullscreen, WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN);
}
void wlr_foreign_toplevel_handle_v1_set_always_on_top(
struct wlr_foreign_toplevel_handle_v1 *toplevel, bool always_on_top) {
set_state(toplevel, always_on_top,
WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ALWAYS_ON_TOP);
}
void wlr_foreign_toplevel_handle_v1_set_on_all_workspaces(
struct wlr_foreign_toplevel_handle_v1 *toplevel, bool on_all_workspaces) {
set_state(toplevel, on_all_workspaces,
WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ON_ALL_WORKSPACES);
}
void wlr_foreign_toplevel_handle_v1_set_rolled_up(
struct wlr_foreign_toplevel_handle_v1 *toplevel, bool rolled_up) {
set_state(toplevel, rolled_up, WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ROLLED_UP);
}
void wlr_foreign_toplevel_handle_v1_set_urgent(
struct wlr_foreign_toplevel_handle_v1 *toplevel, bool urgent) {
set_state(toplevel, urgent, WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_URGENT);
}
static void toplevel_resource_send_parent(
struct wl_resource *toplevel_resource,
struct wlr_foreign_toplevel_handle_v1 *parent) {
@ -541,6 +658,9 @@ wlr_foreign_toplevel_handle_v1_create(
wl_signal_init(&toplevel->events.request_activate);
wl_signal_init(&toplevel->events.request_fullscreen);
wl_signal_init(&toplevel->events.request_close);
wl_signal_init(&toplevel->events.request_always_on_top);
wl_signal_init(&toplevel->events.request_on_all_workspaces);
wl_signal_init(&toplevel->events.request_roll_up);
wl_signal_init(&toplevel->events.set_rectangle);
wl_signal_init(&toplevel->events.destroy);