aboutsummarylogtreecommitdiffstats
path: root/0001-text_input-Implement-input-method-popups.patch
diff options
context:
space:
mode:
authortinywrkb2022-01-28 01:56:55 +0200
committerHyeon Kim2022-01-29 02:11:04 +0900
commit77318b93b258b9a9721ebb7fb2135b0291395b1d (patch)
tree3d1b082f7749f3ac37a74b20897e4a11cabcad21 /0001-text_input-Implement-input-method-popups.patch
parent171956a48da1411684d60144f4e72a0c8b4d139a (diff)
downloadaur-77318b93b258b9a9721ebb7fb2135b0291395b1d.tar.gz
sway-im: bump to 1.7-1, add input method v2 popups patch
Update to the latest stable release and add the input method v2 popups patch. The popups patch is the last bit that is missing for full IME frameworks support in Wayland. Currently, it doesn't look like wlroots changes will be needed, so I think it's better to stay on stable and just port back the patch, if needed, so _pkgver is gone. A minor issue that was fixed is the installation folder of the license. It should match the package name. This confirmed to be working with anthywl, though I'm not sure how well, as I don't really know how to type in Japanese. It doesn't seem to work with recent dev build of Fcitx5.
Diffstat (limited to '0001-text_input-Implement-input-method-popups.patch')
-rw-r--r--0001-text_input-Implement-input-method-popups.patch678
1 files changed, 678 insertions, 0 deletions
diff --git a/0001-text_input-Implement-input-method-popups.patch b/0001-text_input-Implement-input-method-popups.patch
new file mode 100644
index 000000000000..c15bb20649a4
--- /dev/null
+++ b/0001-text_input-Implement-input-method-popups.patch
@@ -0,0 +1,678 @@
+From 9debeb40ceaeae9e577bddcc248a36d99f0a066f Mon Sep 17 00:00:00 2001
+From: Tadeo Kondrak <me@tadeo.ca>
+Date: Tue, 15 Dec 2020 21:24:57 -0700
+Subject: [PATCH] text_input: Implement input-method popups
+
+---
+ include/sway/input/text_input.h | 22 ++
+ include/sway/output.h | 4 +
+ sway/desktop/output.c | 26 +++
+ sway/desktop/render.c | 17 +-
+ sway/input/cursor.c | 27 +++
+ sway/input/text_input.c | 349 +++++++++++++++++++++++++++++++-
+ sway/tree/container.c | 2 +
+ sway/tree/view.c | 3 +
+ 8 files changed, 442 insertions(+), 8 deletions(-)
+
+diff --git a/include/sway/input/text_input.h b/include/sway/input/text_input.h
+index 37744266d9..1ed0c4d70e 100644
+--- a/include/sway/input/text_input.h
++++ b/include/sway/input/text_input.h
+@@ -22,18 +22,37 @@ 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_new_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;
++
++ int x, y;
++ bool visible;
++
++ 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 wl_listener focused_surface_unmap;
++};
++
+ struct sway_text_input {
+ struct sway_input_method_relay *relay;
+
+@@ -66,4 +85,7 @@ struct sway_text_input *sway_text_input_create(
+ struct sway_input_method_relay *relay,
+ struct wlr_text_input_v3 *text_input);
+
++bool sway_input_popup_get_position(
++ struct sway_input_popup *popup, int *lx, int *ly);
++
+ #endif
+diff --git a/include/sway/output.h b/include/sway/output.h
+index 26b9709f98..5c6b0010c4 100644
+--- a/include/sway/output.h
++++ b/include/sway/output.h
+@@ -137,6 +137,10 @@ void output_unmanaged_for_each_surface(struct sway_output *output,
+ void *user_data);
+ #endif
+
++void output_input_popups_for_each_surface(struct sway_output *output,
++ struct wl_list *input_popups, sway_surface_iterator_func_t iterator,
++ void *user_data);
++
+ void output_drag_icons_for_each_surface(struct sway_output *output,
+ struct wl_list *drag_icons, sway_surface_iterator_func_t iterator,
+ void *user_data);
+diff --git a/sway/desktop/output.c b/sway/desktop/output.c
+index 68f095c044..46f3c3a7b2 100644
+--- a/sway/desktop/output.c
++++ b/sway/desktop/output.c
+@@ -22,6 +22,7 @@
+ #include "sway/desktop/transaction.h"
+ #include "sway/input/input-manager.h"
+ #include "sway/input/seat.h"
++#include "sway/input/text_input.h"
+ #include "sway/layers.h"
+ #include "sway/output.h"
+ #include "sway/server.h"
+@@ -246,6 +247,28 @@ void output_unmanaged_for_each_surface(struct sway_output *output,
+ }
+ #endif
+
++void output_input_popups_for_each_surface(struct sway_output *output,
++ struct wl_list *input_popups, sway_surface_iterator_func_t iterator,
++ void *user_data) {
++ struct sway_input_popup *popup;
++ wl_list_for_each(popup, input_popups, link) {
++ int lx, ly;
++ if (!sway_input_popup_get_position(popup, &lx, &ly)) {
++ continue;
++ }
++ if (!popup->popup_surface->mapped || !popup->visible) {
++ continue;
++ }
++
++ double ox = lx - output->lx;
++ double oy = ly - output->ly;
++
++ output_surface_for_each_surface(output,
++ popup->popup_surface->surface, ox, oy,
++ iterator, user_data);
++ }
++}
++
+ void output_drag_icons_for_each_surface(struct sway_output *output,
+ struct wl_list *drag_icons, sway_surface_iterator_func_t iterator,
+ void *user_data) {
+@@ -337,6 +360,9 @@ static void output_for_each_surface(struct sway_output *output,
+ output_layer_for_each_surface(output,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
+ iterator, user_data);
++ output_input_popups_for_each_surface(
++ output, &input_manager_current_seat()->im_relay.input_popups,
++ iterator, user_data);
+ output_drag_icons_for_each_surface(output, &root->drag_icons,
+ iterator, user_data);
+ }
+diff --git a/sway/desktop/render.c b/sway/desktop/render.c
+index c088c936dc..2fc14dd2ff 100644
+--- a/sway/desktop/render.c
++++ b/sway/desktop/render.c
+@@ -19,6 +19,7 @@
+ #include "sway/config.h"
+ #include "sway/input/input-manager.h"
+ #include "sway/input/seat.h"
++#include "sway/input/text_input.h"
+ #include "sway/layers.h"
+ #include "sway/output.h"
+ #include "sway/server.h"
+@@ -201,6 +202,16 @@ static void render_unmanaged(struct sway_output *output,
+ }
+ #endif
+
++static void render_input_popups(struct sway_output *output,
++ pixman_region32_t *damage, struct wl_list *input_popups) {
++ struct render_data data = {
++ .damage = damage,
++ .alpha = 1.0f,
++ };
++ output_input_popups_for_each_surface(output, input_popups,
++ render_surface_iterator, &data);
++}
++
+ static void render_drag_icons(struct sway_output *output,
+ pixman_region32_t *damage, struct wl_list *drag_icons) {
+ struct render_data data = {
+@@ -1029,6 +1040,9 @@ void output_render(struct sway_output *output, struct timespec *when,
+ struct wlr_output *wlr_output = output->wlr_output;
+ struct wlr_renderer *renderer = output->server->renderer;
+
++ struct sway_seat *seat = input_manager_current_seat();
++ struct sway_container *focus = seat_get_focused_container(seat);
++
+ struct sway_workspace *workspace = output->current.active_workspace;
+ if (workspace == NULL) {
+ return;
+@@ -1125,8 +1139,6 @@ void output_render(struct sway_output *output, struct timespec *when,
+
+ render_seatops(output, damage);
+
+- struct sway_seat *seat = input_manager_current_seat();
+- struct sway_container *focus = seat_get_focused_container(seat);
+ if (focus && focus->view) {
+ render_view_popups(focus->view, output, damage, focus->alpha);
+ }
+@@ -1136,6 +1148,7 @@ void output_render(struct sway_output *output, struct timespec *when,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
+ render_layer_popups(output, damage,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
++ render_input_popups(output, damage, &seat->im_relay.input_popups);
+ render_drag_icons(output, damage, &root->drag_icons);
+
+ renderer_end:
+diff --git a/sway/input/cursor.c b/sway/input/cursor.c
+index d8b1abeb46..87a8d7c5d6 100644
+--- a/sway/input/cursor.c
++++ b/sway/input/cursor.c
+@@ -37,6 +37,28 @@ static uint32_t get_current_time_msec(void) {
+ return now.tv_sec * 1000 + now.tv_nsec / 1000000;
+ }
+
++static struct wlr_surface *input_popup_surface_at(struct sway_output *output,
++ struct sway_input_method_relay *relay, double ox, double oy, double *sx, double *sy) {
++ struct sway_input_popup *popup;
++ wl_list_for_each(popup, &relay->input_popups, link) {
++ int lx, ly;
++ if (!sway_input_popup_get_position(popup, &lx, &ly)) {
++ continue;
++ }
++ if (!popup->popup_surface->mapped || !popup->visible) {
++ continue;
++ }
++ double _sx = ox - lx;
++ double _sy = oy - ly;
++ struct wlr_surface *sub = wlr_surface_surface_at(
++ popup->popup_surface->surface, _sx, _sy, sx, sy);
++ if (sub) {
++ return sub;
++ }
++ }
++ return NULL;
++}
++
+ static struct wlr_surface *layer_surface_at(struct sway_output *output,
+ struct wl_list *layer, double ox, double oy, double *sx, double *sy) {
+ struct sway_layer_surface *sway_layer;
+@@ -139,6 +161,11 @@ struct sway_node *node_at_coords(
+ return NULL;
+ }
+
++ if ((*surface = input_popup_surface_at(output,
++ &seat->im_relay, ox, oy, sx, sy))) {
++ return NULL;
++ }
++
+ if (ws->fullscreen) {
+ // Try transient containers
+ for (int i = 0; i < ws->floating->length; ++i) {
+diff --git a/sway/input/text_input.c b/sway/input/text_input.c
+index b8c19c179e..6350b3f933 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/desktop.h"
++#include "sway/output.h"
++#include "sway/layers.h"
+
+ static struct sway_text_input *relay_get_focusable_text_input(
+ struct sway_input_method_relay *relay) {
+@@ -26,6 +31,329 @@ 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 || !popup->visible) {
++ return;
++ }
++
++ struct sway_text_input *text_input =
++ relay_get_focused_text_input(popup->relay);
++ if (text_input == NULL || text_input->input->focused_surface == NULL) {
++ return;
++ }
++
++ struct wlr_surface *focused_surface = text_input->input->focused_surface;
++ if (wlr_surface_is_layer_surface(focused_surface)) {
++ struct wlr_layer_surface_v1 *layer_surface =
++ wlr_layer_surface_v1_from_wlr_surface(focused_surface);
++ struct sway_layer_surface *layer =
++ layer_from_wlr_layer_surface_v1(layer_surface);
++ output_damage_surface(layer->layer_surface->output->data,
++ layer->geo.x + popup->x, layer->geo.y + popup->y,
++ popup->popup_surface->surface, true);
++ return;
++ }
++
++ struct sway_view *view = view_from_wlr_surface(
++ text_input->input->focused_surface);
++ if (view->container == NULL) {
++ sway_log(SWAY_INFO, "Tried to damage popup, but view is gone");
++ return;
++ }
++ desktop_damage_surface(popup->popup_surface->surface,
++ view->container->surface_x - view->geometry.x + popup->x,
++ view->container->surface_y - view->geometry.y + popup->y, true);
++}
++
++static void input_popup_update(struct sway_input_popup *popup) {
++ input_popup_damage(popup);
++
++ struct sway_text_input *text_input =
++ relay_get_focused_text_input(popup->relay);
++
++ if (text_input == NULL || text_input->input->focused_surface == NULL) {
++ return;
++ }
++
++ if (!popup->popup_surface->mapped) {
++ return;
++ }
++
++ bool cursor_rect = text_input->input->current.features
++ & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE;
++ struct wlr_surface *focused_surface = text_input->input->focused_surface;
++ struct wlr_box cursor = text_input->input->current.cursor_rectangle;
++
++ struct wlr_output *output;
++ struct wlr_box *output_box;
++ struct wlr_box parent;
++ if (wlr_surface_is_layer_surface(focused_surface)) {
++ struct wlr_layer_surface_v1 *layer_surface =
++ wlr_layer_surface_v1_from_wlr_surface(focused_surface);
++ struct sway_layer_surface *layer =
++ layer_from_wlr_layer_surface_v1(layer_surface);
++ output = layer->layer_surface->output;
++ output_box = wlr_output_layout_get_box(root->output_layout, output);
++ parent = layer->geo;
++ parent.x += output_box->x;
++ parent.y += output_box->y;
++ } else {
++ struct sway_view *view = view_from_wlr_surface(focused_surface);
++ output = wlr_output_layout_output_at(root->output_layout,
++ view->container->surface_x + view->geometry.x,
++ view->container->surface_y + view->geometry.y);
++ output_box = wlr_output_layout_get_box(root->output_layout, output);
++ parent.x = view->container->surface_x + view->geometry.x;
++ parent.y = view->container->surface_y + view->geometry.y;
++ parent.width = view->geometry.width;
++ parent.height = view->geometry.height;
++ }
++
++ if (!cursor_rect) {
++ cursor.x = 0;
++ cursor.y = 0;
++ cursor.width = parent.width;
++ cursor.height = parent.height;
++ }
++
++ int popup_width = popup->popup_surface->surface->current.width;
++ int popup_height = popup->popup_surface->surface->current.height;
++ int x1 = parent.x + cursor.x;
++ int x2 = parent.x + cursor.x + cursor.width;
++ int y1 = parent.y + cursor.y;
++ int y2 = parent.y + cursor.y + cursor.height;
++ int x = x1;
++ int y = y2;
++
++ int available_right = output_box->x + output_box->width - x1;
++ int available_left = x2 - output_box->x;
++ if (available_right < popup_width && available_left > available_right) {
++ x = x2 - popup_width;
++ }
++
++ int available_down = output_box->y + output_box->height - y2;
++ int available_up = y1 - output_box->y;
++ if (available_down < popup_height && available_up > available_down) {
++ y = y1 - popup_height;
++ }
++
++ popup->x = x - parent.x;
++ popup->y = y - parent.y;
++
++ // Hide popup if cursor position is completely out of bounds
++ bool x1_in_bounds = (cursor.x >= 0 && cursor.x < parent.width);
++ bool y1_in_bounds = (cursor.y >= 0 && cursor.y < parent.height);
++ bool x2_in_bounds = (cursor.x + cursor.width >= 0
++ && cursor.x + cursor.width < parent.width);
++ bool y2_in_bounds = (cursor.y + cursor.height >= 0
++ && cursor.y + cursor.height < parent.height);
++ popup->visible =
++ (x1_in_bounds && y1_in_bounds) || (x2_in_bounds && y2_in_bounds);
++
++ if (cursor_rect) {
++ struct wlr_box box = {
++ .x = x1 - x,
++ .y = y1 - y,
++ .width = cursor.width,
++ .height = cursor.height,
++ };
++ wlr_input_popup_surface_v2_send_text_input_rectangle(
++ popup->popup_surface, &box);
++ }
++
++ input_popup_damage(popup);
++}
++
++static void surface_send_enter_iterator(struct wlr_surface *surface,
++ int x, int y, void *data) {
++ struct wlr_output *wlr_output = data;
++ wlr_surface_send_enter(surface, wlr_output);
++}
++
++static void surface_send_leave_iterator(struct wlr_surface *surface,
++ int x, int y, void *data) {
++ struct wlr_output *wlr_output = data;
++ wlr_surface_send_leave(surface, wlr_output);
++}
++
++static void input_popup_send_outputs(struct sway_input_popup *popup,
++ wlr_surface_iterator_func_t iterator) {
++ struct sway_text_input *text_input =
++ relay_get_focused_text_input(popup->relay);
++ if (text_input == NULL || text_input->input->focused_surface == NULL) {
++ return;
++ }
++ struct wlr_surface *focused_surface = text_input->input->focused_surface;
++ if (wlr_surface_is_layer_surface(focused_surface)) {
++ struct wlr_layer_surface_v1 *layer_surface =
++ wlr_layer_surface_v1_from_wlr_surface(focused_surface);
++ struct sway_layer_surface *layer =
++ layer_from_wlr_layer_surface_v1(layer_surface);
++ wlr_surface_for_each_surface(popup->popup_surface->surface,
++ iterator, layer->layer_surface->output);
++ return;
++ }
++ struct sway_view *view = view_from_wlr_surface(focused_surface);
++ for (int i = 0; i < view->container->outputs->length; i++) {
++ struct sway_output *output = view->container->outputs->items[i];
++ wlr_surface_for_each_surface(popup->popup_surface->surface,
++ iterator, output->wlr_output);
++ }
++}
++
++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_send_outputs(popup, surface_send_enter_iterator);
++ input_popup_update(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_send_outputs(popup, surface_send_leave_iterator);
++ input_popup_update(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);
++ wl_list_remove(&popup->focused_surface_unmap.link);
++ 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);
++ 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_focused_surface_unmap(
++ struct wl_listener *listener, void *data) {
++ struct sway_input_popup *popup =
++ wl_container_of(listener, popup, focused_surface_unmap);
++ input_popup_update(popup);
++}
++
++static void input_popup_set_focus(struct sway_input_popup *popup,
++ struct wlr_surface *surface) {
++ wl_list_remove(&popup->focused_surface_unmap.link);
++
++ if (surface == NULL) {
++ wl_list_init(&popup->focused_surface_unmap.link);
++ goto update_popup;
++ }
++
++ if (wlr_surface_is_layer_surface(surface)) {
++ struct wlr_layer_surface_v1 *layer_surface =
++ wlr_layer_surface_v1_from_wlr_surface(surface);
++ struct sway_layer_surface *layer =
++ layer_from_wlr_layer_surface_v1(layer_surface);
++ wl_signal_add(
++ &layer->layer_surface->events.unmap, &popup->focused_surface_unmap);
++ goto update_popup;
++ }
++
++ struct sway_view *view = view_from_wlr_surface(surface);
++ wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap);
++
++ // Since the focus has changed, the popup may have to adjust
++update_popup:
++ input_popup_update(popup);
++}
++
++static void handle_im_new_popup_surface(struct wl_listener *listener,
++ void *data) {
++ struct sway_input_method_relay *relay = wl_container_of(listener, relay,
++ input_method_new_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.commit,
++ &popup->popup_surface_commit);
++ popup->popup_surface_commit.notify = handle_im_popup_surface_commit;
++ wl_list_init(&popup->focused_surface_unmap.link);
++ popup->focused_surface_unmap.notify = handle_im_focused_surface_unmap;
++
++ struct sway_text_input *text_input = relay_get_focused_text_input(relay);
++ if (text_input != NULL) {
++ input_popup_set_focus(popup, text_input->input->focused_surface);
++ } else {
++ input_popup_set_focus(popup, NULL);
++ }
++
++ wl_list_insert(&relay->input_popups, &popup->link);
++}
++
++bool sway_input_popup_get_position(
++ struct sway_input_popup *popup, int *lx, int *ly) {
++ struct sway_text_input *text_input =
++ relay_get_focused_text_input(popup->relay);
++ if (text_input == NULL || text_input->input->focused_surface == NULL) {
++ *lx = 0;
++ *ly = 0;
++ return false;
++ }
++
++ struct wlr_surface *focused_surface = text_input->input->focused_surface;
++ if (wlr_surface_is_layer_surface(focused_surface)) {
++ struct wlr_layer_surface_v1 *layer_surface =
++ wlr_layer_surface_v1_from_wlr_surface(focused_surface);
++ struct sway_layer_surface *layer =
++ layer_from_wlr_layer_surface_v1(layer_surface);
++ *lx = layer->geo.x + popup->x;
++ *ly = layer->geo.y + popup->y;
++ return true;
++ }
++
++
++ struct sway_view *view = view_from_wlr_surface(
++ text_input->input->focused_surface);
++ if (view->container == NULL) {
++ sway_log(SWAY_INFO, "Tried to find popup, but view is gone");
++ return false;
++ }
++ *lx = view->container->surface_x - view->geometry.x + popup->x;
++ *ly = view->container->surface_y - view->geometry.y + popup->y;
++ return true;
++}
++
++static void text_input_send_enter(struct sway_text_input *text_input,
++ struct wlr_surface *surface) {
++ wlr_text_input_v3_send_enter(text_input->input, surface);
++ struct sway_input_popup *popup;
++ wl_list_for_each(popup, &text_input->relay->input_popups, link) {
++ input_popup_set_focus(popup, surface);
++ }
++}
++
++static void text_input_send_leave(struct sway_text_input *text_input,
++ struct wlr_surface *surface) {
++ wlr_text_input_v3_send_leave(text_input->input);
++ if (text_input->input->focused_surface == surface) {
++ struct sway_input_popup *popup;
++ wl_list_for_each(popup, &text_input->relay->input_popups, link) {
++ input_popup_set_focus(popup, NULL);
++ }
++ }
++}
++
+ 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);
+@@ -111,7 +439,7 @@ static void handle_im_destroy(struct wl_listener *listener, void *data) {
+ // the input method returns
+ text_input_set_pending_focused_surface(text_input,
+ text_input->input->focused_surface);
+- wlr_text_input_v3_send_leave(text_input->input);
++ text_input_send_leave(text_input, text_input->input->focused_surface);
+ }
+ }
+
+@@ -135,8 +463,13 @@ static void relay_send_im_state(struct sway_input_method_relay *relay,
+ input->current.content_type.hint,
+ input->current.content_type.purpose);
+ }
++ struct sway_input_popup *popup;
++ wl_list_for_each(popup, &relay->input_popups, link) {
++ // send_text_input_rectangle is called in this function
++ input_popup_update(popup);
++ }
+ wlr_input_method_v2_send_done(input_method);
+- // TODO: pass intent, display popup size
++ // TODO: pass intent
+ }
+
+ static void handle_text_input_enable(struct wl_listener *listener, void *data) {
+@@ -276,6 +609,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.new_popup_surface,
++ &relay->input_method_new_popup_surface);
++ relay->input_method_new_popup_surface.notify = handle_im_new_popup_surface;
+ 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,8 +621,7 @@ static void relay_handle_input_method(struct wl_listener *listener,
+
+ struct sway_text_input *text_input = relay_get_focusable_text_input(relay);
+ if (text_input) {
+- wlr_text_input_v3_send_enter(text_input->input,
+- text_input->pending_focused_surface);
++ text_input_send_enter(text_input, text_input->pending_focused_surface);
+ text_input_set_pending_focused_surface(text_input, NULL);
+ }
+ }
+@@ -295,6 +630,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,
+@@ -323,8 +659,9 @@ void sway_input_method_relay_set_focus(struct sway_input_method_relay *relay,
+ } else if (text_input->input->focused_surface) {
+ assert(text_input->pending_focused_surface == NULL);
+ if (surface != text_input->input->focused_surface) {
++ text_input_send_leave(
++ text_input, text_input->input->focused_surface);
+ relay_disable_text_input(relay, text_input);
+- wlr_text_input_v3_send_leave(text_input->input);
+ } else {
+ sway_log(SWAY_DEBUG, "IM relay set_focus already focused");
+ continue;
+@@ -335,7 +672,7 @@ void sway_input_method_relay_set_focus(struct sway_input_method_relay *relay,
+ && wl_resource_get_client(text_input->input->resource)
+ == wl_resource_get_client(surface->resource)) {
+ if (relay->input_method) {
+- wlr_text_input_v3_send_enter(text_input->input, surface);
++ text_input_send_enter(text_input, surface);
+ } else {
+ text_input_set_pending_focused_surface(text_input, surface);
+ }
+diff --git a/sway/tree/container.c b/sway/tree/container.c
+index e5149fb622..0b2ad72392 100644
+--- a/sway/tree/container.c
++++ b/sway/tree/container.c
+@@ -396,6 +396,8 @@ static bool surface_is_popup(struct wlr_surface *surface) {
+ wlr_subsurface_from_wlr_surface(surface);
+ surface = subsurface->parent;
+ }
++ if (wlr_surface_is_input_popup_surface_v2(surface))
++ return true;
+ struct wlr_xdg_surface *xdg_surface =
+ wlr_xdg_surface_from_wlr_surface(surface);
+ return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP;
+diff --git a/sway/tree/view.c b/sway/tree/view.c
+index 1318f5fb37..e0108681fc 100644
+--- a/sway/tree/view.c
++++ b/sway/tree/view.c
+@@ -1188,6 +1188,9 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
+ if (wlr_surface_is_layer_surface(wlr_surface)) {
+ return NULL;
+ }
++ if (wlr_surface_is_input_popup_surface_v2(wlr_surface)) {
++ return NULL;
++ }
+
+ const char *role = wlr_surface->role ? wlr_surface->role->name : NULL;
+ sway_log(SWAY_DEBUG, "Surface of unknown type (role %s): %p",