Merge branch 'foreign_toplevel_appmenu' into '0.18'

Draft: wlr-foreign-toplevel-management: add support for relaying information about global menus

See merge request wlroots/wlroots!4986
This commit is contained in:
Daniel Kondor 2025-10-26 08:23:42 +00:00
commit 15382861f3
3 changed files with 229 additions and 4 deletions

View file

@ -45,6 +45,13 @@ struct wlr_foreign_toplevel_handle_v1_output {
struct wl_listener output_destroy;
};
struct wlr_foreign_toplevel_handle_v1_dbus_annotation {
struct wl_list link;
char *interface;
char *bus_name;
char *object_path;
};
struct wlr_foreign_toplevel_handle_v1 {
struct wlr_foreign_toplevel_manager_v1 *manager;
struct wl_list resources;
@ -57,6 +64,9 @@ struct wlr_foreign_toplevel_handle_v1 {
struct wl_list outputs; // wlr_foreign_toplevel_v1_output.link
uint32_t state; // enum wlr_foreign_toplevel_v1_state
struct wl_list client_dbus_annotations; // wlr_foreign_toplevel_handle_v1_dbus_annotation.link
struct wl_list surface_dbus_annotations; // wlr_foreign_toplevel_handle_v1_dbus_annotation.link
struct {
// struct wlr_foreign_toplevel_handle_v1_maximized_event
struct wl_signal request_maximize;
@ -149,5 +159,34 @@ void wlr_foreign_toplevel_handle_v1_set_parent(
struct wlr_foreign_toplevel_handle_v1 *toplevel,
struct wlr_foreign_toplevel_handle_v1 *parent);
/**
* Add or update a DBus annotation for the client associated with this
* toplevel. Arguements should not be NULL.
*/
void wlr_foreign_toplevel_handle_v1_add_client_dbus_annotation(
struct wlr_foreign_toplevel_handle_v1 *toplevel, const char *interface,
const char *bus_name, const char *object_path);
/**
* Remove an existing DBus annotation for the client associated with
* this toplevel.
*/
void wlr_foreign_toplevel_handle_v1_remove_client_dbus_annotation(
struct wlr_foreign_toplevel_handle_v1 *toplevel, const char *interface);
/**
* Add or update a DBus annotation for this toplevel. Arguements should not be NULL.
*/
void wlr_foreign_toplevel_handle_v1_add_surface_dbus_annotation(
struct wlr_foreign_toplevel_handle_v1 *toplevel, const char *interface,
const char *bus_name, const char *object_path);
/**
* Remove an existing DBus annotation for this toplevel.
*/
void wlr_foreign_toplevel_handle_v1_remove_surface_dbus_annotation(
struct wlr_foreign_toplevel_handle_v1 *toplevel, const char *interface);
#endif

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
@ -58,7 +58,7 @@
</description>
</request>
<event name="finished">
<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
@ -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.
@ -266,5 +266,43 @@
</description>
<arg name="parent" type="object" interface="zwlr_foreign_toplevel_handle_v1" allow-null="true"/>
</event>
<!-- Version 4 additions -->
<event name="client_dbus_annotation" since="4">
<description summary="DBus interface available for the client">
This event is emitted when the client associated with this surface
announces that it implements a specific DBus interface, available
at the given DBus name and object path. The DBus interface announced
by this event is not tied to any specific surface, but to a client app.
If an interface is available, both bus_name and object_path will be
non-null. bus_name will typically refer to an unique name, but can
potentially be a well-known name as well. If a previously announced
interface is no longer available, the event will be emitted for it
with bus_name and object_path set to null.
</description>
<arg name="interface" type="string" allow-null="false" summary="The name of the supported DBus interface" />
<arg name="bus_name" type="string" allow-null="true" summary="The bus name where the interface is available" />
<arg name="object_path" type="string" allow-null="true" summary="The DBus object path that implements the interface" />
</event>
<event name="surface_dbus_annotation" since="4">
<description summary="DBus interface available for the client">
This event is emitted when the client associated with this surface
announces that it implements a specific DBus interface, available
at the given DBus name and object path. The DBus interface announced
by this event is specific to this toplevel.
If an interface is available, both bus_name and object_path will be
non-null. bus_name will typically refer to an unique name, but can
potentially be a well-known name as well. If a previously announced
interface is no longer available, the event will be emitted for it
with bus_name and object_path set to null.
</description>
<arg name="interface" type="string" allow-null="false" summary="The name of the supported DBus interface" />
<arg name="bus_name" type="string" allow-null="true" summary="The bus name where the interface is available" />
<arg name="object_path" type="string" allow-null="true" summary="The DBus object path that implements the interface" />
</event>
</interface>
</protocol>

View file

@ -8,7 +8,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
static const struct zwlr_foreign_toplevel_handle_v1_interface toplevel_handle_impl;
@ -486,6 +486,130 @@ void wlr_foreign_toplevel_handle_v1_set_parent(
toplevel_update_idle_source(toplevel);
}
static void toplevel_add_annotation(struct wl_list *list, const char *interface,
const char *bus_name, const char *object_path) {
assert ( (interface != NULL) && (bus_name != NULL) && (object_path != NULL) );
bool found = false;
struct wlr_foreign_toplevel_handle_v1_dbus_annotation *toplevel_annot = NULL;
wl_list_for_each(toplevel_annot, list, link) {
if (!strcmp(toplevel_annot->interface, interface)) {
found = true;
free(toplevel_annot->bus_name);
free(toplevel_annot->object_path);
toplevel_annot->bus_name = strdup(bus_name);
toplevel_annot->object_path = strdup(object_path);
break;
}
}
if (!found) {
// this interface did not exist before
toplevel_annot = calloc(1, sizeof(struct wlr_foreign_toplevel_handle_v1_dbus_annotation));
toplevel_annot->interface = strdup(interface);
toplevel_annot->bus_name = strdup(bus_name);
toplevel_annot->object_path = strdup(object_path);
wl_list_insert(list, &toplevel_annot->link);
}
}
void wlr_foreign_toplevel_handle_v1_add_client_dbus_annotation(
struct wlr_foreign_toplevel_handle_v1 *toplevel, const char *interface,
const char *bus_name, const char *object_path) {
toplevel_add_annotation(&toplevel->client_dbus_annotations,
interface, bus_name, object_path);
struct wl_resource *resource;
wl_resource_for_each(resource, &toplevel->resources) {
if (wl_resource_get_version(resource) >=
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLIENT_DBUS_ANNOTATION_SINCE_VERSION) {
zwlr_foreign_toplevel_handle_v1_send_client_dbus_annotation(
resource, interface, bus_name, object_path);
}
}
toplevel_update_idle_source(toplevel);
}
void wlr_foreign_toplevel_handle_v1_add_surface_dbus_annotation(
struct wlr_foreign_toplevel_handle_v1 *toplevel, const char *interface,
const char *bus_name, const char *object_path) {
toplevel_add_annotation(&toplevel->surface_dbus_annotations,
interface, bus_name, object_path);
struct wl_resource *resource;
wl_resource_for_each(resource, &toplevel->resources) {
if (wl_resource_get_version(resource) >=
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SURFACE_DBUS_ANNOTATION_SINCE_VERSION) {
zwlr_foreign_toplevel_handle_v1_send_surface_dbus_annotation(
resource, interface, bus_name, object_path);
}
}
toplevel_update_idle_source(toplevel);
}
static void toplevel_dbus_annotation_destroy(
struct wlr_foreign_toplevel_handle_v1_dbus_annotation *annot) {
wl_list_remove(&annot->link);
free(annot->interface);
free(annot->bus_name);
free(annot->object_path);
free(annot);
}
void wlr_foreign_toplevel_handle_v1_remove_client_dbus_annotation(
struct wlr_foreign_toplevel_handle_v1 *toplevel, const char *interface) {
assert (interface != NULL);
bool found = false;
struct wlr_foreign_toplevel_handle_v1_dbus_annotation *toplevel_annot, *tmp;
wl_list_for_each_safe(toplevel_annot, tmp, &toplevel->client_dbus_annotations, link) {
if (!strcmp(toplevel_annot->interface, interface)) {
toplevel_dbus_annotation_destroy(toplevel_annot);
found = true;
break;
}
}
if (found) {
struct wl_resource *resource;
wl_resource_for_each(resource, &toplevel->resources) {
if (wl_resource_get_version(resource) >=
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLIENT_DBUS_ANNOTATION_SINCE_VERSION) {
zwlr_foreign_toplevel_handle_v1_send_client_dbus_annotation(
resource, interface, NULL, NULL);
}
}
}
}
void wlr_foreign_toplevel_handle_v1_remove_surface_dbus_annotation(
struct wlr_foreign_toplevel_handle_v1 *toplevel, const char *interface) {
assert (interface != NULL);
bool found = false;
struct wlr_foreign_toplevel_handle_v1_dbus_annotation *toplevel_annot, *tmp;
wl_list_for_each_safe(toplevel_annot, tmp, &toplevel->surface_dbus_annotations, link) {
if (!strcmp(toplevel_annot->interface, interface)) {
toplevel_dbus_annotation_destroy(toplevel_annot);
found = true;
break;
}
}
if (found) {
struct wl_resource *resource;
wl_resource_for_each(resource, &toplevel->resources) {
if (wl_resource_get_version(resource) >=
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SURFACE_DBUS_ANNOTATION_SINCE_VERSION) {
zwlr_foreign_toplevel_handle_v1_send_surface_dbus_annotation(
resource, interface, NULL, NULL);
}
}
}
}
void wlr_foreign_toplevel_handle_v1_destroy(
struct wlr_foreign_toplevel_handle_v1 *toplevel) {
if (!toplevel) {
@ -525,6 +649,14 @@ void wlr_foreign_toplevel_handle_v1_destroy(
wlr_foreign_toplevel_handle_v1_set_parent(tl, NULL);
}
}
struct wlr_foreign_toplevel_handle_v1_dbus_annotation *toplevel_annot, *tmp4;
wl_list_for_each_safe(toplevel_annot, tmp4, &toplevel->client_dbus_annotations, link) {
toplevel_dbus_annotation_destroy(toplevel_annot);
}
wl_list_for_each_safe(toplevel_annot, tmp4, &toplevel->surface_dbus_annotations, link) {
toplevel_dbus_annotation_destroy(toplevel_annot);
}
free(toplevel->title);
free(toplevel->app_id);
@ -568,6 +700,8 @@ wlr_foreign_toplevel_handle_v1_create(
wl_list_init(&toplevel->resources);
wl_list_init(&toplevel->outputs);
wl_list_init(&toplevel->client_dbus_annotations);
wl_list_init(&toplevel->surface_dbus_annotations);
wl_signal_init(&toplevel->events.request_maximize);
wl_signal_init(&toplevel->events.request_minimize);
@ -637,6 +771,20 @@ static void toplevel_send_details_to_toplevel_resource(
toplevel_resource_send_parent(resource, toplevel->parent);
if (wl_resource_get_version(resource) >=
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLIENT_DBUS_ANNOTATION_SINCE_VERSION) {
struct wlr_foreign_toplevel_handle_v1_dbus_annotation *annot;
wl_list_for_each(annot, &toplevel->client_dbus_annotations, link) {
zwlr_foreign_toplevel_handle_v1_send_surface_dbus_annotation(
resource, annot->interface, annot->bus_name, annot->object_path);
}
wl_list_for_each(annot, &toplevel->surface_dbus_annotations, link) {
zwlr_foreign_toplevel_handle_v1_send_surface_dbus_annotation(
resource, annot->interface, annot->bus_name, annot->object_path);
}
}
zwlr_foreign_toplevel_handle_v1_send_done(resource);
}