aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Kondor <kondor.dani@gmail.com>2020-10-18 23:14:35 +0800
committerGitHub <noreply@github.com>2020-10-18 17:14:35 +0200
commit36395e5b1c4107d6945213e0c06eaa58d885f97c (patch)
tree8978f9db77b368e5ebed673ce4d3f15a93892255
parent1ac5257357fdf51b2864149f4896c51afd2eda09 (diff)
foreign-toplevel-management: report parent toplevel
Based on the wlr-protocols PR: https://github.com/swaywm/wlr-protocols/pull/52
-rw-r--r--examples/foreign-toplevel.c41
-rw-r--r--include/wlr/types/wlr_foreign_toplevel_management_v1.h17
-rw-r--r--protocol/wlr-foreign-toplevel-management-unstable-v1.xml15
-rw-r--r--types/wlr_foreign_toplevel_management_v1.c58
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 <wayland-client.h>
#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.
</copyright>
- <interface name="zwlr_foreign_toplevel_manager_v1" version="2">
+ <interface name="zwlr_foreign_toplevel_manager_v1" version="3">
<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="2">
+ <interface name="zwlr_foreign_toplevel_handle_v1" version="3">
<description summary="an opened toplevel">
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.
</description>
</request>
+
+ <!-- Version 3 additions -->
+
+ <event name="parent" since="3">
+ <description summary="parent change">
+ This event is emitted whenever the parent of the toplevel changes.
+
+ No event is emitted when the parent handle is destroyed by the client.
+ </description>
+ <arg name="parent" type="object" interface="zwlr_foreign_toplevel_handle_v1" allow-null="true"/>
+ </event>
</interface>
</protocol>
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);
}