input/text_input: remove event listeners on destroy

sway_input_method_relay can be destroyed from two sources, either the
seat is destroyed or the manager protocol objects are destroyed due
compositor exit. Therefore, finish must check whether it has already
been called.

This fixes a crash in wlroots listener checks. See #8509.
This commit is contained in:
Ferdinand Bachmann 2025-02-18 22:45:53 +01:00
parent 86bbedcce3
commit 257fb5fd6a
2 changed files with 28 additions and 0 deletions

View file

@ -25,8 +25,10 @@ struct sway_input_method_relay {
struct wlr_input_method_v2 *input_method; // doesn't have to be present
struct wl_listener text_input_new;
struct wl_listener text_input_manager_destroy;
struct wl_listener input_method_new;
struct wl_listener input_method_manager_destroy;
struct wl_listener input_method_commit;
struct wl_listener input_method_new_popup_surface;
struct wl_listener input_method_grab_keyboard;

View file

@ -572,6 +572,20 @@ static void relay_handle_input_method(struct wl_listener *listener,
}
}
static void relay_handle_text_input_manager_destroy(struct wl_listener *listener, void *data) {
struct sway_input_method_relay *relay = wl_container_of(listener, relay,
text_input_manager_destroy);
sway_input_method_relay_finish(relay);
}
static void relay_handle_input_method_manager_destroy(struct wl_listener *listener, void *data) {
struct sway_input_method_relay *relay = wl_container_of(listener, relay,
input_method_manager_destroy);
sway_input_method_relay_finish(relay);
}
void sway_input_method_relay_init(struct sway_seat *seat,
struct sway_input_method_relay *relay) {
relay->seat = seat;
@ -581,16 +595,28 @@ void sway_input_method_relay_init(struct sway_seat *seat,
relay->text_input_new.notify = relay_handle_text_input;
wl_signal_add(&server.text_input->events.text_input,
&relay->text_input_new);
relay->text_input_manager_destroy.notify = relay_handle_text_input_manager_destroy;
wl_signal_add(&server.text_input->events.destroy,
&relay->text_input_manager_destroy);
relay->input_method_new.notify = relay_handle_input_method;
wl_signal_add(
&server.input_method->events.input_method,
&relay->input_method_new);
relay->input_method_manager_destroy.notify = relay_handle_input_method_manager_destroy;
wl_signal_add(&server.input_method->events.destroy,
&relay->input_method_manager_destroy);
}
void sway_input_method_relay_finish(struct sway_input_method_relay *relay) {
// return early if finish was already called
// can be called due to seat or manager protocol object being destroyed
if (!relay->input_method_new.link.prev) return;
wl_list_remove(&relay->input_method_new.link);
wl_list_remove(&relay->input_method_manager_destroy.link);
wl_list_remove(&relay->text_input_new.link);
wl_list_remove(&relay->text_input_manager_destroy.link);
}
void sway_input_method_relay_set_focus(struct sway_input_method_relay *relay,