From 36395e5b1c4107d6945213e0c06eaa58d885f97c Mon Sep 17 00:00:00 2001 From: Daniel Kondor Date: Sun, 18 Oct 2020 23:14:35 +0800 Subject: [PATCH] foreign-toplevel-management: report parent toplevel Based on the wlr-protocols PR: https://github.com/swaywm/wlr-protocols/pull/52 --- examples/foreign-toplevel.c | 41 +++++++++++-- .../wlr_foreign_toplevel_management_v1.h | 17 ++++++ ...oreign-toplevel-management-unstable-v1.xml | 15 ++++- types/wlr_foreign_toplevel_management_v1.c | 58 ++++++++++++++++++- 4 files changed, 123 insertions(+), 8 deletions(-) diff --git a/examples/foreign-toplevel.c b/examples/foreign-toplevel.c index 1d41d01a..995d4f6c 100644 --- a/examples/foreign-toplevel.c +++ b/examples/foreign-toplevel.c @@ -7,7 +7,7 @@ #include #include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h" -#define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION 2 +#define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION 3 /** * Usage: @@ -38,11 +38,14 @@ enum toplevel_state_field { TOPLEVEL_STATE_INVALID = (1 << 4), }; +static const uint32_t no_parent = (uint32_t)-1; + struct toplevel_state { char *title; char *app_id; uint32_t state; + uint32_t parent_id; }; static void copy_state(struct toplevel_state *current, @@ -67,6 +70,8 @@ static void copy_state(struct toplevel_state *current, current->state = pending->state; } + current->parent_id = pending->parent_id; + pending->state = TOPLEVEL_STATE_INVALID; } @@ -84,6 +89,12 @@ static void print_toplevel(struct toplevel_v1 *toplevel, bool print_endl) { toplevel->current.title ?: "(nil)", toplevel->current.app_id ?: "(nil)"); + if (toplevel->current.parent_id != no_parent) { + printf(" parent=%u", toplevel->current.parent_id); + } else { + printf(" no parent"); + } + if (print_endl) { printf("\n"); } @@ -172,6 +183,28 @@ static void toplevel_handle_state(void *data, toplevel->pending.state = array_to_state(state); } +static struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager = NULL; +static struct wl_list toplevel_list; + +static void toplevel_handle_parent(void *data, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_parent) { + struct toplevel_v1 *toplevel = data; + toplevel->pending.parent_id = no_parent; + if (zwlr_parent) { + struct toplevel_v1 *toplevel_tmp; + wl_list_for_each(toplevel_tmp, &toplevel_list, link) { + if (toplevel_tmp->zwlr_toplevel == zwlr_parent) { + toplevel->pending.parent_id = toplevel_tmp->id; + break; + } + } + if (toplevel->pending.parent_id == no_parent) { + fprintf(stderr, "Cannot find parent toplevel!\n"); + } + } +} + static void toplevel_handle_done(void *data, struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) { struct toplevel_v1 *toplevel = data; @@ -202,11 +235,9 @@ static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_impl = { .state = toplevel_handle_state, .done = toplevel_handle_done, .closed = toplevel_handle_closed, + .parent = toplevel_handle_parent }; -static struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager = NULL; -static struct wl_list toplevel_list; - static void toplevel_manager_handle_toplevel(void *data, struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager, struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) { @@ -218,6 +249,8 @@ static void toplevel_manager_handle_toplevel(void *data, toplevel->id = global_id++; toplevel->zwlr_toplevel = zwlr_toplevel; + toplevel->current.parent_id = no_parent; + toplevel->pending.parent_id = no_parent; wl_list_insert(&toplevel_list, &toplevel->link); zwlr_foreign_toplevel_handle_v1_add_listener(zwlr_toplevel, &toplevel_impl, diff --git a/include/wlr/types/wlr_foreign_toplevel_management_v1.h b/include/wlr/types/wlr_foreign_toplevel_management_v1.h index 90d88637..6ea9d47f 100644 --- a/include/wlr/types/wlr_foreign_toplevel_management_v1.h +++ b/include/wlr/types/wlr_foreign_toplevel_management_v1.h @@ -50,6 +50,7 @@ struct wlr_foreign_toplevel_handle_v1 { char *title; char *app_id; + struct wlr_foreign_toplevel_handle_v1 *parent; struct wl_list outputs; // wlr_foreign_toplevel_v1_output uint32_t state; // wlr_foreign_toplevel_v1_state @@ -104,6 +105,12 @@ struct wlr_foreign_toplevel_manager_v1 *wlr_foreign_toplevel_manager_v1_create( struct wlr_foreign_toplevel_handle_v1 *wlr_foreign_toplevel_handle_v1_create( struct wlr_foreign_toplevel_manager_v1 *manager); +/* Destroy the given toplevel handle, sending the closed event to any + * client. Also, if the destroyed toplevel is set as a parent of any + * other valid toplevel, clients still holding a handle to both are + * sent a parent signal with NULL parent. If this is not desired, the + * caller should ensure that any child toplevels are destroyed before + * the parent. */ void wlr_foreign_toplevel_handle_v1_destroy( struct wlr_foreign_toplevel_handle_v1 *toplevel); @@ -126,4 +133,14 @@ void wlr_foreign_toplevel_handle_v1_set_activated( void wlr_foreign_toplevel_handle_v1_set_fullscreen( struct wlr_foreign_toplevel_handle_v1* toplevel, bool fullscreen); +/* Set the parent of a toplevel. If the parent changed from its previous + * value, also sends a parent event to all clients that hold handles to + * both toplevel and parent (no message is sent to clients that have + * previously destroyed their parent handle). NULL is allowed as the + * parent, meaning no parent exists. */ +void wlr_foreign_toplevel_handle_v1_set_parent( + struct wlr_foreign_toplevel_handle_v1 *toplevel, + struct wlr_foreign_toplevel_handle_v1 *parent); + + #endif diff --git a/protocol/wlr-foreign-toplevel-management-unstable-v1.xml b/protocol/wlr-foreign-toplevel-management-unstable-v1.xml index a97738f8..206143b4 100644 --- a/protocol/wlr-foreign-toplevel-management-unstable-v1.xml +++ b/protocol/wlr-foreign-toplevel-management-unstable-v1.xml @@ -25,7 +25,7 @@ THIS SOFTWARE. - + 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 @@ - + A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel window. Each app may have multiple opened toplevels. @@ -255,5 +255,16 @@ actually changes, this will be indicated by the state event. + + + + + + This event is emitted whenever the parent of the toplevel changes. + + No event is emitted when the parent handle is destroyed by the client. + + + diff --git a/types/wlr_foreign_toplevel_management_v1.c b/types/wlr_foreign_toplevel_management_v1.c index aed0e203..5e7b731c 100644 --- a/types/wlr_foreign_toplevel_management_v1.c +++ b/types/wlr_foreign_toplevel_management_v1.c @@ -9,7 +9,7 @@ #include "util/signal.h" #include "wlr-foreign-toplevel-management-unstable-v1-protocol.h" -#define FOREIGN_TOPLEVEL_MANAGEMENT_V1_VERSION 2 +#define FOREIGN_TOPLEVEL_MANAGEMENT_V1_VERSION 3 static const struct zwlr_foreign_toplevel_handle_v1_interface toplevel_handle_impl; @@ -405,6 +405,37 @@ void wlr_foreign_toplevel_handle_v1_set_fullscreen( toplevel_send_state(toplevel); } +static void toplevel_resource_send_parent( + struct wl_resource *toplevel_resource, + struct wlr_foreign_toplevel_handle_v1 *parent) { + struct wl_client *client = wl_resource_get_client(toplevel_resource); + struct wl_resource *parent_resource = NULL; + if (parent) { + parent_resource = wl_resource_find_for_client(&parent->resources, client); + if (!parent_resource) { + /* don't send an event if this client destroyed the parent handle */ + return; + } + } + zwlr_foreign_toplevel_handle_v1_send_parent(toplevel_resource, + parent_resource); +} + +void wlr_foreign_toplevel_handle_v1_set_parent( + struct wlr_foreign_toplevel_handle_v1 *toplevel, + struct wlr_foreign_toplevel_handle_v1 *parent) { + if (parent == toplevel->parent) { + /* only send parent event to the clients if there was a change */ + return; + } + struct wl_resource *toplevel_resource, *tmp; + wl_resource_for_each_safe(toplevel_resource, tmp, &toplevel->resources) { + toplevel_resource_send_parent(toplevel_resource, parent); + } + toplevel->parent = parent; + toplevel_update_idle_source(toplevel); +} + void wlr_foreign_toplevel_handle_v1_destroy( struct wlr_foreign_toplevel_handle_v1 *toplevel) { if (!toplevel) { @@ -432,6 +463,19 @@ void wlr_foreign_toplevel_handle_v1_destroy( wl_list_remove(&toplevel->link); + /* need to ensure no other toplevels hold a pointer to this one as + * a parent, so that a later call to foreign_toplevel_manager_bind() + * will not result in a segfault */ + struct wlr_foreign_toplevel_handle_v1 *tl, *tmp3; + wl_list_for_each_safe(tl, tmp3, &toplevel->manager->toplevels, link) { + if (tl->parent == toplevel) { + /* Note: we send a parent signal to all clients in this case; + * the caller should first destroy the child handles if it + * wishes to avoid this behavior. */ + wlr_foreign_toplevel_handle_v1_set_parent(tl, NULL); + } + } + free(toplevel->title); free(toplevel->app_id); free(toplevel); @@ -542,6 +586,8 @@ static void toplevel_send_details_to_toplevel_resource( zwlr_foreign_toplevel_handle_v1_send_state(resource, &states); wl_array_release(&states); + toplevel_resource_send_parent(resource, toplevel->parent); + zwlr_foreign_toplevel_handle_v1_send_done(resource); } @@ -560,9 +606,17 @@ static void foreign_toplevel_manager_bind(struct wl_client *client, void *data, wl_list_insert(&manager->resources, wl_resource_get_link(resource)); struct wlr_foreign_toplevel_handle_v1 *toplevel, *tmp; + /* First loop: create a handle for all toplevels for all clients. + * Separation into two loops avoid the case where a child handle + * is created before a parent handle, so the parent relationship + * could not be sent to a client. */ + wl_list_for_each_safe(toplevel, tmp, &manager->toplevels, link) { + create_toplevel_resource_for_resource(toplevel, resource); + } + /* Second loop: send details about each toplevel. */ wl_list_for_each_safe(toplevel, tmp, &manager->toplevels, link) { struct wl_resource *toplevel_resource = - create_toplevel_resource_for_resource(toplevel, resource); + wl_resource_find_for_client(&toplevel->resources, client); toplevel_send_details_to_toplevel_resource(toplevel, toplevel_resource); }