From 7d26a6debd6e9808d4a4bf7d39b71ad5f5031f79 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Wed, 14 Feb 2018 12:40:13 +0100 Subject: [PATCH 01/10] xdg-shell stable: copy-pasta implementation --- include/rootston/desktop.h | 4 + include/rootston/view.h | 26 + include/wlr/types/wlr_xdg_shell.h | 232 +++++ protocol/meson.build | 2 + rootston/desktop.c | 6 + rootston/meson.build | 1 + rootston/output.c | 37 + rootston/xdg_shell.c | 363 +++++++ types/meson.build | 1 + types/wlr_xdg_shell.c | 1457 +++++++++++++++++++++++++++++ 10 files changed, 2129 insertions(+) create mode 100644 include/wlr/types/wlr_xdg_shell.h create mode 100644 rootston/xdg_shell.c create mode 100644 types/wlr_xdg_shell.c diff --git a/include/rootston/desktop.h b/include/rootston/desktop.h index 6572b242..0132a7e8 100644 --- a/include/rootston/desktop.h +++ b/include/rootston/desktop.h @@ -15,6 +15,7 @@ #include #include #include +#include #include "rootston/config.h" #include "rootston/output.h" #include "rootston/view.h" @@ -34,6 +35,7 @@ struct roots_desktop { struct wlr_compositor *compositor; struct wlr_wl_shell *wl_shell; struct wlr_xdg_shell_v6 *xdg_shell_v6; + struct wlr_xdg_shell *xdg_shell; struct wlr_gamma_control_manager *gamma_control_manager; struct wlr_screenshooter *screenshooter; struct wlr_server_decoration_manager *server_decoration_manager; @@ -43,6 +45,7 @@ struct roots_desktop { struct wl_listener new_output; struct wl_listener layout_change; struct wl_listener xdg_shell_v6_surface; + struct wl_listener xdg_shell_surface; struct wl_listener wl_shell_surface; struct wl_listener decoration_new; @@ -72,6 +75,7 @@ void view_update_position(struct roots_view *view, double x, double y); void view_update_size(struct roots_view *view, uint32_t width, uint32_t height); void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data); +void handle_xdg_shell_surface(struct wl_listener *listener, void *data); void handle_wl_shell_surface(struct wl_listener *listener, void *data); void handle_xwayland_surface(struct wl_listener *listener, void *data); diff --git a/include/rootston/view.h b/include/rootston/view.h index b61ac330..198086c1 100644 --- a/include/rootston/view.h +++ b/include/rootston/view.h @@ -6,6 +6,7 @@ #include #include #include +#include struct roots_wl_shell_surface { struct roots_view *view; @@ -36,6 +37,21 @@ struct roots_xdg_surface_v6 { uint32_t pending_move_resize_configure_serial; }; +struct roots_xdg_surface { + struct roots_view *view; + + struct wl_listener destroy; + struct wl_listener new_popup; + struct wl_listener request_move; + struct wl_listener request_resize; + struct wl_listener request_maximize; + struct wl_listener request_fullscreen; + + struct wl_listener surface_commit; + + uint32_t pending_move_resize_configure_serial; +}; + struct roots_xwayland_surface { struct roots_view *view; @@ -54,6 +70,7 @@ struct roots_xwayland_surface { enum roots_view_type { ROOTS_WL_SHELL_VIEW, ROOTS_XDG_SHELL_V6_VIEW, + ROOTS_XDG_SHELL_VIEW, #ifdef WLR_HAS_XWAYLAND ROOTS_XWAYLAND_VIEW, #endif @@ -90,6 +107,7 @@ struct roots_view { union { struct wlr_wl_shell_surface *wl_shell_surface; struct wlr_xdg_surface_v6 *xdg_surface_v6; + struct wlr_xdg_surface *xdg_surface; #ifdef WLR_HAS_XWAYLAND struct wlr_xwayland_surface *xwayland_surface; #endif @@ -97,6 +115,7 @@ struct roots_view { union { struct roots_wl_shell_surface *roots_wl_shell_surface; struct roots_xdg_surface_v6 *roots_xdg_surface_v6; + struct roots_xdg_surface *roots_xdg_surface; #ifdef WLR_HAS_XWAYLAND struct roots_xwayland_surface *roots_xwayland_surface; #endif @@ -154,6 +173,13 @@ struct roots_xdg_popup_v6 { struct wl_listener new_popup; }; +struct roots_xdg_popup { + struct roots_view_child view_child; + struct wlr_xdg_popup *wlr_popup; + struct wl_listener destroy; + struct wl_listener new_popup; +}; + void view_get_box(const struct roots_view *view, struct wlr_box *box); void view_activate(struct roots_view *view, bool active); void view_move(struct roots_view *view, double x, double y); diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h new file mode 100644 index 00000000..8422863c --- /dev/null +++ b/include/wlr/types/wlr_xdg_shell.h @@ -0,0 +1,232 @@ +#ifndef WLR_TYPES_WLR_XDG_SHELL_H +#define WLR_TYPES_WLR_XDG_SHELL_H + +#include +#include +#include + +struct wlr_xdg_shell { + struct wl_global *wl_global; + struct wl_list clients; + struct wl_list popup_grabs; + uint32_t ping_timeout; + + struct wl_listener display_destroy; + + struct { + struct wl_signal new_surface; + } events; + + void *data; +}; + +struct wlr_xdg_client { + struct wlr_xdg_shell *shell; + struct wl_resource *resource; + struct wl_client *client; + struct wl_list surfaces; + + struct wl_list link; // wlr_xdg_shell::clients + + uint32_t ping_serial; + struct wl_event_source *ping_timer; +}; + +struct wlr_xdg_popup { + struct wlr_xdg_surface *base; + struct wl_list link; + + struct wl_resource *resource; + bool committed; + struct wlr_xdg_surface *parent; + struct wlr_seat *seat; + struct wlr_box geometry; + + struct wl_list grab_link; // wlr_xdg_popup_grab::popups +}; + +// each seat gets a popup grab +struct wlr_xdg_popup_grab { + struct wl_client *client; + struct wlr_seat_pointer_grab pointer_grab; + struct wlr_seat_keyboard_grab keyboard_grab; + struct wlr_seat *seat; + struct wl_list popups; + struct wl_list link; // wlr_xdg_shell::popup_grabs +}; + +enum wlr_xdg_surface_role { + WLR_XDG_SURFACE_ROLE_NONE, + WLR_XDG_SURFACE_ROLE_TOPLEVEL, + WLR_XDG_SURFACE_ROLE_POPUP, +}; + +struct wlr_xdg_toplevel_state { + bool maximized; + bool fullscreen; + bool resizing; + bool activated; + + uint32_t width; + uint32_t height; + + uint32_t max_width; + uint32_t max_height; + + uint32_t min_width; + uint32_t min_height; +}; + +struct wlr_xdg_toplevel { + struct wl_resource *resource; + struct wlr_xdg_surface *base; + struct wlr_xdg_surface *parent; + bool added; + struct wlr_xdg_toplevel_state next; // client protocol requests + struct wlr_xdg_toplevel_state pending; // user configure requests + struct wlr_xdg_toplevel_state current; +}; + +struct wlr_xdg_surface_configure { + struct wl_list link; // wlr_xdg_surface::configure_list + uint32_t serial; + struct wlr_xdg_toplevel_state state; +}; + +struct wlr_xdg_surface { + struct wlr_xdg_client *client; + struct wl_resource *resource; + struct wlr_surface *surface; + struct wl_list link; // wlr_xdg_client::surfaces + enum wlr_xdg_surface_role role; + + union { + struct wlr_xdg_toplevel *toplevel_state; + struct wlr_xdg_popup *popup_state; + }; + + struct wl_list popups; // wlr_xdg_popup::link + + bool configured; + bool added; + uint32_t configure_serial; + struct wl_event_source *configure_idle; + uint32_t configure_next_serial; + struct wl_list configure_list; + + char *title; + char *app_id; + + bool has_next_geometry; + struct wlr_box *next_geometry; + struct wlr_box *geometry; + + struct wl_listener surface_destroy_listener; + + struct { + struct wl_signal destroy; + struct wl_signal ping_timeout; + struct wl_signal new_popup; + + struct wl_signal request_maximize; + struct wl_signal request_fullscreen; + struct wl_signal request_minimize; + struct wl_signal request_move; + struct wl_signal request_resize; + struct wl_signal request_show_window_menu; + } events; + + void *data; +}; + +struct wlr_xdg_toplevel_move_event { + struct wlr_xdg_surface *surface; + struct wlr_seat_client *seat; + uint32_t serial; +}; + +struct wlr_xdg_toplevel_resize_event { + struct wlr_xdg_surface *surface; + struct wlr_seat_client *seat; + uint32_t serial; + uint32_t edges; +}; + +struct wlr_xdg_toplevel_set_fullscreen_event { + struct wlr_xdg_surface *surface; + bool fullscreen; + struct wlr_output *output; +}; + +struct wlr_xdg_toplevel_show_window_menu_event { + struct wlr_xdg_surface *surface; + struct wlr_seat_client *seat; + uint32_t serial; + uint32_t x, y; +}; + +struct wlr_xdg_shell *wlr_xdg_shell_create(struct wl_display *display); +void wlr_xdg_shell_destroy(struct wlr_xdg_shell *xdg_shell); + +/** + * Send a ping to the surface. If the surface does not respond in a reasonable + * amount of time, the ping_timeout event will be emitted. + */ +void wlr_xdg_surface_ping(struct wlr_xdg_surface *surface); + +/** + * Request that this toplevel surface be the given size. Returns the associated + * configure serial. + */ +uint32_t wlr_xdg_toplevel_set_size(struct wlr_xdg_surface *surface, + uint32_t width, uint32_t height); + +/** + * Request that this toplevel surface show itself in an activated or deactivated + * state. Returns the associated configure serial. + */ +uint32_t wlr_xdg_toplevel_set_activated(struct wlr_xdg_surface *surface, + bool activated); + +/** + * Request that this toplevel surface consider itself maximized or not + * maximized. Returns the associated configure serial. + */ +uint32_t wlr_xdg_toplevel_set_maximized(struct wlr_xdg_surface *surface, + bool maximized); + +/** + * Request that this toplevel surface consider itself fullscreen or not + * fullscreen. Returns the associated configure serial. + */ +uint32_t wlr_xdg_toplevel_set_fullscreen(struct wlr_xdg_surface *surface, + bool fullscreen); + +/** + * Request that this toplevel surface consider itself to be resizing or not + * resizing. Returns the associated configure serial. + */ +uint32_t wlr_xdg_toplevel_set_resizing(struct wlr_xdg_surface *surface, + bool resizing); + +/** + * Request that this toplevel surface closes. + */ +void wlr_xdg_toplevel_send_close(struct wlr_xdg_surface *surface); + +/** + * Compute the popup position in surface-local coordinates. + */ +void wlr_xdg_surface_popup_get_position(struct wlr_xdg_surface *surface, + double *popup_sx, double *popup_sy); + +/** + * Find a popup within this surface at the surface-local coordinates. Returns + * the popup and coordinates in the topmost surface coordinate system or NULL if + * no popup is found at that location. + */ +struct wlr_xdg_surface *wlr_xdg_surface_popup_at( + struct wlr_xdg_surface *surface, double sx, double sy, + double *popup_sx, double *popup_sy); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index 0db7c03c..514a6dde 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -22,6 +22,7 @@ wayland_scanner_client = generator( protocols = [ [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], + [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], 'gamma-control.xml', 'gtk-primary-selection.xml', 'idle.xml', @@ -31,6 +32,7 @@ protocols = [ client_protocols = [ [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], + [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], 'gamma-control.xml', 'gtk-primary-selection.xml', 'idle.xml', diff --git a/rootston/desktop.c b/rootston/desktop.c index 80ccbc25..6b28a41c 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "rootston/seat.h" #include "rootston/server.h" @@ -639,6 +640,11 @@ struct roots_desktop *desktop_create(struct roots_server *server, &desktop->xdg_shell_v6_surface); desktop->xdg_shell_v6_surface.notify = handle_xdg_shell_v6_surface; + desktop->xdg_shell = wlr_xdg_shell_create(server->wl_display); + wl_signal_add(&desktop->xdg_shell->events.new_surface, + &desktop->xdg_shell_surface); + desktop->xdg_shell_surface.notify = handle_xdg_shell_surface; + desktop->wl_shell = wlr_wl_shell_create(server->wl_display); wl_signal_add(&desktop->wl_shell->events.new_surface, &desktop->wl_shell_surface); diff --git a/rootston/meson.build b/rootston/meson.build index a53812e3..9dbe37c2 100644 --- a/rootston/meson.build +++ b/rootston/meson.build @@ -10,6 +10,7 @@ sources = [ 'seat.c', 'wl_shell.c', 'xdg_shell_v6.c', + 'xdg_shell.c', ] if get_option('enable_xwayland') sources += ['xwayland.c'] diff --git a/rootston/output.c b/rootston/output.c index 1ea4412e..8ef383c3 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include "rootston/config.h" @@ -81,6 +82,34 @@ static void xdg_surface_v6_for_each_surface(struct wlr_xdg_surface_v6 *surface, } } +static void xdg_surface_for_each_surface(struct wlr_xdg_surface *surface, + double base_x, double base_y, float rotation, + surface_iterator_func_t iterator, void *user_data) { + double width = surface->surface->current->width; + double height = surface->surface->current->height; + + struct wlr_xdg_popup *popup_state; + wl_list_for_each(popup_state, &surface->popups, link) { + struct wlr_xdg_surface *popup = popup_state->base; + if (!popup->configured) { + continue; + } + + double popup_width = popup->surface->current->width; + double popup_height = popup->surface->current->height; + + double popup_sx, popup_sy; + wlr_xdg_surface_popup_get_position(popup, &popup_sx, &popup_sy); + rotate_child_position(&popup_sx, &popup_sy, popup_width, popup_height, + width, height, rotation); + + surface_for_each_surface(popup->surface, base_x + popup_sx, + base_y + popup_sy, rotation, iterator, user_data); + xdg_surface_for_each_surface(popup, base_x + popup_sx, + base_y + popup_sy, rotation, iterator, user_data); + } +} + static void wl_shell_surface_for_each_surface( struct wlr_wl_shell_surface *surface, double lx, double ly, float rotation, bool is_child, surface_iterator_func_t iterator, @@ -117,6 +146,12 @@ static void view_for_each_surface(struct roots_view *view, xdg_surface_v6_for_each_surface(view->xdg_surface_v6, view->x, view->y, view->rotation, iterator, user_data); break; + case ROOTS_XDG_SHELL_VIEW: + surface_for_each_surface(view->wlr_surface, view->x, view->y, + view->rotation, iterator, user_data); + xdg_surface_for_each_surface(view->xdg_surface, view->x, view->y, + view->rotation, iterator, user_data); + break; case ROOTS_WL_SHELL_VIEW: wl_shell_surface_for_each_surface(view->wl_shell_surface, view->x, view->y, view->rotation, false, iterator, user_data); @@ -337,6 +372,8 @@ static bool has_standalone_surface(struct roots_view *view) { switch (view->type) { case ROOTS_XDG_SHELL_V6_VIEW: return wl_list_empty(&view->xdg_surface_v6->popups); + case ROOTS_XDG_SHELL_VIEW: + return wl_list_empty(&view->xdg_surface->popups); case ROOTS_WL_SHELL_VIEW: return wl_list_empty(&view->wl_shell_surface->popups); #ifdef WLR_HAS_XWAYLAND diff --git a/rootston/xdg_shell.c b/rootston/xdg_shell.c new file mode 100644 index 00000000..8340de46 --- /dev/null +++ b/rootston/xdg_shell.c @@ -0,0 +1,363 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "rootston/desktop.h" +#include "rootston/input.h" +#include "rootston/server.h" + +static void popup_destroy(struct roots_view_child *child) { + assert(child->destroy == popup_destroy); + struct roots_xdg_popup *popup = (struct roots_xdg_popup *)child; + if (popup == NULL) { + return; + } + wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->new_popup.link); + view_child_finish(&popup->view_child); + free(popup); +} + +static void popup_handle_destroy(struct wl_listener *listener, void *data) { + struct roots_xdg_popup *popup = + wl_container_of(listener, popup, destroy); + popup_destroy((struct roots_view_child *)popup); +} + +static struct roots_xdg_popup *popup_create(struct roots_view *view, + struct wlr_xdg_popup *wlr_popup); + +static void popup_handle_new_popup(struct wl_listener *listener, void *data) { + struct roots_xdg_popup *popup = + wl_container_of(listener, popup, new_popup); + struct wlr_xdg_popup *wlr_popup = data; + popup_create(popup->view_child.view, wlr_popup); +} + +static struct roots_xdg_popup *popup_create(struct roots_view *view, + struct wlr_xdg_popup *wlr_popup) { + struct roots_xdg_popup *popup = + calloc(1, sizeof(struct roots_xdg_popup)); + if (popup == NULL) { + return NULL; + } + popup->wlr_popup = wlr_popup; + popup->view_child.destroy = popup_destroy; + view_child_init(&popup->view_child, view, wlr_popup->base->surface); + popup->destroy.notify = popup_handle_destroy; + wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); + popup->new_popup.notify = popup_handle_new_popup; + wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); + return popup; +} + + +static void get_size(const struct roots_view *view, struct wlr_box *box) { + assert(view->type == ROOTS_XDG_SHELL_VIEW); + struct wlr_xdg_surface *surface = view->xdg_surface; + + if (surface->geometry->width > 0 && surface->geometry->height > 0) { + box->width = surface->geometry->width; + box->height = surface->geometry->height; + } else { + box->width = view->wlr_surface->current->width; + box->height = view->wlr_surface->current->height; + } +} + +static void activate(struct roots_view *view, bool active) { + assert(view->type == ROOTS_XDG_SHELL_VIEW); + struct wlr_xdg_surface *surface = view->xdg_surface; + if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + wlr_xdg_toplevel_set_activated(surface, active); + } +} + +static void apply_size_constraints(struct wlr_xdg_surface *surface, + uint32_t width, uint32_t height, uint32_t *dest_width, + uint32_t *dest_height) { + *dest_width = width; + *dest_height = height; + + struct wlr_xdg_toplevel_state *state = &surface->toplevel_state->current; + if (width < state->min_width) { + *dest_width = state->min_width; + } else if (state->max_width > 0 && + width > state->max_width) { + *dest_width = state->max_width; + } + if (height < state->min_height) { + *dest_height = state->min_height; + } else if (state->max_height > 0 && + height > state->max_height) { + *dest_height = state->max_height; + } +} + +static void resize(struct roots_view *view, uint32_t width, uint32_t height) { + assert(view->type == ROOTS_XDG_SHELL_VIEW); + struct wlr_xdg_surface *surface = view->xdg_surface; + if (surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + return; + } + + uint32_t constrained_width, constrained_height; + apply_size_constraints(surface, width, height, &constrained_width, + &constrained_height); + + wlr_xdg_toplevel_set_size(surface, constrained_width, + constrained_height); +} + +static void move_resize(struct roots_view *view, double x, double y, + uint32_t width, uint32_t height) { + assert(view->type == ROOTS_XDG_SHELL_VIEW); + struct roots_xdg_surface *roots_surface = view->roots_xdg_surface; + struct wlr_xdg_surface *surface = view->xdg_surface; + if (surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + return; + } + + bool update_x = x != view->x; + bool update_y = y != view->y; + + uint32_t constrained_width, constrained_height; + apply_size_constraints(surface, width, height, &constrained_width, + &constrained_height); + + if (update_x) { + x = x + width - constrained_width; + } + if (update_y) { + y = y + height - constrained_height; + } + + view->pending_move_resize.update_x = update_x; + view->pending_move_resize.update_y = update_y; + view->pending_move_resize.x = x; + view->pending_move_resize.y = y; + view->pending_move_resize.width = constrained_width; + view->pending_move_resize.height = constrained_height; + + uint32_t serial = wlr_xdg_toplevel_set_size(surface, constrained_width, + constrained_height); + if (serial > 0) { + roots_surface->pending_move_resize_configure_serial = serial; + } else if (roots_surface->pending_move_resize_configure_serial == 0) { + view_update_position(view, x, y); + } +} + +static void maximize(struct roots_view *view, bool maximized) { + assert(view->type == ROOTS_XDG_SHELL_VIEW); + struct wlr_xdg_surface *surface = view->xdg_surface; + if (surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + return; + } + + wlr_xdg_toplevel_set_maximized(surface, maximized); +} + +static void set_fullscreen(struct roots_view *view, bool fullscreen) { + assert(view->type == ROOTS_XDG_SHELL_VIEW); + struct wlr_xdg_surface *surface = view->xdg_surface; + if (surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + return; + } + + wlr_xdg_toplevel_set_fullscreen(surface, fullscreen); +} + +static void close(struct roots_view *view) { + assert(view->type == ROOTS_XDG_SHELL_VIEW); + struct wlr_xdg_surface *surface = view->xdg_surface; + if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + wlr_xdg_toplevel_send_close(surface); + } +} + +static void handle_request_move(struct wl_listener *listener, void *data) { + struct roots_xdg_surface *roots_xdg_surface = + wl_container_of(listener, roots_xdg_surface, request_move); + struct roots_view *view = roots_xdg_surface->view; + struct roots_input *input = view->desktop->server->input; + struct wlr_xdg_toplevel_move_event *e = data; + struct roots_seat *seat = input_seat_from_wlr_seat(input, e->seat->seat); + // TODO verify event serial + if (!seat || seat->cursor->mode != ROOTS_CURSOR_PASSTHROUGH) { + return; + } + roots_seat_begin_move(seat, view); +} + +static void handle_request_resize(struct wl_listener *listener, void *data) { + struct roots_xdg_surface *roots_xdg_surface = + wl_container_of(listener, roots_xdg_surface, request_resize); + struct roots_view *view = roots_xdg_surface->view; + struct roots_input *input = view->desktop->server->input; + struct wlr_xdg_toplevel_resize_event *e = data; + // TODO verify event serial + struct roots_seat *seat = input_seat_from_wlr_seat(input, e->seat->seat); + assert(seat); + if (!seat || seat->cursor->mode != ROOTS_CURSOR_PASSTHROUGH) { + return; + } + roots_seat_begin_resize(seat, view, e->edges); +} + +static void handle_request_maximize(struct wl_listener *listener, void *data) { + struct roots_xdg_surface *roots_xdg_surface = + wl_container_of(listener, roots_xdg_surface, request_maximize); + struct roots_view *view = roots_xdg_surface->view; + struct wlr_xdg_surface *surface = view->xdg_surface; + + if (surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + return; + } + + view_maximize(view, surface->toplevel_state->next.maximized); +} + +static void handle_request_fullscreen(struct wl_listener *listener, + void *data) { + struct roots_xdg_surface *roots_xdg_surface = + wl_container_of(listener, roots_xdg_surface, request_fullscreen); + struct roots_view *view = roots_xdg_surface->view; + struct wlr_xdg_surface *surface = view->xdg_surface; + struct wlr_xdg_toplevel_set_fullscreen_event *e = data; + + if (surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + return; + } + + view_set_fullscreen(view, e->fullscreen, e->output); +} + +static void handle_surface_commit(struct wl_listener *listener, void *data) { + struct roots_xdg_surface *roots_surface = + wl_container_of(listener, roots_surface, surface_commit); + struct roots_view *view = roots_surface->view; + struct wlr_xdg_surface *surface = view->xdg_surface; + + view_apply_damage(view); + + struct wlr_box size; + get_size(view, &size); + view_update_size(view, size.width, size.height); + + uint32_t pending_serial = + roots_surface->pending_move_resize_configure_serial; + if (pending_serial > 0 && pending_serial >= surface->configure_serial) { + double x = view->x; + double y = view->y; + if (view->pending_move_resize.update_x) { + x = view->pending_move_resize.x + view->pending_move_resize.width - + size.width; + } + if (view->pending_move_resize.update_y) { + y = view->pending_move_resize.y + view->pending_move_resize.height - + size.height; + } + view_update_position(view, x, y); + + if (pending_serial == surface->configure_serial) { + roots_surface->pending_move_resize_configure_serial = 0; + } + } +} + +static void handle_new_popup(struct wl_listener *listener, void *data) { + struct roots_xdg_surface *roots_xdg_surface = + wl_container_of(listener, roots_xdg_surface, new_popup); + struct wlr_xdg_popup *wlr_popup = data; + popup_create(roots_xdg_surface->view, wlr_popup); +} + +static void handle_destroy(struct wl_listener *listener, void *data) { + struct roots_xdg_surface *roots_xdg_surface = + wl_container_of(listener, roots_xdg_surface, destroy); + wl_list_remove(&roots_xdg_surface->surface_commit.link); + wl_list_remove(&roots_xdg_surface->destroy.link); + wl_list_remove(&roots_xdg_surface->new_popup.link); + wl_list_remove(&roots_xdg_surface->request_move.link); + wl_list_remove(&roots_xdg_surface->request_resize.link); + wl_list_remove(&roots_xdg_surface->request_maximize.link); + wl_list_remove(&roots_xdg_surface->request_fullscreen.link); + wl_list_remove(&roots_xdg_surface->view->link); + view_finish(roots_xdg_surface->view); + free(roots_xdg_surface->view); + free(roots_xdg_surface); +} + +void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { + struct wlr_xdg_surface *surface = data; + assert(surface->role != WLR_XDG_SURFACE_ROLE_NONE); + + if (surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + wlr_log(L_DEBUG, "new xdg popup"); + return; + } + + struct roots_desktop *desktop = + wl_container_of(listener, desktop, xdg_shell_surface); + + wlr_log(L_DEBUG, "new xdg toplevel: title=%s, app_id=%s", + surface->title, surface->app_id); + wlr_xdg_surface_ping(surface); + + struct roots_xdg_surface *roots_surface = + calloc(1, sizeof(struct roots_xdg_surface)); + if (!roots_surface) { + return; + } + roots_surface->surface_commit.notify = handle_surface_commit; + wl_signal_add(&surface->surface->events.commit, + &roots_surface->surface_commit); + roots_surface->destroy.notify = handle_destroy; + wl_signal_add(&surface->events.destroy, &roots_surface->destroy); + roots_surface->request_move.notify = handle_request_move; + wl_signal_add(&surface->events.request_move, &roots_surface->request_move); + roots_surface->request_resize.notify = handle_request_resize; + wl_signal_add(&surface->events.request_resize, + &roots_surface->request_resize); + roots_surface->request_maximize.notify = handle_request_maximize; + wl_signal_add(&surface->events.request_maximize, + &roots_surface->request_maximize); + roots_surface->request_fullscreen.notify = handle_request_fullscreen; + wl_signal_add(&surface->events.request_fullscreen, + &roots_surface->request_fullscreen); + roots_surface->new_popup.notify = handle_new_popup; + wl_signal_add(&surface->events.new_popup, &roots_surface->new_popup); + + struct roots_view *view = calloc(1, sizeof(struct roots_view)); + if (!view) { + free(roots_surface); + return; + } + view->type = ROOTS_XDG_SHELL_VIEW; + + view->xdg_surface = surface; + view->roots_xdg_surface = roots_surface; + view->wlr_surface = surface->surface; + view->activate = activate; + view->resize = resize; + view->move_resize = move_resize; + view->maximize = maximize; + view->set_fullscreen = set_fullscreen; + view->close = close; + roots_surface->view = view; + + struct wlr_box box; + get_size(view, &box); + view->width = box.width; + view->height = box.height; + + view_init(view, desktop); + wl_list_insert(&desktop->views, &view->link); + + view_setup(view); +} diff --git a/types/meson.build b/types/meson.build index 2731f9bc..da47aeff 100644 --- a/types/meson.build +++ b/types/meson.build @@ -26,6 +26,7 @@ lib_wlr_types = static_library( 'wlr_wl_shell.c', 'wlr_xcursor_manager.c', 'wlr_xdg_shell_v6.c', + 'wlr_xdg_shell.c', ), include_directories: wlr_inc, dependencies: [wayland_server, pixman, wlr_protos], diff --git a/types/wlr_xdg_shell.c b/types/wlr_xdg_shell.c new file mode 100644 index 00000000..6b2d42ee --- /dev/null +++ b/types/wlr_xdg_shell.c @@ -0,0 +1,1457 @@ +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util/signal.h" +#include "xdg-shell-protocol.h" + +static const char *wlr_desktop_xdg_toplevel_role = "xdg_toplevel"; +static const char *wlr_desktop_xdg_popup_role = "xdg_popup"; + +struct wlr_xdg_positioner { + struct wl_resource *resource; + + struct wlr_box anchor_rect; + enum xdg_positioner_anchor anchor; + enum xdg_positioner_gravity gravity; + enum xdg_positioner_constraint_adjustment constraint_adjustment; + + struct { + int32_t width, height; + } size; + + struct { + int32_t x, y; + } offset; +}; + + +static void resource_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static struct wlr_xdg_surface *xdg_popup_grab_get_topmost( + struct wlr_xdg_popup_grab *grab) { + struct wlr_xdg_popup *popup; + wl_list_for_each(popup, &grab->popups, grab_link) { + return popup->base; + } + + return NULL; +} + +static void xdg_pointer_grab_end(struct wlr_seat_pointer_grab *grab) { + struct wlr_xdg_popup_grab *popup_grab = grab->data; + + struct wlr_xdg_popup *popup, *tmp; + wl_list_for_each_safe(popup, tmp, &popup_grab->popups, grab_link) { + xdg_popup_send_popup_done(popup->resource); + } + + wlr_seat_pointer_end_grab(grab->seat); +} + +static void xdg_pointer_grab_enter(struct wlr_seat_pointer_grab *grab, + struct wlr_surface *surface, double sx, double sy) { + struct wlr_xdg_popup_grab *popup_grab = grab->data; + if (wl_resource_get_client(surface->resource) == popup_grab->client) { + wlr_seat_pointer_enter(grab->seat, surface, sx, sy); + } else { + wlr_seat_pointer_clear_focus(grab->seat); + } +} + +static void xdg_pointer_grab_motion(struct wlr_seat_pointer_grab *grab, + uint32_t time, double sx, double sy) { + wlr_seat_pointer_send_motion(grab->seat, time, sx, sy); +} + +static uint32_t xdg_pointer_grab_button(struct wlr_seat_pointer_grab *grab, + uint32_t time, uint32_t button, uint32_t state) { + uint32_t serial = + wlr_seat_pointer_send_button(grab->seat, time, button, state); + if (serial) { + return serial; + } else { + xdg_pointer_grab_end(grab); + return 0; + } +} + +static void xdg_pointer_grab_axis(struct wlr_seat_pointer_grab *grab, + uint32_t time, enum wlr_axis_orientation orientation, double value) { + wlr_seat_pointer_send_axis(grab->seat, time, orientation, value); +} + +static void xdg_pointer_grab_cancel(struct wlr_seat_pointer_grab *grab) { + xdg_pointer_grab_end(grab); +} + +static const struct wlr_pointer_grab_interface xdg_pointer_grab_impl = { + .enter = xdg_pointer_grab_enter, + .motion = xdg_pointer_grab_motion, + .button = xdg_pointer_grab_button, + .cancel = xdg_pointer_grab_cancel, + .axis = xdg_pointer_grab_axis, +}; + +static void xdg_keyboard_grab_enter(struct wlr_seat_keyboard_grab *grab, + struct wlr_surface *surface, uint32_t keycodes[], size_t num_keycodes, + struct wlr_keyboard_modifiers *modifiers) { + // keyboard focus should remain on the popup +} + +static void xdg_keyboard_grab_key(struct wlr_seat_keyboard_grab *grab, uint32_t time, + uint32_t key, uint32_t state) { + wlr_seat_keyboard_send_key(grab->seat, time, key, state); +} + +static void xdg_keyboard_grab_modifiers(struct wlr_seat_keyboard_grab *grab, + struct wlr_keyboard_modifiers *modifiers) { + wlr_seat_keyboard_send_modifiers(grab->seat, modifiers); +} + +static void xdg_keyboard_grab_cancel(struct wlr_seat_keyboard_grab *grab) { + wlr_seat_keyboard_end_grab(grab->seat); +} + +static const struct wlr_keyboard_grab_interface xdg_keyboard_grab_impl = { + .enter = xdg_keyboard_grab_enter, + .key = xdg_keyboard_grab_key, + .modifiers = xdg_keyboard_grab_modifiers, + .cancel = xdg_keyboard_grab_cancel, +}; + +static struct wlr_xdg_popup_grab *xdg_shell_popup_grab_from_seat( + struct wlr_xdg_shell *shell, struct wlr_seat *seat) { + struct wlr_xdg_popup_grab *xdg_grab; + wl_list_for_each(xdg_grab, &shell->popup_grabs, link) { + if (xdg_grab->seat == seat) { + return xdg_grab; + } + } + + xdg_grab = calloc(1, sizeof(struct wlr_xdg_popup_grab)); + if (!xdg_grab) { + return NULL; + } + + xdg_grab->pointer_grab.data = xdg_grab; + xdg_grab->pointer_grab.interface = &xdg_pointer_grab_impl; + xdg_grab->keyboard_grab.data = xdg_grab; + xdg_grab->keyboard_grab.interface = &xdg_keyboard_grab_impl; + + wl_list_init(&xdg_grab->popups); + + wl_list_insert(&shell->popup_grabs, &xdg_grab->link); + xdg_grab->seat = seat; + + return xdg_grab; +} + + +static void xdg_surface_destroy(struct wlr_xdg_surface *surface) { + // TODO: probably need to ungrab before this event + wlr_signal_emit_safe(&surface->events.destroy, surface); + + if (surface->configure_idle) { + wl_event_source_remove(surface->configure_idle); + } + + struct wlr_xdg_surface_configure *configure, *tmp; + wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) { + free(configure); + } + + if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + wl_resource_set_user_data(surface->toplevel_state->resource, NULL); + free(surface->toplevel_state); + } + + if (surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + wl_resource_set_user_data(surface->popup_state->resource, NULL); + + if (surface->popup_state->seat) { + struct wlr_xdg_popup_grab *grab = + xdg_shell_popup_grab_from_seat(surface->client->shell, + surface->popup_state->seat); + + struct wlr_xdg_surface *topmost = + xdg_popup_grab_get_topmost(grab); + + if (topmost != surface) { + wl_resource_post_error(surface->client->resource, + XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP, + "xdg_popup was destroyed while it was not the topmost " + "popup."); + } + + wl_list_remove(&surface->popup_state->grab_link); + + if (wl_list_empty(&grab->popups)) { + if (grab->seat->pointer_state.grab == &grab->pointer_grab) { + wlr_seat_pointer_end_grab(grab->seat); + } + if (grab->seat->keyboard_state.grab == &grab->keyboard_grab) { + wlr_seat_keyboard_end_grab(grab->seat); + } + } + } + + wl_list_remove(&surface->popup_state->link); + free(surface->popup_state); + } + + wl_resource_set_user_data(surface->resource, NULL); + wl_list_remove(&surface->link); + wl_list_remove(&surface->surface_destroy_listener.link); + wlr_surface_set_role_committed(surface->surface, NULL, NULL); + free(surface->geometry); + free(surface->next_geometry); + free(surface->title); + free(surface->app_id); + free(surface); +} + + +static void xdg_positioner_destroy(struct wl_resource *resource) { + struct wlr_xdg_positioner *positioner = + wl_resource_get_user_data(resource); + free(positioner); + +} + +static void xdg_positioner_protocol_set_size(struct wl_client *client, + struct wl_resource *resource, int32_t width, int32_t height) { + struct wlr_xdg_positioner *positioner = + wl_resource_get_user_data(resource); + + if (width < 1 || height < 1) { + wl_resource_post_error(resource, + XDG_POSITIONER_ERROR_INVALID_INPUT, + "width and height must be positives and non-zero"); + return; + } + + positioner->size.width = width; + positioner->size.height = height; +} + +static void xdg_positioner_protocol_set_anchor_rect(struct wl_client *client, + struct wl_resource *resource, int32_t x, int32_t y, int32_t width, + int32_t height) { + struct wlr_xdg_positioner *positioner = + wl_resource_get_user_data(resource); + + if (width < 1 || height < 1) { + wl_resource_post_error(resource, + XDG_POSITIONER_ERROR_INVALID_INPUT, + "width and height must be positives and non-zero"); + return; + } + + positioner->anchor_rect.x = x; + positioner->anchor_rect.y = y; + positioner->anchor_rect.width = width; + positioner->anchor_rect.height = height; +} + +static void xdg_positioner_protocol_set_anchor(struct wl_client *client, + struct wl_resource *resource, uint32_t anchor) { + struct wlr_xdg_positioner *positioner = + wl_resource_get_user_data(resource); + + /* post error if anchor > XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT ? */ + + positioner->anchor = anchor; +} + +static void xdg_positioner_protocol_set_gravity(struct wl_client *client, + struct wl_resource *resource, uint32_t gravity) { + struct wlr_xdg_positioner *positioner = + wl_resource_get_user_data(resource); + + /* post error if gravity > XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT ? */ + + positioner->gravity = gravity; +} + +static void xdg_positioner_protocol_set_constraint_adjustment( + struct wl_client *client, struct wl_resource *resource, + uint32_t constraint_adjustment) { + struct wlr_xdg_positioner *positioner = + wl_resource_get_user_data(resource); + + positioner->constraint_adjustment = constraint_adjustment; +} + +static void xdg_positioner_protocol_set_offset(struct wl_client *client, + struct wl_resource *resource, int32_t x, int32_t y) { + struct wlr_xdg_positioner *positioner = + wl_resource_get_user_data(resource); + + positioner->offset.x = x; + positioner->offset.y = y; +} + +static const struct xdg_positioner_interface + xdg_positioner_implementation = { + .destroy = resource_destroy, + .set_size = xdg_positioner_protocol_set_size, + .set_anchor_rect = xdg_positioner_protocol_set_anchor_rect, + .set_anchor = xdg_positioner_protocol_set_anchor, + .set_gravity = xdg_positioner_protocol_set_gravity, + .set_constraint_adjustment = + xdg_positioner_protocol_set_constraint_adjustment, + .set_offset = xdg_positioner_protocol_set_offset, +}; + +static void xdg_shell_create_positioner(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t id) { + struct wlr_xdg_positioner *positioner = + calloc(1, sizeof(struct wlr_xdg_positioner)); + if (positioner == NULL) { + wl_client_post_no_memory(wl_client); + return; + } + + positioner->resource = wl_resource_create(wl_client, + &xdg_positioner_interface, + wl_resource_get_version(resource), + id); + if (positioner->resource == NULL) { + free(positioner); + wl_client_post_no_memory(wl_client); + return; + } + + wl_resource_set_implementation(positioner->resource, + &xdg_positioner_implementation, + positioner, xdg_positioner_destroy); +} + +static void xdg_popup_protocol_grab(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *seat_resource, + uint32_t serial) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_seat_client *seat_client = wl_resource_get_user_data(seat_resource); + + if (surface->popup_state->committed) { + wl_resource_post_error(surface->popup_state->resource, + XDG_POPUP_ERROR_INVALID_GRAB, + "xdg_popup is already mapped"); + return; + } + + struct wlr_xdg_popup_grab *popup_grab = + xdg_shell_popup_grab_from_seat(surface->client->shell, + seat_client->seat); + + struct wlr_xdg_surface *topmost = xdg_popup_grab_get_topmost(popup_grab); + bool parent_is_toplevel = + surface->popup_state->parent->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL; + + if ((topmost == NULL && !parent_is_toplevel) || + (topmost != NULL && topmost != surface->popup_state->parent)) { + wl_resource_post_error(surface->client->resource, + XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP, + "xdg_popup was not created on the topmost popup"); + return; + } + + popup_grab->client = surface->client->client; + surface->popup_state->seat = seat_client->seat; + + wl_list_insert(&popup_grab->popups, &surface->popup_state->grab_link); + + wlr_seat_pointer_start_grab(seat_client->seat, + &popup_grab->pointer_grab); + wlr_seat_keyboard_start_grab(seat_client->seat, + &popup_grab->keyboard_grab); +} + +static const struct xdg_popup_interface xdg_popup_implementation = { + .destroy = resource_destroy, + .grab = xdg_popup_protocol_grab, +}; + + +static struct wlr_box xdg_positioner_get_geometry( + struct wlr_xdg_positioner *positioner, + struct wlr_xdg_surface *surface, struct wlr_xdg_surface *parent) { + struct wlr_box geometry = { + .x = positioner->offset.x, + .y = positioner->offset.y, + .width = positioner->size.width, + .height = positioner->size.height, + }; + + switch (positioner->anchor) { + case XDG_POSITIONER_ANCHOR_TOP: + case XDG_POSITIONER_ANCHOR_TOP_LEFT: + case XDG_POSITIONER_ANCHOR_TOP_RIGHT: + geometry.y += positioner->anchor_rect.y; + break; + case XDG_POSITIONER_ANCHOR_BOTTOM: + case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: + case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: + geometry.y += + positioner->anchor_rect.y + positioner->anchor_rect.height; + break; + default: + geometry.y += + positioner->anchor_rect.y + positioner->anchor_rect.height / 2; + } + + switch (positioner->anchor) { + case XDG_POSITIONER_ANCHOR_LEFT: + case XDG_POSITIONER_ANCHOR_TOP_LEFT: + case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: + geometry.x += positioner->anchor_rect.x; + break; + case XDG_POSITIONER_ANCHOR_RIGHT: + case XDG_POSITIONER_ANCHOR_TOP_RIGHT: + case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: + geometry.x += positioner->anchor_rect.x + positioner->anchor_rect.width; + break; + default: + geometry.x += + positioner->anchor_rect.x + positioner->anchor_rect.width / 2; + } + + switch (positioner->gravity) { + case XDG_POSITIONER_GRAVITY_TOP: + case XDG_POSITIONER_GRAVITY_TOP_LEFT: + case XDG_POSITIONER_GRAVITY_TOP_RIGHT: + geometry.y -= geometry.height; + break; + case XDG_POSITIONER_GRAVITY_BOTTOM: + case XDG_POSITIONER_GRAVITY_BOTTOM_LEFT: + case XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT: + geometry.y = geometry.y; + break; + default: + geometry.y -= geometry.height / 2; + } + + switch (positioner->gravity) { + case XDG_POSITIONER_GRAVITY_LEFT: + case XDG_POSITIONER_GRAVITY_TOP_LEFT: + case XDG_POSITIONER_GRAVITY_BOTTOM_LEFT: + geometry.x -= geometry.width; + break; + case XDG_POSITIONER_GRAVITY_RIGHT: + case XDG_POSITIONER_GRAVITY_TOP_RIGHT: + case XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT: + geometry.x = geometry.x; + break; + default: + geometry.x -= geometry.width / 2; + } + + if (positioner->constraint_adjustment == + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE) { + return geometry; + } + + // TODO: add compositor policy configuration and the code here + + return geometry; +} + +static void xdg_popup_resource_destroy(struct wl_resource *resource) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + if (surface != NULL) { + xdg_surface_destroy(surface); + } +} + +static void xdg_surface_get_popup(struct wl_client *client, + struct wl_resource *resource, uint32_t id, + struct wl_resource *parent_resource, + struct wl_resource *positioner_resource) { + struct wlr_xdg_surface *surface = + wl_resource_get_user_data(resource); + struct wlr_xdg_surface *parent = + wl_resource_get_user_data(parent_resource); + struct wlr_xdg_positioner *positioner = + wl_resource_get_user_data(positioner_resource); + + if (positioner->size.width == 0 || positioner->anchor_rect.width == 0) { + wl_resource_post_error(resource, + XDG_WM_BASE_ERROR_INVALID_POSITIONER, + "positioner object is not complete"); + return; + } + + if (wlr_surface_set_role(surface->surface, wlr_desktop_xdg_popup_role, + resource, XDG_WM_BASE_ERROR_ROLE)) { + return; + } + + surface->popup_state = calloc(1, sizeof(struct wlr_xdg_popup)); + if (!surface->popup_state) { + wl_resource_post_no_memory(resource); + return; + } + + surface->popup_state->resource = + wl_resource_create(client, &xdg_popup_interface, + wl_resource_get_version(resource), id); + if (surface->popup_state->resource == NULL) { + free(surface->popup_state); + wl_resource_post_no_memory(resource); + return; + } + + surface->role = WLR_XDG_SURFACE_ROLE_POPUP; + surface->popup_state->base = surface; + surface->popup_state->parent = parent; + surface->popup_state->geometry = + xdg_positioner_get_geometry(positioner, surface, parent); + wl_list_insert(&parent->popups, &surface->popup_state->link); + + wl_resource_set_implementation(surface->popup_state->resource, + &xdg_popup_implementation, surface, + xdg_popup_resource_destroy); + + wlr_signal_emit_safe(&parent->events.new_popup, surface->popup_state); +} + + +static void xdg_toplevel_protocol_set_parent(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *parent_resource) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *parent = NULL; + + if (parent_resource != NULL) { + parent = wl_resource_get_user_data(parent_resource); + } + + surface->toplevel_state->parent = parent; +} + +static void xdg_toplevel_protocol_set_title(struct wl_client *client, + struct wl_resource *resource, const char *title) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + char *tmp; + + tmp = strdup(title); + if (tmp == NULL) { + return; + } + + free(surface->title); + surface->title = tmp; +} + +static void xdg_toplevel_protocol_set_app_id(struct wl_client *client, + struct wl_resource *resource, const char *app_id) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + char *tmp; + + tmp = strdup(app_id); + if (tmp == NULL) { + return; + } + + free(surface->app_id); + surface->app_id = tmp; +} + +static void xdg_toplevel_protocol_show_window_menu(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *seat_resource, + uint32_t serial, int32_t x, int32_t y) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_seat_client *seat = + wl_resource_get_user_data(seat_resource); + + if (!surface->configured) { + wl_resource_post_error(surface->toplevel_state->resource, + XDG_SURFACE_ERROR_NOT_CONSTRUCTED, + "surface has not been configured yet"); + return; + } + + if (!wlr_seat_validate_grab_serial(seat->seat, serial)) { + wlr_log(L_DEBUG, "invalid serial for grab"); + return; + } + + struct wlr_xdg_toplevel_show_window_menu_event event = { + .surface = surface, + .seat = seat, + .serial = serial, + .x = x, + .y = y, + }; + + wlr_signal_emit_safe(&surface->events.request_show_window_menu, &event); +} + +static void xdg_toplevel_protocol_move(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *seat_resource, + uint32_t serial) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_seat_client *seat = + wl_resource_get_user_data(seat_resource); + + if (!surface->configured) { + wl_resource_post_error(surface->toplevel_state->resource, + XDG_SURFACE_ERROR_NOT_CONSTRUCTED, + "surface has not been configured yet"); + return; + } + + if (!wlr_seat_validate_grab_serial(seat->seat, serial)) { + wlr_log(L_DEBUG, "invalid serial for grab"); + return; + } + + struct wlr_xdg_toplevel_move_event event = { + .surface = surface, + .seat = seat, + .serial = serial, + }; + + wlr_signal_emit_safe(&surface->events.request_move, &event); +} + +static void xdg_toplevel_protocol_resize(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *seat_resource, + uint32_t serial, uint32_t edges) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_seat_client *seat = + wl_resource_get_user_data(seat_resource); + + if (!surface->configured) { + wl_resource_post_error(surface->toplevel_state->resource, + XDG_SURFACE_ERROR_NOT_CONSTRUCTED, + "surface has not been configured yet"); + return; + } + + if (!wlr_seat_validate_grab_serial(seat->seat, serial)) { + wlr_log(L_DEBUG, "invalid serial for grab"); + return; + } + + struct wlr_xdg_toplevel_resize_event event = { + .surface = surface, + .seat = seat, + .serial = serial, + .edges = edges, + }; + + wlr_signal_emit_safe(&surface->events.request_resize, &event); +} + +static void xdg_toplevel_protocol_set_max_size(struct wl_client *client, + struct wl_resource *resource, int32_t width, int32_t height) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + surface->toplevel_state->next.max_width = width; + surface->toplevel_state->next.max_height = height; +} + +static void xdg_toplevel_protocol_set_min_size(struct wl_client *client, + struct wl_resource *resource, int32_t width, int32_t height) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + surface->toplevel_state->next.min_width = width; + surface->toplevel_state->next.min_height = height; +} + +static void xdg_toplevel_protocol_set_maximized(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + surface->toplevel_state->next.maximized = true; + wlr_signal_emit_safe(&surface->events.request_maximize, surface); +} + +static void xdg_toplevel_protocol_unset_maximized(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + surface->toplevel_state->next.maximized = false; + wlr_signal_emit_safe(&surface->events.request_maximize, surface); +} + +static void xdg_toplevel_protocol_set_fullscreen(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *output_resource) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + + struct wlr_output *output = NULL; + if (output_resource != NULL) { + output = wl_resource_get_user_data(output_resource); + } + + surface->toplevel_state->next.fullscreen = true; + + struct wlr_xdg_toplevel_set_fullscreen_event event = { + .surface = surface, + .fullscreen = true, + .output = output, + }; + + wlr_signal_emit_safe(&surface->events.request_fullscreen, &event); +} + +static void xdg_toplevel_protocol_unset_fullscreen(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + + surface->toplevel_state->next.fullscreen = false; + + struct wlr_xdg_toplevel_set_fullscreen_event event = { + .surface = surface, + .fullscreen = false, + .output = NULL, + }; + + wlr_signal_emit_safe(&surface->events.request_fullscreen, &event); +} + +static void xdg_toplevel_protocol_set_minimized(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + wlr_signal_emit_safe(&surface->events.request_minimize, surface); +} + +static const struct xdg_toplevel_interface xdg_toplevel_implementation = +{ + .destroy = resource_destroy, + .set_parent = xdg_toplevel_protocol_set_parent, + .set_title = xdg_toplevel_protocol_set_title, + .set_app_id = xdg_toplevel_protocol_set_app_id, + .show_window_menu = xdg_toplevel_protocol_show_window_menu, + .move = xdg_toplevel_protocol_move, + .resize = xdg_toplevel_protocol_resize, + .set_max_size = xdg_toplevel_protocol_set_max_size, + .set_min_size = xdg_toplevel_protocol_set_min_size, + .set_maximized = xdg_toplevel_protocol_set_maximized, + .unset_maximized = xdg_toplevel_protocol_unset_maximized, + .set_fullscreen = xdg_toplevel_protocol_set_fullscreen, + .unset_fullscreen = xdg_toplevel_protocol_unset_fullscreen, + .set_minimized = xdg_toplevel_protocol_set_minimized +}; + +static void xdg_surface_resource_destroy(struct wl_resource *resource) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + if (surface != NULL) { + xdg_surface_destroy(surface); + } +} + +static void xdg_toplevel_resource_destroy(struct wl_resource *resource) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + if (surface != NULL) { + xdg_surface_destroy(surface); + } +} + +static void xdg_surface_get_toplevel(struct wl_client *client, + struct wl_resource *resource, uint32_t id) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + + if (wlr_surface_set_role(surface->surface, wlr_desktop_xdg_toplevel_role, + resource, XDG_WM_BASE_ERROR_ROLE)) { + return; + } + + surface->toplevel_state = calloc(1, sizeof(struct wlr_xdg_toplevel)); + if (surface->toplevel_state == NULL) { + wl_resource_post_no_memory(resource); + return; + } + + surface->role = WLR_XDG_SURFACE_ROLE_TOPLEVEL; + surface->toplevel_state->base = surface; + + struct wl_resource *toplevel_resource = wl_resource_create(client, + &xdg_toplevel_interface, wl_resource_get_version(resource), id); + if (toplevel_resource == NULL) { + free(surface->toplevel_state); + wl_resource_post_no_memory(resource); + return; + } + + surface->toplevel_state->resource = toplevel_resource; + + wl_resource_set_implementation(toplevel_resource, + &xdg_toplevel_implementation, surface, + xdg_toplevel_resource_destroy); +} + +static void wlr_xdg_toplevel_ack_configure( + struct wlr_xdg_surface *surface, + struct wlr_xdg_surface_configure *configure) { + assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + surface->toplevel_state->next = configure->state; + surface->toplevel_state->pending.width = 0; + surface->toplevel_state->pending.height = 0; +} + +static void xdg_surface_ack_configure(struct wl_client *client, + struct wl_resource *resource, uint32_t serial) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + + if (surface->role == WLR_XDG_SURFACE_ROLE_NONE) { + wl_resource_post_error(surface->resource, + XDG_SURFACE_ERROR_NOT_CONSTRUCTED, + "xdg_surface must have a role"); + return; + } + + bool found = false; + struct wlr_xdg_surface_configure *configure, *tmp; + wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) { + if (configure->serial < serial) { + wl_list_remove(&configure->link); + free(configure); + } else if (configure->serial == serial) { + wl_list_remove(&configure->link); + found = true; + break; + } else { + break; + } + } + if (!found) { + wl_resource_post_error(surface->client->resource, + XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE, + "wrong configure serial: %u", serial); + return; + } + + switch (surface->role) { + case WLR_XDG_SURFACE_ROLE_NONE: + assert(0 && "not reached"); + break; + case WLR_XDG_SURFACE_ROLE_TOPLEVEL: + wlr_xdg_toplevel_ack_configure(surface, configure); + break; + case WLR_XDG_SURFACE_ROLE_POPUP: + break; + } + + surface->configured = true; + surface->configure_serial = serial; + + free(configure); +} + +static void xdg_surface_set_window_geometry(struct wl_client *client, + struct wl_resource *resource, int32_t x, int32_t y, int32_t width, + int32_t height) { + struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + + if (surface->role == WLR_XDG_SURFACE_ROLE_NONE) { + wl_resource_post_error(surface->resource, + XDG_SURFACE_ERROR_NOT_CONSTRUCTED, + "xdg_surface must have a role"); + return; + } + + surface->has_next_geometry = true; + surface->next_geometry->height = height; + surface->next_geometry->width = width; + surface->next_geometry->x = x; + surface->next_geometry->y = y; + +} + +static const struct xdg_surface_interface xdg_surface_implementation = { + .destroy = resource_destroy, + .get_toplevel = xdg_surface_get_toplevel, + .get_popup = xdg_surface_get_popup, + .ack_configure = xdg_surface_ack_configure, + .set_window_geometry = xdg_surface_set_window_geometry, +}; + +static bool wlr_xdg_surface_toplevel_state_compare( + struct wlr_xdg_toplevel *state) { + struct { + struct wlr_xdg_toplevel_state state; + uint32_t width; + uint32_t height; + + } configured; + + // is pending state different from current state? + if (!state->base->configured) { + return false; + } + + if (wl_list_empty(&state->base->configure_list)) { + // last configure is actually the current state, just use it + configured.state = state->current; + configured.width = state->base->surface->current->width; + configured.height = state->base->surface->current->width; + } else { + struct wlr_xdg_surface_configure *configure = + wl_container_of(state->base->configure_list.prev, configure, link); + configured.state = configure->state; + configured.width = configure->state.width; + configured.height = configure->state.height; + } + + if (state->pending.activated != configured.state.activated) { + return false; + } + if (state->pending.fullscreen != configured.state.fullscreen) { + return false; + } + if (state->pending.maximized != configured.state.maximized) { + return false; + } + if (state->pending.resizing != configured.state.resizing) { + return false; + } + + if (state->pending.width == configured.width && + state->pending.height == configured.height) { + return true; + } + + if (state->pending.width == 0 && state->pending.height == 0) { + return true; + } + + return false; +} + +static void wlr_xdg_toplevel_send_configure( + struct wlr_xdg_surface *surface, + struct wlr_xdg_surface_configure *configure) { + assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + uint32_t *s; + struct wl_array states; + + configure->state = surface->toplevel_state->pending; + + wl_array_init(&states); + if (surface->toplevel_state->pending.maximized) { + s = wl_array_add(&states, sizeof(uint32_t)); + if (!s) { + wlr_log(L_ERROR, "Could not allocate state for maximized xdg_toplevel"); + goto error_out; + } + *s = XDG_TOPLEVEL_STATE_MAXIMIZED; + } + if (surface->toplevel_state->pending.fullscreen) { + s = wl_array_add(&states, sizeof(uint32_t)); + if (!s) { + wlr_log(L_ERROR, "Could not allocate state for fullscreen xdg_toplevel"); + goto error_out; + } + *s = XDG_TOPLEVEL_STATE_FULLSCREEN; + } + if (surface->toplevel_state->pending.resizing) { + s = wl_array_add(&states, sizeof(uint32_t)); + if (!s) { + wlr_log(L_ERROR, "Could not allocate state for resizing xdg_toplevel"); + goto error_out; + } + *s = XDG_TOPLEVEL_STATE_RESIZING; + } + if (surface->toplevel_state->pending.activated) { + s = wl_array_add(&states, sizeof(uint32_t)); + if (!s) { + wlr_log(L_ERROR, "Could not allocate state for activated xdg_toplevel"); + goto error_out; + } + *s = XDG_TOPLEVEL_STATE_ACTIVATED; + } + + uint32_t width = surface->toplevel_state->pending.width; + uint32_t height = surface->toplevel_state->pending.height; + + if (width == 0 || height == 0) { + width = surface->geometry->width; + height = surface->geometry->height; + } + + xdg_toplevel_send_configure(surface->toplevel_state->resource, width, + height, &states); + + wl_array_release(&states); + return; + +error_out: + wl_array_release(&states); + wl_resource_post_no_memory(surface->toplevel_state->resource); +} + +static void wlr_xdg_surface_send_configure(void *user_data) { + struct wlr_xdg_surface *surface = user_data; + + surface->configure_idle = NULL; + + struct wlr_xdg_surface_configure *configure = + calloc(1, sizeof(struct wlr_xdg_surface_configure)); + if (configure == NULL) { + wl_client_post_no_memory(surface->client->client); + return; + } + + wl_list_insert(surface->configure_list.prev, &configure->link); + configure->serial = surface->configure_next_serial; + + switch (surface->role) { + case WLR_XDG_SURFACE_ROLE_NONE: + assert(0 && "not reached"); + break; + case WLR_XDG_SURFACE_ROLE_TOPLEVEL: + wlr_xdg_toplevel_send_configure(surface, configure); + break; + case WLR_XDG_SURFACE_ROLE_POPUP: + xdg_popup_send_configure(surface->popup_state->resource, + surface->popup_state->geometry.x, + surface->popup_state->geometry.y, + surface->popup_state->geometry.width, + surface->popup_state->geometry.height); + break; + } + + xdg_surface_send_configure(surface->resource, configure->serial); +} + +static uint32_t wlr_xdg_surface_schedule_configure( + struct wlr_xdg_surface *surface) { + struct wl_display *display = wl_client_get_display(surface->client->client); + struct wl_event_loop *loop = wl_display_get_event_loop(display); + bool pending_same = false; + + switch (surface->role) { + case WLR_XDG_SURFACE_ROLE_NONE: + assert(0 && "not reached"); + break; + case WLR_XDG_SURFACE_ROLE_TOPLEVEL: + pending_same = + wlr_xdg_surface_toplevel_state_compare(surface->toplevel_state); + break; + case WLR_XDG_SURFACE_ROLE_POPUP: + break; + } + + if (surface->configure_idle != NULL) { + if (!pending_same) { + // configure request already scheduled + return surface->configure_next_serial; + } + + // configure request not necessary anymore + wl_event_source_remove(surface->configure_idle); + surface->configure_idle = NULL; + return 0; + } else { + if (pending_same) { + // configure request not necessary + return 0; + } + + surface->configure_next_serial = wl_display_next_serial(display); + surface->configure_idle = wl_event_loop_add_idle(loop, + wlr_xdg_surface_send_configure, surface); + return surface->configure_next_serial; + } +} + +static void handle_wlr_surface_destroyed(struct wl_listener *listener, + void *data) { + struct wlr_xdg_surface *xdg_surface = + wl_container_of(listener, xdg_surface, surface_destroy_listener); + xdg_surface_destroy(xdg_surface); +} + +static void wlr_xdg_surface_toplevel_committed( + struct wlr_xdg_surface *surface) { + assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + + if (!wlr_surface_has_buffer(surface->surface) + && !surface->toplevel_state->added) { + // on the first commit, send a configure request to tell the client it + // is added + wlr_xdg_surface_schedule_configure(surface); + surface->toplevel_state->added = true; + return; + } + + if (!wlr_surface_has_buffer(surface->surface)) { + return; + } + + surface->toplevel_state->current = surface->toplevel_state->next; +} + +static void wlr_xdg_surface_popup_committed( + struct wlr_xdg_surface *surface) { + assert(surface->role == WLR_XDG_SURFACE_ROLE_POPUP); + + if (!surface->popup_state->committed) { + wlr_xdg_surface_schedule_configure(surface); + surface->popup_state->committed = true; + } +} + +static void handle_wlr_surface_committed(struct wlr_surface *wlr_surface, + void *role_data) { + struct wlr_xdg_surface *surface = role_data; + + if (wlr_surface_has_buffer(surface->surface) && !surface->configured) { + wl_resource_post_error(surface->resource, + XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, + "xdg_surface has never been configured"); + return; + } + + if (surface->has_next_geometry) { + surface->has_next_geometry = false; + surface->geometry->x = surface->next_geometry->x; + surface->geometry->y = surface->next_geometry->y; + surface->geometry->width = surface->next_geometry->width; + surface->geometry->height = surface->next_geometry->height; + } + + switch (surface->role) { + case WLR_XDG_SURFACE_ROLE_NONE: + wl_resource_post_error(surface->resource, + XDG_SURFACE_ERROR_NOT_CONSTRUCTED, + "xdg_surface must have a role"); + break; + case WLR_XDG_SURFACE_ROLE_TOPLEVEL: + wlr_xdg_surface_toplevel_committed(surface); + break; + case WLR_XDG_SURFACE_ROLE_POPUP: + wlr_xdg_surface_popup_committed(surface); + break; + } + + if (surface->configured && !surface->added) { + surface->added = true; + wlr_signal_emit_safe(&surface->client->shell->events.new_surface, surface); + } +} + +static void xdg_shell_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 = + wl_resource_get_user_data(client_resource); + + struct wlr_xdg_surface *surface; + if (!(surface = calloc(1, sizeof(struct wlr_xdg_surface)))) { + wl_client_post_no_memory(wl_client); + return; + } + + if (!(surface->geometry = calloc(1, sizeof(struct wlr_box)))) { + free(surface); + wl_client_post_no_memory(wl_client); + return; + } + + if (!(surface->next_geometry = calloc(1, sizeof(struct wlr_box)))) { + free(surface->geometry); + free(surface); + wl_client_post_no_memory(wl_client); + return; + } + + surface->client = client; + surface->role = WLR_XDG_SURFACE_ROLE_NONE; + surface->surface = wl_resource_get_user_data(surface_resource); + surface->resource = wl_resource_create(wl_client, + &xdg_surface_interface, wl_resource_get_version(client_resource), + id); + if (surface->resource == NULL) { + free(surface->next_geometry); + free(surface->geometry); + free(surface); + wl_client_post_no_memory(wl_client); + return; + } + + if (wlr_surface_has_buffer(surface->surface)) { + wl_resource_destroy(surface->resource); + free(surface->next_geometry); + free(surface->geometry); + free(surface); + wl_resource_post_error(surface_resource, + XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, + "xdg_surface must not have a buffer at creation"); + return; + } + + wl_list_init(&surface->configure_list); + wl_list_init(&surface->popups); + + wl_signal_init(&surface->events.request_maximize); + wl_signal_init(&surface->events.request_fullscreen); + wl_signal_init(&surface->events.request_minimize); + wl_signal_init(&surface->events.request_move); + wl_signal_init(&surface->events.request_resize); + wl_signal_init(&surface->events.request_show_window_menu); + wl_signal_init(&surface->events.destroy); + wl_signal_init(&surface->events.ping_timeout); + wl_signal_init(&surface->events.new_popup); + + wl_signal_add(&surface->surface->events.destroy, + &surface->surface_destroy_listener); + surface->surface_destroy_listener.notify = handle_wlr_surface_destroyed; + + wlr_surface_set_role_committed(surface->surface, + handle_wlr_surface_committed, surface); + + wlr_log(L_DEBUG, "new xdg_surface %p (res %p)", surface, surface->resource); + wl_resource_set_implementation(surface->resource, + &xdg_surface_implementation, surface, xdg_surface_resource_destroy); + wl_list_insert(&client->surfaces, &surface->link); +} + +static void xdg_shell_pong(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t serial) { + struct wlr_xdg_client *client = wl_resource_get_user_data(resource); + + if (client->ping_serial != serial) { + return; + } + + wl_event_source_timer_update(client->ping_timer, 0); + client->ping_serial = 0; +} + +static struct xdg_wm_base_interface xdg_shell_impl = { + .destroy = resource_destroy, + .create_positioner = xdg_shell_create_positioner, + .get_xdg_surface = xdg_shell_get_xdg_surface, + .pong = xdg_shell_pong, +}; + +static void wlr_xdg_client_destroy(struct wl_resource *resource) { + struct wlr_xdg_client *client = wl_resource_get_user_data(resource); + + struct wlr_xdg_surface *surface, *tmp = NULL; + wl_list_for_each_safe(surface, tmp, &client->surfaces, link) { + xdg_surface_destroy(surface); + } + + if (client->ping_timer != NULL) { + wl_event_source_remove(client->ping_timer); + } + + wl_list_remove(&client->link); + free(client); +} + +static int wlr_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) { + wlr_signal_emit_safe(&surface->events.ping_timeout, surface); + } + + 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; + assert(wl_client && xdg_shell); + + struct wlr_xdg_client *client = + calloc(1, sizeof(struct wlr_xdg_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, + wlr_xdg_client_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, + wlr_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); + wlr_xdg_shell_destroy(xdg_shell); +} + +struct wlr_xdg_shell *wlr_xdg_shell_create(struct wl_display *display) { + struct wlr_xdg_shell *xdg_shell = + calloc(1, sizeof(struct wlr_xdg_shell)); + if (!xdg_shell) { + return NULL; + } + + xdg_shell->ping_timeout = 10000; + + wl_list_init(&xdg_shell->clients); + wl_list_init(&xdg_shell->popup_grabs); + + struct wl_global *wl_global = wl_global_create(display, + &xdg_wm_base_interface, 1, xdg_shell, xdg_shell_bind); + if (!wl_global) { + free(xdg_shell); + return NULL; + } + xdg_shell->wl_global = wl_global; + + wl_signal_init(&xdg_shell->events.new_surface); + + xdg_shell->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &xdg_shell->display_destroy); + + return xdg_shell; +} + +void wlr_xdg_shell_destroy(struct wlr_xdg_shell *xdg_shell) { + if (!xdg_shell) { + return; + } + wl_list_remove(&xdg_shell->display_destroy.link); + wl_global_destroy(xdg_shell->wl_global); + free(xdg_shell); +} + +void wlr_xdg_surface_ping(struct wlr_xdg_surface *surface) { + if (surface->client->ping_serial != 0) { + // already pinged + return; + } + + surface->client->ping_serial = + wl_display_next_serial(wl_client_get_display(surface->client->client)); + wl_event_source_timer_update(surface->client->ping_timer, + surface->client->shell->ping_timeout); + xdg_wm_base_send_ping(surface->client->resource, + surface->client->ping_serial); +} + +uint32_t wlr_xdg_toplevel_set_size(struct wlr_xdg_surface *surface, + uint32_t width, uint32_t height) { + assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + surface->toplevel_state->pending.width = width; + surface->toplevel_state->pending.height = height; + + return wlr_xdg_surface_schedule_configure(surface); +} + +uint32_t wlr_xdg_toplevel_set_activated(struct wlr_xdg_surface *surface, + bool activated) { + assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + surface->toplevel_state->pending.activated = activated; + + return wlr_xdg_surface_schedule_configure(surface); +} + +uint32_t wlr_xdg_toplevel_set_maximized(struct wlr_xdg_surface *surface, + bool maximized) { + assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + surface->toplevel_state->pending.maximized = maximized; + + return wlr_xdg_surface_schedule_configure(surface); +} + +uint32_t wlr_xdg_toplevel_set_fullscreen(struct wlr_xdg_surface *surface, + bool fullscreen) { + assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + surface->toplevel_state->pending.fullscreen = fullscreen; + + return wlr_xdg_surface_schedule_configure(surface); +} + +uint32_t wlr_xdg_toplevel_set_resizing(struct wlr_xdg_surface *surface, + bool resizing) { + assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + surface->toplevel_state->pending.resizing = resizing; + + return wlr_xdg_surface_schedule_configure(surface); +} + +void wlr_xdg_toplevel_send_close(struct wlr_xdg_surface *surface) { + assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + xdg_toplevel_send_close(surface->toplevel_state->resource); +} + +void wlr_xdg_surface_popup_get_position(struct wlr_xdg_surface *surface, + double *popup_sx, double *popup_sy) { + assert(surface->role == WLR_XDG_SURFACE_ROLE_POPUP); + struct wlr_xdg_surface *parent = surface->popup_state->parent; + *popup_sx = parent->geometry->x + surface->popup_state->geometry.x - + surface->geometry->x; + *popup_sy = parent->geometry->y + surface->popup_state->geometry.y - + surface->geometry->y; +} + +struct wlr_xdg_surface *wlr_xdg_surface_popup_at( + struct wlr_xdg_surface *surface, double sx, double sy, + double *popup_sx, double *popup_sy) { + // XXX: I think this is so complicated because we're mixing geometry + // coordinates with surface coordinates. Input handling should only deal + // with surface coordinates. + struct wlr_xdg_popup *popup_state; + wl_list_for_each(popup_state, &surface->popups, link) { + struct wlr_xdg_surface *popup = popup_state->base; + + double _popup_sx = + surface->geometry->x + popup_state->geometry.x; + double _popup_sy = + surface->geometry->y + popup_state->geometry.y; + int popup_width = popup_state->geometry.width; + int popup_height = popup_state->geometry.height; + + struct wlr_xdg_surface *_popup = + wlr_xdg_surface_popup_at(popup, + sx - _popup_sx + popup->geometry->x, + sy - _popup_sy + popup->geometry->y, + popup_sx, popup_sy); + if (_popup) { + *popup_sx = *popup_sx + _popup_sx - popup->geometry->x; + *popup_sy = *popup_sy + _popup_sy - popup->geometry->y; + return _popup; + } + + if ((sx > _popup_sx && sx < _popup_sx + popup_width) && + (sy > _popup_sy && sy < _popup_sy + popup_height)) { + if (pixman_region32_contains_point(&popup->surface->current->input, + sx - _popup_sx + popup->geometry->x, + sy - _popup_sy + popup->geometry->y, NULL)) { + *popup_sx = _popup_sx - popup->geometry->x; + *popup_sy = _popup_sy - popup->geometry->y; + return popup; + } + } + } + + return NULL; +} From 1080bf69f8b9d1b25287fd987266f45602480735 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Wed, 14 Feb 2018 19:02:24 +0100 Subject: [PATCH 02/10] xdg_shell: add input validatoin for positioner anchor/gravity --- types/wlr_xdg_shell.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/types/wlr_xdg_shell.c b/types/wlr_xdg_shell.c index 6b2d42ee..985c180a 100644 --- a/types/wlr_xdg_shell.c +++ b/types/wlr_xdg_shell.c @@ -270,7 +270,12 @@ static void xdg_positioner_protocol_set_anchor(struct wl_client *client, struct wlr_xdg_positioner *positioner = wl_resource_get_user_data(resource); - /* post error if anchor > XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT ? */ + if (anchor > XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT) { + wl_resource_post_error(resource, + XDG_POSITIONER_ERROR_INVALID_INPUT, + "invalid anchor value"); + return; + } positioner->anchor = anchor; } @@ -280,7 +285,12 @@ static void xdg_positioner_protocol_set_gravity(struct wl_client *client, struct wlr_xdg_positioner *positioner = wl_resource_get_user_data(resource); - /* post error if gravity > XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT ? */ + if (gravity > XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) { + wl_resource_post_error(resource, + XDG_POSITIONER_ERROR_INVALID_INPUT, + "invalid gravity value"); + return; + } positioner->gravity = gravity; } From db9c2c11f65dbc1e01485b8afaa21c874d15140f Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Wed, 14 Feb 2018 19:02:45 +0100 Subject: [PATCH 03/10] meson: require wayland-protocols >=1.12 for xdg-shell stable --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 1cf7781a..2fc78d33 100644 --- a/meson.build +++ b/meson.build @@ -49,7 +49,7 @@ add_project_arguments('-DWL_HIDE_DEPRECATED', language: 'c') wayland_server = dependency('wayland-server') wayland_client = dependency('wayland-client') wayland_egl = dependency('wayland-egl') -wayland_protos = dependency('wayland-protocols') +wayland_protos = dependency('wayland-protocols', version: '>=1.12') egl = dependency('egl') glesv2 = dependency('glesv2') drm = dependency('libdrm') From 6ae96c4832626642670aa6fdc207f659812753f3 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Thu, 15 Feb 2018 07:16:28 +0100 Subject: [PATCH 04/10] wlr_xdg_shell: (style) add break to final switch case --- types/wlr_xdg_shell.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/types/wlr_xdg_shell.c b/types/wlr_xdg_shell.c index 985c180a..0735d416 100644 --- a/types/wlr_xdg_shell.c +++ b/types/wlr_xdg_shell.c @@ -420,6 +420,7 @@ static struct wlr_box xdg_positioner_get_geometry( default: geometry.y += positioner->anchor_rect.y + positioner->anchor_rect.height / 2; + break; } switch (positioner->anchor) { @@ -436,6 +437,7 @@ static struct wlr_box xdg_positioner_get_geometry( default: geometry.x += positioner->anchor_rect.x + positioner->anchor_rect.width / 2; + break; } switch (positioner->gravity) { @@ -451,6 +453,7 @@ static struct wlr_box xdg_positioner_get_geometry( break; default: geometry.y -= geometry.height / 2; + break; } switch (positioner->gravity) { @@ -466,6 +469,7 @@ static struct wlr_box xdg_positioner_get_geometry( break; default: geometry.x -= geometry.width / 2; + break; } if (positioner->constraint_adjustment == From d1b28ec812248b337fd53f9478c36234f44c71f3 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Thu, 15 Feb 2018 07:17:02 +0100 Subject: [PATCH 05/10] wayland protocol headers: use double-quote for includes These headers are not installed so we should look for these locally --- examples/idle.c | 2 +- examples/screenshot.c | 12 ++++++------ types/wlr_primary_selection.c | 2 +- types/wlr_server_decoration.c | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/idle.c b/examples/idle.c index 57c366d1..2b155c68 100644 --- a/examples/idle.c +++ b/examples/idle.c @@ -6,8 +6,8 @@ #include #include #include -#include #include +#include "idle-client-protocol.h" static struct org_kde_kwin_idle *idle_manager = NULL; static struct wl_seat *seat = NULL; diff --git a/examples/screenshot.c b/examples/screenshot.c index 7de2ab8e..c163df75 100644 --- a/examples/screenshot.c +++ b/examples/screenshot.c @@ -23,20 +23,20 @@ #define _XOPEN_SOURCE 700 #define _POSIX_C_SOURCE 199309L +#include +#include +#include #include #include #include #include +#include #include #include -#include -#include #include -#include -#include -#include -#include "util/os-compatibility.h" #include +#include "screenshooter-client-protocol.h" +#include "util/os-compatibility.h" static struct wl_shm *shm = NULL; static struct orbital_screenshooter *screenshooter = NULL; diff --git a/types/wlr_primary_selection.c b/types/wlr_primary_selection.c index 28fe63c1..2ce071e7 100644 --- a/types/wlr_primary_selection.c +++ b/types/wlr_primary_selection.c @@ -1,12 +1,12 @@ #define _XOPEN_SOURCE 700 #include -#include #include #include #include #include #include #include +#include "gtk-primary-selection-protocol.h" #include "util/signal.h" static void offer_handle_receive(struct wl_client *client, diff --git a/types/wlr_server_decoration.c b/types/wlr_server_decoration.c index 556193e2..f483d741 100644 --- a/types/wlr_server_decoration.c +++ b/types/wlr_server_decoration.c @@ -1,9 +1,9 @@ #include -#include #include #include #include #include +#include "server-decoration-protocol.h" #include "util/signal.h" static void server_decoration_handle_release(struct wl_client *client, From b46cc3cafd38fcb9fdeddfb3c26cff71f17e40bd Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Thu, 15 Feb 2018 07:18:15 +0100 Subject: [PATCH 06/10] wayland protocol build: remove client protocols we do not use Only include client protocols that we use on the client side. Since these are not installed, there should not be any change with this. Testers - please note 'ninja -C build clean' does not remove the old headers, you need to start from a new directory. --- protocol/meson.build | 4 ---- 1 file changed, 4 deletions(-) diff --git a/protocol/meson.build b/protocol/meson.build index 514a6dde..2853971f 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -32,12 +32,8 @@ protocols = [ client_protocols = [ [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], - [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], - 'gamma-control.xml', - 'gtk-primary-selection.xml', 'idle.xml', 'screenshooter.xml', - 'server-decoration.xml', ] wl_protos_src = [] From 714f90a9d0027d562bf22a563edee23ca877e618 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Thu, 15 Feb 2018 07:29:55 +0100 Subject: [PATCH 07/10] xdg_shell stable: allow zero-sized positioner set_anchor_rect This is a protocol difference with xdg-shell-unstable-v6 --- types/wlr_xdg_shell.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/wlr_xdg_shell.c b/types/wlr_xdg_shell.c index 0735d416..914ee322 100644 --- a/types/wlr_xdg_shell.c +++ b/types/wlr_xdg_shell.c @@ -252,10 +252,10 @@ static void xdg_positioner_protocol_set_anchor_rect(struct wl_client *client, struct wlr_xdg_positioner *positioner = wl_resource_get_user_data(resource); - if (width < 1 || height < 1) { + if (width < 0 || height < 0) { wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, - "width and height must be positives and non-zero"); + "width and height must be positives"); return; } From 56ab3e9b10fdaaf41eef6e47f81ffe54fd9d5a77 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Thu, 15 Feb 2018 08:07:57 +0100 Subject: [PATCH 08/10] xdg_shell stable: fix zero-sized positioner anchor_rect We used 0 as unset-check value before, which was fine when 0-width was invalid, but isn't anymore --- types/wlr_xdg_shell.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/types/wlr_xdg_shell.c b/types/wlr_xdg_shell.c index 914ee322..53ff25d0 100644 --- a/types/wlr_xdg_shell.c +++ b/types/wlr_xdg_shell.c @@ -334,6 +334,10 @@ static void xdg_shell_create_positioner(struct wl_client *wl_client, return; } + /* set widths to detect improper usages of get_popup */ + positioner->size.width = -1; + positioner->anchor_rect.width = -1; + positioner->resource = wl_resource_create(wl_client, &xdg_positioner_interface, wl_resource_get_version(resource), @@ -500,7 +504,7 @@ static void xdg_surface_get_popup(struct wl_client *client, struct wlr_xdg_positioner *positioner = wl_resource_get_user_data(positioner_resource); - if (positioner->size.width == 0 || positioner->anchor_rect.width == 0) { + if (positioner->size.width == -1 || positioner->anchor_rect.width == -1) { wl_resource_post_error(resource, XDG_WM_BASE_ERROR_INVALID_POSITIONER, "positioner object is not complete"); From ac78bdb6bceb8e50fd490570c7d106209c9a0a0a Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Thu, 15 Feb 2018 12:26:20 +0100 Subject: [PATCH 09/10] xdg shells: fix typo s/positives/positive/ --- types/wlr_xdg_shell.c | 4 ++-- types/wlr_xdg_shell_v6.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/types/wlr_xdg_shell.c b/types/wlr_xdg_shell.c index 53ff25d0..48ace842 100644 --- a/types/wlr_xdg_shell.c +++ b/types/wlr_xdg_shell.c @@ -238,7 +238,7 @@ static void xdg_positioner_protocol_set_size(struct wl_client *client, if (width < 1 || height < 1) { wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, - "width and height must be positives and non-zero"); + "width and height must be positive and non-zero"); return; } @@ -255,7 +255,7 @@ static void xdg_positioner_protocol_set_anchor_rect(struct wl_client *client, if (width < 0 || height < 0) { wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, - "width and height must be positives"); + "width and height must be positive"); return; } diff --git a/types/wlr_xdg_shell_v6.c b/types/wlr_xdg_shell_v6.c index eb18f022..a3bd16d2 100644 --- a/types/wlr_xdg_shell_v6.c +++ b/types/wlr_xdg_shell_v6.c @@ -238,7 +238,7 @@ static void xdg_positioner_protocol_set_size(struct wl_client *client, if (width < 1 || height < 1) { wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, - "width and height must be positives and non-zero"); + "width and height must be positive and non-zero"); return; } @@ -255,7 +255,7 @@ static void xdg_positioner_protocol_set_anchor_rect(struct wl_client *client, if (width < 1 || height < 1) { wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, - "width and height must be positives and non-zero"); + "width and height must be positive and non-zero"); return; } From 4b354745fedba78de8cb3402873464b33a818806 Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 16 Feb 2018 18:38:06 +0100 Subject: [PATCH 10/10] xdg-shell: wrap wl_resource_get_user_data --- types/wlr_xdg_shell.c | 241 ++++++++++++++++++++++++--------------- types/wlr_xdg_shell_v6.c | 7 +- 2 files changed, 153 insertions(+), 95 deletions(-) diff --git a/types/wlr_xdg_shell.c b/types/wlr_xdg_shell.c index 53ff25d0..fcedf804 100644 --- a/types/wlr_xdg_shell.c +++ b/types/wlr_xdg_shell.c @@ -223,17 +223,25 @@ static void xdg_surface_destroy(struct wlr_xdg_surface *surface) { } +static const struct xdg_positioner_interface xdg_positioner_implementation; + +static struct wlr_xdg_positioner *xdg_positioner_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &xdg_positioner_interface, + &xdg_positioner_implementation)); + return wl_resource_get_user_data(resource); +} + static void xdg_positioner_destroy(struct wl_resource *resource) { struct wlr_xdg_positioner *positioner = - wl_resource_get_user_data(resource); + xdg_positioner_from_resource(resource); free(positioner); - } static void xdg_positioner_protocol_set_size(struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { struct wlr_xdg_positioner *positioner = - wl_resource_get_user_data(resource); + xdg_positioner_from_resource(resource); if (width < 1 || height < 1) { wl_resource_post_error(resource, @@ -250,7 +258,7 @@ static void xdg_positioner_protocol_set_anchor_rect(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { struct wlr_xdg_positioner *positioner = - wl_resource_get_user_data(resource); + xdg_positioner_from_resource(resource); if (width < 0 || height < 0) { wl_resource_post_error(resource, @@ -268,7 +276,7 @@ static void xdg_positioner_protocol_set_anchor_rect(struct wl_client *client, static void xdg_positioner_protocol_set_anchor(struct wl_client *client, struct wl_resource *resource, uint32_t anchor) { struct wlr_xdg_positioner *positioner = - wl_resource_get_user_data(resource); + xdg_positioner_from_resource(resource); if (anchor > XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT) { wl_resource_post_error(resource, @@ -283,7 +291,7 @@ static void xdg_positioner_protocol_set_anchor(struct wl_client *client, static void xdg_positioner_protocol_set_gravity(struct wl_client *client, struct wl_resource *resource, uint32_t gravity) { struct wlr_xdg_positioner *positioner = - wl_resource_get_user_data(resource); + xdg_positioner_from_resource(resource); if (gravity > XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) { wl_resource_post_error(resource, @@ -299,7 +307,7 @@ static void xdg_positioner_protocol_set_constraint_adjustment( struct wl_client *client, struct wl_resource *resource, uint32_t constraint_adjustment) { struct wlr_xdg_positioner *positioner = - wl_resource_get_user_data(resource); + xdg_positioner_from_resource(resource); positioner->constraint_adjustment = constraint_adjustment; } @@ -307,7 +315,7 @@ static void xdg_positioner_protocol_set_constraint_adjustment( static void xdg_positioner_protocol_set_offset(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y) { struct wlr_xdg_positioner *positioner = - wl_resource_get_user_data(resource); + xdg_positioner_from_resource(resource); positioner->offset.x = x; positioner->offset.y = y; @@ -353,52 +361,6 @@ static void xdg_shell_create_positioner(struct wl_client *wl_client, positioner, xdg_positioner_destroy); } -static void xdg_popup_protocol_grab(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *seat_resource, - uint32_t serial) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); - struct wlr_seat_client *seat_client = wl_resource_get_user_data(seat_resource); - - if (surface->popup_state->committed) { - wl_resource_post_error(surface->popup_state->resource, - XDG_POPUP_ERROR_INVALID_GRAB, - "xdg_popup is already mapped"); - return; - } - - struct wlr_xdg_popup_grab *popup_grab = - xdg_shell_popup_grab_from_seat(surface->client->shell, - seat_client->seat); - - struct wlr_xdg_surface *topmost = xdg_popup_grab_get_topmost(popup_grab); - bool parent_is_toplevel = - surface->popup_state->parent->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL; - - if ((topmost == NULL && !parent_is_toplevel) || - (topmost != NULL && topmost != surface->popup_state->parent)) { - wl_resource_post_error(surface->client->resource, - XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP, - "xdg_popup was not created on the topmost popup"); - return; - } - - popup_grab->client = surface->client->client; - surface->popup_state->seat = seat_client->seat; - - wl_list_insert(&popup_grab->popups, &surface->popup_state->grab_link); - - wlr_seat_pointer_start_grab(seat_client->seat, - &popup_grab->pointer_grab); - wlr_seat_keyboard_start_grab(seat_client->seat, - &popup_grab->keyboard_grab); -} - -static const struct xdg_popup_interface xdg_popup_implementation = { - .destroy = resource_destroy, - .grab = xdg_popup_protocol_grab, -}; - - static struct wlr_box xdg_positioner_get_geometry( struct wlr_xdg_positioner *positioner, struct wlr_xdg_surface *surface, struct wlr_xdg_surface *parent) { @@ -486,23 +448,90 @@ static struct wlr_box xdg_positioner_get_geometry( return geometry; } + +static const struct xdg_popup_interface xdg_popup_implementation; + +static struct wlr_xdg_surface *xdg_surface_from_xdg_popup_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &xdg_popup_interface, + &xdg_popup_implementation)); + return wl_resource_get_user_data(resource); +} + +static void xdg_popup_protocol_grab(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *seat_resource, + uint32_t serial) { + struct wlr_xdg_surface *surface = + xdg_surface_from_xdg_popup_resource(resource); + struct wlr_seat_client *seat_client = + wlr_seat_client_from_resource(seat_resource); + + if (surface->popup_state->committed) { + wl_resource_post_error(surface->popup_state->resource, + XDG_POPUP_ERROR_INVALID_GRAB, + "xdg_popup is already mapped"); + return; + } + + struct wlr_xdg_popup_grab *popup_grab = + xdg_shell_popup_grab_from_seat(surface->client->shell, + seat_client->seat); + + struct wlr_xdg_surface *topmost = xdg_popup_grab_get_topmost(popup_grab); + bool parent_is_toplevel = + surface->popup_state->parent->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL; + + if ((topmost == NULL && !parent_is_toplevel) || + (topmost != NULL && topmost != surface->popup_state->parent)) { + wl_resource_post_error(surface->client->resource, + XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP, + "xdg_popup was not created on the topmost popup"); + return; + } + + popup_grab->client = surface->client->client; + surface->popup_state->seat = seat_client->seat; + + wl_list_insert(&popup_grab->popups, &surface->popup_state->grab_link); + + wlr_seat_pointer_start_grab(seat_client->seat, + &popup_grab->pointer_grab); + wlr_seat_keyboard_start_grab(seat_client->seat, + &popup_grab->keyboard_grab); +} + +static const struct xdg_popup_interface xdg_popup_implementation = { + .destroy = resource_destroy, + .grab = xdg_popup_protocol_grab, +}; + static void xdg_popup_resource_destroy(struct wl_resource *resource) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = + xdg_surface_from_xdg_popup_resource(resource); if (surface != NULL) { xdg_surface_destroy(surface); } } +static const struct xdg_surface_interface xdg_surface_implementation; + +static struct wlr_xdg_surface *xdg_surface_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &xdg_surface_interface, + &xdg_surface_implementation)); + return wl_resource_get_user_data(resource); +} + static void xdg_surface_get_popup(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *parent_resource, struct wl_resource *positioner_resource) { struct wlr_xdg_surface *surface = - wl_resource_get_user_data(resource); + xdg_surface_from_resource(resource); struct wlr_xdg_surface *parent = - wl_resource_get_user_data(parent_resource); + xdg_surface_from_resource(parent_resource); struct wlr_xdg_positioner *positioner = - wl_resource_get_user_data(positioner_resource); + xdg_positioner_from_resource(positioner_resource); if (positioner->size.width == -1 || positioner->anchor_rect.width == -1) { wl_resource_post_error(resource, @@ -546,13 +575,23 @@ static void xdg_surface_get_popup(struct wl_client *client, } +static const struct xdg_toplevel_interface xdg_toplevel_implementation; + +static struct wlr_xdg_surface *xdg_surface_from_xdg_toplevel_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &xdg_toplevel_interface, + &xdg_toplevel_implementation)); + return wl_resource_get_user_data(resource); +} + static void xdg_toplevel_protocol_set_parent(struct wl_client *client, struct wl_resource *resource, struct wl_resource *parent_resource) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = + xdg_surface_from_xdg_toplevel_resource(resource); struct wlr_xdg_surface *parent = NULL; if (parent_resource != NULL) { - parent = wl_resource_get_user_data(parent_resource); + parent = xdg_surface_from_xdg_toplevel_resource(parent_resource); } surface->toplevel_state->parent = parent; @@ -560,7 +599,8 @@ static void xdg_toplevel_protocol_set_parent(struct wl_client *client, static void xdg_toplevel_protocol_set_title(struct wl_client *client, struct wl_resource *resource, const char *title) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = + xdg_surface_from_xdg_toplevel_resource(resource); char *tmp; tmp = strdup(title); @@ -574,7 +614,8 @@ static void xdg_toplevel_protocol_set_title(struct wl_client *client, static void xdg_toplevel_protocol_set_app_id(struct wl_client *client, struct wl_resource *resource, const char *app_id) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = + xdg_surface_from_xdg_toplevel_resource(resource); char *tmp; tmp = strdup(app_id); @@ -589,9 +630,10 @@ static void xdg_toplevel_protocol_set_app_id(struct wl_client *client, static void xdg_toplevel_protocol_show_window_menu(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial, int32_t x, int32_t y) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = + xdg_surface_from_xdg_toplevel_resource(resource); struct wlr_seat_client *seat = - wl_resource_get_user_data(seat_resource); + wlr_seat_client_from_resource(seat_resource); if (!surface->configured) { wl_resource_post_error(surface->toplevel_state->resource, @@ -619,9 +661,10 @@ static void xdg_toplevel_protocol_show_window_menu(struct wl_client *client, static void xdg_toplevel_protocol_move(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = + xdg_surface_from_xdg_toplevel_resource(resource); struct wlr_seat_client *seat = - wl_resource_get_user_data(seat_resource); + wlr_seat_client_from_resource(seat_resource); if (!surface->configured) { wl_resource_post_error(surface->toplevel_state->resource, @@ -647,9 +690,10 @@ static void xdg_toplevel_protocol_move(struct wl_client *client, static void xdg_toplevel_protocol_resize(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial, uint32_t edges) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = + xdg_surface_from_xdg_toplevel_resource(resource); struct wlr_seat_client *seat = - wl_resource_get_user_data(seat_resource); + wlr_seat_client_from_resource(seat_resource); if (!surface->configured) { wl_resource_post_error(surface->toplevel_state->resource, @@ -675,39 +719,44 @@ static void xdg_toplevel_protocol_resize(struct wl_client *client, static void xdg_toplevel_protocol_set_max_size(struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = + xdg_surface_from_xdg_toplevel_resource(resource); surface->toplevel_state->next.max_width = width; surface->toplevel_state->next.max_height = height; } static void xdg_toplevel_protocol_set_min_size(struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = + xdg_surface_from_xdg_toplevel_resource(resource); surface->toplevel_state->next.min_width = width; surface->toplevel_state->next.min_height = height; } static void xdg_toplevel_protocol_set_maximized(struct wl_client *client, struct wl_resource *resource) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = + xdg_surface_from_xdg_toplevel_resource(resource); surface->toplevel_state->next.maximized = true; wlr_signal_emit_safe(&surface->events.request_maximize, surface); } static void xdg_toplevel_protocol_unset_maximized(struct wl_client *client, struct wl_resource *resource) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = + xdg_surface_from_xdg_toplevel_resource(resource); surface->toplevel_state->next.maximized = false; wlr_signal_emit_safe(&surface->events.request_maximize, surface); } static void xdg_toplevel_protocol_set_fullscreen(struct wl_client *client, struct wl_resource *resource, struct wl_resource *output_resource) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = + xdg_surface_from_xdg_toplevel_resource(resource); struct wlr_output *output = NULL; if (output_resource != NULL) { - output = wl_resource_get_user_data(output_resource); + output = wlr_output_from_resource(output_resource); } surface->toplevel_state->next.fullscreen = true; @@ -723,7 +772,8 @@ static void xdg_toplevel_protocol_set_fullscreen(struct wl_client *client, static void xdg_toplevel_protocol_unset_fullscreen(struct wl_client *client, struct wl_resource *resource) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = + xdg_surface_from_xdg_toplevel_resource(resource); surface->toplevel_state->next.fullscreen = false; @@ -738,12 +788,12 @@ static void xdg_toplevel_protocol_unset_fullscreen(struct wl_client *client, static void xdg_toplevel_protocol_set_minimized(struct wl_client *client, struct wl_resource *resource) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = + xdg_surface_from_xdg_toplevel_resource(resource); wlr_signal_emit_safe(&surface->events.request_minimize, surface); } -static const struct xdg_toplevel_interface xdg_toplevel_implementation = -{ +static const struct xdg_toplevel_interface xdg_toplevel_implementation = { .destroy = resource_destroy, .set_parent = xdg_toplevel_protocol_set_parent, .set_title = xdg_toplevel_protocol_set_title, @@ -761,14 +811,16 @@ static const struct xdg_toplevel_interface xdg_toplevel_implementation = }; static void xdg_surface_resource_destroy(struct wl_resource *resource) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = + xdg_surface_from_resource(resource); if (surface != NULL) { xdg_surface_destroy(surface); } } static void xdg_toplevel_resource_destroy(struct wl_resource *resource) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = + xdg_surface_from_xdg_toplevel_resource(resource); if (surface != NULL) { xdg_surface_destroy(surface); } @@ -776,7 +828,7 @@ static void xdg_toplevel_resource_destroy(struct wl_resource *resource) { static void xdg_surface_get_toplevel(struct wl_client *client, struct wl_resource *resource, uint32_t id) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = xdg_surface_from_resource(resource); if (wlr_surface_set_role(surface->surface, wlr_desktop_xdg_toplevel_role, resource, XDG_WM_BASE_ERROR_ROLE)) { @@ -818,7 +870,7 @@ static void wlr_xdg_toplevel_ack_configure( static void xdg_surface_ack_configure(struct wl_client *client, struct wl_resource *resource, uint32_t serial) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = xdg_surface_from_resource(resource); if (surface->role == WLR_XDG_SURFACE_ROLE_NONE) { wl_resource_post_error(surface->resource, @@ -868,7 +920,7 @@ static void xdg_surface_ack_configure(struct wl_client *client, static void xdg_surface_set_window_geometry(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { - struct wlr_xdg_surface *surface = wl_resource_get_user_data(resource); + struct wlr_xdg_surface *surface = xdg_surface_from_resource(resource); if (surface->role == WLR_XDG_SURFACE_ROLE_NONE) { wl_resource_post_error(surface->resource, @@ -897,9 +949,7 @@ static bool wlr_xdg_surface_toplevel_state_compare( struct wlr_xdg_toplevel *state) { struct { struct wlr_xdg_toplevel_state state; - uint32_t width; - uint32_t height; - + uint32_t width, height; } configured; // is pending state different from current state? @@ -1158,11 +1208,20 @@ static void handle_wlr_surface_committed(struct wlr_surface *wlr_surface, } } +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_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 = - wl_resource_get_user_data(client_resource); + xdg_client_from_resource(client_resource); struct wlr_xdg_surface *surface; if (!(surface = calloc(1, sizeof(struct wlr_xdg_surface)))) { @@ -1185,7 +1244,7 @@ static void xdg_shell_get_xdg_surface(struct wl_client *wl_client, surface->client = client; surface->role = WLR_XDG_SURFACE_ROLE_NONE; - surface->surface = wl_resource_get_user_data(surface_resource); + surface->surface = wlr_surface_from_resource(surface_resource); surface->resource = wl_resource_create(wl_client, &xdg_surface_interface, wl_resource_get_version(client_resource), id); @@ -1236,7 +1295,7 @@ static void xdg_shell_get_xdg_surface(struct wl_client *wl_client, static void xdg_shell_pong(struct wl_client *wl_client, struct wl_resource *resource, uint32_t serial) { - struct wlr_xdg_client *client = wl_resource_get_user_data(resource); + struct wlr_xdg_client *client = xdg_client_from_resource(resource); if (client->ping_serial != serial) { return; @@ -1246,7 +1305,7 @@ static void xdg_shell_pong(struct wl_client *wl_client, client->ping_serial = 0; } -static struct xdg_wm_base_interface xdg_shell_impl = { +static const struct xdg_wm_base_interface xdg_shell_impl = { .destroy = resource_destroy, .create_positioner = xdg_shell_create_positioner, .get_xdg_surface = xdg_shell_get_xdg_surface, @@ -1254,7 +1313,7 @@ static struct xdg_wm_base_interface xdg_shell_impl = { }; static void wlr_xdg_client_destroy(struct wl_resource *resource) { - struct wlr_xdg_client *client = wl_resource_get_user_data(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) { diff --git a/types/wlr_xdg_shell_v6.c b/types/wlr_xdg_shell_v6.c index 1ea29664..aa833695 100644 --- a/types/wlr_xdg_shell_v6.c +++ b/types/wlr_xdg_shell_v6.c @@ -487,7 +487,8 @@ static void xdg_popup_resource_destroy(struct wl_resource *resource) { static const struct zxdg_surface_v6_interface zxdg_surface_v6_implementation; -static struct wlr_xdg_surface_v6 *xdg_surface_from_resource(struct wl_resource *resource) { +static struct wlr_xdg_surface_v6 *xdg_surface_from_resource( + struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zxdg_surface_v6_interface, &zxdg_surface_v6_implementation)); return wl_resource_get_user_data(resource); @@ -918,9 +919,7 @@ static bool wlr_xdg_surface_v6_toplevel_state_compare( struct wlr_xdg_toplevel_v6 *state) { struct { struct wlr_xdg_toplevel_v6_state state; - uint32_t width; - uint32_t height; - + uint32_t width, height; } configured; // is pending state different from current state?