From cd1204f71f7222ea896c94bea3194d3b220845be Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sun, 8 Oct 2017 16:49:37 -0400 Subject: [PATCH 1/8] wl-shell: render popups in the right place --- include/wlr/types/wlr_wl_shell.h | 6 +++- rootston/output.c | 32 +++++++++++++++++-- types/wlr_wl_shell.c | 55 +++++++++++++++++++++++++++++--- 3 files changed, 84 insertions(+), 9 deletions(-) diff --git a/include/wlr/types/wlr_wl_shell.h b/include/wlr/types/wlr_wl_shell.h index 0db99989..ae30f5c4 100644 --- a/include/wlr/types/wlr_wl_shell.h +++ b/include/wlr/types/wlr_wl_shell.h @@ -18,7 +18,6 @@ struct wlr_wl_shell { }; struct wlr_wl_shell_surface_transient_state { - struct wlr_wl_shell_surface *parent; int32_t x; int32_t y; enum wl_shell_surface_transient flags; @@ -55,6 +54,10 @@ struct wlr_wl_shell_surface { struct wl_listener surface_destroy_listener; + struct wlr_wl_shell_surface *parent; + struct wl_list child_link; + struct wl_list children; // transient and popups + struct { struct wl_signal destroy; struct wl_signal ping_timeout; @@ -108,5 +111,6 @@ void wlr_wl_shell_surface_ping(struct wlr_wl_shell_surface *surface); void wlr_wl_shell_surface_configure(struct wlr_wl_shell_surface *surface, enum wl_shell_surface_resize edges, int32_t width, int32_t height); void wlr_wl_shell_surface_popup_done(struct wlr_wl_shell_surface *surface); +bool wlr_wl_shell_surface_is_transient(struct wlr_wl_shell_surface *surface); #endif diff --git a/rootston/output.c b/rootston/output.c index d1b2c7de..90ba1eb7 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -97,13 +97,39 @@ static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface, } } +static void render_wl_shell_surface(struct wlr_wl_shell_surface *surface, struct roots_desktop *desktop, + struct wlr_output *wlr_output, struct timespec *when, double lx, + double ly, float rotation, bool is_child) { + if (is_child || !wlr_wl_shell_surface_is_transient(surface)) { + render_surface(surface->surface, desktop, wlr_output, when, + lx, ly, rotation); + struct wlr_wl_shell_surface *child; + wl_list_for_each(child, &surface->children, child_link) { + render_wl_shell_surface(child, desktop, wlr_output, when, + lx + child->transient_state->x, + ly + child->transient_state->y, + rotation, true); + } + } +} + 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, view->rotation); - if (view->type == ROOTS_XDG_SHELL_V6_VIEW) { + switch (view->type) { + case ROOTS_XDG_SHELL_V6_VIEW: + render_surface(view->wlr_surface, desktop, wlr_output, when, + view->x, view->y, view->rotation); render_xdg_v6_popups(view->xdg_surface_v6, desktop, wlr_output, when, view->x, view->y, view->rotation); + break; + case ROOTS_WL_SHELL_VIEW: + render_wl_shell_surface(view->wl_shell_surface, desktop, wlr_output, + when, view->x, view->y, view->rotation, false); + break; + case ROOTS_XWAYLAND_VIEW: + render_surface(view->wlr_surface, desktop, wlr_output, when, + view->x, view->y, view->rotation); + break; } } diff --git a/types/wlr_wl_shell.c b/types/wlr_wl_shell.c index e1507e2f..209bf302 100644 --- a/types/wlr_wl_shell.c +++ b/types/wlr_wl_shell.c @@ -93,22 +93,53 @@ static void shell_surface_set_toplevel(struct wl_client *client, NULL); } +static void shell_surface_set_parent(struct wlr_wl_shell_surface *surface, + struct wlr_wl_shell_surface *parent) { + assert(surface); + surface->parent = parent; + wl_list_remove(&surface->child_link); + wl_list_init(&surface->child_link); + if (parent) { + wl_list_insert(&parent->children, &surface->child_link); + } +} + +static struct wlr_wl_shell_surface *shell_get_shell_surface(struct wlr_wl_shell *shell, + struct wlr_surface *surface) { + if (surface) { + struct wlr_wl_shell_surface *wl_surface; + wl_list_for_each(wl_surface, &shell->surfaces, link) { + if (wl_surface->surface == surface) { + return wl_surface; + } + } + } + return NULL; +} + static void shell_surface_set_transient(struct wl_client *client, struct wl_resource *resource, struct wl_resource *parent_resource, int32_t x, int32_t y, enum wl_shell_surface_transient flags) { wlr_log(L_DEBUG, "got shell surface transient"); struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource); - struct wlr_wl_shell_surface *parent = + struct wlr_surface *parent = wl_resource_get_user_data(parent_resource); // TODO: check if parent_resource == NULL? + struct wlr_wl_shell_surface *wl_parent = shell_get_shell_surface(surface->shell, parent); + + if (!wl_parent) { + return; + } + struct wlr_wl_shell_surface_transient_state *transient_state = calloc(1, sizeof(struct wlr_wl_shell_surface_transient_state)); if (transient_state == NULL) { wl_client_post_no_memory(client); return; } - transient_state->parent = parent; + + shell_surface_set_parent(surface, wl_parent); transient_state->x = x; transient_state->y = y; transient_state->flags = flags; @@ -158,9 +189,10 @@ static void shell_surface_set_popup(struct wl_client *client, struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource); struct wlr_seat_handle *seat_handle = wl_resource_get_user_data(seat_resource); - struct wlr_wl_shell_surface *parent = + struct wlr_surface *parent = wl_resource_get_user_data(parent_resource); - // TODO: check if parent_resource == NULL? + + struct wlr_wl_shell_surface *wl_parent = shell_get_shell_surface(surface->shell, parent); struct wlr_wl_shell_surface_transient_state *transient_state = calloc(1, sizeof(struct wlr_wl_shell_surface_transient_state)); @@ -168,7 +200,7 @@ static void shell_surface_set_popup(struct wl_client *client, wl_client_post_no_memory(client); return; } - transient_state->parent = parent; + shell_surface_set_parent(surface, wl_parent); transient_state->x = x; transient_state->y = y; transient_state->flags = flags; @@ -263,6 +295,13 @@ struct wl_shell_surface_interface shell_surface_interface = { static void wl_shell_surface_destroy(struct wlr_wl_shell_surface *surface) { wl_signal_emit(&surface->events.destroy, surface); wl_resource_set_user_data(surface->resource, NULL); + + struct wlr_wl_shell_surface *child; + wl_list_for_each(child, &surface->children, child_link) { + shell_surface_set_parent(child, NULL); + } + wl_list_remove(&surface->child_link); + wl_list_remove(&surface->link); wl_list_remove(&surface->surface_destroy_listener.link); wl_event_source_remove(surface->ping_timer); @@ -311,6 +350,8 @@ static void wl_shell_get_shell_surface(struct wl_client *client, wl_client_post_no_memory(client); return; } + wl_list_init(&wl_surface->child_link); + wl_list_init(&wl_surface->children); wl_surface->shell = wl_shell; wl_surface->client = client; @@ -430,3 +471,7 @@ void wlr_wl_shell_surface_configure(struct wlr_wl_shell_surface *surface, void wlr_wl_shell_surface_popup_done(struct wlr_wl_shell_surface *surface) { wl_shell_surface_send_popup_done(surface->resource); } + +bool wlr_wl_shell_surface_is_transient(struct wlr_wl_shell_surface *surface) { + return surface->parent != NULL; +} From 22a16a59ca13863c0017a2c8dfa6e49b26a13744 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Mon, 9 Oct 2017 06:16:51 -0400 Subject: [PATCH 2/8] wl-shell: basic pointer grab --- include/wlr/types/wlr_wl_shell.h | 14 ++- types/wlr_seat.c | 5 +- types/wlr_wl_shell.c | 142 +++++++++++++++++++++++++++++-- 3 files changed, 149 insertions(+), 12 deletions(-) diff --git a/include/wlr/types/wlr_wl_shell.h b/include/wlr/types/wlr_wl_shell.h index ae30f5c4..162874e9 100644 --- a/include/wlr/types/wlr_wl_shell.h +++ b/include/wlr/types/wlr_wl_shell.h @@ -3,11 +3,13 @@ #include #include +#include struct wlr_wl_shell { struct wl_global *wl_global; struct wl_list wl_resources; struct wl_list surfaces; + struct wl_list popup_grabs; uint32_t ping_timeout; struct { @@ -24,10 +26,19 @@ struct wlr_wl_shell_surface_transient_state { }; struct wlr_wl_shell_surface_popup_state { - struct wlr_seat_handle *seat_handle; + struct wlr_seat *seat; uint32_t serial; }; +// each seat gets a popup grab +struct wlr_wl_shell_popup_grab { + struct wl_client *client; + struct wlr_seat_pointer_grab pointer_grab; + struct wlr_seat *seat; + struct wl_list popups; + struct wl_list link; // wlr_wl_shell::popup_grabs +}; + enum wlr_wl_shell_surface_state { WLR_WL_SHELL_SURFACE_STATE_NONE, WLR_WL_SHELL_SURFACE_STATE_TOPLEVEL, @@ -48,6 +59,7 @@ struct wlr_wl_shell_surface { enum wlr_wl_shell_surface_state state; struct wlr_wl_shell_surface_transient_state *transient_state; struct wlr_wl_shell_surface_popup_state *popup_state; + struct wl_list grab_link; // wlr_wl_shell_popup_grab::popups char *title; char *class; diff --git a/types/wlr_seat.c b/types/wlr_seat.c index d58b2129..ef81655c 100644 --- a/types/wlr_seat.c +++ b/types/wlr_seat.c @@ -514,8 +514,9 @@ void wlr_seat_pointer_start_grab(struct wlr_seat *wlr_seat, 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; - wl_signal_emit(&wlr_seat->events.pointer_grab_end, grab); + if (grab != wlr_seat->pointer_state.default_grab) { + wlr_seat->pointer_state.grab = wlr_seat->pointer_state.default_grab; + } } void wlr_seat_pointer_notify_enter(struct wlr_seat *wlr_seat, diff --git a/types/wlr_wl_shell.c b/types/wlr_wl_shell.c index 209bf302..c5394d97 100644 --- a/types/wlr_wl_shell.c +++ b/types/wlr_wl_shell.c @@ -11,6 +11,61 @@ static const char *wlr_wl_shell_surface_role = "wl_shell_surface"; +static void shell_pointer_grab_end(struct wlr_seat_pointer_grab *grab) { + struct wlr_wl_shell_popup_grab *popup_grab = grab->data; + + struct wlr_wl_shell_surface *popup, *tmp = NULL; + wl_list_for_each_safe(popup, tmp, &popup_grab->popups, grab_link) { + wl_shell_surface_send_popup_done(popup->resource); + } + + wlr_seat_pointer_end_grab(grab->seat); +} + +static void shell_pointer_grab_enter(struct wlr_seat_pointer_grab *grab, + struct wlr_surface *surface, double sx, double sy) { + struct wlr_wl_shell_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 shell_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 shell_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 { + shell_pointer_grab_end(grab); + return 0; + } +} + +static void shell_pointer_grab_cancel(struct wlr_seat_pointer_grab *grab) { + shell_pointer_grab_end(grab); +} + +static void shell_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 const struct wlr_pointer_grab_interface shell_pointer_grab_impl = { + .enter = shell_pointer_grab_enter, + .motion = shell_pointer_grab_motion, + .button = shell_pointer_grab_button, + .cancel = shell_pointer_grab_cancel, + .axis = shell_pointer_grab_axis, +}; + static void shell_surface_pong(struct wl_client *client, struct wl_resource *resource, uint32_t serial) { wlr_log(L_DEBUG, "got shell surface pong"); @@ -47,6 +102,48 @@ static void shell_surface_move(struct wl_client *client, free(event); } +static struct wlr_wl_shell_popup_grab *shell_popup_grab_from_seat( + struct wlr_wl_shell *shell, struct wlr_seat *seat) { + struct wlr_wl_shell_popup_grab *shell_grab; + wl_list_for_each(shell_grab, &shell->popup_grabs, link) { + if (shell_grab->seat == seat) { + return shell_grab; + } + } + + shell_grab = calloc(1, sizeof(struct wlr_wl_shell_popup_grab)); + if (!shell_grab) { + return NULL; + } + + shell_grab->pointer_grab.data = shell_grab; + shell_grab->pointer_grab.interface = &shell_pointer_grab_impl; + + wl_list_init(&shell_grab->popups); + + wl_list_insert(&shell->popup_grabs, &shell_grab->link); + shell_grab->seat = seat; + + return shell_grab; +} + +static void shell_destroy_popup_state(struct wlr_wl_shell_surface *surface) { + if (surface->popup_state) { + wl_list_remove(&surface->grab_link); + struct wlr_wl_shell_popup_grab *grab = + shell_popup_grab_from_seat(surface->shell, + surface->popup_state->seat); + if (wl_list_empty(&grab->popups)) { + if (grab->seat->pointer_state.grab == &grab->pointer_grab) { + wlr_seat_pointer_end_grab(grab->seat); + } + } + free(surface->popup_state); + surface->popup_state = NULL; + } +} + + static void shell_surface_resize(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial, enum wl_shell_surface_resize edges) { @@ -76,12 +173,17 @@ static void shell_surface_set_state(struct wlr_wl_shell_surface *surface, enum wlr_wl_shell_surface_state state, struct wlr_wl_shell_surface_transient_state *transient_state, struct wlr_wl_shell_surface_popup_state *popup_state) { + bool is_new = (surface->state == WLR_WL_SHELL_SURFACE_STATE_NONE); surface->state = state; free(surface->transient_state); surface->transient_state = transient_state; - free(surface->popup_state); + shell_destroy_popup_state(surface); surface->popup_state = popup_state; + if (is_new) { + wl_signal_emit(&surface->shell->events.new_surface, surface); + } + wl_signal_emit(&surface->events.set_state, surface); } @@ -96,10 +198,12 @@ static void shell_surface_set_toplevel(struct wl_client *client, static void shell_surface_set_parent(struct wlr_wl_shell_surface *surface, struct wlr_wl_shell_surface *parent) { assert(surface); + if (surface->parent == parent) { + return; + } surface->parent = parent; - wl_list_remove(&surface->child_link); - wl_list_init(&surface->child_link); if (parent) { + wl_list_remove(&surface->child_link); wl_list_insert(&parent->children, &surface->child_link); } } @@ -180,19 +284,34 @@ static void shell_surface_set_fullscreen(struct wl_client *client, free(event); } + static void shell_surface_set_popup(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial, struct wl_resource *parent_resource, int32_t x, int32_t y, enum wl_shell_surface_transient flags) { - // TODO: do a pointer grab - wlr_log(L_DEBUG, "got shell surface popup"); struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource); struct wlr_seat_handle *seat_handle = wl_resource_get_user_data(seat_resource); struct wlr_surface *parent = wl_resource_get_user_data(parent_resource); + struct wlr_wl_shell_popup_grab *grab = + shell_popup_grab_from_seat(surface->shell, seat_handle->wlr_seat); + if (!grab) { + wl_client_post_no_memory(client); + return; + } - struct wlr_wl_shell_surface *wl_parent = shell_get_shell_surface(surface->shell, parent); + struct wlr_wl_shell_surface *wl_parent = + shell_get_shell_surface(surface->shell, parent); + + if (surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP) { + surface->transient_state->x = x; + surface->transient_state->y = y; + shell_surface_set_parent(surface, wl_parent); + grab->client = surface->client; + wlr_seat_pointer_start_grab(seat_handle->wlr_seat, &grab->pointer_grab); + return; + } struct wlr_wl_shell_surface_transient_state *transient_state = calloc(1, sizeof(struct wlr_wl_shell_surface_transient_state)); @@ -212,11 +331,15 @@ static void shell_surface_set_popup(struct wl_client *client, wl_client_post_no_memory(client); return; } - popup_state->seat_handle = seat_handle; + popup_state->seat = seat_handle->wlr_seat; popup_state->serial = serial; shell_surface_set_state(surface, WLR_WL_SHELL_SURFACE_STATE_POPUP, transient_state, popup_state); + + grab->client = surface->client; + wl_list_insert(&grab->popups, &surface->grab_link); + wlr_seat_pointer_start_grab(seat_handle->wlr_seat, &grab->pointer_grab); } static void shell_surface_set_maximized(struct wl_client *client, @@ -294,6 +417,7 @@ struct wl_shell_surface_interface shell_surface_interface = { static void wl_shell_surface_destroy(struct wlr_wl_shell_surface *surface) { wl_signal_emit(&surface->events.destroy, surface); + shell_destroy_popup_state(surface); wl_resource_set_user_data(surface->resource, NULL); struct wlr_wl_shell_surface *child; @@ -306,7 +430,6 @@ static void wl_shell_surface_destroy(struct wlr_wl_shell_surface *surface) { wl_list_remove(&surface->surface_destroy_listener.link); wl_event_source_remove(surface->ping_timer); free(surface->transient_state); - free(surface->popup_state); free(surface->title); free(surface->class); free(surface); @@ -350,6 +473,7 @@ static void wl_shell_get_shell_surface(struct wl_client *client, wl_client_post_no_memory(client); return; } + wl_list_init(&wl_surface->grab_link); wl_list_init(&wl_surface->child_link); wl_list_init(&wl_surface->children); @@ -388,7 +512,6 @@ static void wl_shell_get_shell_surface(struct wl_client *client, } wl_list_insert(&wl_shell->surfaces, &wl_surface->link); - wl_signal_emit(&wl_shell->events.new_surface, wl_surface); } static struct wl_shell_interface wl_shell_impl = { @@ -431,6 +554,7 @@ struct wlr_wl_shell *wlr_wl_shell_create(struct wl_display *display) { wl_shell->wl_global = wl_global; wl_list_init(&wl_shell->wl_resources); wl_list_init(&wl_shell->surfaces); + wl_list_init(&wl_shell->popup_grabs); wl_signal_init(&wl_shell->events.new_surface); return wl_shell; } From d4c065e59b1d0275d91dac83bed8cfa54d2e57b5 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Mon, 9 Oct 2017 18:36:11 -0400 Subject: [PATCH 3/8] wl_shell: map transient surfaces closer to parent --- rootston/output.c | 2 +- rootston/wl_shell.c | 23 ++++++++++++++++++++++- types/wlr_wl_shell.c | 12 ++++++------ 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/rootston/output.c b/rootston/output.c index 90ba1eb7..29a4413f 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -100,7 +100,7 @@ static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface, static void render_wl_shell_surface(struct wlr_wl_shell_surface *surface, struct roots_desktop *desktop, struct wlr_output *wlr_output, struct timespec *when, double lx, double ly, float rotation, bool is_child) { - if (is_child || !wlr_wl_shell_surface_is_transient(surface)) { + if (is_child || surface->state != WLR_WL_SHELL_SURFACE_STATE_POPUP) { render_surface(surface->surface, desktop, wlr_output, when, lx, ly, rotation); struct wlr_wl_shell_surface *child; diff --git a/rootston/wl_shell.c b/rootston/wl_shell.c index 009a8c06..34f53c7a 100644 --- a/rootston/wl_shell.c +++ b/rootston/wl_shell.c @@ -73,6 +73,14 @@ static void handle_destroy(struct wl_listener *listener, void *data) { free(roots_surface); } +static int shell_surface_compare_equals(const void *item, const void *cmp_to) { + const struct roots_view *view = item; + if (view->type == ROOTS_WL_SHELL_VIEW && view->wl_shell_surface == cmp_to) { + return 0; + } + return -1; +} + void handle_wl_shell_surface(struct wl_listener *listener, void *data) { struct roots_desktop *desktop = wl_container_of(listener, desktop, wl_shell_surface); @@ -107,7 +115,20 @@ void handle_wl_shell_surface(struct wl_listener *listener, void *data) { struct roots_view *view = calloc(1, sizeof(struct roots_view)); view->type = ROOTS_WL_SHELL_VIEW; - view->x = view->y = 200; + + if (surface->state == WLR_WL_SHELL_SURFACE_STATE_TRANSIENT) { + // we need to map it relative to the parent + int i = + list_seq_find(desktop->views, + shell_surface_compare_equals, surface->parent); + if (i != -1) { + struct roots_view *parent = desktop->views->items[i]; + view->x = parent->x + surface->transient_state->x; + view->y = parent->y + surface->transient_state->y; + } + } else { + view->x = view->y = 200; + } view->wl_shell_surface = surface; view->roots_wl_shell_surface = roots_surface; view->wlr_surface = surface->surface; diff --git a/types/wlr_wl_shell.c b/types/wlr_wl_shell.c index c5394d97..6421cc45 100644 --- a/types/wlr_wl_shell.c +++ b/types/wlr_wl_shell.c @@ -195,9 +195,9 @@ static void shell_surface_set_toplevel(struct wl_client *client, NULL); } -static void shell_surface_set_parent(struct wlr_wl_shell_surface *surface, +static void popup_set_parent(struct wlr_wl_shell_surface *surface, struct wlr_wl_shell_surface *parent) { - assert(surface); + assert(surface && surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP); if (surface->parent == parent) { return; } @@ -243,7 +243,7 @@ static void shell_surface_set_transient(struct wl_client *client, return; } - shell_surface_set_parent(surface, wl_parent); + surface->parent = wl_parent; transient_state->x = x; transient_state->y = y; transient_state->flags = flags; @@ -307,7 +307,7 @@ static void shell_surface_set_popup(struct wl_client *client, if (surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP) { surface->transient_state->x = x; surface->transient_state->y = y; - shell_surface_set_parent(surface, wl_parent); + popup_set_parent(surface, wl_parent); grab->client = surface->client; wlr_seat_pointer_start_grab(seat_handle->wlr_seat, &grab->pointer_grab); return; @@ -319,7 +319,6 @@ static void shell_surface_set_popup(struct wl_client *client, wl_client_post_no_memory(client); return; } - shell_surface_set_parent(surface, wl_parent); transient_state->x = x; transient_state->y = y; transient_state->flags = flags; @@ -337,6 +336,7 @@ static void shell_surface_set_popup(struct wl_client *client, shell_surface_set_state(surface, WLR_WL_SHELL_SURFACE_STATE_POPUP, transient_state, popup_state); + popup_set_parent(surface, wl_parent); grab->client = surface->client; wl_list_insert(&grab->popups, &surface->grab_link); wlr_seat_pointer_start_grab(seat_handle->wlr_seat, &grab->pointer_grab); @@ -422,7 +422,7 @@ static void wl_shell_surface_destroy(struct wlr_wl_shell_surface *surface) { struct wlr_wl_shell_surface *child; wl_list_for_each(child, &surface->children, child_link) { - shell_surface_set_parent(child, NULL); + popup_set_parent(child, NULL); } wl_list_remove(&surface->child_link); From fe3c6c929be467b3e1ec860c94c9524a6686bd61 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Tue, 10 Oct 2017 10:00:09 -0400 Subject: [PATCH 4/8] wl-shell: popup input handling --- include/wlr/types/wlr_wl_shell.h | 9 ++++++++ rootston/desktop.c | 21 ++++++++++++++++++ rootston/wl_shell.c | 37 ++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/include/wlr/types/wlr_wl_shell.h b/include/wlr/types/wlr_wl_shell.h index 162874e9..6133b80d 100644 --- a/include/wlr/types/wlr_wl_shell.h +++ b/include/wlr/types/wlr_wl_shell.h @@ -125,4 +125,13 @@ void wlr_wl_shell_surface_configure(struct wlr_wl_shell_surface *surface, void wlr_wl_shell_surface_popup_done(struct wlr_wl_shell_surface *surface); bool wlr_wl_shell_surface_is_transient(struct wlr_wl_shell_surface *surface); +/** + * 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_wl_shell_surface *wlr_wl_shell_surface_popup_at( + struct wlr_wl_shell_surface *surface, double sx, double sy, + double *popup_sx, double *popup_sy); + #endif diff --git a/rootston/desktop.c b/rootston/desktop.c index bef0fad4..d5cac575 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -103,6 +103,12 @@ struct roots_view *view_at(struct roots_desktop *desktop, double lx, double ly, for (int i = desktop->views->length - 1; i >= 0; --i) { struct roots_view *view = desktop->views->items[i]; + if (view->type == ROOTS_WL_SHELL_VIEW && + view->wl_shell_surface->state == + WLR_WL_SHELL_SURFACE_STATE_POPUP) { + continue; + } + double view_sx = lx - view->x; double view_sy = ly - view->y; @@ -138,6 +144,21 @@ struct roots_view *view_at(struct roots_desktop *desktop, double lx, double ly, } } + if (view->type == ROOTS_WL_SHELL_VIEW) { + // TODO: test if this works with rotated views + double popup_sx, popup_sy; + struct wlr_wl_shell_surface *popup = + wlr_wl_shell_surface_popup_at(view->wl_shell_surface, + 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 = wlr_surface_subsurface_at(view->wlr_surface, diff --git a/rootston/wl_shell.c b/rootston/wl_shell.c index 34f53c7a..a629575b 100644 --- a/rootston/wl_shell.c +++ b/rootston/wl_shell.c @@ -138,3 +138,40 @@ void handle_wl_shell_surface(struct wl_listener *listener, void *data) { roots_surface->view = view; list_add(desktop->views, view); } + +struct wlr_wl_shell_surface *wlr_wl_shell_surface_popup_at( + struct wlr_wl_shell_surface *surface, double sx, double sy, + double *popup_sx, double *popup_sy) { + struct wlr_wl_shell_surface *popup; + wl_list_for_each(popup, &surface->children, child_link) { + double _popup_sx = popup->transient_state->x; + double _popup_sy = popup->transient_state->y; + int popup_width = + popup->surface->current->buffer_width; + int popup_height = + popup->surface->current->buffer_height; + + struct wlr_wl_shell_surface *_popup = + wlr_wl_shell_surface_popup_at(popup, + popup->transient_state->x, + popup->transient_state->y, + popup_sx, popup_sy); + if (_popup) { + *popup_sx = sx + _popup_sx; + *popup_sy = sy + _popup_sy; + 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, sy - _popup_sy, NULL)) { + *popup_sx = _popup_sx; + *popup_sy = _popup_sy; + return popup; + } + } + } + + return NULL; +} From 1b2742d0b2debeeb783e1a15ef9bb92c633506be Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Tue, 10 Oct 2017 10:28:43 -0400 Subject: [PATCH 5/8] bug: emit surface new event later --- include/wlr/types/wlr_wl_shell.h | 2 ++ types/wlr_wl_shell.c | 22 +++++++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/include/wlr/types/wlr_wl_shell.h b/include/wlr/types/wlr_wl_shell.h index 6133b80d..e768d9d5 100644 --- a/include/wlr/types/wlr_wl_shell.h +++ b/include/wlr/types/wlr_wl_shell.h @@ -51,6 +51,7 @@ struct wlr_wl_shell_surface { struct wl_client *client; struct wl_resource *resource; struct wlr_surface *surface; + bool configured; struct wl_list link; uint32_t ping_serial; @@ -65,6 +66,7 @@ struct wlr_wl_shell_surface { char *class; struct wl_listener surface_destroy_listener; + struct wl_listener surface_commit_listener; struct wlr_wl_shell_surface *parent; struct wl_list child_link; diff --git a/types/wlr_wl_shell.c b/types/wlr_wl_shell.c index 6421cc45..de018865 100644 --- a/types/wlr_wl_shell.c +++ b/types/wlr_wl_shell.c @@ -8,6 +8,7 @@ #include #include #include +#include static const char *wlr_wl_shell_surface_role = "wl_shell_surface"; @@ -173,17 +174,12 @@ static void shell_surface_set_state(struct wlr_wl_shell_surface *surface, enum wlr_wl_shell_surface_state state, struct wlr_wl_shell_surface_transient_state *transient_state, struct wlr_wl_shell_surface_popup_state *popup_state) { - bool is_new = (surface->state == WLR_WL_SHELL_SURFACE_STATE_NONE); surface->state = state; free(surface->transient_state); surface->transient_state = transient_state; shell_destroy_popup_state(surface); surface->popup_state = popup_state; - if (is_new) { - wl_signal_emit(&surface->shell->events.new_surface, surface); - } - wl_signal_emit(&surface->events.set_state, surface); } @@ -428,6 +424,7 @@ static void wl_shell_surface_destroy(struct wlr_wl_shell_surface *surface) { wl_list_remove(&surface->link); wl_list_remove(&surface->surface_destroy_listener.link); + wl_list_remove(&surface->surface_commit_listener.link); wl_event_source_remove(surface->ping_timer); free(surface->transient_state); free(surface->title); @@ -448,6 +445,17 @@ static void handle_wlr_surface_destroyed(struct wl_listener *listener, wl_container_of(listener, surface, surface_destroy_listener); wl_shell_surface_destroy(surface); } +static void handle_wlr_surface_committed(struct wl_listener *listener, + void *data) { + struct wlr_wl_shell_surface *surface = + wl_container_of(listener, surface, surface_commit_listener); + if (!surface->configured && + surface->surface->texture->valid && + surface->state != WLR_WL_SHELL_SURFACE_STATE_NONE) { + surface->configured = true; + wl_signal_emit(&surface->shell->events.new_surface, surface); + } +} static int wlr_wl_shell_surface_ping_timeout(void *user_data) { struct wlr_wl_shell_surface *surface = user_data; @@ -503,6 +511,10 @@ static void wl_shell_get_shell_surface(struct wl_client *client, &wl_surface->surface_destroy_listener); wl_surface->surface_destroy_listener.notify = handle_wlr_surface_destroyed; + wl_signal_add(&wl_surface->surface->events.commit, + &wl_surface->surface_commit_listener); + wl_surface->surface_commit_listener.notify = handle_wlr_surface_committed; + struct wl_display *display = wl_client_get_display(client); struct wl_event_loop *loop = wl_display_get_event_loop(display); wl_surface->ping_timer = wl_event_loop_add_timer(loop, From f7e7f6271ddb44ed80ab928116ab731a7d878edc Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Tue, 10 Oct 2017 10:54:10 -0400 Subject: [PATCH 6/8] wl-shell: cleanup --- include/wlr/types/wlr_wl_shell.h | 6 +- rootston/output.c | 10 +- rootston/wl_shell.c | 37 -------- types/wlr_wl_shell.c | 151 +++++++++++++++++++------------ 4 files changed, 98 insertions(+), 106 deletions(-) diff --git a/include/wlr/types/wlr_wl_shell.h b/include/wlr/types/wlr_wl_shell.h index e768d9d5..bea92b8b 100644 --- a/include/wlr/types/wlr_wl_shell.h +++ b/include/wlr/types/wlr_wl_shell.h @@ -69,8 +69,8 @@ struct wlr_wl_shell_surface { struct wl_listener surface_commit_listener; struct wlr_wl_shell_surface *parent; - struct wl_list child_link; - struct wl_list children; // transient and popups + struct wl_list popup_link; + struct wl_list popups; struct { struct wl_signal destroy; @@ -124,8 +124,6 @@ void wlr_wl_shell_destroy(struct wlr_wl_shell *wlr_wl_shell); void wlr_wl_shell_surface_ping(struct wlr_wl_shell_surface *surface); void wlr_wl_shell_surface_configure(struct wlr_wl_shell_surface *surface, enum wl_shell_surface_resize edges, int32_t width, int32_t height); -void wlr_wl_shell_surface_popup_done(struct wlr_wl_shell_surface *surface); -bool wlr_wl_shell_surface_is_transient(struct wlr_wl_shell_surface *surface); /** * Find a popup within this surface at the surface-local coordinates. Returns diff --git a/rootston/output.c b/rootston/output.c index 29a4413f..2c061739 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -103,11 +103,11 @@ static void render_wl_shell_surface(struct wlr_wl_shell_surface *surface, struct if (is_child || surface->state != WLR_WL_SHELL_SURFACE_STATE_POPUP) { render_surface(surface->surface, desktop, wlr_output, when, lx, ly, rotation); - struct wlr_wl_shell_surface *child; - wl_list_for_each(child, &surface->children, child_link) { - render_wl_shell_surface(child, desktop, wlr_output, when, - lx + child->transient_state->x, - ly + child->transient_state->y, + struct wlr_wl_shell_surface *popup; + wl_list_for_each(popup, &surface->popups, popup_link) { + render_wl_shell_surface(popup, desktop, wlr_output, when, + lx + popup->transient_state->x, + ly + popup->transient_state->y, rotation, true); } } diff --git a/rootston/wl_shell.c b/rootston/wl_shell.c index a629575b..34f53c7a 100644 --- a/rootston/wl_shell.c +++ b/rootston/wl_shell.c @@ -138,40 +138,3 @@ void handle_wl_shell_surface(struct wl_listener *listener, void *data) { roots_surface->view = view; list_add(desktop->views, view); } - -struct wlr_wl_shell_surface *wlr_wl_shell_surface_popup_at( - struct wlr_wl_shell_surface *surface, double sx, double sy, - double *popup_sx, double *popup_sy) { - struct wlr_wl_shell_surface *popup; - wl_list_for_each(popup, &surface->children, child_link) { - double _popup_sx = popup->transient_state->x; - double _popup_sy = popup->transient_state->y; - int popup_width = - popup->surface->current->buffer_width; - int popup_height = - popup->surface->current->buffer_height; - - struct wlr_wl_shell_surface *_popup = - wlr_wl_shell_surface_popup_at(popup, - popup->transient_state->x, - popup->transient_state->y, - popup_sx, popup_sy); - if (_popup) { - *popup_sx = sx + _popup_sx; - *popup_sy = sy + _popup_sy; - 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, sy - _popup_sy, NULL)) { - *popup_sx = _popup_sx; - *popup_sy = _popup_sy; - return popup; - } - } - } - - return NULL; -} diff --git a/types/wlr_wl_shell.c b/types/wlr_wl_shell.c index de018865..5851c63a 100644 --- a/types/wlr_wl_shell.c +++ b/types/wlr_wl_shell.c @@ -10,7 +10,7 @@ #include #include -static const char *wlr_wl_shell_surface_role = "wl_shell_surface"; +static const char *wlr_wl_shell_surface_role = "wl-shell-surface"; static void shell_pointer_grab_end(struct wlr_seat_pointer_grab *grab) { struct wlr_wl_shell_popup_grab *popup_grab = grab->data; @@ -67,7 +67,7 @@ static const struct wlr_pointer_grab_interface shell_pointer_grab_impl = { .axis = shell_pointer_grab_axis, }; -static void shell_surface_pong(struct wl_client *client, +static void shell_surface_protocol_pong(struct wl_client *client, struct wl_resource *resource, uint32_t serial) { wlr_log(L_DEBUG, "got shell surface pong"); struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource); @@ -79,7 +79,7 @@ static void shell_surface_pong(struct wl_client *client, surface->ping_serial = 0; } -static void shell_surface_move(struct wl_client *client, +static void shell_surface_protocol_move(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial) { wlr_log(L_DEBUG, "got shell surface move"); @@ -128,7 +128,8 @@ static struct wlr_wl_shell_popup_grab *shell_popup_grab_from_seat( return shell_grab; } -static void shell_destroy_popup_state(struct wlr_wl_shell_surface *surface) { +static void shell_surface_destroy_popup_state( + struct wlr_wl_shell_surface *surface) { if (surface->popup_state) { wl_list_remove(&surface->grab_link); struct wlr_wl_shell_popup_grab *grab = @@ -145,7 +146,7 @@ static void shell_destroy_popup_state(struct wlr_wl_shell_surface *surface) { } -static void shell_surface_resize(struct wl_client *client, +static void shell_surface_protocol_resize(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial, enum wl_shell_surface_resize edges) { wlr_log(L_DEBUG, "got shell surface resize"); @@ -177,13 +178,13 @@ static void shell_surface_set_state(struct wlr_wl_shell_surface *surface, surface->state = state; free(surface->transient_state); surface->transient_state = transient_state; - shell_destroy_popup_state(surface); + shell_surface_destroy_popup_state(surface); surface->popup_state = popup_state; wl_signal_emit(&surface->events.set_state, surface); } -static void shell_surface_set_toplevel(struct wl_client *client, +static void shell_surface_protocol_set_toplevel(struct wl_client *client, struct wl_resource *resource) { wlr_log(L_DEBUG, "got shell surface toplevel"); struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource); @@ -191,7 +192,7 @@ static void shell_surface_set_toplevel(struct wl_client *client, NULL); } -static void popup_set_parent(struct wlr_wl_shell_surface *surface, +static void shell_surface_popup_set_parent(struct wlr_wl_shell_surface *surface, struct wlr_wl_shell_surface *parent) { assert(surface && surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP); if (surface->parent == parent) { @@ -199,13 +200,13 @@ static void popup_set_parent(struct wlr_wl_shell_surface *surface, } surface->parent = parent; if (parent) { - wl_list_remove(&surface->child_link); - wl_list_insert(&parent->children, &surface->child_link); + wl_list_remove(&surface->popup_link); + wl_list_insert(&parent->popups, &surface->popup_link); } } -static struct wlr_wl_shell_surface *shell_get_shell_surface(struct wlr_wl_shell *shell, - struct wlr_surface *surface) { +static struct wlr_wl_shell_surface *shell_find_shell_surface( + struct wlr_wl_shell *shell, struct wlr_surface *surface) { if (surface) { struct wlr_wl_shell_surface *wl_surface; wl_list_for_each(wl_surface, &shell->surfaces, link) { @@ -217,7 +218,7 @@ static struct wlr_wl_shell_surface *shell_get_shell_surface(struct wlr_wl_shell return NULL; } -static void shell_surface_set_transient(struct wl_client *client, +static void shell_surface_protocol_set_transient(struct wl_client *client, struct wl_resource *resource, struct wl_resource *parent_resource, int32_t x, int32_t y, enum wl_shell_surface_transient flags) { wlr_log(L_DEBUG, "got shell surface transient"); @@ -226,7 +227,8 @@ static void shell_surface_set_transient(struct wl_client *client, wl_resource_get_user_data(parent_resource); // TODO: check if parent_resource == NULL? - struct wlr_wl_shell_surface *wl_parent = shell_get_shell_surface(surface->shell, parent); + struct wlr_wl_shell_surface *wl_parent = + shell_find_shell_surface(surface->shell, parent); if (!wl_parent) { return; @@ -248,7 +250,7 @@ static void shell_surface_set_transient(struct wl_client *client, transient_state, NULL); } -static void shell_surface_set_fullscreen(struct wl_client *client, +static void shell_surface_protocol_set_fullscreen(struct wl_client *client, struct wl_resource *resource, enum wl_shell_surface_fullscreen_method method, uint32_t framerate, struct wl_resource *output_resource) { @@ -281,7 +283,7 @@ static void shell_surface_set_fullscreen(struct wl_client *client, } -static void shell_surface_set_popup(struct wl_client *client, +static void shell_surface_protocol_set_popup(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial, struct wl_resource *parent_resource, int32_t x, int32_t y, enum wl_shell_surface_transient flags) { @@ -298,12 +300,12 @@ static void shell_surface_set_popup(struct wl_client *client, } struct wlr_wl_shell_surface *wl_parent = - shell_get_shell_surface(surface->shell, parent); + shell_find_shell_surface(surface->shell, parent); if (surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP) { surface->transient_state->x = x; surface->transient_state->y = y; - popup_set_parent(surface, wl_parent); + shell_surface_popup_set_parent(surface, wl_parent); grab->client = surface->client; wlr_seat_pointer_start_grab(seat_handle->wlr_seat, &grab->pointer_grab); return; @@ -332,13 +334,13 @@ static void shell_surface_set_popup(struct wl_client *client, shell_surface_set_state(surface, WLR_WL_SHELL_SURFACE_STATE_POPUP, transient_state, popup_state); - popup_set_parent(surface, wl_parent); + shell_surface_popup_set_parent(surface, wl_parent); grab->client = surface->client; wl_list_insert(&grab->popups, &surface->grab_link); wlr_seat_pointer_start_grab(seat_handle->wlr_seat, &grab->pointer_grab); } -static void shell_surface_set_maximized(struct wl_client *client, +static void shell_surface_protocol_set_maximized(struct wl_client *client, struct wl_resource *resource, struct wl_resource *output_resource) { wlr_log(L_DEBUG, "got shell surface maximized"); struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource); @@ -366,7 +368,7 @@ static void shell_surface_set_maximized(struct wl_client *client, free(event); } -static void shell_surface_set_title(struct wl_client *client, +static void shell_surface_protocol_set_title(struct wl_client *client, struct wl_resource *resource, const char *title) { wlr_log(L_DEBUG, "new shell surface title: %s", title); struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource); @@ -382,7 +384,7 @@ static void shell_surface_set_title(struct wl_client *client, wl_signal_emit(&surface->events.set_title, surface); } -static void shell_surface_set_class(struct wl_client *client, +static void shell_surface_protocol_set_class(struct wl_client *client, struct wl_resource *resource, const char *class) { wlr_log(L_DEBUG, "new shell surface class: %s", class); struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource); @@ -398,29 +400,29 @@ static void shell_surface_set_class(struct wl_client *client, wl_signal_emit(&surface->events.set_class, surface); } -struct wl_shell_surface_interface shell_surface_interface = { - .pong = shell_surface_pong, - .move = shell_surface_move, - .resize = shell_surface_resize, - .set_toplevel = shell_surface_set_toplevel, - .set_transient = shell_surface_set_transient, - .set_fullscreen = shell_surface_set_fullscreen, - .set_popup = shell_surface_set_popup, - .set_maximized = shell_surface_set_maximized, - .set_title = shell_surface_set_title, - .set_class = shell_surface_set_class, +static const struct wl_shell_surface_interface shell_surface_impl = { + .pong = shell_surface_protocol_pong, + .move = shell_surface_protocol_move, + .resize = shell_surface_protocol_resize, + .set_toplevel = shell_surface_protocol_set_toplevel, + .set_transient = shell_surface_protocol_set_transient, + .set_fullscreen = shell_surface_protocol_set_fullscreen, + .set_popup = shell_surface_protocol_set_popup, + .set_maximized = shell_surface_protocol_set_maximized, + .set_title = shell_surface_protocol_set_title, + .set_class = shell_surface_protocol_set_class, }; -static void wl_shell_surface_destroy(struct wlr_wl_shell_surface *surface) { +static void shell_surface_destroy(struct wlr_wl_shell_surface *surface) { wl_signal_emit(&surface->events.destroy, surface); - shell_destroy_popup_state(surface); + shell_surface_destroy_popup_state(surface); wl_resource_set_user_data(surface->resource, NULL); struct wlr_wl_shell_surface *child; - wl_list_for_each(child, &surface->children, child_link) { - popup_set_parent(child, NULL); + wl_list_for_each(child, &surface->popups, popup_link) { + shell_surface_popup_set_parent(child, NULL); } - wl_list_remove(&surface->child_link); + wl_list_remove(&surface->popup_link); wl_list_remove(&surface->link); wl_list_remove(&surface->surface_destroy_listener.link); @@ -432,10 +434,10 @@ static void wl_shell_surface_destroy(struct wlr_wl_shell_surface *surface) { free(surface); } -static void wl_shell_surface_resource_destroy(struct wl_resource *resource) { +static void shell_surface_resource_destroy(struct wl_resource *resource) { struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource); if (surface != NULL) { - wl_shell_surface_destroy(surface); + shell_surface_destroy(surface); } } @@ -443,7 +445,7 @@ static void handle_wlr_surface_destroyed(struct wl_listener *listener, void *data) { struct wlr_wl_shell_surface *surface = wl_container_of(listener, surface, surface_destroy_listener); - wl_shell_surface_destroy(surface); + shell_surface_destroy(surface); } static void handle_wlr_surface_committed(struct wl_listener *listener, void *data) { @@ -457,7 +459,7 @@ static void handle_wlr_surface_committed(struct wl_listener *listener, } } -static int wlr_wl_shell_surface_ping_timeout(void *user_data) { +static int shell_surface_ping_timeout(void *user_data) { struct wlr_wl_shell_surface *surface = user_data; wl_signal_emit(&surface->events.ping_timeout, surface); @@ -465,7 +467,7 @@ static int wlr_wl_shell_surface_ping_timeout(void *user_data) { return 1; } -static void wl_shell_get_shell_surface(struct wl_client *client, +static void shell_protocol_get_shell_surface(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_surface *surface = wl_resource_get_user_data(surface_resource); @@ -482,8 +484,8 @@ static void wl_shell_get_shell_surface(struct wl_client *client, return; } wl_list_init(&wl_surface->grab_link); - wl_list_init(&wl_surface->child_link); - wl_list_init(&wl_surface->children); + wl_list_init(&wl_surface->popup_link); + wl_list_init(&wl_surface->popups); wl_surface->shell = wl_shell; wl_surface->client = client; @@ -494,8 +496,8 @@ static void wl_shell_get_shell_surface(struct wl_client *client, wlr_log(L_DEBUG, "new wl_shell %p (res %p)", wl_surface, wl_surface->resource); wl_resource_set_implementation(wl_surface->resource, - &shell_surface_interface, wl_surface, - wl_shell_surface_resource_destroy); + &shell_surface_impl, wl_surface, + shell_surface_resource_destroy); wl_signal_init(&wl_surface->events.destroy); wl_signal_init(&wl_surface->events.ping_timeout); @@ -518,7 +520,7 @@ static void wl_shell_get_shell_surface(struct wl_client *client, struct wl_display *display = wl_client_get_display(client); struct wl_event_loop *loop = wl_display_get_event_loop(display); wl_surface->ping_timer = wl_event_loop_add_timer(loop, - wlr_wl_shell_surface_ping_timeout, wl_surface); + shell_surface_ping_timeout, wl_surface); if (wl_surface->ping_timer == NULL) { wl_client_post_no_memory(client); } @@ -526,15 +528,15 @@ static void wl_shell_get_shell_surface(struct wl_client *client, wl_list_insert(&wl_shell->surfaces, &wl_surface->link); } -static struct wl_shell_interface wl_shell_impl = { - .get_shell_surface = wl_shell_get_shell_surface +static struct wl_shell_interface shell_impl = { + .get_shell_surface = shell_protocol_get_shell_surface }; -static void wl_shell_destroy(struct wl_resource *resource) { +static void shell_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } -static void wl_shell_bind(struct wl_client *wl_client, void *_wl_shell, +static void shell_bind(struct wl_client *wl_client, void *_wl_shell, uint32_t version, uint32_t id) { struct wlr_wl_shell *wl_shell = _wl_shell; assert(wl_client && wl_shell); @@ -546,8 +548,8 @@ static void wl_shell_bind(struct wl_client *wl_client, void *_wl_shell, } struct wl_resource *wl_resource = wl_resource_create(wl_client, &wl_shell_interface, version, id); - wl_resource_set_implementation(wl_resource, &wl_shell_impl, wl_shell, - wl_shell_destroy); + wl_resource_set_implementation(wl_resource, &shell_impl, wl_shell, + shell_destroy); wl_list_insert(&wl_shell->wl_resources, wl_resource_get_link(wl_resource)); } @@ -558,7 +560,7 @@ struct wlr_wl_shell *wlr_wl_shell_create(struct wl_display *display) { } wl_shell->ping_timeout = 10000; struct wl_global *wl_global = wl_global_create(display, &wl_shell_interface, - 1, wl_shell, wl_shell_bind); + 1, wl_shell, shell_bind); if (!wl_global) { free(wl_shell); return NULL; @@ -604,10 +606,39 @@ void wlr_wl_shell_surface_configure(struct wlr_wl_shell_surface *surface, wl_shell_surface_send_configure(surface->resource, edges, width, height); } -void wlr_wl_shell_surface_popup_done(struct wlr_wl_shell_surface *surface) { - wl_shell_surface_send_popup_done(surface->resource); -} +struct wlr_wl_shell_surface *wlr_wl_shell_surface_popup_at( + struct wlr_wl_shell_surface *surface, double sx, double sy, + double *popup_sx, double *popup_sy) { + struct wlr_wl_shell_surface *popup; + wl_list_for_each(popup, &surface->popups, popup_link) { + double _popup_sx = popup->transient_state->x; + double _popup_sy = popup->transient_state->y; + int popup_width = + popup->surface->current->buffer_width; + int popup_height = + popup->surface->current->buffer_height; -bool wlr_wl_shell_surface_is_transient(struct wlr_wl_shell_surface *surface) { - return surface->parent != NULL; + struct wlr_wl_shell_surface *_popup = + wlr_wl_shell_surface_popup_at(popup, + popup->transient_state->x, + popup->transient_state->y, + popup_sx, popup_sy); + if (_popup) { + *popup_sx = sx + _popup_sx; + *popup_sy = sy + _popup_sy; + 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, sy - _popup_sy, NULL)) { + *popup_sx = _popup_sx; + *popup_sy = _popup_sy; + return popup; + } + } + } + + return NULL; } From 821cf92498733b43494e2eda21e3342f6f3579e2 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Tue, 10 Oct 2017 12:20:39 -0400 Subject: [PATCH 7/8] wl-shell: fix popup grab --- include/wlr/types/wlr_wl_shell.h | 1 + types/wlr_wl_shell.c | 47 ++++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/include/wlr/types/wlr_wl_shell.h b/include/wlr/types/wlr_wl_shell.h index bea92b8b..6f6d0182 100644 --- a/include/wlr/types/wlr_wl_shell.h +++ b/include/wlr/types/wlr_wl_shell.h @@ -71,6 +71,7 @@ struct wlr_wl_shell_surface { struct wlr_wl_shell_surface *parent; struct wl_list popup_link; struct wl_list popups; + bool popup_mapped; struct { struct wl_signal destroy; diff --git a/types/wlr_wl_shell.c b/types/wlr_wl_shell.c index 5851c63a..022f865a 100644 --- a/types/wlr_wl_shell.c +++ b/types/wlr_wl_shell.c @@ -17,10 +17,38 @@ static void shell_pointer_grab_end(struct wlr_seat_pointer_grab *grab) { struct wlr_wl_shell_surface *popup, *tmp = NULL; wl_list_for_each_safe(popup, tmp, &popup_grab->popups, grab_link) { - wl_shell_surface_send_popup_done(popup->resource); + if (popup->popup_mapped) { + wl_shell_surface_send_popup_done(popup->resource); + popup->popup_mapped = false; + } } - wlr_seat_pointer_end_grab(grab->seat); + + if (grab->seat->pointer_state.grab == grab) { + wlr_seat_pointer_end_grab(grab->seat); + } +} + +static void shell_pointer_grab_maybe_end(struct wlr_seat_pointer_grab *grab) { + struct wlr_wl_shell_popup_grab *popup_grab = grab->data; + + if (grab->seat->pointer_state.grab != grab) { + return; + } + + bool has_mapped = false; + + struct wlr_wl_shell_surface *popup, *tmp = NULL; + wl_list_for_each_safe(popup, tmp, &popup_grab->popups, grab_link) { + if (popup->popup_mapped) { + has_mapped = true; + break; + } + } + + if (!has_mapped) { + shell_pointer_grab_end(grab); + } } static void shell_pointer_grab_enter(struct wlr_seat_pointer_grab *grab, @@ -307,6 +335,7 @@ static void shell_surface_protocol_set_popup(struct wl_client *client, surface->transient_state->y = y; shell_surface_popup_set_parent(surface, wl_parent); grab->client = surface->client; + surface->popup_mapped = true; wlr_seat_pointer_start_grab(seat_handle->wlr_seat, &grab->pointer_grab); return; } @@ -337,6 +366,7 @@ static void shell_surface_protocol_set_popup(struct wl_client *client, shell_surface_popup_set_parent(surface, wl_parent); grab->client = surface->client; wl_list_insert(&grab->popups, &surface->grab_link); + surface->popup_mapped = true; wlr_seat_pointer_start_grab(seat_handle->wlr_seat, &grab->pointer_grab); } @@ -457,6 +487,16 @@ static void handle_wlr_surface_committed(struct wl_listener *listener, surface->configured = true; wl_signal_emit(&surface->shell->events.new_surface, surface); } + + if (surface->popup_mapped && + surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP && + !surface->surface->texture->valid) { + surface->popup_mapped = false; + struct wlr_wl_shell_popup_grab *grab = + shell_popup_grab_from_seat(surface->shell, + surface->popup_state->seat); + shell_pointer_grab_maybe_end(&grab->pointer_grab); + } } static int shell_surface_ping_timeout(void *user_data) { @@ -611,6 +651,9 @@ struct wlr_wl_shell_surface *wlr_wl_shell_surface_popup_at( double *popup_sx, double *popup_sy) { struct wlr_wl_shell_surface *popup; wl_list_for_each(popup, &surface->popups, popup_link) { + if (!popup->popup_mapped) { + continue; + } double _popup_sx = popup->transient_state->x; double _popup_sy = popup->transient_state->y; int popup_width = From 0a6f54384ceca7f639bcaa3dc27c2c407e3e5843 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Tue, 10 Oct 2017 12:24:56 -0400 Subject: [PATCH 8/8] wl-shell: documentation --- include/wlr/types/wlr_wl_shell.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/include/wlr/types/wlr_wl_shell.h b/include/wlr/types/wlr_wl_shell.h index 6f6d0182..4e814817 100644 --- a/include/wlr/types/wlr_wl_shell.h +++ b/include/wlr/types/wlr_wl_shell.h @@ -52,7 +52,7 @@ struct wlr_wl_shell_surface { struct wl_resource *resource; struct wlr_surface *surface; bool configured; - struct wl_list link; + struct wl_list link; // wlr_wl_shell::surfaces uint32_t ping_serial; struct wl_event_source *ping_timer; @@ -119,10 +119,25 @@ struct wlr_wl_shell_surface_set_maximized_event { struct wlr_output *output; }; +/** + * Create a wl_shell for this display. + */ struct wlr_wl_shell *wlr_wl_shell_create(struct wl_display *display); + +/** + * Destroy this surface. + */ void wlr_wl_shell_destroy(struct wlr_wl_shell *wlr_wl_shell); +/** + * Send a ping to the surface. If the surface does not respond with a pong + * within a reasonable amount of time, the ping timeout event will be emitted. + */ void wlr_wl_shell_surface_ping(struct wlr_wl_shell_surface *surface); + +/** + * Request that the surface configure itself to be the given size. + */ void wlr_wl_shell_surface_configure(struct wlr_wl_shell_surface *surface, enum wl_shell_surface_resize edges, int32_t width, int32_t height);