#include #include #include "types/wlr_xdg_shell.h" #define WM_BASE_VERSION 6 static const struct xdg_wm_base_interface xdg_shell_impl; static struct wlr_xdg_client *xdg_client_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &xdg_wm_base_interface, &xdg_shell_impl)); return wl_resource_get_user_data(resource); } static void xdg_shell_handle_create_positioner(struct wl_client *wl_client, struct wl_resource *resource, uint32_t id) { struct wlr_xdg_client *client = xdg_client_from_resource(resource); create_xdg_positioner(client, id); } static void xdg_shell_handle_get_xdg_surface(struct wl_client *wl_client, struct wl_resource *client_resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_xdg_client *client = xdg_client_from_resource(client_resource); struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); create_xdg_surface(client, surface, id); } static void xdg_shell_handle_pong(struct wl_client *wl_client, struct wl_resource *resource, uint32_t serial) { struct wlr_xdg_client *client = xdg_client_from_resource(resource); if (client->ping_serial != serial) { return; } wl_event_source_timer_update(client->ping_timer, 0); client->ping_serial = 0; } static void xdg_shell_handle_destroy(struct wl_client *wl_client, struct wl_resource *resource) { struct wlr_xdg_client *client = xdg_client_from_resource(resource); if (!wl_list_empty(&client->surfaces)) { wl_resource_post_error(client->resource, XDG_WM_BASE_ERROR_DEFUNCT_SURFACES, "xdg_wm_base was destroyed before children"); return; } wl_resource_destroy(resource); } static const struct xdg_wm_base_interface xdg_shell_impl = { .destroy = xdg_shell_handle_destroy, .create_positioner = xdg_shell_handle_create_positioner, .get_xdg_surface = xdg_shell_handle_get_xdg_surface, .pong = xdg_shell_handle_pong, }; static void xdg_client_handle_resource_destroy(struct wl_resource *resource) { struct wlr_xdg_client *client = xdg_client_from_resource(resource); struct wlr_xdg_surface *surface, *tmp = NULL; wl_list_for_each_safe(surface, tmp, &client->surfaces, link) { destroy_xdg_surface(surface); } if (client->ping_timer != NULL) { wl_event_source_remove(client->ping_timer); } wl_list_remove(&client->link); free(client); } static int xdg_client_ping_timeout(void *user_data) { struct wlr_xdg_client *client = user_data; struct wlr_xdg_surface *surface; wl_list_for_each(surface, &client->surfaces, link) { wl_signal_emit_mutable(&surface->events.ping_timeout, NULL); } client->ping_serial = 0; return 1; } static void xdg_shell_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_xdg_shell *xdg_shell = data; struct wlr_xdg_client *client = calloc(1, sizeof(*client)); if (client == NULL) { wl_client_post_no_memory(wl_client); return; } wl_list_init(&client->surfaces); client->resource = wl_resource_create(wl_client, &xdg_wm_base_interface, version, id); if (client->resource == NULL) { free(client); wl_client_post_no_memory(wl_client); return; } client->client = wl_client; client->shell = xdg_shell; wl_resource_set_implementation(client->resource, &xdg_shell_impl, client, xdg_client_handle_resource_destroy); wl_list_insert(&xdg_shell->clients, &client->link); struct wl_display *display = wl_client_get_display(client->client); struct wl_event_loop *loop = wl_display_get_event_loop(display); client->ping_timer = wl_event_loop_add_timer(loop, xdg_client_ping_timeout, client); if (client->ping_timer == NULL) { wl_client_post_no_memory(client->client); } } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_shell *xdg_shell = wl_container_of(listener, xdg_shell, display_destroy); wl_signal_emit_mutable(&xdg_shell->events.destroy, xdg_shell); wl_list_remove(&xdg_shell->display_destroy.link); wl_global_destroy(xdg_shell->global); free(xdg_shell); } struct wlr_xdg_shell *wlr_xdg_shell_create(struct wl_display *display, uint32_t version) { assert(version <= WM_BASE_VERSION); struct wlr_xdg_shell *xdg_shell = calloc(1, sizeof(*xdg_shell)); if (!xdg_shell) { return NULL; } xdg_shell->version = version; xdg_shell->ping_timeout = 10000; wl_list_init(&xdg_shell->clients); wl_list_init(&xdg_shell->popup_grabs); struct wl_global *global = wl_global_create(display, &xdg_wm_base_interface, version, xdg_shell, xdg_shell_bind); if (!global) { free(xdg_shell); return NULL; } xdg_shell->global = global; wl_signal_init(&xdg_shell->events.new_surface); wl_signal_init(&xdg_shell->events.new_toplevel); wl_signal_init(&xdg_shell->events.new_popup); wl_signal_init(&xdg_shell->events.destroy); xdg_shell->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &xdg_shell->display_destroy); return xdg_shell; }