From e399a4cebca847c7cdee7ae27ee1718f00f0fc3d Mon Sep 17 00:00:00 2001 From: Tadeo Kondrak Date: Tue, 15 Dec 2020 21:24:57 -0700 Subject: [PATCH] Implement input_method_v2 popups --- include/log.h | 2 +- include/sway/input/text_input.h | 17 ++++ sway/desktop/render.c | 33 ++++++++ sway/input/text_input.c | 135 ++++++++++++++++++++++++++++++++ 4 files changed, 186 insertions(+), 1 deletion(-) diff --git a/include/log.h b/include/log.h index 41399a9fa4..601dea95ff 100644 --- a/include/log.h +++ b/include/log.h @@ -53,6 +53,6 @@ bool _sway_assert(bool condition, const char* format, ...) ATTRIB_PRINTF(2, 3); _sway_abort("[%s:%d] " FMT, _SWAY_FILENAME, __LINE__, ##__VA_ARGS__) #define sway_assert(COND, FMT, ...) \ - _sway_assert(COND, "[%s:%d] %s:" FMT, _SWAY_FILENAME, __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__) + _sway_assert(COND, "[%s:%d] %s: " FMT, _SWAY_FILENAME, __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__) #endif diff --git a/include/sway/input/text_input.h b/include/sway/input/text_input.h index 447f761aed..a461e22610 100644 --- a/include/sway/input/text_input.h +++ b/include/sway/input/text_input.h @@ -22,18 +22,35 @@ struct sway_input_method_relay { struct sway_seat *seat; struct wl_list text_inputs; // sway_text_input::link + struct wl_list input_popups; // sway_input_popup::link struct wlr_input_method_v2 *input_method; // doesn't have to be present struct wl_listener text_input_new; struct wl_listener input_method_new; struct wl_listener input_method_commit; + struct wl_listener input_method_popup_surface; struct wl_listener input_method_grab_keyboard; struct wl_listener input_method_destroy; struct wl_listener input_method_keyboard_grab_destroy; }; +struct sway_input_popup { + struct sway_input_method_relay *relay; + struct wlr_input_popup_surface_v2 *popup_surface; + struct sway_text_input *text_input; + + int x, y; + + struct wl_list link; + + struct wl_listener popup_map; + struct wl_listener popup_unmap; + struct wl_listener popup_destroy; + struct wl_listener popup_surface_commit; +}; + struct sway_text_input { struct sway_input_method_relay *relay; diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 3a42229352..40d63f6f8e 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -986,6 +986,37 @@ static void render_seatops(struct sway_output *output, } } +static void render_im_popups(struct sway_output *output, + pixman_region32_t *damage) { + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + struct sway_input_popup *popup; + wl_list_for_each(popup, &seat->im_relay.input_popups, link) { + if (!popup->popup_surface->mapped) { + continue; + } + struct render_data data = { + .damage = damage, + .alpha = 1.0f, + }; + struct sway_view *view = view_from_wlr_surface( + popup->text_input->input->focused_surface); + struct wlr_surface *popup_surface = + popup->popup_surface->surface; + struct wlr_box box = { + .x = output->lx + view->geometry.x + + view->container->surface_x + popup->x, + .y = output->ly + view->geometry.y + + view->container->surface_y + popup->y, + .width = popup_surface->current.width, + .height = popup_surface->current.height, + }; + render_surface_iterator(output, view, + popup->popup_surface->surface, &box, 0.0, &data); + } + } +} + void output_render(struct sway_output *output, struct timespec *when, pixman_region32_t *damage) { struct wlr_output *wlr_output = output->wlr_output; @@ -1097,6 +1128,8 @@ void output_render(struct sway_output *output, struct timespec *when, render_view_popups(focus->view, output, damage, focus->alpha); } + render_im_popups(output, damage); + render_overlay: render_layer_toplevel(output, damage, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); diff --git a/sway/input/text_input.c b/sway/input/text_input.c index 5d39609d4c..6f8547eee6 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -3,6 +3,11 @@ #include "log.h" #include "sway/input/seat.h" #include "sway/input/text_input.h" +#include "sway/tree/view.h" +#include "sway/tree/container.h" +#include "sway/tree/workspace.h" +#include "sway/desktop.h" +#include "sway/output.h" static struct sway_text_input *relay_get_focusable_text_input( struct sway_input_method_relay *relay) { @@ -26,6 +31,98 @@ static struct sway_text_input *relay_get_focused_text_input( return NULL; } +static void input_popup_damage(struct sway_input_popup *popup) { + if (!popup->popup_surface->mapped) { + return; + } + desktop_damage_surface(popup->popup_surface->surface, + popup->x, popup->y, true); + if (popup->text_input == NULL || + popup->text_input->input->focused_surface == NULL) { + return; + } + + struct sway_view *view = + view_from_wlr_surface(popup->text_input->input->focused_surface); + desktop_damage_view(view); +} + +static void input_popup_update(struct sway_input_popup *popup) { + input_popup_damage(popup); + + if (popup->text_input == NULL || + popup->text_input->input->focused_surface == NULL) { + return; + } + + struct sway_view *view = + view_from_wlr_surface(popup->text_input->input->focused_surface); + if (view == NULL) { + return; + } + + struct wlr_box cursor_rectangle = + popup->text_input->input->current.cursor_rectangle; + popup->x = cursor_rectangle.x; + popup->y = cursor_rectangle.y + cursor_rectangle.height; + + input_popup_damage(popup); +} + +static void handle_im_popup_map(struct wl_listener *listener, void *data) { + struct sway_input_popup *popup = + wl_container_of(listener, popup, popup_map); + input_popup_damage(popup); +} + +static void handle_im_popup_unmap(struct wl_listener *listener, void *data) { + struct sway_input_popup *popup = + wl_container_of(listener, popup, popup_unmap); + input_popup_damage(popup); +} + +static void handle_im_popup_destroy(struct wl_listener *listener, void *data) { + struct sway_input_popup *popup = + wl_container_of(listener, popup, popup_destroy); + popup->popup_surface->data = NULL; + wl_list_remove(&popup->popup_surface_commit.link); + wl_list_remove(&popup->popup_destroy.link); + wl_list_remove(&popup->popup_unmap.link); + wl_list_remove(&popup->popup_map.link); + if (popup->text_input != NULL) { + } + wl_list_remove(&popup->link); + free(popup); +} + +static void handle_im_popup_surface_commit(struct wl_listener *listener, + void *data) { + struct sway_input_popup *popup = + wl_container_of(listener, popup, popup_surface_commit); + input_popup_update(popup); +} + +static void handle_im_popup(struct wl_listener *listener, void *data) { + struct sway_input_method_relay *relay = wl_container_of(listener, relay, + input_method_popup_surface); + struct sway_input_popup *popup = calloc(1, sizeof(*popup)); + popup->relay = relay; + popup->popup_surface = data; + popup->popup_surface->data = popup; + wl_signal_add(&popup->popup_surface->events.map, &popup->popup_map); + popup->popup_map.notify = handle_im_popup_map; + wl_signal_add( + &popup->popup_surface->events.unmap, &popup->popup_unmap); + popup->popup_unmap.notify = handle_im_popup_unmap; + wl_signal_add( + &popup->popup_surface->events.destroy, &popup->popup_destroy); + popup->popup_destroy.notify = handle_im_popup_destroy; + wl_signal_add(&popup->popup_surface->surface->events.destroy, + &popup->popup_surface_commit); + popup->popup_surface_commit.notify = handle_im_popup_surface_commit; + wl_list_insert(&relay->input_popups, &popup->link); +} + static void handle_im_commit(struct wl_listener *listener, void *data) { struct sway_input_method_relay *relay = wl_container_of(listener, relay, input_method_commit); @@ -53,6 +150,13 @@ static void handle_im_commit(struct wl_listener *listener, void *data) { context->current.delete.after_length); } wlr_text_input_v3_send_done(text_input->input); + + struct sway_input_popup *popup; + wl_list_for_each(popup, &text_input->relay->input_popups, link) { + if (popup->text_input == text_input) { + input_popup_update(popup); + } + } } static void handle_im_keyboard_grab_destroy(struct wl_listener *listener, void *data) { @@ -85,6 +189,7 @@ static void handle_im_grab_keyboard(struct wl_listener *listener, void *data) { relay->input_method_keyboard_grab_destroy.notify = handle_im_keyboard_grab_destroy; } + static void text_input_set_pending_focused_surface( struct sway_text_input *text_input, struct wlr_surface *surface) { wl_list_remove(&text_input->pending_focused_surface_destroy.link); @@ -140,8 +245,15 @@ static void handle_text_input_enable(struct wl_listener *listener, void *data) { sway_log(SWAY_INFO, "Enabling text input when input method is gone"); return; } + wlr_input_method_v2_send_activate(text_input->relay->input_method); relay_send_im_state(text_input->relay, text_input->input); + + struct sway_input_popup *popup; + wl_list_for_each(popup, &text_input->relay->input_popups, link) { + popup->text_input = text_input; + input_popup_update(popup); + } } static void handle_text_input_commit(struct wl_listener *listener, @@ -158,6 +270,14 @@ static void handle_text_input_commit(struct wl_listener *listener, return; } relay_send_im_state(text_input->relay, text_input->input); + + // Text location may have changed, update popup + struct sway_input_popup *popup; + wl_list_for_each(popup, &text_input->relay->input_popups, link) { + if (popup->text_input == text_input) { + input_popup_update(popup); + } + } } static void relay_disable_text_input(struct sway_input_method_relay *relay, @@ -166,6 +286,17 @@ static void relay_disable_text_input(struct sway_input_method_relay *relay, sway_log(SWAY_DEBUG, "Disabling text input, but input method is gone"); return; } + if (text_input->input->focused_surface == NULL) { + return; + } + + struct sway_input_popup *popup; + wl_list_for_each(popup, &text_input->relay->input_popups, link) { + if (popup->text_input == text_input) { + popup->text_input = NULL; + } + } + wlr_input_method_v2_send_deactivate(relay->input_method); relay_send_im_state(relay, text_input->input); } @@ -266,6 +397,9 @@ static void relay_handle_input_method(struct wl_listener *listener, wl_signal_add(&relay->input_method->events.commit, &relay->input_method_commit); relay->input_method_commit.notify = handle_im_commit; + wl_signal_add(&relay->input_method->events.popup_surface, + &relay->input_method_popup_surface); + relay->input_method_popup_surface.notify = handle_im_popup; wl_signal_add(&relay->input_method->events.grab_keyboard, &relay->input_method_grab_keyboard); relay->input_method_grab_keyboard.notify = handle_im_grab_keyboard; @@ -285,6 +419,7 @@ void sway_input_method_relay_init(struct sway_seat *seat, struct sway_input_method_relay *relay) { relay->seat = seat; wl_list_init(&relay->text_inputs); + wl_list_init(&relay->input_popups); relay->text_input_new.notify = relay_handle_text_input; wl_signal_add(&server.text_input->events.text_input,