From 0889c1816edd6a2fdd62680bbc2ed504d4893a69 Mon Sep 17 00:00:00 2001 From: Michael Weiser Date: Wed, 18 Mar 2020 22:14:37 +0100 Subject: [PATCH] criteria: Add additional call sites Now that there are criteria that can be re-executed on state change and match the state of keyboard shortcuts inhibitors, add additional criteria execution call sites right after keyboard shortcuts inhibitor state changes. This allows to immediately and efficiently visually reflect inhibitor state changes to the user (or trigger any other desired action). Since the surface implemenation might already have been destroyed when the keyboard shortcuts inhibitor destruction handler is called, we need to defer criteria execution via the event loop and then go looking whether a matching surface still exists and derive the view to execute the criteria on from that. Signed-off-by: Michael Weiser --- include/sway/tree/view.h | 3 ++ sway/commands/seat/shortcuts_inhibitor.c | 19 +++++++++-- sway/commands/shortcuts_inhibitor.c | 12 +++++-- sway/input/input-manager.c | 13 ++++++++ sway/sway-input.5.scd | 7 ++-- sway/tree/view.c | 42 ++++++++++++++++++++++++ 6 files changed, 89 insertions(+), 7 deletions(-) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 9230f456c..cdfe62736 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -343,4 +343,7 @@ void view_save_buffer(struct sway_view *view); bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor); +void view_schedule_criteria_execution_from_wlr_surface( + struct wlr_surface *wlr_surface); + #endif diff --git a/sway/commands/seat/shortcuts_inhibitor.c b/sway/commands/seat/shortcuts_inhibitor.c index 7c7f99cf0..8850a427f 100644 --- a/sway/commands/seat/shortcuts_inhibitor.c +++ b/sway/commands/seat/shortcuts_inhibitor.c @@ -2,6 +2,7 @@ #include "sway/commands.h" #include "sway/input/seat.h" #include "sway/input/input-manager.h" +#include "sway/tree/view.h" #include "util.h" static struct cmd_results *handle_action(struct seat_config *sc, @@ -12,8 +13,17 @@ static struct cmd_results *handle_action(struct seat_config *sc, wl_list_for_each(sway_inhibitor, &seat->keyboard_shortcuts_inhibitors, link) { - wlr_keyboard_shortcuts_inhibitor_v1_deactivate( - sway_inhibitor->inhibitor); + struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor = + sway_inhibitor->inhibitor; + wlr_keyboard_shortcuts_inhibitor_v1_deactivate(inhibitor); + + // execute criteria on the affected view after + // inhibitor changed state + struct sway_view *view = + view_from_wlr_surface(inhibitor->surface); + if (view) { + view_execute_criteria(view); + } } sway_log(SWAY_DEBUG, "Deactivated all keyboard shortcuts inhibitors"); @@ -46,6 +56,11 @@ static struct cmd_results *handle_action(struct seat_config *sc, sway_log(SWAY_DEBUG, "%sctivated keyboard shortcuts inhibitor", inhibit ? "A" : "Dea"); + + struct sway_view *view = view_from_wlr_surface(inhibitor->surface); + if (view) { + view_execute_criteria(view); + } } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/shortcuts_inhibitor.c b/sway/commands/shortcuts_inhibitor.c index ffa1a5c99..0d372e806 100644 --- a/sway/commands/shortcuts_inhibitor.c +++ b/sway/commands/shortcuts_inhibitor.c @@ -33,12 +33,20 @@ struct cmd_results *cmd_shortcuts_inhibitor(int argc, char **argv) { continue; } - wlr_keyboard_shortcuts_inhibitor_v1_deactivate( - sway_inhibitor->inhibitor); + struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor = + sway_inhibitor->inhibitor; + wlr_keyboard_shortcuts_inhibitor_v1_deactivate(inhibitor); sway_log(SWAY_DEBUG, "Deactivated keyboard shortcuts " "inhibitor for seat %s on view", seat->wlr_seat->name); + // execute criteria on the affected view after + // inhibitor changed state + struct sway_view *view = + view_from_wlr_surface(inhibitor->surface); + if (view) { + view_execute_criteria(view); + } } } else { return cmd_results_new(CMD_INVALID, diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index dc07cbf06..81bef1b51 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -304,6 +304,11 @@ static void handle_keyboard_shortcuts_inhibitor_destroy( sway_log(SWAY_DEBUG, "Removing keyboard shortcuts inhibitor"); + // avoid use-after free of view by scheduling deferred execution of + // criteria + view_schedule_criteria_execution_from_wlr_surface( + sway_inhibitor->inhibitor->surface); + // sway_seat::keyboard_shortcuts_inhibitors wl_list_remove(&sway_inhibitor->link); wl_list_remove(&sway_inhibitor->destroy.link); @@ -353,6 +358,10 @@ static void handle_keyboard_shortcuts_inhibit_new_inhibitor( } if (inhibit == SHORTCUTS_INHIBIT_DISABLE) { + if (view) { + view_execute_criteria(view); + } + /** * Here we deny to honour the inhibitor by never sending the * activate signal. We can not, however, destroy the inhibitor @@ -368,6 +377,10 @@ static void handle_keyboard_shortcuts_inhibit_new_inhibitor( } wlr_keyboard_shortcuts_inhibitor_v1_activate(inhibitor); + + if (view) { + view_execute_criteria(view); + } } void handle_virtual_keyboard(struct wl_listener *listener, void *data) { diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 9ec5eeaea..0d1494360 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -266,9 +266,10 @@ correct seat. escape a state where shortcuts are inhibited and the client becomes uncooperative. It is worth noting that whether disabled or deactivated inhibitors are removed is entirely up to the client. Depending on the - client it may therefore be possible to (re-)activate them later. Any - visual indication that an inhibitor is present is currently left to the - client as well. + client it may therefore be possible to (re-)activate them later. + Note that visual indication of inhibitor presence and state beyond what + the client may provide can be implemented using criteria with match + _shortcuts_inhibitor_. *seat* xcursor_theme [] Override the system default XCursor theme. The default seat's diff --git a/sway/tree/view.c b/sway/tree/view.c index 2b4b6c09b..6a3f05484 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -1176,3 +1176,45 @@ bool view_is_transient_for(struct sway_view *child, return child->impl->is_transient_for && child->impl->is_transient_for(child, ancestor); } + +struct view_surface_iterator_context { + struct sway_view *view; + struct wlr_surface *wlr_surface; +}; + +static void view_criteria_execution_surface_iterator( + struct wlr_surface *surface, int sx, int sy, void *data) { + struct view_surface_iterator_context *context = data; + + if (surface != context->wlr_surface) { + return; + } + + view_execute_criteria(context->view); +} + +static void view_criteria_execution_container_iterator( + struct sway_container *container, void *data) { + if (container->view) { + struct view_surface_iterator_context context = { + .view = container->view, + .wlr_surface = data, + }; + + view_for_each_surface(container->view, + view_criteria_execution_surface_iterator, + &context); + } +} + +static void view_execute_criteria_from_wlr_surface(void *data) { + // here we intentionally go looking for a surface matching our argument + // because that surface might have since been freed + root_for_each_container(view_criteria_execution_container_iterator, data); +} + +void view_schedule_criteria_execution_from_wlr_surface( + struct wlr_surface *wlr_surface) { + wl_event_loop_add_idle(server.wl_event_loop, + view_execute_criteria_from_wlr_surface, wlr_surface); +}