Merge branch 'responsiveness' into 'master'

foreign-toplevel-management: Allow to emit toplevels responsiveness state

See merge request wlroots/wlroots!4924
This commit is contained in:
Arun Mani J 2026-02-12 14:23:31 +05:30
commit 4366ed1655
4 changed files with 143 additions and 3 deletions

View file

@ -12,6 +12,7 @@
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_output.h>
@ -68,6 +69,8 @@ struct tinywl_server {
struct wlr_output_layout *output_layout;
struct wl_list outputs;
struct wl_listener new_output;
struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager;
};
struct tinywl_output {
@ -83,15 +86,20 @@ struct tinywl_toplevel {
struct wl_list link;
struct tinywl_server *server;
struct wlr_xdg_toplevel *xdg_toplevel;
struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel;
struct wlr_scene_tree *scene_tree;
struct wl_listener map;
struct wl_listener unmap;
struct wl_listener commit;
struct wl_listener set_app_id;
struct wl_listener set_title;
struct wl_listener set_parent;
struct wl_listener destroy;
struct wl_listener request_move;
struct wl_listener request_resize;
struct wl_listener request_maximize;
struct wl_listener request_fullscreen;
struct wl_listener ping_timeout;
};
struct tinywl_popup {
@ -151,6 +159,8 @@ static void focus_toplevel(struct tinywl_toplevel *toplevel) {
wlr_seat_keyboard_notify_enter(seat, surface,
keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers);
}
wlr_log(WLR_DEBUG, "Pinging toplevel %s", toplevel->xdg_toplevel->title);
wlr_xdg_surface_ping(toplevel->xdg_toplevel->base);
}
static void keyboard_handle_modifiers(
@ -704,18 +714,54 @@ static void xdg_toplevel_commit(struct wl_listener *listener, void *data) {
}
}
static void xdg_toplevel_set_app_id(struct wl_listener *listener, void *data) {
/* Called when the toplevel wants to set its app ID. */
struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, set_app_id);
const char *app_id = toplevel->xdg_toplevel->app_id;
wlr_foreign_toplevel_handle_v1_set_app_id(toplevel->foreign_toplevel, app_id);
}
static void xdg_toplevel_set_title(struct wl_listener *listener, void *data) {
/* Called when the toplevel wants to set its title. */
struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, set_title);
const char *title = toplevel->xdg_toplevel->title;
wlr_foreign_toplevel_handle_v1_set_title(toplevel->foreign_toplevel, title);
}
static void xdg_toplevel_set_parent(struct wl_listener *listener, void *data) {
/* Called when the toplevel wants to set its parent. */
struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, set_parent);
/* Iterate through list of toplevels and find the tinywl_toplevel whose xdg_toplevel is the
* current toplevel's parent. */
struct wlr_foreign_toplevel_handle_v1 *foreign_parent_toplevel = NULL;
struct tinywl_toplevel *parent_toplevel;
wl_list_for_each(parent_toplevel, &toplevel->server->toplevels, link) {
if (parent_toplevel->xdg_toplevel == toplevel->xdg_toplevel->parent) {
foreign_parent_toplevel = parent_toplevel->foreign_toplevel;
}
}
wlr_foreign_toplevel_handle_v1_set_parent(toplevel->foreign_toplevel, foreign_parent_toplevel);
}
static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) {
/* Called when the xdg_toplevel is destroyed. */
struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, destroy);
wlr_foreign_toplevel_handle_v1_destroy(toplevel->foreign_toplevel);
wl_list_remove(&toplevel->map.link);
wl_list_remove(&toplevel->unmap.link);
wl_list_remove(&toplevel->commit.link);
wl_list_remove(&toplevel->set_app_id.link);
wl_list_remove(&toplevel->set_title.link);
wl_list_remove(&toplevel->set_parent.link);
wl_list_remove(&toplevel->destroy.link);
wl_list_remove(&toplevel->request_move.link);
wl_list_remove(&toplevel->request_resize.link);
wl_list_remove(&toplevel->request_maximize.link);
wl_list_remove(&toplevel->request_fullscreen.link);
wl_list_remove(&toplevel->ping_timeout.link);
free(toplevel);
}
@ -800,6 +846,14 @@ static void xdg_toplevel_request_fullscreen(
}
}
static void xdg_toplevel_ping_timeout(
struct wl_listener *listener, void *data) {
struct tinywl_toplevel *toplevel =
wl_container_of(listener, toplevel, ping_timeout);
wlr_log(WLR_DEBUG, "Ping timeout for toplevel %s", toplevel->xdg_toplevel->title);
wlr_foreign_toplevel_handle_v1_set_responsive(toplevel->foreign_toplevel, false);
}
static void server_new_xdg_toplevel(struct wl_listener *listener, void *data) {
/* This event is raised when a client creates a new toplevel (application window). */
struct tinywl_server *server = wl_container_of(listener, server, new_xdg_toplevel);
@ -814,6 +868,11 @@ static void server_new_xdg_toplevel(struct wl_listener *listener, void *data) {
toplevel->scene_tree->node.data = toplevel;
xdg_toplevel->base->data = toplevel->scene_tree;
/* Setup foreign_toplevel_handle */
struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel =
wlr_foreign_toplevel_handle_v1_create(server->foreign_toplevel_manager);
toplevel->foreign_toplevel = foreign_toplevel;
/* Listen to the various events it can emit */
toplevel->map.notify = xdg_toplevel_map;
wl_signal_add(&xdg_toplevel->base->surface->events.map, &toplevel->map);
@ -821,6 +880,12 @@ static void server_new_xdg_toplevel(struct wl_listener *listener, void *data) {
wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &toplevel->unmap);
toplevel->commit.notify = xdg_toplevel_commit;
wl_signal_add(&xdg_toplevel->base->surface->events.commit, &toplevel->commit);
toplevel->set_app_id.notify = xdg_toplevel_set_app_id;
wl_signal_add(&xdg_toplevel->events.set_app_id, &toplevel->set_app_id);
toplevel->set_title.notify = xdg_toplevel_set_title;
wl_signal_add(&xdg_toplevel->events.set_title, &toplevel->set_title);
toplevel->set_parent.notify = xdg_toplevel_set_parent;
wl_signal_add(&xdg_toplevel->events.set_parent, &toplevel->set_parent);
toplevel->destroy.notify = xdg_toplevel_destroy;
wl_signal_add(&xdg_toplevel->events.destroy, &toplevel->destroy);
@ -834,6 +899,10 @@ static void server_new_xdg_toplevel(struct wl_listener *listener, void *data) {
wl_signal_add(&xdg_toplevel->events.request_maximize, &toplevel->request_maximize);
toplevel->request_fullscreen.notify = xdg_toplevel_request_fullscreen;
wl_signal_add(&xdg_toplevel->events.request_fullscreen, &toplevel->request_fullscreen);
/* Check for responsiveness */
toplevel->ping_timeout.notify = xdg_toplevel_ping_timeout;
wl_signal_add(&xdg_toplevel->base->events.ping_timeout, &toplevel->ping_timeout);
}
static void xdg_popup_commit(struct wl_listener *listener, void *data) {
@ -1038,6 +1107,14 @@ int main(int argc, char *argv[]) {
wl_signal_add(&server.seat->events.request_set_selection,
&server.request_set_selection);
/* Setup foreign_toplevel_management. This protocol allows creation of taskbars, docks etc.
* which can manage the windows (like maximize, minimize etc.). The current code of tinywl does
* not process these requests. However, this protocol has been added to demonstrate the usage of
* responsiveness event. This event allows tinywl to inform foreign_toplevel_management clients
* of unresponsive apps.
*/
server.foreign_toplevel_manager = wlr_foreign_toplevel_manager_v1_create(server.wl_display);
/* Add a Unix socket to the Wayland display. */
const char *socket = wl_display_add_socket_auto(server.wl_display);
if (!socket) {