]> git.mdlowis.com Git - proto/labwc.git/commitdiff
IME: support IME popup
authortokyo4j <hrak1529@gmail.com>
Thu, 22 Feb 2024 18:50:55 +0000 (03:50 +0900)
committerJohan Malm <johanmalm@users.noreply.github.com>
Thu, 14 Mar 2024 20:11:03 +0000 (20:11 +0000)
include/input/ime.h
include/node.h
src/debug.c
src/desktop.c
src/input/ime.c
src/layers.c
src/xdg.c

index 54ac22be326705f91dab4d777d7436862ca3d1d4..06dd16ff86c3c85d1a88b345f4b1043485e2eeab 100644 (file)
@@ -29,12 +29,19 @@ struct input_method_relay {
         */
        struct text_input *active_text_input;
 
+       struct wlr_input_popup_surface_v2 *popup_surface;
+       struct wlr_scene_tree *popup_tree;
+
        struct wl_listener new_text_input;
        struct wl_listener new_input_method;
 
        struct wl_listener input_method_commit;
        struct wl_listener input_method_grab_keyboard;
        struct wl_listener input_method_destroy;
+       struct wl_listener input_method_new_popup_surface;
+
+       struct wl_listener popup_surface_destroy;
+       struct wl_listener popup_surface_commit;
 
        struct wl_listener keyboard_grab_destroy;
        struct wl_listener focused_surface_destroy;
index 419aa996854e365591b61f2ff9101e9c37aa30ff..d0a41c71d27c8ad4ce50b6d6a9ec427e9a5de562 100644 (file)
@@ -15,6 +15,7 @@ enum node_descriptor_type {
        LAB_NODE_DESC_XDG_POPUP,
        LAB_NODE_DESC_LAYER_SURFACE,
        LAB_NODE_DESC_LAYER_POPUP,
+       LAB_NODE_DESC_IME_POPUP,
        LAB_NODE_DESC_MENUITEM,
        LAB_NODE_DESC_TREE,
        LAB_NODE_DESC_SSD_BUTTON,
index af722f3ef5052090eb385c696cbce69478313c5a..d8b9c1795b856ee5b76eb5092cdd0a8cf5aac0a3 100644 (file)
@@ -141,6 +141,11 @@ get_special(struct server *server, struct wlr_scene_node *node)
                /* Created on-demand */
                return "seat->region_overlay";
        }
+       if (server->seat.input_method_relay->popup_tree
+                       && node == &server->seat.input_method_relay->popup_tree->node) {
+               /* Created on-demand */
+               return "seat->im_relay->popup_tree";
+       }
        if (server->osd_state.preview_outline
                        && node == &server->osd_state.preview_outline->tree->node) {
                /* Created on-demand */
index 4aca14b8776ed534cc4e5837bf5cbf097dfdb6a4..4141e974135b52ce7625576469b37f2d204897b8 100644 (file)
@@ -326,6 +326,10 @@ get_cursor_context(struct server *server)
                                ret.type = LAB_SSD_CLIENT;
                                ret.surface = get_surface_from_layer_node(node);
                                return ret;
+                       case LAB_NODE_DESC_IME_POPUP:
+                               ret.type = LAB_SSD_CLIENT;
+                               ret.surface = lab_wlr_surface_from_node(ret.node);
+                               return ret;
                        case LAB_NODE_DESC_MENUITEM:
                                /* Always return the top scene node for menu items */
                                ret.node = node;
index 18d542070c19969ff90821794d48cb9e4c3a2a9a..bb08f6d5d4220671b4b02ee4fc109e492e38bedc 100644 (file)
@@ -152,6 +152,94 @@ update_text_inputs_focused_surface(struct input_method_relay *relay)
        }
 }
 
+static void
+update_popup_position(struct input_method_relay *relay)
+{
+       struct server *server = relay->seat->server;
+       struct text_input *text_input = relay->active_text_input;
+
+       if (!text_input || !relay->focused_surface || !relay->popup_surface
+                       || !relay->popup_surface->surface->mapped) {
+               return;
+       }
+
+       struct wlr_box cursor_rect;
+       struct wlr_xdg_surface *xdg_surface =
+               wlr_xdg_surface_try_from_wlr_surface(relay->focused_surface);
+       struct wlr_layer_surface_v1 *layer_surface =
+               wlr_layer_surface_v1_try_from_wlr_surface(relay->focused_surface);
+
+       if ((text_input->input->current.features
+                       & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE)
+                       && (xdg_surface || layer_surface)) {
+               cursor_rect = text_input->input->current.cursor_rectangle;
+
+               /*
+                * wlr_surface->data is:
+                * - for XDG surfaces: view->scene_node
+                * - for layer surfaces: lab_layer_surface->scene_layer_surface->tree
+                * - for layer popups: lab_layer_popup->scene_tree
+                */
+               struct wlr_scene_tree *tree = relay->focused_surface->data;
+               int lx, ly;
+               wlr_scene_node_coords(&tree->node, &lx, &ly);
+               cursor_rect.x += lx;
+               cursor_rect.y += ly;
+
+               if (xdg_surface) {
+                       /* Take into account invisible xdg-shell CSD borders */
+                       struct wlr_box geo;
+                       wlr_xdg_surface_get_geometry(xdg_surface, &geo);
+                       cursor_rect.x -= geo.x;
+                       cursor_rect.y -= geo.y;
+               }
+       } else {
+               cursor_rect = (struct wlr_box){0};
+       }
+
+       struct output *output =
+               output_nearest_to(server, cursor_rect.x, cursor_rect.y);
+       if (!output_is_usable(output)) {
+               wlr_log(WLR_ERROR,
+                       "Cannot position IME popup (unusable output)");
+               return;
+       }
+       struct wlr_box output_box;
+       wlr_output_layout_get_box(
+               server->output_layout, output->wlr_output, &output_box);
+
+       /* Use xdg-positioner utilities to position popup */
+       struct wlr_xdg_positioner_rules rules = {
+               .anchor_rect = cursor_rect,
+               .anchor = XDG_POSITIONER_ANCHOR_BOTTOM_LEFT,
+               .gravity = XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT,
+               .size = {
+                       .width = relay->popup_surface->surface->current.width,
+                       .height = relay->popup_surface->surface->current.height,
+               },
+               .constraint_adjustment =
+                       XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y
+                       | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X,
+       };
+
+       struct wlr_box popup_box;
+       wlr_xdg_positioner_rules_get_geometry(&rules, &popup_box);
+       wlr_xdg_positioner_rules_unconstrain_box(&rules, &output_box, &popup_box);
+
+       wlr_scene_node_set_position(
+               &relay->popup_tree->node, popup_box.x, popup_box.y);
+       /* Make sure IME popup is always on top, above layer-shell surfaces */
+       wlr_scene_node_raise_to_top(&relay->popup_tree->node);
+
+       wlr_input_popup_surface_v2_send_text_input_rectangle(
+               relay->popup_surface, &(struct wlr_box){
+                       .x = cursor_rect.x - popup_box.x,
+                       .y = cursor_rect.y - popup_box.y,
+                       .width = cursor_rect.width,
+                       .height = cursor_rect.height,
+               });
+}
+
 static void
 handle_input_method_commit(struct wl_listener *listener, void *data)
 {
@@ -236,6 +324,59 @@ handle_input_method_destroy(struct wl_listener *listener, void *data)
        update_active_text_input(relay);
 }
 
+static void
+handle_popup_surface_destroy(struct wl_listener *listener, void *data)
+{
+       struct input_method_relay *relay =
+               wl_container_of(listener, relay, popup_surface_destroy);
+       wl_list_remove(&relay->popup_surface_destroy.link);
+       wl_list_remove(&relay->popup_surface_commit.link);
+       relay->popup_surface = NULL;
+       relay->popup_tree = NULL;
+}
+
+static void
+handle_popup_surface_commit(struct wl_listener *listener, void *data)
+{
+       struct input_method_relay *relay =
+               wl_container_of(listener, relay, popup_surface_commit);
+       update_popup_position(relay);
+}
+
+static void
+handle_input_method_new_popup_surface(struct wl_listener *listener, void *data)
+{
+       struct input_method_relay *relay = wl_container_of(listener, relay,
+               input_method_new_popup_surface);
+
+       if (relay->popup_surface) {
+               /*
+                * With current input-method-v2 protocol, creating multiple IME
+                * popups is not useful because they just stack up at the same
+                * position.
+                *
+                * Discussed here:
+                * https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/40#note_847039
+                */
+               wlr_log(WLR_INFO,
+                       "Creating multiple IME popups is not supported");
+               return;
+       }
+
+       relay->popup_surface = data;
+
+       wl_signal_add(&relay->popup_surface->events.destroy,
+               &relay->popup_surface_destroy);
+       wl_signal_add(&relay->popup_surface->surface->events.commit,
+               &relay->popup_surface_commit);
+
+       relay->popup_tree = wlr_scene_subsurface_tree_create(
+               &relay->seat->server->scene->tree,
+               relay->popup_surface->surface);
+       node_descriptor_create(
+               &relay->popup_tree->node, LAB_NODE_DESC_IME_POPUP, NULL);
+}
+
 static void
 handle_new_input_method(struct wl_listener *listener, void *data)
 {
@@ -268,6 +409,11 @@ handle_new_input_method(struct wl_listener *listener, void *data)
        wl_signal_add(&relay->input_method->events.destroy,
                &relay->input_method_destroy);
 
+       relay->input_method_new_popup_surface.notify =
+               handle_input_method_new_popup_surface;
+       wl_signal_add(&relay->input_method->events.new_popup_surface,
+               &relay->input_method_new_popup_surface);
+
        update_text_inputs_focused_surface(relay);
        update_active_text_input(relay);
 }
@@ -308,6 +454,7 @@ handle_text_input_enable(struct wl_listener *listener, void *data)
 
        update_active_text_input(relay);
        if (relay->active_text_input == text_input) {
+               update_popup_position(relay);
                send_state_to_input_method(relay);
        }
 }
@@ -333,6 +480,7 @@ handle_text_input_commit(struct wl_listener *listener, void *data)
        struct input_method_relay *relay = text_input->relay;
 
        if (relay->active_text_input == text_input) {
+               update_popup_position(relay);
                send_state_to_input_method(relay);
        }
 }
@@ -412,6 +560,8 @@ input_method_relay_create(struct seat *seat)
                &relay->new_input_method);
 
        relay->focused_surface_destroy.notify = handle_focused_surface_destroy;
+       relay->popup_surface_destroy.notify = handle_popup_surface_destroy;
+       relay->popup_surface_commit.notify = handle_popup_surface_commit;
 
        return relay;
 }
index 20e9e6838fb7f36a4317649ba999d4b172c74331..efb5462f66bed2ba41aa8c41d1f2ec7813122c3a 100644 (file)
@@ -298,6 +298,10 @@ create_popup(struct wlr_xdg_popup *wlr_popup, struct wlr_scene_tree *parent)
                free(popup);
                return NULL;
        }
+
+       /* In support of IME popup */
+       wlr_popup->base->surface->data = popup->scene_tree;
+
        node_descriptor_create(&popup->scene_tree->node,
                LAB_NODE_DESC_LAYER_POPUP, popup);
 
@@ -424,6 +428,9 @@ handle_new_layer_surface(struct wl_listener *listener, void *data)
                return;
        }
 
+       /* In support of IME popup */
+       layer_surface->surface->data = surface->scene_layer_surface->tree;
+
        node_descriptor_create(&surface->scene_layer_surface->tree->node,
                LAB_NODE_DESC_LAYER_SURFACE, surface);
 
index 73c9ef6897b8601511ac8d1818761c6ae2679f34..1ad535547c858f06fe110a72d9a9054b3553c63d 100644 (file)
--- a/src/xdg.c
+++ b/src/xdg.c
@@ -773,7 +773,7 @@ xdg_surface_new(struct wl_listener *listener, void *data)
         */
        kde_server_decoration_set_view(view, xdg_surface->surface);
 
-       /* In support of xdg popups */
+       /* In support of xdg popups and IME popup */
        xdg_surface->surface->data = tree;
 
        view_connect_map(view, xdg_surface->surface);