From c665f905ff8c49d8ce651cac517fa76dfc5454f6 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sat, 30 Sep 2017 19:33:39 -0400 Subject: [PATCH 01/14] xdg-positioner --- types/wlr_xdg_shell_v6.c | 154 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 149 insertions(+), 5 deletions(-) diff --git a/types/wlr_xdg_shell_v6.c b/types/wlr_xdg_shell_v6.c index c30291d1..36ef7901 100644 --- a/types/wlr_xdg_shell_v6.c +++ b/types/wlr_xdg_shell_v6.c @@ -13,11 +13,160 @@ static const char *wlr_desktop_xdg_toplevel_role = "xdg_toplevel"; +struct wlr_xdg_positioner_v6 { + struct wl_resource *resource; + + struct wlr_box anchor_rect; + enum zxdg_positioner_v6_anchor anchor; + enum zxdg_positioner_v6_gravity gravity; + enum zxdg_positioner_v6_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 void xdg_positioner_destroy(struct wl_resource *resource) { + struct wlr_xdg_positioner_v6 *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_v6 *positioner = + wl_resource_get_user_data(resource); + + 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"); + 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_v6 *positioner = + wl_resource_get_user_data(resource); + + 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"); + 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_v6 *positioner = + wl_resource_get_user_data(resource); + + if (((anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP ) && + (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM)) || + ((anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) && + (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT))) { + wl_resource_post_error(resource, + ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, + "same-axis values are not allowed"); + return; + } + + 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_v6 *positioner = + wl_resource_get_user_data(resource); + + if (((gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) && + (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM)) || + ((gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) && + (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT))) { + wl_resource_post_error(resource, + ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, + "same-axis values are not allowed"); + return; + } + + 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_v6 *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_v6 *positioner = + wl_resource_get_user_data(resource); + + positioner->offset.x = x; + positioner->offset.y = y; +} + +static const struct zxdg_positioner_v6_interface zxdg_positioner_v6_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_v6 *positioner = + calloc(1, sizeof(struct wlr_xdg_positioner_v6)); + if (positioner == NULL) { + wl_client_post_no_memory(wl_client); + return; + } + + positioner->resource = wl_resource_create(wl_client, + &zxdg_positioner_v6_interface, + wl_resource_get_version(resource), + id); + if (positioner->resource == NULL) { + wl_client_post_no_memory(wl_client); + free(positioner); + return; + } + + wl_resource_set_implementation(positioner->resource, + &zxdg_positioner_v6_implementation, + positioner, xdg_positioner_destroy); +} + static void xdg_toplevel_protocol_set_parent(struct wl_client *client, struct wl_resource *resource, struct wl_resource *parent_resource) { struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); @@ -376,11 +525,6 @@ static const struct zxdg_surface_v6_interface zxdg_surface_v6_implementation = { .set_window_geometry = xdg_surface_set_window_geometry, }; -static void xdg_shell_create_positioner(struct wl_client *client, - struct wl_resource *resource, uint32_t id) { - wlr_log(L_DEBUG, "TODO: xdg shell create positioner"); -} - static bool wlr_xdg_surface_v6_toplevel_state_compare( struct wlr_xdg_toplevel_v6 *state) { // is pending state different from current state? From 97cdcccaf07b27a71bc8a06d4b2df32fc368b481 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sun, 1 Oct 2017 06:47:14 -0400 Subject: [PATCH 02/14] xdg-surface: get popup --- include/wlr/types/wlr_xdg_shell_v6.h | 15 ++- types/wlr_xdg_shell_v6.c | 193 ++++++++++++++++++++++----- 2 files changed, 173 insertions(+), 35 deletions(-) diff --git a/include/wlr/types/wlr_xdg_shell_v6.h b/include/wlr/types/wlr_xdg_shell_v6.h index cc52d9c7..2d38dba0 100644 --- a/include/wlr/types/wlr_xdg_shell_v6.h +++ b/include/wlr/types/wlr_xdg_shell_v6.h @@ -28,6 +28,15 @@ struct wlr_xdg_client_v6 { struct wl_event_source *ping_timer; }; +struct wlr_xdg_popup_v6 { + struct wlr_xdg_surface_v6 *base; + + struct wl_resource *resource; + bool committed; + struct wlr_xdg_surface_v6 *parent; + struct wlr_seat *seat; + struct wlr_box geometry; +}; enum wlr_xdg_surface_v6_role { WLR_XDG_SURFACE_V6_ROLE_NONE, @@ -74,7 +83,11 @@ struct wlr_xdg_surface_v6 { struct wlr_surface *surface; struct wl_list link; // wlr_xdg_client_v6::surfaces enum wlr_xdg_surface_v6_role role; - struct wlr_xdg_toplevel_v6 *toplevel_state; + + union { + struct wlr_xdg_toplevel_v6 *toplevel_state; + struct wlr_xdg_popup_v6 *popup_state; + }; bool configured; struct wl_event_source *configure_idle; diff --git a/types/wlr_xdg_shell_v6.c b/types/wlr_xdg_shell_v6.c index 36ef7901..0619ed5b 100644 --- a/types/wlr_xdg_shell_v6.c +++ b/types/wlr_xdg_shell_v6.c @@ -12,6 +12,7 @@ #include "xdg-shell-unstable-v6-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_v6 { struct wl_resource *resource; @@ -36,6 +37,40 @@ static void resource_destroy(struct wl_client *client, wl_resource_destroy(resource); } +static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) { + wl_signal_emit(&surface->events.destroy, surface); + wl_resource_set_user_data(surface->resource, NULL); + + if (surface->configure_idle) { + wl_event_source_remove(surface->configure_idle); + } + + struct wlr_xdg_surface_v6_configure *configure, *tmp; + wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) { + free(configure); + } + + if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { + wl_resource_set_user_data(surface->toplevel_state->resource, NULL); + free(surface->toplevel_state); + } + + if (surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { + wl_resource_set_user_data(surface->popup_state->resource, NULL); + free(surface->popup_state); + } + + wl_list_remove(&surface->link); + wl_list_remove(&surface->surface_destroy_listener.link); + wl_list_remove(&surface->surface_commit_listener.link); + 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_v6 *positioner = wl_resource_get_user_data(resource); @@ -167,6 +202,130 @@ 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) { + wlr_log(L_DEBUG, "TODO: xdg popup grab"); +} + +static const struct zxdg_popup_v6_interface zxdg_popup_v6_implementation = { + .destroy = resource_destroy, + .grab = xdg_popup_protocol_grab, +}; + + +static struct wlr_box xdg_positioner_get_geometry( + struct wlr_xdg_positioner_v6 *positioner, + struct wlr_xdg_surface_v6 *surface, struct wlr_xdg_surface_v6 *parent) { + struct wlr_box geometry = { + .x = positioner->offset.x, + .y = positioner->offset.y, + .width = positioner->size.width, + .height = positioner->size.height, + }; + + if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) { + geometry.y += positioner->anchor_rect.y; + } else if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM) { + geometry.y += + positioner->anchor_rect.y + positioner->anchor_rect.height; + } else { + geometry.y += + positioner->anchor_rect.y + positioner->anchor_rect.height / 2; + } + + if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) { + geometry.x += positioner->anchor_rect.x; + } else if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT) { + geometry.x += positioner->anchor_rect.x + positioner->anchor_rect.width; + } else { + geometry.x += + positioner->anchor_rect.x + positioner->anchor_rect.width / 2; + } + + if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) { + geometry.y -= geometry.height; + } else if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM) { + geometry.y = geometry.y; + } else { + geometry.y -= geometry.height / 2; + } + + if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) { + geometry.x -= geometry.width; + } else if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT) { + geometry.x = geometry.x; + } else { + geometry.x -= geometry.width / 2; + } + + if (positioner->constraint_adjustment == + ZXDG_POSITIONER_V6_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_v6 *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_v6 *surface = + wl_resource_get_user_data(resource); + struct wlr_xdg_surface_v6 *parent = + wl_resource_get_user_data(parent_resource); + struct wlr_xdg_positioner_v6 *positioner = + wl_resource_get_user_data(positioner_resource); + + if (positioner->size.width == 0 || positioner->anchor_rect.width == 0) { + wl_resource_post_error(resource, + ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER, + "positioner object is not complete"); + return; + } + + if (wlr_surface_set_role(surface->surface, wlr_desktop_xdg_popup_role, + resource, ZXDG_SHELL_V6_ERROR_ROLE)) { + return; + } + + surface->popup_state = calloc(1, sizeof(struct wlr_xdg_popup_v6)); + if (!surface->popup_state) { + wl_client_post_no_memory(client); + return; + } + + surface->popup_state->resource = + wl_resource_create(client, &zxdg_popup_v6_interface, + wl_resource_get_version(resource), id); + if (surface->popup_state->resource == NULL) { + free(surface->popup_state); + return; + } + + surface->role = WLR_XDG_SURFACE_V6_ROLE_POPUP; + surface->popup_state->parent = parent; + surface->popup_state->geometry = + xdg_positioner_get_geometry(positioner, surface, parent); + + wl_resource_set_implementation(surface->popup_state->resource, + &zxdg_popup_v6_implementation, surface, + xdg_popup_resource_destroy); + + // TODO: set relative to parent? +} + + static void xdg_toplevel_protocol_set_parent(struct wl_client *client, struct wl_resource *resource, struct wl_resource *parent_resource) { struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); @@ -365,34 +524,6 @@ static const struct zxdg_toplevel_v6_interface zxdg_toplevel_v6_implementation = .set_minimized = xdg_toplevel_protocol_set_minimized }; -static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) { - wl_signal_emit(&surface->events.destroy, surface); - wl_resource_set_user_data(surface->resource, NULL); - - if (surface->configure_idle) { - wl_event_source_remove(surface->configure_idle); - } - - struct wlr_xdg_surface_v6_configure *configure, *tmp; - wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) { - free(configure); - } - - if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { - wl_resource_set_user_data(surface->toplevel_state->resource, NULL); - free(surface->toplevel_state); - } - - wl_list_remove(&surface->link); - wl_list_remove(&surface->surface_destroy_listener.link); - wl_list_remove(&surface->surface_commit_listener.link); - free(surface->geometry); - free(surface->next_geometry); - free(surface->title); - free(surface->app_id); - free(surface); -} - static void xdg_surface_resource_destroy(struct wl_resource *resource) { struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); if (surface != NULL) { @@ -435,12 +566,6 @@ static void xdg_surface_get_toplevel(struct wl_client *client, xdg_toplevel_resource_destroy); } -static void xdg_surface_get_popup(struct wl_client *client, - struct wl_resource *resource, uint32_t id, struct wl_resource *parent, - struct wl_resource *wl_positioner) { - wlr_log(L_DEBUG, "TODO xdg surface get popup"); -} - static void wlr_xdg_toplevel_v6_ack_configure( struct wlr_xdg_surface_v6 *surface, struct wlr_xdg_surface_v6_configure *configure) { From 86b66f1d6fbfc50be89b1e4987747fb7aa21cd31 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sun, 1 Oct 2017 11:47:14 -0400 Subject: [PATCH 03/14] xdg-popup: configure requests --- types/wlr_xdg_shell_v6.c | 58 +++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/types/wlr_xdg_shell_v6.c b/types/wlr_xdg_shell_v6.c index 0619ed5b..718e12fe 100644 --- a/types/wlr_xdg_shell_v6.c +++ b/types/wlr_xdg_shell_v6.c @@ -607,9 +607,15 @@ static void xdg_surface_ack_configure(struct wl_client *client, return; } - // TODO handle popups - if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { + switch (surface->role) { + case WLR_XDG_SURFACE_V6_ROLE_NONE: + assert(0 && "not reached"); + break; + case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL: wlr_xdg_toplevel_v6_ack_configure(surface, configure); + break; + case WLR_XDG_SURFACE_V6_ROLE_POPUP: + break; } if (!surface->configured) { @@ -723,9 +729,6 @@ static void wlr_xdg_surface_send_configure(void *user_data) { struct wlr_xdg_surface_v6 *surface = user_data; struct wl_display *display = wl_client_get_display(surface->client->client); - // TODO handle popups - assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); - surface->configure_idle = NULL; struct wlr_xdg_surface_v6_configure *configure = @@ -738,21 +741,42 @@ static void wlr_xdg_surface_send_configure(void *user_data) { wl_list_insert(surface->configure_list.prev, &configure->link); configure->serial = wl_display_next_serial(display); - wlr_xdg_toplevel_v6_send_configure(surface, configure); + switch (surface->role) { + case WLR_XDG_SURFACE_V6_ROLE_NONE: + assert(0 && "not reached"); + break; + case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL: + wlr_xdg_toplevel_v6_send_configure(surface, configure); + break; + case WLR_XDG_SURFACE_V6_ROLE_POPUP: + zxdg_popup_v6_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; + } zxdg_surface_v6_send_configure(surface->resource, configure->serial); } static void wlr_xdg_surface_v6_schedule_configure( struct wlr_xdg_surface_v6 *surface, bool force) { - // TODO handle popups - assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); - 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; - bool pending_same = !force && - wlr_xdg_surface_v6_toplevel_state_compare(surface->toplevel_state); + switch (surface->role) { + case WLR_XDG_SURFACE_V6_ROLE_NONE: + assert(0 && "not reached"); + break; + case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL: + pending_same = !force && + wlr_xdg_surface_v6_toplevel_state_compare(surface->toplevel_state); + break; + case WLR_XDG_SURFACE_V6_ROLE_POPUP: + break; + } if (surface->configure_idle != NULL) { if (!pending_same) { @@ -803,6 +827,16 @@ static void wlr_xdg_surface_v6_toplevel_committed( surface->toplevel_state->current = surface->toplevel_state->next; } +static void wlr_xdg_surface_v6_popup_committed( + struct wlr_xdg_surface_v6 *surface) { + assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP); + + if (!surface->popup_state->committed) { + wlr_xdg_surface_v6_schedule_configure(surface, true); + surface->popup_state->committed = true; + } +} + static void handle_wlr_surface_committed(struct wl_listener *listener, void *data) { struct wlr_xdg_surface_v6 *surface = @@ -833,7 +867,7 @@ static void handle_wlr_surface_committed(struct wl_listener *listener, wlr_xdg_surface_v6_toplevel_committed(surface); break; case WLR_XDG_SURFACE_V6_ROLE_POPUP: - wlr_log(L_DEBUG, "TODO: popup surface committed"); + wlr_xdg_surface_v6_popup_committed(surface); break; } From e003296c23fb0a974a13121cd940d6bf3fe63fac Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Mon, 2 Oct 2017 09:15:21 -0400 Subject: [PATCH 04/14] xdg-popup: render popups in the right place --- include/wlr/types/wlr_xdg_shell_v6.h | 3 +++ rootston/output.c | 23 +++++++++++++++++++++++ rootston/xdg_shell_v6.c | 11 +++++++++-- types/wlr_xdg_shell_v6.c | 4 ++++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/include/wlr/types/wlr_xdg_shell_v6.h b/include/wlr/types/wlr_xdg_shell_v6.h index 2d38dba0..b7acc2c4 100644 --- a/include/wlr/types/wlr_xdg_shell_v6.h +++ b/include/wlr/types/wlr_xdg_shell_v6.h @@ -89,6 +89,9 @@ struct wlr_xdg_surface_v6 { struct wlr_xdg_popup_v6 *popup_state; }; + struct wl_list popups; + struct wl_list popup_link; + bool configured; struct wl_event_source *configure_idle; struct wl_list configure_list; diff --git a/rootston/output.c b/rootston/output.c index 14d1783e..06786395 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -53,10 +53,33 @@ static void render_surface(struct wlr_surface *surface, } } +static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface, + struct roots_desktop *desktop, struct wlr_output *wlr_output, + struct timespec *when, double base_x, double base_y) { + struct wlr_xdg_surface_v6 *popup; + wl_list_for_each(popup, &surface->popups, popup_link) { + if (!popup->configured) { + continue; + } + + double popup_x = base_x + surface->geometry->x + + popup->popup_state->geometry.x - popup->geometry->x; + double popup_y = base_y + surface->geometry->y + + popup->popup_state->geometry.y - popup->geometry->y; + render_surface(popup->surface, desktop, wlr_output, when, popup_x, + popup_y); + render_xdg_v6_popups(popup, desktop, wlr_output, when, popup_x, popup_y); + } +} + static void render_view(struct roots_view *view, struct roots_desktop *desktop, struct wlr_output *wlr_output, struct timespec *when) { render_surface(view->wlr_surface, desktop, wlr_output, when, view->x, view->y); + if (view->type == ROOTS_XDG_SHELL_V6_VIEW) { + render_xdg_v6_popups(view->xdg_surface_v6, desktop, wlr_output, + when, view->x, view->y); + } } static void output_frame_notify(struct wl_listener *listener, void *data) { diff --git a/rootston/xdg_shell_v6.c b/rootston/xdg_shell_v6.c index ab34f52a..ff0dd090 100644 --- a/rootston/xdg_shell_v6.c +++ b/rootston/xdg_shell_v6.c @@ -73,11 +73,18 @@ static void handle_destroy(struct wl_listener *listener, void *data) { } void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { + struct wlr_xdg_surface_v6 *surface = data; + assert(surface->role != WLR_XDG_SURFACE_V6_ROLE_NONE); + + if (surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { + wlr_log(L_DEBUG, "new xdg popup"); + return; + } + struct roots_desktop *desktop = wl_container_of(listener, desktop, xdg_shell_v6_surface); - struct wlr_xdg_surface_v6 *surface = data; - wlr_log(L_DEBUG, "new xdg surface: title=%s, app_id=%s", + wlr_log(L_DEBUG, "new xdg toplevel: title=%s, app_id=%s", surface->title, surface->app_id); wlr_xdg_surface_v6_ping(surface); diff --git a/types/wlr_xdg_shell_v6.c b/types/wlr_xdg_shell_v6.c index 718e12fe..6e32ebdf 100644 --- a/types/wlr_xdg_shell_v6.c +++ b/types/wlr_xdg_shell_v6.c @@ -57,6 +57,7 @@ static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) { if (surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { wl_resource_set_user_data(surface->popup_state->resource, NULL); + wl_list_remove(&surface->popup_link); free(surface->popup_state); } @@ -317,6 +318,8 @@ static void xdg_surface_get_popup(struct wl_client *client, surface->popup_state->parent = parent; surface->popup_state->geometry = xdg_positioner_get_geometry(positioner, surface, parent); + wl_list_insert(&surface->popup_state->parent->popups, + &surface->popup_link); wl_resource_set_implementation(surface->popup_state->resource, &zxdg_popup_v6_implementation, surface, @@ -914,6 +917,7 @@ static void xdg_shell_get_xdg_surface(struct wl_client *wl_client, } wl_list_init(&surface->configure_list); + wl_list_init(&surface->popups); wl_signal_init(&surface->events.request_minimize); wl_signal_init(&surface->events.request_move); From f9379f9a4694a769c75a7647f05986f38d62f0f1 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Tue, 3 Oct 2017 15:57:22 -0400 Subject: [PATCH 05/14] rootston-desktop: use window geometry for input bounds --- rootston/desktop.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rootston/desktop.c b/rootston/desktop.c index 8d1d34d6..9781581e 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -40,6 +40,15 @@ void view_get_input_bounds(struct roots_view *view, struct wlr_box *box) { view->get_input_bounds(view, box); return; } + + if (view->type == ROOTS_XDG_SHELL_V6_VIEW) { + box->x = view->xdg_surface_v6->geometry->x; + box->y = view->xdg_surface_v6->geometry->y; + box->width = view->xdg_surface_v6->geometry->width; + box->height = view->xdg_surface_v6->geometry->height; + return; + } + box->x = box->y = 0; box->width = view->wlr_surface->current->width; box->height = view->wlr_surface->current->height; From 4c9807d3a68688ee0d6f96e3fc2a60f5edcca857 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Tue, 3 Oct 2017 15:57:43 -0400 Subject: [PATCH 06/14] rootston-desktop: popup input handling --- rootston/desktop.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/rootston/desktop.c b/rootston/desktop.c index 9781581e..2c4611a8 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -93,6 +93,36 @@ static struct wlr_subsurface *subsurface_at(struct wlr_surface *surface, return NULL; } +static struct wlr_xdg_surface_v6 *xdg_v6_popup_at( + struct wlr_xdg_surface_v6 *surface, double sx, double sy, + double *popup_sx, double *popup_sy) { + struct wlr_xdg_surface_v6 *popup; + wl_list_for_each(popup, &surface->popups, popup_link) { + double _popup_sx = surface->geometry->x + popup->popup_state->geometry.x; + double _popup_sy = surface->geometry->y + popup->popup_state->geometry.y; + int popup_width = popup->popup_state->geometry.width; + int popup_height = popup->popup_state->geometry.height; + + struct wlr_xdg_surface_v6 *_popup = + xdg_v6_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)) { + *popup_sx = _popup_sx - popup->geometry->x; + *popup_sy = _popup_sy - popup->geometry->y; + return popup; + } + } + + return NULL; +} + struct roots_view *view_at(struct roots_desktop *desktop, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { for (int i = desktop->views->length - 1; i >= 0; --i) { @@ -101,6 +131,20 @@ struct roots_view *view_at(struct roots_desktop *desktop, double lx, double ly, double view_sx = lx - view->x; double view_sy = ly - view->y; + if (view->type == ROOTS_XDG_SHELL_V6_VIEW) { + double popup_sx, popup_sy; + struct wlr_xdg_surface_v6 *popup = + xdg_v6_popup_at(view->xdg_surface_v6, view_sx, view_sy, + &popup_sx, &popup_sy); + + if (popup) { + *sx = view_sx - popup_sx; + *sy = view_sy - popup_sy; + *surface = popup->surface; + return view; + } + } + double sub_x, sub_y; struct wlr_subsurface *subsurface = subsurface_at(view->wlr_surface, view_sx, view_sy, &sub_x, &sub_y); From 17b134e1783acab00e494d5343b3611ece65e582 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Thu, 5 Oct 2017 13:28:26 -0400 Subject: [PATCH 07/14] wlr-seat: pointer grab interface --- include/wlr/types/wlr_seat.h | 83 +++++++++++++++++++++++++++++++++++- rootston/cursor.c | 8 ++-- types/wlr_seat.c | 80 ++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 6 deletions(-) diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index 489bd529..ad120d6e 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -24,6 +24,30 @@ struct wlr_seat_handle { struct wl_list link; }; +struct wlr_seat_pointer_grab; + +struct wlr_pointer_grab_interface { + void (*enter)(struct wlr_seat_pointer_grab *grab, + struct wlr_surface *surface, double sx, double sy); + void (*motion)(struct wlr_seat_pointer_grab *grab, uint32_t time, + double sx, double sy); + uint32_t (*button)(struct wlr_seat_pointer_grab *grab, uint32_t time, + uint32_t button, uint32_t state); + void (*axis)(struct wlr_seat_pointer_grab *grab, uint32_t time, + enum wlr_axis_orientation orientation, double value); + void (*cancel)(struct wlr_seat_pointer_grab *grab); +}; + +/** + * Passed to `wlr_seat_pointer_start_grab()` to start a grab of the pointer. The + * grabber is responsible for handling pointer events for the seat. + */ +struct wlr_seat_pointer_grab { + const struct wlr_pointer_grab_interface *interface; + struct wlr_seat *seat; + void *data; +}; + struct wlr_seat_pointer_state { struct wlr_seat *wlr_seat; struct wlr_seat_handle *focused_handle; @@ -31,6 +55,9 @@ struct wlr_seat_pointer_state { struct wl_listener surface_destroy; struct wl_listener resource_destroy; + + struct wlr_seat_pointer_grab *grab; + struct wlr_seat_pointer_grab *default_grab; }; struct wlr_seat_keyboard { @@ -109,6 +136,8 @@ bool wlr_seat_pointer_surface_has_focus(struct wlr_seat *wlr_seat, * Send a pointer enter event to the given surface and consider it to be the * focused surface for the pointer. This will send a leave event to the last * surface that was entered. Coordinates for the enter event are surface-local. + * Compositor should use `wlr_seat_pointer_notify_enter()` to change pointer + * focus to respect pointer grabs. */ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat, struct wlr_surface *surface, double sx, double sy); @@ -120,21 +149,71 @@ void wlr_seat_pointer_clear_focus(struct wlr_seat *wlr_seat); /** * Send a motion event to the surface with pointer focus. Coordinates for the - * motion event are surface-local. + * motion event are surface-local. Compositors should use + * `wlr_seat_pointer_notify_motion()` to send motion events to respect pointer + * grabs. */ void wlr_seat_pointer_send_motion(struct wlr_seat *wlr_seat, uint32_t time, double sx, double sy); /** * Send a button event to the surface with pointer focus. Coordinates for the - * button event are surface-local. Returns the serial. + * button event are surface-local. Returns the serial. Compositors should use + * `wlr_seat_pointer_notify_button()` to send button events to respect pointer + * grabs. */ uint32_t wlr_seat_pointer_send_button(struct wlr_seat *wlr_seat, uint32_t time, uint32_t button, uint32_t state); +/** + * Send an axis event to the surface with pointer focus. Compositors should use + * `wlr_seat_pointer_notify_axis()` to send axis events to respect pointer + * grabs. + **/ void wlr_seat_pointer_send_axis(struct wlr_seat *wlr_seat, uint32_t time, enum wlr_axis_orientation orientation, double value); +/** + * Start a grab of the pointer of this seat. The grabber is responsible for + * handling all pointer events until the grab ends. + */ +void wlr_seat_pointer_start_grab(struct wlr_seat *wlr_seat, + struct wlr_seat_pointer_grab *grab); + +/** + * End the grab of the pointer of this seat. This reverts the grab back to the + * default grab for the pointer. + */ +void wlr_seat_pointer_end_grab(struct wlr_seat *wlr_seat); + +/** + * Notify the seat of a pointer enter event to the given surface and request it to be the + * focused surface for the pointer. Pass surface-local coordinates where the + * enter occurred. + */ +void wlr_seat_pointer_notify_enter(struct wlr_seat *wlr_seat, + struct wlr_surface *surface, double sx, double sy); + +/** + * Notify the seat of motion over the given surface. Pass surface-local + * coordinates where the pointer motion occurred. + */ +void wlr_seat_pointer_notify_motion(struct wlr_seat *wlr_seat, uint32_t time, + double sx, double sy); + +/** + * Notify the seat that a button has been pressed. Returns the serial of the + * button press or zero if no button press was sent. + */ +uint32_t wlr_seat_pointer_notify_button(struct wlr_seat *wlr_seat, + uint32_t time, uint32_t button, uint32_t state); + +/** + * Notify the seat of an axis event. + */ +void wlr_seat_pointer_notify_axis(struct wlr_seat *wlr_seat, uint32_t time, + enum wlr_axis_orientation orientation, double value); + /** * Attaches this keyboard to the seat. Key events from this keyboard will be * propegated to the focused client. diff --git a/rootston/cursor.c b/rootston/cursor.c index c24e6989..a0fd36d0 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -55,8 +55,8 @@ void cursor_update_position(struct roots_input *input, uint32_t time) { view = view_at(desktop, input->cursor->x, input->cursor->y, &surface, &sx, &sy); if (view) { - wlr_seat_pointer_enter(input->wl_seat, surface, sx, sy); - wlr_seat_pointer_send_motion(input->wl_seat, time, sx, sy); + wlr_seat_pointer_notify_enter(input->wl_seat, surface, sx, sy); + wlr_seat_pointer_notify_motion(input->wl_seat, time, sx, sy); } else { wlr_seat_pointer_clear_focus(input->wl_seat); } @@ -137,7 +137,7 @@ static void handle_cursor_axis(struct wl_listener *listener, void *data) { struct roots_input *input = wl_container_of(listener, input, cursor_axis); struct wlr_event_pointer_axis *event = data; - wlr_seat_pointer_send_axis(input->wl_seat, event->time_sec, + wlr_seat_pointer_notify_axis(input->wl_seat, event->time_sec, event->orientation, event->delta); } @@ -149,7 +149,7 @@ static void do_cursor_button_press(struct roots_input *input, double sx, sy; struct roots_view *view = view_at(desktop, input->cursor->x, input->cursor->y, &surface, &sx, &sy); - uint32_t serial = wlr_seat_pointer_send_button( + uint32_t serial = wlr_seat_pointer_notify_button( input->wl_seat, time, button, state); int i; switch (state) { diff --git a/types/wlr_seat.c b/types/wlr_seat.c index 2d0bb01c..e6decc8a 100644 --- a/types/wlr_seat.c +++ b/types/wlr_seat.c @@ -173,6 +173,38 @@ static void wl_seat_bind(struct wl_client *wl_client, void *_wlr_seat, wl_signal_emit(&wlr_seat->events.client_bound, handle); } +static void default_pointer_enter(struct wlr_seat_pointer_grab *grab, + struct wlr_surface *surface, double sx, double sy) { + wlr_seat_pointer_enter(grab->seat, surface, sx, sy); +} + +static void default_pointer_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 default_pointer_button(struct wlr_seat_pointer_grab *grab, + uint32_t time, uint32_t button, uint32_t state) { + return wlr_seat_pointer_send_button(grab->seat, time, button, state); +} + +static void default_pointer_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 default_pointer_cancel(struct wlr_seat_pointer_grab *grab) { + // cannot be cancelled +} + +static const struct wlr_pointer_grab_interface default_pointer_grab_impl = { + .enter = default_pointer_enter, + .motion = default_pointer_motion, + .button = default_pointer_button, + .axis = default_pointer_axis, + .cancel = default_pointer_cancel, +}; + struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { struct wlr_seat *wlr_seat = calloc(1, sizeof(struct wlr_seat)); if (!wlr_seat) { @@ -183,6 +215,17 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { wl_list_init(&wlr_seat->pointer_state.surface_destroy.link); wl_list_init(&wlr_seat->pointer_state.resource_destroy.link); + struct wlr_seat_pointer_grab *default_grab = + calloc(1, sizeof(struct wlr_seat_pointer_grab)); + if (!default_grab) { + free(wlr_seat); + return NULL; + } + default_grab->interface = &default_pointer_grab_impl; + default_grab->seat = wlr_seat; + wlr_seat->pointer_state.default_grab = default_grab; + wlr_seat->pointer_state.grab = default_grab; + wlr_seat->keyboard_state.wlr_seat = wlr_seat; wl_list_init(&wlr_seat->keyboard_state.resource_destroy.link); wl_list_init( @@ -218,6 +261,7 @@ void wlr_seat_destroy(struct wlr_seat *wlr_seat) { } wl_global_destroy(wlr_seat->wl_global); + free(wlr_seat->pointer_state.default_grab); free(wlr_seat->data_device); free(wlr_seat->name); free(wlr_seat); @@ -386,6 +430,42 @@ void wlr_seat_pointer_send_axis(struct wlr_seat *wlr_seat, uint32_t time, pointer_send_frame(pointer); } +void wlr_seat_pointer_start_grab(struct wlr_seat *wlr_seat, + struct wlr_seat_pointer_grab *grab) { + grab->seat = wlr_seat; + wlr_seat->pointer_state.grab = grab; + // TODO: replay the last enter +} + +void wlr_seat_pointer_end_grab(struct wlr_seat *wlr_seat) { + wlr_seat->pointer_state.grab = wlr_seat->pointer_state.default_grab; + // TODO: replay the last enter +} + +void wlr_seat_pointer_notify_enter(struct wlr_seat *wlr_seat, + struct wlr_surface *surface, double sx, double sy) { + struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; + grab->interface->enter(grab, surface, sx, sy); +} + +void wlr_seat_pointer_notify_motion(struct wlr_seat *wlr_seat, uint32_t time, + double sx, double sy) { + struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; + grab->interface->motion(grab, time, sx, sy); +} + +uint32_t wlr_seat_pointer_notify_button(struct wlr_seat *wlr_seat, + uint32_t time, uint32_t button, uint32_t state) { + struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; + return grab->interface->button(grab, time, button, state); +} + +void wlr_seat_pointer_notify_axis(struct wlr_seat *wlr_seat, uint32_t time, + enum wlr_axis_orientation orientation, double value) { + struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; + grab->interface->axis(grab, time, orientation, value); +} + static void keyboard_switch_seat_keyboard(struct wlr_seat_handle *handle, struct wlr_seat_keyboard *seat_kb) { if (handle->seat_keyboard == seat_kb) { From 9bf03e1a7195ed6e9261d9f258e7a2e9fb03cef8 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Thu, 5 Oct 2017 14:25:56 -0400 Subject: [PATCH 08/14] bug: use milisecond granularity for time events --- rootston/cursor.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rootston/cursor.c b/rootston/cursor.c index a0fd36d0..531d82cc 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -120,7 +120,7 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) { struct wlr_event_pointer_motion *event = data; wlr_cursor_move(input->cursor, event->device, event->delta_x, event->delta_y); - cursor_update_position(input, (uint32_t)event->time_usec); + cursor_update_position(input, (uint32_t)(event->time_usec / 1000)); } static void handle_cursor_motion_absolute(struct wl_listener *listener, @@ -130,7 +130,7 @@ static void handle_cursor_motion_absolute(struct wl_listener *listener, struct wlr_event_pointer_motion_absolute *event = data; wlr_cursor_warp_absolute(input->cursor, event->device, event->x_mm / event->width_mm, event->y_mm / event->height_mm); - cursor_update_position(input, (uint32_t)event->time_usec); + cursor_update_position(input, (uint32_t)(event->time_usec / 1000)); } static void handle_cursor_axis(struct wl_listener *listener, void *data) { @@ -176,7 +176,7 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) { struct roots_input *input = wl_container_of(listener, input, cursor_button); struct wlr_event_pointer_button *event = data; do_cursor_button_press(input, input->cursor, event->device, - (uint32_t)event->time_usec, event->button, event->state); + (uint32_t)(event->time_usec / 1000), event->button, event->state); } static void handle_tool_axis(struct wl_listener *listener, void *data) { @@ -186,7 +186,7 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) { (event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { wlr_cursor_warp_absolute(input->cursor, event->device, event->x_mm / event->width_mm, event->y_mm / event->height_mm); - cursor_update_position(input, (uint32_t)event->time_usec); + cursor_update_position(input, (uint32_t)(event->time_usec / 1000)); } } @@ -194,7 +194,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { struct roots_input *input = wl_container_of(listener, input, cursor_tool_tip); struct wlr_event_tablet_tool_tip *event = data; do_cursor_button_press(input, input->cursor, event->device, - (uint32_t)event->time_usec, BTN_LEFT, event->state); + (uint32_t)(event->time_usec / 1000), BTN_LEFT, event->state); } void cursor_initialize(struct roots_input *input) { From 7b697fe841c7c5167e506f4ce6eed6ee935f69e4 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Thu, 5 Oct 2017 14:38:12 -0400 Subject: [PATCH 09/14] bug: fix x11 event time --- backend/x11/backend.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/x11/backend.c b/backend/x11/backend.c index 88c022d1..e0f57f82 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -47,7 +47,7 @@ static bool handle_x11_event(struct wlr_x11_backend *x11, xcb_generic_event_t *e xcb_key_press_event_t *ev = (xcb_key_press_event_t *)event; struct wlr_event_keyboard_key key = { .time_sec = ev->time / 1000, - .time_usec = (ev->time % 1000) * 1000, + .time_usec = ev->time * 1000, .keycode = ev->detail - 8, .state = event->response_type == XCB_KEY_PRESS ? WLR_KEY_PRESSED : WLR_KEY_RELEASED, @@ -63,7 +63,7 @@ static bool handle_x11_event(struct wlr_x11_backend *x11, xcb_generic_event_t *e struct wlr_event_pointer_button button = { .device = &x11->pointer_dev, .time_sec = ev->time / 1000, - .time_usec = (ev->time % 1000) * 1000, + .time_usec = ev->time * 1000, .button = xcb_button_to_wl(ev->detail), .state = event->response_type == XCB_BUTTON_PRESS ? WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED, @@ -78,7 +78,7 @@ static bool handle_x11_event(struct wlr_x11_backend *x11, xcb_generic_event_t *e struct wlr_event_pointer_motion_absolute abs = { .device = &x11->pointer_dev, .time_sec = ev->time / 1000, - .time_usec = (ev->time % 1000) * 1000, + .time_usec = ev->time * 1000, .x_mm = ev->event_x, .y_mm = ev->event_y, .width_mm = output->wlr_output.width, @@ -109,7 +109,7 @@ static bool handle_x11_event(struct wlr_x11_backend *x11, xcb_generic_event_t *e struct wlr_event_pointer_motion_absolute abs = { .device = &x11->pointer_dev, .time_sec = x11->time / 1000, - .time_usec = (x11->time % 1000) * 1000, + .time_usec = x11->time * 1000, .x_mm = pointer->root_x, .y_mm = pointer->root_y, .width_mm = output->wlr_output.width, From d3ebf99b0fcadff6b70e6d7e90b956330cefe8a1 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Fri, 6 Oct 2017 06:54:43 -0400 Subject: [PATCH 10/14] xdg-popup: basic pointer grab --- include/wlr/types/wlr_xdg_shell_v6.h | 14 ++++ types/wlr_xdg_shell_v6.c | 110 ++++++++++++++++++++++++++- 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/include/wlr/types/wlr_xdg_shell_v6.h b/include/wlr/types/wlr_xdg_shell_v6.h index b7acc2c4..408df16a 100644 --- a/include/wlr/types/wlr_xdg_shell_v6.h +++ b/include/wlr/types/wlr_xdg_shell_v6.h @@ -2,11 +2,13 @@ #define WLR_TYPES_WLR_XDG_SHELL_V6_H #include +#include #include struct wlr_xdg_shell_v6 { struct wl_global *wl_global; struct wl_list clients; + struct wl_list popup_grabs; uint32_t ping_timeout; struct { @@ -36,6 +38,18 @@ struct wlr_xdg_popup_v6 { struct wlr_xdg_surface_v6 *parent; struct wlr_seat *seat; struct wlr_box geometry; + + struct wl_list grab_link; // wlr_xdg_popup_grab_v6::popups +}; + +// each seat gets a popup grab +struct wlr_xdg_popup_grab_v6 { + struct wl_client *client; + struct wlr_seat_pointer_grab pointer_grab; + struct wlr_seat *seat; + struct wl_list popups; + struct wl_list link; // wlr_xdg_shell_v6::popup_grabs + // TODO: seat_handle destroy will release the grab }; enum wlr_xdg_surface_v6_role { diff --git a/types/wlr_xdg_shell_v6.c b/types/wlr_xdg_shell_v6.c index 6e32ebdf..f9546fea 100644 --- a/types/wlr_xdg_shell_v6.c +++ b/types/wlr_xdg_shell_v6.c @@ -56,8 +56,10 @@ static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) { } if (surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { + // TODO: get the current grab and if it has no more popups, end the grab wl_resource_set_user_data(surface->popup_state->resource, NULL); wl_list_remove(&surface->popup_link); + wl_list_remove(&surface->popup_state->grab_link); free(surface->popup_state); } @@ -203,10 +205,114 @@ static void xdg_shell_create_positioner(struct wl_client *wl_client, positioner, xdg_positioner_destroy); } +static void xdg_pointer_grab_end(struct wlr_seat_pointer_grab *grab) { + struct wlr_xdg_popup_grab_v6 *popup_grab = grab->data; + + struct wlr_xdg_popup_v6 *popup, *tmp; + wl_list_for_each_safe(popup, tmp, &popup_grab->popups, grab_link) { + wl_list_remove(&popup->grab_link); + wl_list_init(&popup->grab_link); + zxdg_popup_v6_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_v6 *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, +}; + +static struct wlr_xdg_popup_grab_v6 *xdg_shell_popup_grab_from_seat( + struct wlr_xdg_shell_v6 *shell, struct wlr_seat *seat) { + struct wlr_xdg_popup_grab_v6 *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_v6)); + if (!xdg_grab) { + return NULL; + } + + xdg_grab->pointer_grab.data = xdg_grab; + xdg_grab->pointer_grab.interface = &xdg_pointer_grab_impl; + // TODO: keyboard grab + + 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_popup_protocol_grab(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial) { - wlr_log(L_DEBUG, "TODO: xdg popup grab"); + struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); + struct wlr_seat_handle *handle = wl_resource_get_user_data(seat_resource); + //bool parent_is_toplevel = + //surface->popup_state->parent->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL; + + // TODO: handle make sure the grab is valid for the protocol + + if (surface->popup_state->committed) { + wl_resource_post_error(surface->popup_state->resource, + ZXDG_POPUP_V6_ERROR_INVALID_GRAB, + "xdg_popup is already mapped"); + return; + } + + struct wlr_xdg_popup_grab_v6 *popup_grab = + xdg_shell_popup_grab_from_seat(surface->client->shell, + handle->wlr_seat); + + popup_grab->client = surface->client->client; + + wl_list_insert(&popup_grab->popups, &surface->popup_state->grab_link); + + wlr_seat_pointer_start_grab(handle->wlr_seat, &popup_grab->pointer_grab); } static const struct zxdg_popup_v6_interface zxdg_popup_v6_implementation = { @@ -318,6 +424,7 @@ static void xdg_surface_get_popup(struct wl_client *client, surface->popup_state->parent = parent; surface->popup_state->geometry = xdg_positioner_get_geometry(positioner, surface, parent); + wl_list_init(&surface->popup_state->grab_link); wl_list_insert(&surface->popup_state->parent->popups, &surface->popup_link); @@ -1037,6 +1144,7 @@ struct wlr_xdg_shell_v6 *wlr_xdg_shell_v6_create(struct wl_display *display) { 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, &zxdg_shell_v6_interface, 1, xdg_shell, xdg_shell_bind); From 48ae4fc5882d5d1b47e951b9f4fc0c64584d856e Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Fri, 6 Oct 2017 07:32:59 -0400 Subject: [PATCH 11/14] rootston: handle grab end --- include/rootston/input.h | 2 ++ include/wlr/types/wlr_seat.h | 3 +++ rootston/cursor.c | 9 +++++++++ types/wlr_seat.c | 9 +++++++-- 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/include/rootston/input.h b/include/rootston/input.h index ae3e3b80..4b9bd1dd 100644 --- a/include/rootston/input.h +++ b/include/rootston/input.h @@ -105,6 +105,8 @@ struct roots_input { struct wl_listener cursor_axis; struct wl_listener cursor_tool_axis; struct wl_listener cursor_tool_tip; + + struct wl_listener pointer_grab_end; }; struct roots_input *input_create(struct roots_server *server, diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index ad120d6e..6b66fd69 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -94,6 +94,9 @@ struct wlr_seat { struct { struct wl_signal client_bound; struct wl_signal client_unbound; + + struct wl_signal pointer_grab_begin; + struct wl_signal pointer_grab_end; } events; void *data; diff --git a/rootston/cursor.c b/rootston/cursor.c index 7ea1ed4d..df5869e5 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -265,6 +265,12 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { (uint32_t)(event->time_usec / 1000), BTN_LEFT, event->state); } +static void handle_pointer_grab_end(struct wl_listener *listener, void *data) { + struct roots_input *input = + wl_container_of(listener, input, pointer_grab_end); + cursor_update_position(input, 0); +} + void cursor_initialize(struct roots_input *input) { struct wlr_cursor *cursor = input->cursor; @@ -292,6 +298,9 @@ void cursor_initialize(struct roots_input *input) { wl_list_init(&input->cursor_tool_tip.link); wl_signal_add(&cursor->events.tablet_tool_tip, &input->cursor_tool_tip); input->cursor_tool_tip.notify = handle_tool_tip; + + wl_signal_add(&input->wl_seat->events.pointer_grab_end, &input->pointer_grab_end); + input->pointer_grab_end.notify = handle_pointer_grab_end; } static void reset_device_mappings(struct roots_config *config, diff --git a/types/wlr_seat.c b/types/wlr_seat.c index efb95a78..c4fd3f65 100644 --- a/types/wlr_seat.c +++ b/types/wlr_seat.c @@ -246,6 +246,9 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { wl_signal_init(&wlr_seat->events.client_bound); wl_signal_init(&wlr_seat->events.client_unbound); + wl_signal_init(&wlr_seat->events.pointer_grab_begin); + wl_signal_init(&wlr_seat->events.pointer_grab_end); + return wlr_seat; } @@ -438,12 +441,14 @@ void wlr_seat_pointer_start_grab(struct wlr_seat *wlr_seat, struct wlr_seat_pointer_grab *grab) { grab->seat = wlr_seat; wlr_seat->pointer_state.grab = grab; - // TODO: replay the last enter + + wl_signal_emit(&wlr_seat->events.pointer_grab_begin, grab); } void wlr_seat_pointer_end_grab(struct wlr_seat *wlr_seat) { + struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; wlr_seat->pointer_state.grab = wlr_seat->pointer_state.default_grab; - // TODO: replay the last enter + wl_signal_emit(&wlr_seat->events.pointer_grab_end, grab); } void wlr_seat_pointer_notify_enter(struct wlr_seat *wlr_seat, From 67ea30775390cf535ae6431186b367a6a63a3621 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Fri, 6 Oct 2017 07:52:18 -0400 Subject: [PATCH 12/14] xdg-shell: cleanup --- include/wlr/types/wlr_xdg_shell_v6.h | 2 -- types/wlr_xdg_shell_v6.c | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/include/wlr/types/wlr_xdg_shell_v6.h b/include/wlr/types/wlr_xdg_shell_v6.h index 408df16a..c7067e80 100644 --- a/include/wlr/types/wlr_xdg_shell_v6.h +++ b/include/wlr/types/wlr_xdg_shell_v6.h @@ -49,7 +49,6 @@ struct wlr_xdg_popup_grab_v6 { struct wlr_seat *seat; struct wl_list popups; struct wl_list link; // wlr_xdg_shell_v6::popup_grabs - // TODO: seat_handle destroy will release the grab }; enum wlr_xdg_surface_v6_role { @@ -84,7 +83,6 @@ struct wlr_xdg_toplevel_v6 { struct wlr_xdg_toplevel_v6_state current; }; -// TODO split up into toplevel and popup configure struct wlr_xdg_surface_v6_configure { struct wl_list link; // wlr_xdg_surface_v6::configure_list uint32_t serial; diff --git a/types/wlr_xdg_shell_v6.c b/types/wlr_xdg_shell_v6.c index f9546fea..eb0547d3 100644 --- a/types/wlr_xdg_shell_v6.c +++ b/types/wlr_xdg_shell_v6.c @@ -170,7 +170,8 @@ static void xdg_positioner_protocol_set_offset(struct wl_client *client, positioner->offset.y = y; } -static const struct zxdg_positioner_v6_interface zxdg_positioner_v6_implementation = { +static const struct zxdg_positioner_v6_interface + zxdg_positioner_v6_implementation = { .destroy = resource_destroy, .set_size = xdg_positioner_protocol_set_size, .set_anchor_rect = xdg_positioner_protocol_set_anchor_rect, @@ -259,6 +260,7 @@ static const struct wlr_pointer_grab_interface xdg_pointer_grab_impl = { .motion = xdg_pointer_grab_motion, .button = xdg_pointer_grab_button, .cancel = xdg_pointer_grab_cancel, + .axis = xdg_pointer_grab_axis, }; static struct wlr_xdg_popup_grab_v6 *xdg_shell_popup_grab_from_seat( @@ -431,8 +433,6 @@ static void xdg_surface_get_popup(struct wl_client *client, wl_resource_set_implementation(surface->popup_state->resource, &zxdg_popup_v6_implementation, surface, xdg_popup_resource_destroy); - - // TODO: set relative to parent? } From 27ee171d251e60d302f063196ccc5100028dcfff Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Fri, 6 Oct 2017 08:41:43 -0400 Subject: [PATCH 13/14] xdg-popup: protocol errors and ungrab --- types/wlr_xdg_shell_v6.c | 222 +++++++++++++++++++++++---------------- 1 file changed, 131 insertions(+), 91 deletions(-) diff --git a/types/wlr_xdg_shell_v6.c b/types/wlr_xdg_shell_v6.c index eb0547d3..50e812a7 100644 --- a/types/wlr_xdg_shell_v6.c +++ b/types/wlr_xdg_shell_v6.c @@ -37,9 +37,100 @@ static void resource_destroy(struct wl_client *client, wl_resource_destroy(resource); } +static struct wlr_xdg_surface_v6 *xdg_popup_grab_get_topmost( + struct wlr_xdg_popup_grab_v6 *grab) { + struct wlr_xdg_popup_v6 *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_v6 *popup_grab = grab->data; + + struct wlr_xdg_popup_v6 *popup, *tmp; + wl_list_for_each_safe(popup, tmp, &popup_grab->popups, grab_link) { + zxdg_popup_v6_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_v6 *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 struct wlr_xdg_popup_grab_v6 *xdg_shell_popup_grab_from_seat( + struct wlr_xdg_shell_v6 *shell, struct wlr_seat *seat) { + struct wlr_xdg_popup_grab_v6 *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_v6)); + if (!xdg_grab) { + return NULL; + } + + xdg_grab->pointer_grab.data = xdg_grab; + xdg_grab->pointer_grab.interface = &xdg_pointer_grab_impl; + // TODO: keyboard grab + + 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_v6 *surface) { wl_signal_emit(&surface->events.destroy, surface); - wl_resource_set_user_data(surface->resource, NULL); if (surface->configure_idle) { wl_event_source_remove(surface->configure_idle); @@ -56,13 +147,36 @@ static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) { } if (surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { - // TODO: get the current grab and if it has no more popups, end the grab wl_resource_set_user_data(surface->popup_state->resource, NULL); + + if (surface->popup_state->seat) { + struct wlr_xdg_popup_grab_v6 *grab = + xdg_shell_popup_grab_from_seat(surface->client->shell, + surface->popup_state->seat); + + struct wlr_xdg_surface_v6 *topmost = + xdg_popup_grab_get_topmost(grab); + + if (topmost != surface) { + wl_resource_post_error(surface->client->resource, + ZXDG_SHELL_V6_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) && + grab->seat->pointer_state.grab == &grab->pointer_grab) { + wlr_seat_pointer_end_grab(grab->seat); + } + } + wl_list_remove(&surface->popup_link); - wl_list_remove(&surface->popup_state->grab_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); wl_list_remove(&surface->surface_commit_listener.link); @@ -206,98 +320,11 @@ static void xdg_shell_create_positioner(struct wl_client *wl_client, positioner, xdg_positioner_destroy); } -static void xdg_pointer_grab_end(struct wlr_seat_pointer_grab *grab) { - struct wlr_xdg_popup_grab_v6 *popup_grab = grab->data; - - struct wlr_xdg_popup_v6 *popup, *tmp; - wl_list_for_each_safe(popup, tmp, &popup_grab->popups, grab_link) { - wl_list_remove(&popup->grab_link); - wl_list_init(&popup->grab_link); - zxdg_popup_v6_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_v6 *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 struct wlr_xdg_popup_grab_v6 *xdg_shell_popup_grab_from_seat( - struct wlr_xdg_shell_v6 *shell, struct wlr_seat *seat) { - struct wlr_xdg_popup_grab_v6 *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_v6)); - if (!xdg_grab) { - return NULL; - } - - xdg_grab->pointer_grab.data = xdg_grab; - xdg_grab->pointer_grab.interface = &xdg_pointer_grab_impl; - // TODO: keyboard grab - - 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_popup_protocol_grab(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial) { struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource); struct wlr_seat_handle *handle = wl_resource_get_user_data(seat_resource); - //bool parent_is_toplevel = - //surface->popup_state->parent->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL; - - // TODO: handle make sure the grab is valid for the protocol if (surface->popup_state->committed) { wl_resource_post_error(surface->popup_state->resource, @@ -310,7 +337,20 @@ static void xdg_popup_protocol_grab(struct wl_client *client, xdg_shell_popup_grab_from_seat(surface->client->shell, handle->wlr_seat); + struct wlr_xdg_surface_v6 *topmost = xdg_popup_grab_get_topmost(popup_grab); + bool parent_is_toplevel = + surface->popup_state->parent->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL; + + if ((topmost == NULL && !parent_is_toplevel) || + (topmost != NULL && topmost != surface->popup_state->parent)) { + wl_resource_post_error(surface->client->resource, + ZXDG_SHELL_V6_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 = handle->wlr_seat; wl_list_insert(&popup_grab->popups, &surface->popup_state->grab_link); @@ -423,10 +463,10 @@ static void xdg_surface_get_popup(struct wl_client *client, } surface->role = WLR_XDG_SURFACE_V6_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_init(&surface->popup_state->grab_link); wl_list_insert(&surface->popup_state->parent->popups, &surface->popup_link); From 4680943e749bfd441d31c988faa50f2b1ae043ad Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sat, 7 Oct 2017 11:02:11 -0400 Subject: [PATCH 14/14] xdg-seat: keyboard grabs --- include/wlr/types/wlr_seat.h | 69 +++++++++++++++- include/wlr/types/wlr_xdg_shell_v6.h | 1 + rootston/cursor.c | 2 +- types/wlr_seat.c | 115 ++++++++++++++++++++++++--- types/wlr_xdg_shell_v6.c | 42 +++++++++- 5 files changed, 211 insertions(+), 18 deletions(-) diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index 6b66fd69..d3d3e00d 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -38,6 +38,28 @@ struct wlr_pointer_grab_interface { void (*cancel)(struct wlr_seat_pointer_grab *grab); }; +struct wlr_seat_keyboard_grab; + +struct wlr_keyboard_grab_interface { + void (*enter)(struct wlr_seat_keyboard_grab *grab, struct wlr_surface *surface); + void (*key)(struct wlr_seat_keyboard_grab *grab, uint32_t time, + uint32_t key, uint32_t state); + void (*modifiers)(struct wlr_seat_keyboard_grab *grab, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group); + void (*cancel)(struct wlr_seat_keyboard_grab *grab); +}; + +/** + * Passed to `wlr_seat_keyboard_start_grab()` to start a grab of the keyboard. + * The grabber is responsible for handling keyboard events for the seat. + */ +struct wlr_seat_keyboard_grab { + const struct wlr_keyboard_grab_interface *interface; + struct wlr_seat *seat; + void *data; +}; + /** * Passed to `wlr_seat_pointer_start_grab()` to start a grab of the pointer. The * grabber is responsible for handling pointer events for the seat. @@ -77,6 +99,9 @@ struct wlr_seat_keyboard_state { struct wl_listener surface_destroy; struct wl_listener resource_destroy; + + struct wlr_seat_keyboard_grab *grab; + struct wlr_seat_keyboard_grab *default_grab; }; struct wlr_seat { @@ -97,12 +122,14 @@ struct wlr_seat { struct wl_signal pointer_grab_begin; struct wl_signal pointer_grab_end; + + struct wl_signal keyboard_grab_begin; + struct wl_signal keyboard_grab_end; } events; void *data; }; - /** * Allocates a new wlr_seat and adds a wl_seat global to the display. */ @@ -231,10 +258,48 @@ void wlr_seat_attach_keyboard(struct wlr_seat *seat, */ void wlr_seat_detach_keyboard(struct wlr_seat *seat, struct wlr_keyboard *kb); +/** + * Start a grab of the keyboard of this seat. The grabber is responsible for + * handling all keyboard events until the grab ends. + */ +void wlr_seat_keyboard_start_grab(struct wlr_seat *wlr_seat, + struct wlr_seat_keyboard_grab *grab); + +/** + * End the grab of the keyboard of this seat. This reverts the grab back to the + * default grab for the keyboard. + */ +void wlr_seat_keyboard_end_grab(struct wlr_seat *wlr_seat); + +/** + * Send the keyboard key to focused keyboard resources. Compositors should use + * `wlr_seat_attach_keyboard()` to automatically handle keyboard events. + */ +void wlr_seat_keyboard_send_key(struct wlr_seat *seat, uint32_t time, + uint32_t key, uint32_t state); + +/** + * Send the modifier state to focused keyboard resources. Compositors should use + * `wlr_seat_attach_keyboard()` to automatically handle keyboard events. + */ +void wlr_seat_keyboard_send_modifiers(struct wlr_seat *seat, + uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, + uint32_t group); + +/** + * Notify the seat that the keyboard focus has changed and request it to be the + * focused surface for this keyboard. Defers to any current grab of the seat's + * keyboard. + */ +void wlr_seat_keyboard_notify_enter(struct wlr_seat *wlr_seat, + struct wlr_surface *surface); + /** * Send a keyboard enter event to the given surface and consider it to be the * focused surface for the keyboard. This will send a leave event to the last - * surface that was entered. Pass an array of currently pressed keys. + * surface that was entered. Compositors should use + * `wlr_seat_keyboard_notify_enter()` to change keyboard focus to respect + * keyboard grabs. */ void wlr_seat_keyboard_enter(struct wlr_seat *wlr_seat, struct wlr_surface *surface); diff --git a/include/wlr/types/wlr_xdg_shell_v6.h b/include/wlr/types/wlr_xdg_shell_v6.h index c7067e80..0a633822 100644 --- a/include/wlr/types/wlr_xdg_shell_v6.h +++ b/include/wlr/types/wlr_xdg_shell_v6.h @@ -46,6 +46,7 @@ struct wlr_xdg_popup_v6 { struct wlr_xdg_popup_grab_v6 { 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_v6::popup_grabs diff --git a/rootston/cursor.c b/rootston/cursor.c index df5869e5..2918b50f 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -234,7 +234,7 @@ static void do_cursor_button_press(struct roots_input *input, % (sizeof(input->input_events) / sizeof(input->input_events[0])); set_view_focus(input, desktop, view); if (view) { - wlr_seat_keyboard_enter(input->wl_seat, surface); + wlr_seat_keyboard_notify_enter(input->wl_seat, surface); } break; } diff --git a/types/wlr_seat.c b/types/wlr_seat.c index c4fd3f65..4f8d3744 100644 --- a/types/wlr_seat.c +++ b/types/wlr_seat.c @@ -205,6 +205,34 @@ static const struct wlr_pointer_grab_interface default_pointer_grab_impl = { .cancel = default_pointer_cancel, }; +static void default_keyboard_enter(struct wlr_seat_keyboard_grab *grab, + struct wlr_surface *surface) { + wlr_seat_keyboard_enter(grab->seat, surface); +} + +static void default_keyboard_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 default_keyboard_modifiers(struct wlr_seat_keyboard_grab *grab, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) { + wlr_seat_keyboard_send_modifiers(grab->seat, mods_depressed, + mods_latched, mods_locked, group); +} + +static void default_keyboard_cancel(struct wlr_seat_keyboard_grab *grab) { + // cannot be cancelled +} + +static const struct wlr_keyboard_grab_interface default_keyboard_grab_impl = { + .enter = default_keyboard_enter, + .key = default_keyboard_key, + .modifiers = default_keyboard_modifiers, + .cancel = default_keyboard_cancel, +}; + struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { struct wlr_seat *wlr_seat = calloc(1, sizeof(struct wlr_seat)); if (!wlr_seat) { @@ -215,16 +243,28 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { wl_list_init(&wlr_seat->pointer_state.surface_destroy.link); wl_list_init(&wlr_seat->pointer_state.resource_destroy.link); - struct wlr_seat_pointer_grab *default_grab = + struct wlr_seat_pointer_grab *pointer_grab = calloc(1, sizeof(struct wlr_seat_pointer_grab)); - if (!default_grab) { + if (!pointer_grab) { free(wlr_seat); return NULL; } - default_grab->interface = &default_pointer_grab_impl; - default_grab->seat = wlr_seat; - wlr_seat->pointer_state.default_grab = default_grab; - wlr_seat->pointer_state.grab = default_grab; + pointer_grab->interface = &default_pointer_grab_impl; + pointer_grab->seat = wlr_seat; + wlr_seat->pointer_state.default_grab = pointer_grab; + wlr_seat->pointer_state.grab = pointer_grab; + + struct wlr_seat_keyboard_grab *keyboard_grab = + calloc(1, sizeof(struct wlr_seat_keyboard_grab)); + if (!keyboard_grab) { + free(pointer_grab); + free(wlr_seat); + return NULL; + } + keyboard_grab->interface = &default_keyboard_grab_impl; + keyboard_grab->seat = wlr_seat; + wlr_seat->keyboard_state.default_grab = keyboard_grab; + wlr_seat->keyboard_state.grab = keyboard_grab; wlr_seat->keyboard_state.wlr_seat = wlr_seat; wl_list_init(&wlr_seat->keyboard_state.resource_destroy.link); @@ -249,6 +289,9 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { wl_signal_init(&wlr_seat->events.pointer_grab_begin); wl_signal_init(&wlr_seat->events.pointer_grab_end); + wl_signal_init(&wlr_seat->events.keyboard_grab_begin); + wl_signal_init(&wlr_seat->events.keyboard_grab_end); + return wlr_seat; } @@ -265,6 +308,7 @@ void wlr_seat_destroy(struct wlr_seat *wlr_seat) { wl_global_destroy(wlr_seat->wl_global); free(wlr_seat->pointer_state.default_grab); + free(wlr_seat->keyboard_state.default_grab); free(wlr_seat->data_device); free(wlr_seat->name); free(wlr_seat); @@ -495,6 +539,18 @@ static void keyboard_switch_seat_keyboard(struct wlr_seat_handle *handle, handle->seat_keyboard = seat_kb; } +void wlr_seat_keyboard_send_key(struct wlr_seat *wlr_seat, uint32_t time, + uint32_t key, uint32_t state) { + struct wlr_seat_handle *handle = wlr_seat->keyboard_state.focused_handle; + if (!handle || !handle->keyboard) { + return; + } + + uint32_t serial = wl_display_next_serial(wlr_seat->display); + wl_keyboard_send_key(handle->keyboard, serial, + time, key, state); +} + static void keyboard_key_notify(struct wl_listener *listener, void *data) { struct wlr_seat_keyboard *seat_kb = wl_container_of(listener, seat_kb, key); struct wlr_seat *seat = seat_kb->seat; @@ -508,9 +564,9 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) { struct wlr_event_keyboard_key *event = data; enum wlr_key_state key_state = event->state; - uint32_t key_serial = wl_display_next_serial(seat->display); - wl_keyboard_send_key(handle->keyboard, key_serial, - (uint32_t)event->time_usec, event->keycode, key_state); + struct wlr_seat_keyboard_grab *grab = seat->keyboard_state.grab; + grab->interface->key(grab, (uint32_t)(event->time_usec / 1000), + event->keycode, key_state); } static void keyboard_modifiers_notify(struct wl_listener *listener, @@ -527,8 +583,9 @@ static void keyboard_modifiers_notify(struct wl_listener *listener, struct wlr_keyboard *keyboard = seat_kb->keyboard; - uint32_t modifiers_serial = wl_display_next_serial(seat->display); - wl_keyboard_send_modifiers(handle->keyboard, modifiers_serial, + struct wlr_seat_keyboard_grab *grab = seat->keyboard_state.grab; + + grab->interface->modifiers(grab, keyboard->modifiers.depressed, keyboard->modifiers.latched, keyboard->modifiers.locked, keyboard->modifiers.group); } @@ -584,6 +641,21 @@ void wlr_seat_detach_keyboard(struct wlr_seat *seat, struct wlr_keyboard *kb) { } } +void wlr_seat_keyboard_start_grab(struct wlr_seat *wlr_seat, + struct wlr_seat_keyboard_grab *grab) { + grab->seat = wlr_seat; + wlr_seat->keyboard_state.grab = grab; + + wl_signal_emit(&wlr_seat->events.keyboard_grab_begin, grab); +} + +void wlr_seat_keyboard_end_grab(struct wlr_seat *wlr_seat) { + struct wlr_seat_keyboard_grab *grab = wlr_seat->keyboard_state.grab; + wlr_seat->keyboard_state.grab = wlr_seat->keyboard_state.default_grab; + + wl_signal_emit(&wlr_seat->events.keyboard_grab_end, grab); +} + static void keyboard_surface_destroy_notify(struct wl_listener *listener, void *data) { struct wlr_seat_keyboard_state *state = wl_container_of( @@ -604,6 +676,21 @@ static void keyboard_resource_destroy_notify(struct wl_listener *listener, wlr_seat_keyboard_clear_focus(state->wlr_seat); } +void wlr_seat_keyboard_send_modifiers(struct wlr_seat *seat, + uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, + uint32_t group) { + struct wlr_seat_handle *handle = seat->keyboard_state.focused_handle; + if (!handle || !handle->keyboard) { + return; + } + + uint32_t serial = wl_display_next_serial(seat->display); + + wl_keyboard_send_modifiers(handle->keyboard, serial, + mods_depressed, mods_latched, + mods_locked, group); +} + void wlr_seat_keyboard_enter(struct wlr_seat *wlr_seat, struct wlr_surface *surface) { if (wlr_seat->keyboard_state.focused_surface == surface) { @@ -660,6 +747,12 @@ void wlr_seat_keyboard_enter(struct wlr_seat *wlr_seat, wlr_seat->keyboard_state.focused_surface = surface; } +void wlr_seat_keyboard_notify_enter(struct wlr_seat *wlr_seat, struct + wlr_surface *surface) { + struct wlr_seat_keyboard_grab *grab = wlr_seat->keyboard_state.grab; + grab->interface->enter(grab, surface); +} + void wlr_seat_keyboard_clear_focus(struct wlr_seat *wlr_seat) { struct wl_array keys; wl_array_init(&keys); diff --git a/types/wlr_xdg_shell_v6.c b/types/wlr_xdg_shell_v6.c index 50e812a7..fe9a5359 100644 --- a/types/wlr_xdg_shell_v6.c +++ b/types/wlr_xdg_shell_v6.c @@ -102,6 +102,33 @@ static const struct wlr_pointer_grab_interface xdg_pointer_grab_impl = { .axis = xdg_pointer_grab_axis, }; +static void xdg_keyboard_grab_enter(struct wlr_seat_keyboard_grab *grab, struct wlr_surface *surface) { + // 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, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) { + wlr_seat_keyboard_send_modifiers(grab->seat, mods_depressed, mods_latched, + mods_locked, group); +} + +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_v6 *xdg_shell_popup_grab_from_seat( struct wlr_xdg_shell_v6 *shell, struct wlr_seat *seat) { struct wlr_xdg_popup_grab_v6 *xdg_grab; @@ -118,7 +145,8 @@ static struct wlr_xdg_popup_grab_v6 *xdg_shell_popup_grab_from_seat( xdg_grab->pointer_grab.data = xdg_grab; xdg_grab->pointer_grab.interface = &xdg_pointer_grab_impl; - // TODO: keyboard grab + xdg_grab->keyboard_grab.data = xdg_grab; + xdg_grab->keyboard_grab.interface = &xdg_keyboard_grab_impl; wl_list_init(&xdg_grab->popups); @@ -130,6 +158,7 @@ static struct wlr_xdg_popup_grab_v6 *xdg_shell_popup_grab_from_seat( static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) { + // TODO: probably need to ungrab before this event wl_signal_emit(&surface->events.destroy, surface); if (surface->configure_idle) { @@ -166,9 +195,13 @@ static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) { wl_list_remove(&surface->popup_state->grab_link); - if (wl_list_empty(&grab->popups) && - grab->seat->pointer_state.grab == &grab->pointer_grab) { - wlr_seat_pointer_end_grab(grab->seat); + 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); + } } } @@ -355,6 +388,7 @@ static void xdg_popup_protocol_grab(struct wl_client *client, wl_list_insert(&popup_grab->popups, &surface->popup_state->grab_link); wlr_seat_pointer_start_grab(handle->wlr_seat, &popup_grab->pointer_grab); + wlr_seat_keyboard_start_grab(handle->wlr_seat, &popup_grab->keyboard_grab); } static const struct zxdg_popup_v6_interface zxdg_popup_v6_implementation = {