From c86f2f73fd3c8448afa862f48fbb4835d380e5f9 Mon Sep 17 00:00:00 2001 From: Silvan Jegen Date: Sat, 20 Jun 2020 15:44:49 +0200 Subject: [PATCH 01/12] implement text-input and input-method protocol support This is mostly copied from the sway implementation here: https://github.com/swaywm/sway/pull/4740/files --- dwl.c | 375 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 374 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index abff38a09..038729873 100644 --- a/dwl.c +++ b/dwl.c @@ -2,6 +2,7 @@ * See LICENSE file for copyright and license details. */ #define _POSIX_C_SOURCE 200809L +#include #include #include #include @@ -23,9 +24,11 @@ #include #include #include -#include #include +#include #include +#include +#include #include #include #include @@ -39,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -200,6 +204,56 @@ typedef struct { int monitor; } Rule; +/** + * The relay structure manages the relationship between text-input and + * input_method interfaces on a given seat. Multiple text-input interfaces may + * be bound to a relay, but at most one will be focused (receiving events) at + * a time. At most one input-method interface may be bound to the seat. The + * relay manages life cycle of both sides. When both sides are present and + * focused, the relay passes messages between them. + * + * Text input focus is a subset of keyboard focus - if the text-input is + * in the focused state, wl_keyboard sent an enter as well. However, having + * wl_keyboard focused doesn't mean that text-input will be focused. + */ +struct dwl_input_method_relay { + struct wl_list text_inputs; // dwl_text_input::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_destroy; +}; + +struct dwl_text_input { + struct dwl_input_method_relay *relay; + + struct wlr_text_input_v3 *input; + // The surface getting seat's focus. Stored for when text-input cannot + // be sent an enter event immediately after getting focus, e.g. when + // there's no input method available. Cleared once text-input is entered. + struct wlr_surface *pending_focused_surface; + + struct wl_list link; + + struct wl_listener pending_focused_surface_destroy; + + struct wl_listener text_input_enable; + struct wl_listener text_input_commit; + struct wl_listener text_input_disable; + struct wl_listener text_input_destroy; +}; + +/* Used to move all of the data necessary to render a surface from the top-level + * frame handler to the per-surface render function. */ +struct render_data { + struct wlr_output *output; + struct timespec *when; + int x, y; /* layout-relative */ +}; + /* function declarations */ static void applybounds(Client *c, struct wlr_box *bbox); static void applyexclusive(struct wlr_box *usable_area, uint32_t anchor, @@ -292,6 +346,14 @@ static void virtualkeyboard(struct wl_listener *listener, void *data); static Monitor *xytomon(double x, double y); static struct wlr_scene_node *xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, LayerSurface **pl, double *nx, double *ny); +void dwl_input_method_relay_init(struct dwl_input_method_relay *relay); +// Updates currently focused surface. Surface must belong to the same +// seat. +void dwl_input_method_relay_set_focus(struct dwl_input_method_relay *relay, + struct wlr_surface *surface); +struct dwl_text_input *dwl_text_input_create( + struct dwl_input_method_relay *relay, + struct wlr_text_input_v3 *text_input); static void zoom(const Arg *arg); /* variables */ @@ -319,6 +381,9 @@ static struct wlr_cursor *cursor; static struct wlr_xcursor_manager *cursor_mgr; static struct wlr_seat *seat; +struct wlr_input_method_manager_v2 *input_method_manager; +struct wlr_text_input_manager_v3 *text_input_manager; +struct dwl_input_method_relay *input_relay; static struct wl_list keyboards; static unsigned int cursor_mode; static Client *grabc; @@ -1155,6 +1220,8 @@ focusclient(Client *c, int lift) else wlr_seat_keyboard_notify_enter(seat, client_surface(c), NULL, 0, NULL); + dwl_input_method_relay_set_focus(input_relay, client_surface(c)); + /* Activate the new client */ client_activate_surface(client_surface(c), 1); } @@ -1912,6 +1979,305 @@ setsel(struct wl_listener *listener, void *data) wlr_seat_set_selection(seat, event->source, event->serial); } +// text-input-, and input_method-relevant functions +static struct dwl_text_input *relay_get_focusable_text_input( + struct dwl_input_method_relay *relay) { + struct dwl_text_input *text_input = NULL; + wl_list_for_each(text_input, &relay->text_inputs, link) { + if (text_input->pending_focused_surface) { + return text_input; + } + } + return NULL; +} + +static struct dwl_text_input *relay_get_focused_text_input( + struct dwl_input_method_relay *relay) { + struct dwl_text_input *text_input = NULL; + wl_list_for_each(text_input, &relay->text_inputs, link) { + if (text_input->input->focused_surface) { + return text_input; + } + } + return NULL; +} + +static void handle_im_commit(struct wl_listener *listener, void *data) { + struct wlr_input_method_v2 *context; + + struct dwl_input_method_relay *relay = wl_container_of(listener, relay, + input_method_commit); + + struct dwl_text_input *text_input = relay_get_focused_text_input(relay); + if (!text_input) { + return; + } + + context = data; + assert(context == relay->input_method); + if (context->current.preedit.text) { + wlr_text_input_v3_send_preedit_string(text_input->input, + context->current.preedit.text, + context->current.preedit.cursor_begin, + context->current.preedit.cursor_end); + } + if (context->current.commit_text) { + wlr_text_input_v3_send_commit_string(text_input->input, + context->current.commit_text); + } + if (context->current.delete.before_length + || context->current.delete.after_length) { + wlr_text_input_v3_send_delete_surrounding_text(text_input->input, + context->current.delete.before_length, + context->current.delete.after_length); + } + wlr_text_input_v3_send_done(text_input->input); +} + +static void text_input_set_pending_focused_surface( + struct dwl_text_input *text_input, struct wlr_surface *surface) { + wl_list_remove(&text_input->pending_focused_surface_destroy.link); + text_input->pending_focused_surface = surface; + + if (surface) { + wl_signal_add(&surface->events.destroy, + &text_input->pending_focused_surface_destroy); + } else { + wl_list_init(&text_input->pending_focused_surface_destroy.link); + } +} + +static void handle_im_destroy(struct wl_listener *listener, void *data) { + struct dwl_text_input *text_input; + + struct dwl_input_method_relay *relay = wl_container_of(listener, relay, + input_method_destroy); + struct wlr_input_method_v2 *context = data; + assert(context == relay->input_method); + relay->input_method = NULL; + + text_input = relay_get_focused_text_input(relay); + if (text_input) { + // keyboard focus is still there, so keep the surface at hand in case + // 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); + } +} + +static void relay_send_im_state(struct dwl_input_method_relay *relay, + struct wlr_text_input_v3 *input) { + struct wlr_input_method_v2 *input_method = relay->input_method; + if (!input_method) { + wlr_log(WLR_INFO, "Sending IM_DONE but im is gone"); + return; + } + // TODO: only send each of those if they were modified + wlr_input_method_v2_send_surrounding_text(input_method, + input->current.surrounding.text, input->current.surrounding.cursor, + input->current.surrounding.anchor); + wlr_input_method_v2_send_text_change_cause(input_method, + input->current.text_change_cause); + wlr_input_method_v2_send_content_type(input_method, + input->current.content_type.hint, input->current.content_type.purpose); + wlr_input_method_v2_send_done(input_method); + // TODO: pass intent, display popup size +} + +static void handle_text_input_enable(struct wl_listener *listener, void *data) { + struct dwl_text_input *text_input = wl_container_of(listener, text_input, + text_input_enable); + if (text_input->relay->input_method == NULL) { + wlr_log(WLR_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); +} + +static void handle_text_input_commit(struct wl_listener *listener, + void *data) { + struct dwl_text_input *text_input = wl_container_of(listener, text_input, + text_input_commit); + if (!text_input->input->current_enabled) { + wlr_log(WLR_INFO, "Inactive text input tried to commit an update"); + return; + } + wlr_log(WLR_DEBUG, "Text input committed update"); + if (text_input->relay->input_method == NULL) { + wlr_log(WLR_INFO, "Text input committed, but input method is gone"); + return; + } + relay_send_im_state(text_input->relay, text_input->input); +} + +static void relay_disable_text_input(struct dwl_input_method_relay *relay, + struct dwl_text_input *text_input) { + if (relay->input_method == NULL) { + wlr_log(WLR_DEBUG, "Disabling text input, but input method is gone"); + return; + } + wlr_input_method_v2_send_deactivate(relay->input_method); + relay_send_im_state(relay, text_input->input); +} + +static void handle_text_input_disable(struct wl_listener *listener, + void *data) { + struct dwl_text_input *text_input = wl_container_of(listener, text_input, + text_input_disable); + relay_disable_text_input(text_input->relay, text_input); +} + +static void handle_text_input_destroy(struct wl_listener *listener, + void *data) { + struct dwl_text_input *text_input = wl_container_of(listener, text_input, + text_input_destroy); + + if (text_input->input->current_enabled) { + relay_disable_text_input(text_input->relay, text_input); + } + text_input_set_pending_focused_surface(text_input, NULL); + wl_list_remove(&text_input->text_input_commit.link); + wl_list_remove(&text_input->text_input_destroy.link); + wl_list_remove(&text_input->text_input_disable.link); + wl_list_remove(&text_input->text_input_enable.link); + wl_list_remove(&text_input->link); + free(text_input); +} + +static void handle_pending_focused_surface_destroy(struct wl_listener *listener, + void *data) { + struct dwl_text_input *text_input = wl_container_of(listener, text_input, + pending_focused_surface_destroy); + struct wlr_surface *surface = data; + assert(text_input->pending_focused_surface == surface); + text_input->pending_focused_surface = NULL; + wl_list_remove(&text_input->pending_focused_surface_destroy.link); + wl_list_init(&text_input->pending_focused_surface_destroy.link); +} + +struct dwl_text_input *dwl_text_input_create( + struct dwl_input_method_relay *relay, + struct wlr_text_input_v3 *text_input) { + struct dwl_text_input *input; + input = calloc(1, sizeof(*input)); + if (!input) { + return NULL; + } + input->input = text_input; + input->relay = relay; + + wl_list_insert(&relay->text_inputs, &input->link); + + input->text_input_enable.notify = handle_text_input_enable; + wl_signal_add(&text_input->events.enable, &input->text_input_enable); + + input->text_input_commit.notify = handle_text_input_commit; + wl_signal_add(&text_input->events.commit, &input->text_input_commit); + + input->text_input_disable.notify = handle_text_input_disable; + wl_signal_add(&text_input->events.disable, &input->text_input_disable); + + input->text_input_destroy.notify = handle_text_input_destroy; + wl_signal_add(&text_input->events.destroy, &input->text_input_destroy); + + input->pending_focused_surface_destroy.notify = + handle_pending_focused_surface_destroy; + wl_list_init(&input->pending_focused_surface_destroy.link); + return input; +} + +static void relay_handle_text_input(struct wl_listener *listener, + void *data) { + struct dwl_input_method_relay *relay = wl_container_of(listener, relay, + text_input_new); + struct wlr_text_input_v3 *wlr_text_input = data; + if (seat != wlr_text_input->seat) { + return; + } + + dwl_text_input_create(relay, wlr_text_input); +} + +static void relay_handle_input_method(struct wl_listener *listener, + void *data) { + struct dwl_text_input *text_input; + + struct dwl_input_method_relay *relay = wl_container_of(listener, relay, + input_method_new); + + struct wlr_input_method_v2 *input_method = data; + if (seat != input_method->seat) { + return; + } + + if (relay->input_method != NULL) { + wlr_log(WLR_INFO, "Attempted to connect second input method to a seat"); + wlr_input_method_v2_send_unavailable(input_method); + return; + } + + relay->input_method = input_method; + 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.destroy, + &relay->input_method_destroy); + relay->input_method_destroy.notify = handle_im_destroy; + + 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_set_pending_focused_surface(text_input, NULL); + } +} + +void dwl_input_method_relay_init(struct dwl_input_method_relay *relay) { + wl_list_init(&relay->text_inputs); + + relay->text_input_new.notify = relay_handle_text_input; + wl_signal_add(&text_input_manager->events.text_input, + &relay->text_input_new); + + relay->input_method_new.notify = relay_handle_input_method; + wl_signal_add(&input_method_manager->events.input_method, + &relay->input_method_new); +} + +void dwl_input_method_relay_set_focus(struct dwl_input_method_relay *relay, + struct wlr_surface *surface) { + struct dwl_text_input *text_input; + wl_list_for_each(text_input, &relay->text_inputs, link) { + if (text_input->pending_focused_surface) { + assert(text_input->input->focused_surface == NULL); + if (surface != text_input->pending_focused_surface) { + text_input_set_pending_focused_surface(text_input, NULL); + } + } else if (text_input->input->focused_surface) { + assert(text_input->pending_focused_surface == NULL); + if (surface != text_input->input->focused_surface) { + relay_disable_text_input(relay, text_input); + wlr_text_input_v3_send_leave(text_input->input); + } else { + wlr_log(WLR_DEBUG, "IM relay set_focus already focused"); + continue; + } + } + + if (surface + && 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); + } else { + text_input_set_pending_focused_surface(text_input, surface); + } + } + } +} + void setup(void) { @@ -2067,6 +2433,13 @@ setup(void) wlr_scene_set_presentation(scene, wlr_presentation_create(dpy, backend)); + /* create text_input-, and input_method-protocol relevant globals */ + input_method_manager = wlr_input_method_manager_v2_create(dpy); + text_input_manager = wlr_text_input_manager_v3_create(dpy); + + input_relay = calloc(1, sizeof(*input_relay)); + dwl_input_method_relay_init(input_relay); + #ifdef XWAYLAND /* * Initialise the XWayland X server. From 27bf627a97d58807c714ef552fdaa6bcf88839f1 Mon Sep 17 00:00:00 2001 From: Silvan Jegen Date: Sun, 28 Mar 2021 13:38:26 +0200 Subject: [PATCH 02/12] implement input popup rendering Based on https://github.com/swaywm/sway/pull/5890 --- dwl.c | 268 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 268 insertions(+) diff --git a/dwl.c b/dwl.c index 038729873..8726ce8f6 100644 --- a/dwl.c +++ b/dwl.c @@ -218,6 +218,7 @@ typedef struct { */ struct dwl_input_method_relay { struct wl_list text_inputs; // dwl_text_input::link + struct wl_list input_popups; // dwl_input_popup::link struct wlr_input_method_v2 *input_method; // doesn't have to be present struct wl_listener text_input_new; @@ -225,6 +226,7 @@ struct dwl_input_method_relay { struct wl_listener input_method_new; struct wl_listener input_method_commit; struct wl_listener input_method_destroy; + struct wl_listener input_method_new_popup_surface; }; struct dwl_text_input { @@ -246,6 +248,27 @@ struct dwl_text_input { struct wl_listener text_input_destroy; }; + +struct dwl_input_popup { + struct dwl_input_method_relay *relay; + struct wlr_input_popup_surface_v2 *popup_surface; + + struct wlr_scene_node *scene; + struct wlr_scene_node *scene_surface; + + int x, y; + bool visible; + + struct wl_list link; + struct wl_list view_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; +}; + /* Used to move all of the data necessary to render a surface from the top-level * frame handler to the per-surface render function. */ struct render_data { @@ -370,6 +393,9 @@ static struct wlr_xdg_shell *xdg_shell; static struct wlr_xdg_activation_v1 *activation; static struct wl_list clients; /* tiling order */ static struct wl_list fstack; /* focus order */ + +static struct wl_list input_popups; /* dwl_input_popup::view_link to be rendered for the input method */ + static struct wlr_idle *idle; static struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr; static struct wlr_input_inhibit_manager *input_inhibit_mgr; @@ -1022,6 +1048,7 @@ createnotify(struct wl_listener *listener, void *data) } else if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_NONE) return; + /* Allocate a Client for this surface */ c = xdg_surface->data = ecalloc(1, sizeof(*c)); c->surface.xdg = xdg_surface; @@ -2200,6 +2227,241 @@ static void relay_handle_text_input(struct wl_listener *listener, dwl_text_input_create(relay, wlr_text_input); } +static LayerSurface* layer_surface_from_wlr_layer_surface_v1( + struct wlr_layer_surface_v1* layer_surface) { + return layer_surface->data; +} + +static Client* client_from_wlr_surface(struct wlr_surface* surface) { + return wlr_xdg_surface_from_wlr_surface(surface)->data; +} + +static void get_parent_and_output_box(struct wlr_surface *focused_surface, + struct wlr_box *parent, struct wlr_box *output_box) { + struct wlr_output *output; + struct wlr_box *output_box_tmp; + + if (wlr_surface_is_layer_surface(focused_surface)) { + struct wlr_layer_surface_v1 *layer_surface = + wlr_layer_surface_v1_from_wlr_surface(focused_surface); + LayerSurface* layer = + layer_surface_from_wlr_layer_surface_v1(layer_surface); + output = layer->layer_surface->output; + output_box_tmp = wlr_output_layout_get_box(output_layout, output); + *parent = layer->geo; + parent->x += output_box_tmp->x; + parent->y += output_box_tmp->y; + } else { + Client *client = client_from_wlr_surface(focused_surface); + output = wlr_output_layout_output_at(output_layout, + client->geom.x, client->geom.y); + output_box_tmp = wlr_output_layout_get_box(output_layout, output); + parent->x = client->geom.x + client->bw; + parent->y = client->geom.y + client->bw; + parent->width = client->geom.width; + parent->height = client->geom.height; + } + + *output_box = *output_box_tmp; +} + +static void input_popup_update(struct dwl_input_popup *popup) { + struct wlr_surface* focused_surface; + struct wlr_box output_box, parent, cursor; + int x1, x2, y1, y2, x, y, available_right, available_left, available_down, + available_up, popup_width, popup_height; + bool cursor_rect, x1_in_bounds, y1_in_bounds, x2_in_bounds, y2_in_bounds; + + struct dwl_text_input *text_input = + relay_get_focused_text_input(popup->relay); + if (!text_input|| !text_input->input->focused_surface) { + return; + } + + if (!popup->popup_surface->mapped) { + return; + } + + cursor_rect = text_input->input->current.features + & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE; + focused_surface = text_input->input->focused_surface; + cursor = text_input->input->current.cursor_rectangle; + + get_parent_and_output_box(focused_surface, &parent, &output_box); + + if (!cursor_rect) { + cursor.x = 0; + cursor.y = 0; + cursor.width = parent.width; + cursor.height = parent.height; + } + + popup_width = popup->popup_surface->surface->current.width; + popup_height = popup->popup_surface->surface->current.height; + x1 = parent.x + cursor.x; + x2 = parent.x + cursor.x + cursor.width; + y1 = parent.y + cursor.y; + y2 = parent.y + cursor.y + cursor.height; + x = x1; + y = y2; + + available_right = output_box.x + output_box.width - x1; + available_left = x2 - output_box.x; + if (available_right < popup_width && available_left > available_right) { + x = x2 - popup_width; + } + + available_down = output_box.y + output_box.height - y2; + available_up = y1 - output_box.y; + if (available_down < popup_height && available_up > available_down) { + y = y1 - popup_height; + } + + popup->x = x; + popup->y = y; + + // Hide popup if cursor position is completely out of bounds + x1_in_bounds = (cursor.x >= 0 && cursor.x < parent.width); + y1_in_bounds = (cursor.y >= 0 && cursor.y < parent.height); + x2_in_bounds = (cursor.x + cursor.width >= 0 + && cursor.x + cursor.width < parent.width); + 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); + } +} + +static void handle_im_popup_map(struct wl_listener *listener, void *data) { + struct dwl_input_popup *popup = + wl_container_of(listener, popup, popup_map); + + popup->scene = &wlr_scene_tree_create(layers[LyrFloat])->node; + popup->scene_surface = wlr_scene_subsurface_tree_create(popup->scene, + popup->popup_surface->surface); + popup->scene_surface->data = popup; + + input_popup_update(popup); + + wlr_scene_node_set_position(popup->scene, popup->x, popup->y); +} + +static void handle_im_popup_unmap(struct wl_listener *listener, void *data) { + struct dwl_input_popup *popup = + wl_container_of(listener, popup, popup_unmap); + input_popup_update(popup); + + wlr_scene_node_destroy(popup->scene); +} + +static void handle_im_popup_destroy(struct wl_listener *listener, void *data) { + struct dwl_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->view_link); + wl_list_remove(&popup->link); + free(popup); +} + +static void handle_im_popup_surface_commit(struct wl_listener *listener, + void *data) { + struct dwl_input_popup *popup = + wl_container_of(listener, popup, popup_surface_commit); + input_popup_update(popup); +} + +static void handle_im_focused_surface_destroy( + struct wl_listener *listener, void *data) { + struct dwl_input_popup *popup = + wl_container_of(listener, popup, focused_surface_unmap); + wl_list_remove(&popup->view_link); + wl_list_init(&popup->view_link); + input_popup_update(popup); +} + +static void input_popup_set_focus(struct dwl_input_popup *popup, + struct wlr_surface *surface) { + Client* client; + struct wlr_layer_surface_v1* layer_surface; + LayerSurface* layer; + + wl_list_remove(&popup->view_link); + wl_list_remove(&popup->focused_surface_unmap.link); + + if (wlr_surface_is_layer_surface(surface)) { + layer_surface = wlr_layer_surface_v1_from_wlr_surface(surface); + layer = layer_surface_from_wlr_layer_surface_v1(layer_surface); + wl_list_insert(&input_popups, &popup->view_link); + wl_signal_add(&layer->layer_surface->events.unmap, + &popup->focused_surface_unmap); + input_popup_update(popup); + return; + } + + client = client_from_wlr_surface(surface); + wl_list_insert(&input_popups, &popup->view_link); + wl_signal_add(&client->surface.xdg->events.unmap, + &popup->focused_surface_unmap); + input_popup_update(popup); +} + + + +static void handle_im_new_popup_surface(struct wl_listener *listener, void *data) { + struct dwl_text_input* text_input; + + struct dwl_input_method_relay *relay = wl_container_of(listener, relay, + input_method_new_popup_surface); + struct dwl_input_popup *popup = calloc(1, sizeof(*popup)); + popup->relay = relay; + popup->popup_surface = data; + popup->popup_surface->data = popup; + + wl_list_init(&popup->view_link); + + 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_destroy; + + text_input = relay_get_focused_text_input(relay); + if (text_input) { + 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); +} + + static void relay_handle_input_method(struct wl_listener *listener, void *data) { struct dwl_text_input *text_input; @@ -2222,6 +2484,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.destroy, &relay->input_method_destroy); relay->input_method_destroy.notify = handle_im_destroy; @@ -2236,6 +2501,7 @@ static void relay_handle_input_method(struct wl_listener *listener, void dwl_input_method_relay_init(struct dwl_input_method_relay *relay) { wl_list_init(&relay->text_inputs); + wl_list_init(&relay->input_popups); relay->text_input_new.notify = relay_handle_text_input; wl_signal_add(&text_input_manager->events.text_input, @@ -2359,6 +2625,8 @@ setup(void) wl_list_init(&clients); wl_list_init(&fstack); + wl_list_init(&input_popups); + idle = wlr_idle_create(dpy); idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy); From ac1aef7ead881c5da5672852a32e437ef93f50f3 Mon Sep 17 00:00:00 2001 From: Silvan Jegen Date: Mon, 29 Mar 2021 15:17:55 +0200 Subject: [PATCH 03/12] implement input method keyboard grab --- dwl.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/dwl.c b/dwl.c index 8726ce8f6..2aa007183 100644 --- a/dwl.c +++ b/dwl.c @@ -227,6 +227,8 @@ struct dwl_input_method_relay { struct wl_listener input_method_commit; struct wl_listener input_method_destroy; struct wl_listener input_method_new_popup_surface; + struct wl_listener input_method_grab_keyboard; + struct wl_listener input_method_keyboard_grab_destroy; }; struct dwl_text_input { @@ -377,6 +379,9 @@ void dwl_input_method_relay_set_focus(struct dwl_input_method_relay *relay, struct dwl_text_input *dwl_text_input_create( struct dwl_input_method_relay *relay, struct wlr_text_input_v3 *text_input); +static void handle_im_grab_keyboard(struct wl_listener *listener, void *data); +static void handle_im_keyboard_grab_destroy(struct wl_listener *listener, + void *data); static void zoom(const Arg *arg); /* variables */ @@ -1369,6 +1374,26 @@ keybinding(uint32_t mods, xkb_keysym_t sym) return handled; } +/** + * Get keyboard grab of the seat from sway_keyboard if we should forward events + * to it. + * + * Returns NULL if the keyboard is not grabbed by an input method, + * or if event is from virtual keyboard of the same client as grab. + * TODO: see https://github.com/swaywm/wlroots/issues/2322 + */ +static struct wlr_input_method_keyboard_grab_v2 *keyboard_get_im_grab(Keyboard* kb) { + struct wlr_input_method_v2 *input_method = input_relay->input_method; + struct wlr_virtual_keyboard_v1 *virtual_keyboard = + wlr_input_device_get_virtual_keyboard(kb->device); + if (!input_method || !input_method->keyboard_grab || (virtual_keyboard && + wl_resource_get_client(virtual_keyboard->resource) == + wl_resource_get_client(input_method->keyboard_grab->resource))) { + return NULL; + } + return input_method->keyboard_grab; +} + void keypress(struct wl_listener *listener, void *data) { @@ -1397,6 +1422,16 @@ keypress(struct wl_listener *listener, void *data) handled = keybinding(mods, syms[i]) || handled; if (!handled) { + /* if there is a keyboard grab, we send the key there */ + struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(kb); + if (kb_grab) { + wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, + kb->device->keyboard); + wlr_input_method_keyboard_grab_v2_send_key(kb_grab, + event->time_msec, event->keycode, event->state); + return; + } + /* Pass unhandled keycodes along to the client. */ wlr_seat_set_keyboard(seat, kb->device); wlr_seat_keyboard_notify_key(seat, event->time_msec, @@ -1410,6 +1445,13 @@ keypressmod(struct wl_listener *listener, void *data) /* This event is raised when a modifier key, such as shift or alt, is * pressed. We simply communicate this to the client. */ Keyboard *kb = wl_container_of(listener, kb, modifiers); + struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(kb); + if (kb_grab) { + wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab, + &kb->device->keyboard->modifiers); + return; + } + /* * A seat can only have one keyboard, but this is a limitation of the * Wayland protocol - not wlroots. We assign all connected keyboards to the @@ -1489,6 +1531,38 @@ mapnotify(struct wl_listener *listener, void *data) c->mon->un_map = 1; } +static void handle_im_grab_keyboard(struct wl_listener *listener, void *data) { + struct dwl_input_method_relay *relay = wl_container_of(listener, relay, + input_method_grab_keyboard); + struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; + + // send modifier state to grab + struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(seat); + wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, + active_keyboard); + wlr_input_method_keyboard_grab_v2_send_modifiers(keyboard_grab, + &active_keyboard->modifiers); + + wl_signal_add(&keyboard_grab->events.destroy, + &relay->input_method_keyboard_grab_destroy); + relay->input_method_keyboard_grab_destroy.notify = + handle_im_keyboard_grab_destroy; +} + +static void handle_im_keyboard_grab_destroy(struct wl_listener *listener, void *data) { + struct dwl_input_method_relay *relay = wl_container_of(listener, relay, + input_method_keyboard_grab_destroy); + struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; + wl_list_remove(&relay->input_method_keyboard_grab_destroy.link); + + if (keyboard_grab->keyboard) { + // send modifier state to original client + wlr_seat_keyboard_notify_modifiers(keyboard_grab->input_method->seat, + &keyboard_grab->keyboard->modifiers); + } +} + + void monocle(Monitor *m) { @@ -2487,6 +2561,9 @@ static void relay_handle_input_method(struct wl_listener *listener, 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; wl_signal_add(&relay->input_method->events.destroy, &relay->input_method_destroy); relay->input_method_destroy.notify = handle_im_destroy; From fbd698479a4f9f36fde2c6e1c408a8712a061e3a Mon Sep 17 00:00:00 2001 From: Silvan Jegen Date: Sun, 22 May 2022 14:06:22 +0200 Subject: [PATCH 04/12] Fix imports --- dwl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 2aa007183..7af7ae2bd 100644 --- a/dwl.c +++ b/dwl.c @@ -24,11 +24,10 @@ #include #include #include -#include #include +#include #include #include -#include #include #include #include From fe8280bf3924934cefe0e74b2ded3c3dbacd7760 Mon Sep 17 00:00:00 2001 From: Silvan Jegen Date: Sun, 22 May 2022 14:06:54 +0200 Subject: [PATCH 05/12] Remove now unneeded render_data struct definition --- dwl.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/dwl.c b/dwl.c index 7af7ae2bd..0ea8a05aa 100644 --- a/dwl.c +++ b/dwl.c @@ -270,14 +270,6 @@ struct dwl_input_popup { struct wl_listener focused_surface_unmap; }; -/* Used to move all of the data necessary to render a surface from the top-level - * frame handler to the per-surface render function. */ -struct render_data { - struct wlr_output *output; - struct timespec *when; - int x, y; /* layout-relative */ -}; - /* function declarations */ static void applybounds(Client *c, struct wlr_box *bbox); static void applyexclusive(struct wlr_box *usable_area, uint32_t anchor, From 561acf650a98240f4b722cfa27ff859e3cfbf96b Mon Sep 17 00:00:00 2001 From: Silvan Jegen Date: Sun, 22 May 2022 14:08:53 +0200 Subject: [PATCH 06/12] Fix whitespace issue --- dwl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dwl.c b/dwl.c index 0ea8a05aa..4e8f3f234 100644 --- a/dwl.c +++ b/dwl.c @@ -1044,7 +1044,6 @@ createnotify(struct wl_listener *listener, void *data) } else if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_NONE) return; - /* Allocate a Client for this surface */ c = xdg_surface->data = ecalloc(1, sizeof(*c)); c->surface.xdg = xdg_surface; From 491fa291fabd79ff10d8183dcb9ad38a4865b2e3 Mon Sep 17 00:00:00 2001 From: Silvan Jegen Date: Sun, 22 May 2022 14:18:06 +0200 Subject: [PATCH 07/12] Free input_relay --- dwl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dwl.c b/dwl.c index 4e8f3f234..a7f94f716 100644 --- a/dwl.c +++ b/dwl.c @@ -765,6 +765,11 @@ chvt(const Arg *arg) wlr_session_change_vt(wlr_backend_get_session(backend), arg->ui); } +static void +destroy_input_relay(struct dwl_input_method_relay *relay) { + free(relay); +} + void cleanup(void) { @@ -779,6 +784,8 @@ cleanup(void) wlr_output_layout_destroy(output_layout); wlr_seat_destroy(seat); wl_display_destroy(dpy); + + destroy_input_relay(input_relay); } void From 8841d17ec8e31e0daf7589865e94ed90ccb36593 Mon Sep 17 00:00:00 2001 From: Silvan Jegen Date: Sun, 22 May 2022 14:59:27 +0200 Subject: [PATCH 08/12] Use the LISTEN macro everywhere --- dwl.c | 69 +++++++++++++++++++++++++++-------------------------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/dwl.c b/dwl.c index a7f94f716..050d0a624 100644 --- a/dwl.c +++ b/dwl.c @@ -2268,17 +2268,17 @@ struct dwl_text_input *dwl_text_input_create( wl_list_insert(&relay->text_inputs, &input->link); - input->text_input_enable.notify = handle_text_input_enable; - wl_signal_add(&text_input->events.enable, &input->text_input_enable); + LISTEN(&text_input->events.enable, &input->text_input_enable, + handle_text_input_enable); - input->text_input_commit.notify = handle_text_input_commit; - wl_signal_add(&text_input->events.commit, &input->text_input_commit); + LISTEN(&text_input->events.commit, &input->text_input_commit, + handle_text_input_commit); - input->text_input_disable.notify = handle_text_input_disable; - wl_signal_add(&text_input->events.disable, &input->text_input_disable); + LISTEN(&text_input->events.disable, &input->text_input_disable, + handle_text_input_disable); - input->text_input_destroy.notify = handle_text_input_destroy; - wl_signal_add(&text_input->events.destroy, &input->text_input_destroy); + LISTEN(&text_input->events.destroy, &input->text_input_destroy, + handle_text_input_destroy); input->pending_focused_surface_destroy.notify = handle_pending_focused_surface_destroy; @@ -2504,20 +2504,17 @@ static void handle_im_new_popup_surface(struct wl_listener *listener, void *data wl_list_init(&popup->view_link); - wl_signal_add(&popup->popup_surface->events.map, &popup->popup_map); - popup->popup_map.notify = handle_im_popup_map; + LISTEN(&popup->popup_surface->events.map, &popup->popup_map, + handle_im_popup_map); - wl_signal_add( - &popup->popup_surface->events.unmap, &popup->popup_unmap); - popup->popup_unmap.notify = handle_im_popup_unmap; + LISTEN(&popup->popup_surface->events.unmap, &popup->popup_unmap, + handle_im_popup_unmap); - wl_signal_add( - &popup->popup_surface->events.destroy, &popup->popup_destroy); - popup->popup_destroy.notify = handle_im_popup_destroy; + LISTEN(&popup->popup_surface->events.destroy, &popup->popup_destroy, + 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; + LISTEN(&popup->popup_surface->surface->events.commit, + &popup->popup_surface_commit, handle_im_popup_surface_commit); wl_list_init(&popup->focused_surface_unmap.link); popup->focused_surface_unmap.notify = handle_im_focused_surface_destroy; @@ -2552,18 +2549,18 @@ static void relay_handle_input_method(struct wl_listener *listener, } relay->input_method = input_method; - 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; - wl_signal_add(&relay->input_method->events.destroy, - &relay->input_method_destroy); - relay->input_method_destroy.notify = handle_im_destroy; + + LISTEN(&relay->input_method->events.commit, &relay->input_method_commit, + handle_im_commit); + + LISTEN(&relay->input_method->events.new_popup_surface, + &relay->input_method_new_popup_surface, handle_im_new_popup_surface); + + LISTEN(&relay->input_method->events.grab_keyboard, + &relay->input_method_grab_keyboard, handle_im_grab_keyboard); + + LISTEN(&relay->input_method->events.destroy, &relay->input_method_destroy, + handle_im_destroy); text_input = relay_get_focusable_text_input(relay); if (text_input) { @@ -2577,13 +2574,11 @@ void dwl_input_method_relay_init(struct dwl_input_method_relay *relay) { wl_list_init(&relay->text_inputs); wl_list_init(&relay->input_popups); - relay->text_input_new.notify = relay_handle_text_input; - wl_signal_add(&text_input_manager->events.text_input, - &relay->text_input_new); + LISTEN(&text_input_manager->events.text_input, &relay->text_input_new, + relay_handle_text_input); - relay->input_method_new.notify = relay_handle_input_method; - wl_signal_add(&input_method_manager->events.input_method, - &relay->input_method_new); + LISTEN(&input_method_manager->events.input_method, &relay->input_method_new, + relay_handle_input_method); } void dwl_input_method_relay_set_focus(struct dwl_input_method_relay *relay, From cc9f4a3dc40ebd0c7d602df76baeea56c0a46c7a Mon Sep 17 00:00:00 2001 From: Silvan Jegen Date: Sun, 22 May 2022 15:35:13 +0200 Subject: [PATCH 09/12] Remove now unused input_popup lists --- dwl.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/dwl.c b/dwl.c index 050d0a624..8b7d87cdc 100644 --- a/dwl.c +++ b/dwl.c @@ -217,7 +217,6 @@ typedef struct { */ struct dwl_input_method_relay { struct wl_list text_inputs; // dwl_text_input::link - struct wl_list input_popups; // dwl_input_popup::link struct wlr_input_method_v2 *input_method; // doesn't have to be present struct wl_listener text_input_new; @@ -260,9 +259,6 @@ struct dwl_input_popup { int x, y; bool visible; - struct wl_list link; - struct wl_list view_link; - struct wl_listener popup_map; struct wl_listener popup_unmap; struct wl_listener popup_destroy; @@ -390,8 +386,6 @@ static struct wlr_xdg_activation_v1 *activation; static struct wl_list clients; /* tiling order */ static struct wl_list fstack; /* focus order */ -static struct wl_list input_popups; /* dwl_input_popup::view_link to be rendered for the input method */ - static struct wlr_idle *idle; static struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr; static struct wlr_input_inhibit_manager *input_inhibit_mgr; @@ -2443,8 +2437,6 @@ static void handle_im_popup_destroy(struct wl_listener *listener, void *data) { 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->view_link); - wl_list_remove(&popup->link); free(popup); } @@ -2459,8 +2451,6 @@ static void handle_im_focused_surface_destroy( struct wl_listener *listener, void *data) { struct dwl_input_popup *popup = wl_container_of(listener, popup, focused_surface_unmap); - wl_list_remove(&popup->view_link); - wl_list_init(&popup->view_link); input_popup_update(popup); } @@ -2470,13 +2460,11 @@ static void input_popup_set_focus(struct dwl_input_popup *popup, struct wlr_layer_surface_v1* layer_surface; LayerSurface* layer; - wl_list_remove(&popup->view_link); wl_list_remove(&popup->focused_surface_unmap.link); if (wlr_surface_is_layer_surface(surface)) { layer_surface = wlr_layer_surface_v1_from_wlr_surface(surface); layer = layer_surface_from_wlr_layer_surface_v1(layer_surface); - wl_list_insert(&input_popups, &popup->view_link); wl_signal_add(&layer->layer_surface->events.unmap, &popup->focused_surface_unmap); input_popup_update(popup); @@ -2484,7 +2472,6 @@ static void input_popup_set_focus(struct dwl_input_popup *popup, } client = client_from_wlr_surface(surface); - wl_list_insert(&input_popups, &popup->view_link); wl_signal_add(&client->surface.xdg->events.unmap, &popup->focused_surface_unmap); input_popup_update(popup); @@ -2502,8 +2489,6 @@ static void handle_im_new_popup_surface(struct wl_listener *listener, void *data popup->popup_surface = data; popup->popup_surface->data = popup; - wl_list_init(&popup->view_link); - LISTEN(&popup->popup_surface->events.map, &popup->popup_map, handle_im_popup_map); @@ -2525,8 +2510,6 @@ static void handle_im_new_popup_surface(struct wl_listener *listener, void *data } else { input_popup_set_focus(popup, NULL); } - - wl_list_insert(&relay->input_popups, &popup->link); } @@ -2572,7 +2555,6 @@ static void relay_handle_input_method(struct wl_listener *listener, void dwl_input_method_relay_init(struct dwl_input_method_relay *relay) { wl_list_init(&relay->text_inputs); - wl_list_init(&relay->input_popups); LISTEN(&text_input_manager->events.text_input, &relay->text_input_new, relay_handle_text_input); @@ -2694,8 +2676,6 @@ setup(void) wl_list_init(&clients); wl_list_init(&fstack); - wl_list_init(&input_popups); - idle = wlr_idle_create(dpy); idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy); From 5493e0bbf2dcb6ec49a4661f741cca3a2bdb29f2 Mon Sep 17 00:00:00 2001 From: Silvan Jegen Date: Sun, 12 Jun 2022 14:50:47 +0200 Subject: [PATCH 10/12] dwl: use a dedicated layer for input popups Otherwise we seem to have conflicts with other floating clients. --- dwl.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index e0a9c53fc..d17c1a68c 100644 --- a/dwl.c +++ b/dwl.c @@ -71,7 +71,7 @@ /* enums */ enum { CurNormal, CurMove, CurResize }; /* cursor */ enum { XDGShell, LayerShell, X11Managed, X11Unmanaged }; /* client types */ -enum { LyrBg, LyrBottom, LyrTop, LyrOverlay, LyrTile, LyrFloat, LyrNoFocus, NUM_LAYERS }; /* scene layers */ +enum { LyrBg, LyrBottom, LyrTop, LyrOverlay, LyrTile, LyrFloat, LyrNoFocus, LyrInputPopup, NUM_LAYERS }; /* scene layers */ #ifdef XWAYLAND enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar, NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */ @@ -2432,7 +2432,7 @@ static void handle_im_popup_map(struct wl_listener *listener, void *data) { struct dwl_input_popup *popup = wl_container_of(listener, popup, popup_map); - popup->scene = &wlr_scene_tree_create(layers[LyrFloat])->node; + popup->scene = &wlr_scene_tree_create(layers[LyrInputPopup])->node; popup->scene_surface = wlr_scene_subsurface_tree_create(popup->scene, popup->popup_surface->surface); popup->scene_surface->data = popup; @@ -2648,6 +2648,7 @@ setup(void) layers[LyrTop] = &wlr_scene_tree_create(&scene->node)->node; layers[LyrOverlay] = &wlr_scene_tree_create(&scene->node)->node; layers[LyrNoFocus] = &wlr_scene_tree_create(&scene->node)->node; + layers[LyrInputPopup] = &wlr_scene_tree_create(&scene->node)->node; /* Create a renderer with the default implementation */ if (!(drw = wlr_renderer_autocreate(backend))) @@ -3070,7 +3071,8 @@ xytonode(double x, double y, struct wlr_surface **psurface, Client *c = NULL; LayerSurface *l = NULL; const int *layer; - int focus_order[] = { LyrOverlay, LyrTop, LyrFloat, LyrTile, LyrBottom, LyrBg }; + int focus_order[] = { LyrOverlay, LyrTop, LyrFloat, LyrInputPopup, LyrTile, + LyrBottom, LyrBg }; for (layer = focus_order; layer < END(focus_order); layer++) { if ((node = wlr_scene_node_at(layers[*layer], x, y, nx, ny))) { From a92504d8bf255322b2e00c249ab80c667b48c0b5 Mon Sep 17 00:00:00 2001 From: Silvan Jegen Date: Sun, 26 Jun 2022 13:48:08 +0200 Subject: [PATCH 11/12] dwl: don't destroy popup scene on unmap Instead we disable it and destroy it at destroy time. --- dwl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index d17c1a68c..4376e5452 100644 --- a/dwl.c +++ b/dwl.c @@ -2447,7 +2447,7 @@ static void handle_im_popup_unmap(struct wl_listener *listener, void *data) { wl_container_of(listener, popup, popup_unmap); input_popup_update(popup); - wlr_scene_node_destroy(popup->scene); + wlr_scene_node_set_enabled(popup->scene, 0); } static void handle_im_popup_destroy(struct wl_listener *listener, void *data) { @@ -2458,6 +2458,8 @@ static void handle_im_popup_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&popup->popup_destroy.link); wl_list_remove(&popup->popup_unmap.link); wl_list_remove(&popup->popup_map.link); + wlr_scene_node_destroy(popup->scene); + free(popup); } From f9bedb79087c16c2bf06cb8e9085699a1300944c Mon Sep 17 00:00:00 2001 From: Silvan Jegen Date: Sat, 25 Jun 2022 15:58:31 +0200 Subject: [PATCH 12/12] dwl: don't show input popup if client is not enabled We link clients and their input popups which lets us disable the popups' scene nodes in case the associated client's scene is not enabled on a tag. --- dwl.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index 4376e5452..1fb1c3786 100644 --- a/dwl.c +++ b/dwl.c @@ -100,6 +100,7 @@ typedef struct { struct wlr_scene_node *scene_surface; struct wl_list link; struct wl_list flink; + struct wl_list input_popups; /* dwl_input_popup::client_link */ union { struct wlr_xdg_surface *xdg; struct wlr_xwayland_surface *xwayland; @@ -257,6 +258,8 @@ struct dwl_input_popup { struct wlr_scene_node *scene; struct wlr_scene_node *scene_surface; + struct wl_list client_link; + int x, y; bool visible; @@ -563,9 +566,14 @@ applyrules(Client *c) void arrange(Monitor *m) { + struct dwl_input_popup *popup; Client *c; - wl_list_for_each(c, &clients, link) + wl_list_for_each(c, &clients, link) { wlr_scene_node_set_enabled(c->scene, VISIBLEON(c, c->mon)); + wl_list_for_each(popup, &c->input_popups, client_link) { + wlr_scene_node_set_enabled(popup->scene, VISIBLEON(c, c->mon)); + } + } if (m->lt[m->sellt]->arrange) m->lt[m->sellt]->arrange(m); @@ -1062,6 +1070,8 @@ createnotify(struct wl_listener *listener, void *data) LISTEN(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen, fullscreennotify); c->isfullscreen = 0; + + wl_list_init(&c->input_popups); } void @@ -2458,6 +2468,7 @@ static void handle_im_popup_destroy(struct wl_listener *listener, void *data) { 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->client_link); wlr_scene_node_destroy(popup->scene); free(popup); @@ -2500,10 +2511,10 @@ static void input_popup_set_focus(struct dwl_input_popup *popup, input_popup_update(popup); } - - static void handle_im_new_popup_surface(struct wl_listener *listener, void *data) { struct dwl_text_input* text_input; + struct wlr_surface* focused_surface; + Client* client; struct dwl_input_method_relay *relay = wl_container_of(listener, relay, input_method_new_popup_surface); @@ -2529,6 +2540,9 @@ static void handle_im_new_popup_surface(struct wl_listener *listener, void *data text_input = relay_get_focused_text_input(relay); if (text_input) { + focused_surface = text_input->input->focused_surface; + client = client_from_wlr_surface(focused_surface); + wl_list_insert(&client->input_popups, &popup->client_link); input_popup_set_focus(popup, text_input->input->focused_surface); } else { input_popup_set_focus(popup, NULL);