diff --git a/CHANGELOG.md b/CHANGELOG.md index 31bd2d1b..86939342 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,7 +43,8 @@ copied to. The default is `primary`, which corresponds to the behavior in older foot releases (https://codeberg.org/dnkl/foot/issues/288). - +* IME popup location support: foot now sends the location of the cursor + so any popup can be displayed near the text that is being typed. ### Changed @@ -114,6 +115,7 @@ * [pc](https://codeberg.org/pc) * [FollieHiyuki](https://codeberg.org/FollieHiyuki) * jbeich +* [tdeo](https://codeberg.org/tdeo) ## 1.6.2 diff --git a/ime.c b/ime.c index c46da897..928a9769 100644 --- a/ime.c +++ b/ime.c @@ -327,6 +327,36 @@ ime_reset(struct seat *seat) ime_reset_commit(seat); } +void +ime_send_cursor_rect(struct seat *seat, struct terminal *term) +{ + if (unlikely(seat->wayl->text_input_manager == NULL)) + return; + + if (!term->ime.enabled) + return; + + if (seat->ime.cursor_rect.pending.x == seat->ime.cursor_rect.sent.x && + seat->ime.cursor_rect.pending.y == seat->ime.cursor_rect.sent.y && + seat->ime.cursor_rect.pending.width == seat->ime.cursor_rect.sent.width && + seat->ime.cursor_rect.pending.height == seat->ime.cursor_rect.sent.height) + { + return; + } + + zwp_text_input_v3_set_cursor_rectangle( + seat->wl_text_input, + seat->ime.cursor_rect.pending.x / term->scale, + seat->ime.cursor_rect.pending.y / term->scale, + seat->ime.cursor_rect.pending.width / term->scale, + seat->ime.cursor_rect.pending.height / term->scale); + + zwp_text_input_v3_commit(seat->wl_text_input); + seat->ime.serial++; + + seat->ime.cursor_rect.sent = seat->ime.cursor_rect.pending; +} + void ime_enable(struct seat *seat) { @@ -347,6 +377,15 @@ ime_enable(struct seat *seat) ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL); + zwp_text_input_v3_set_cursor_rectangle( + seat->wl_text_input, + seat->ime.cursor_rect.pending.x / term->scale, + seat->ime.cursor_rect.pending.y / term->scale, + seat->ime.cursor_rect.pending.width / term->scale, + seat->ime.cursor_rect.pending.height / term->scale); + + seat->ime.cursor_rect.sent = seat->ime.cursor_rect.pending; + zwp_text_input_v3_commit(seat->wl_text_input); seat->ime.serial++; } @@ -364,6 +403,42 @@ ime_disable(struct seat *seat) seat->ime.serial++; } +void +ime_update_cursor_rect(struct seat *seat, struct terminal *term) +{ + /* Set in render_ime_preedit() */ + if (term->ime.preedit.cells != NULL) + goto update; + + /* Set in render_search_box() */ + if (term->is_searching) + goto update; + + int x, y, width, height; + int col = term->grid->cursor.point.col; + int row = term->grid->cursor.point.row; + row += term->grid->offset; + row -= term->grid->view; + row &= term->grid->num_rows - 1; + x = term->margins.left + col * term->cell_width; + y = term->margins.top + row * term->cell_height; + + if (term->cursor_style == CURSOR_BAR) + width = 1; + else + width = term->cell_width; + + height = term->cell_height; + + seat->ime.cursor_rect.pending.x = x; + seat->ime.cursor_rect.pending.y = y; + seat->ime.cursor_rect.pending.width = width; + seat->ime.cursor_rect.pending.height = height; + +update: + ime_send_cursor_rect(seat, term); +} + const struct zwp_text_input_v3_listener text_input_listener = { .enter = &enter, .leave = &leave, @@ -377,9 +452,11 @@ const struct zwp_text_input_v3_listener text_input_listener = { void ime_enable(struct seat *seat) {} void ime_disable(struct seat *seat) {} +void ime_update_cursor_rect(struct seat *seat, struct terminal *term) {} void ime_reset_preedit(struct seat *seat) {} void ime_reset_commit(struct seat *seat) {} void ime_reset(struct seat *seat) {} +void ime_send_cursor_rect(struct seat *seat, struct terminal *term) {} #endif diff --git a/ime.h b/ime.h index 7036bbde..ed2fe507 100644 --- a/ime.h +++ b/ime.h @@ -9,10 +9,13 @@ extern const struct zwp_text_input_v3_listener text_input_listener; #endif /* FOOT_IME_ENABLED */ struct seat; +struct terminal; void ime_enable(struct seat *seat); void ime_disable(struct seat *seat); +void ime_update_cursor_rect(struct seat *seat, struct terminal *term); void ime_reset_preedit(struct seat *seat); void ime_reset_commit(struct seat *seat); void ime_reset(struct seat *seat); +void ime_send_cursor_rect(struct seat *seat, struct terminal *term); diff --git a/render.c b/render.c index 6d7a54fa..aa415a18 100644 --- a/render.c +++ b/render.c @@ -34,6 +34,7 @@ #include "shm.h" #include "util.h" #include "xmalloc.h" +#include "ime.h" #define TIME_SCROLL_DAMAGE 0 @@ -1206,6 +1207,7 @@ render_ime_preedit(struct terminal *term, struct buffer *buf) struct fcft_font *font = attrs_to_font(term, &start_cell->attrs); draw_bar(term, buf->pix[0], font, &cursor_color, x, y); } + term_ime_set_cursor_rect(term, x, y, 1, term->cell_height); } else if (end > start) { @@ -1214,6 +1216,9 @@ render_ime_preedit(struct terminal *term, struct buffer *buf) int cols = end - start; draw_unfocused_block(term, buf->pix[0], &cursor_color, x, y, cols); } + + term_ime_set_cursor_rect( + term, x, y, (end - start) * term->cell_width, term->cell_height); } } @@ -2275,6 +2280,9 @@ render_search_box(struct terminal *term) unsigned long cookie = shm_cookie_search(term); struct buffer *buf = shm_get_buffer(term->wl->shm, width, height, cookie, false, 1); +#define WINDOW_X(x) (margin + x) +#define WINDOW_Y(y) (term->height - margin - height + y) + /* Background - yellow on empty/match, red on mismatch */ pixman_color_t color = color_hex_to_pixman( term->search.match_len == text_len @@ -2362,6 +2370,7 @@ render_search_box(struct terminal *term) width = widths[i], next_cell_idx += width) { + /* Convert subsurface coordinates to window coordinates*/ /* Render cursor */ if (i == term->search.cursor) { #if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED @@ -2390,6 +2399,9 @@ render_search_box(struct terminal *term) /* Bar-styled cursor, if in the visible area */ if (start >= 0 && start <= visible_cells) draw_bar(term, buf->pix[0], font, &fg, x + start * term->cell_width, y); + term_ime_set_cursor_rect(term, + WINDOW_X(x + start * term->cell_width), WINDOW_Y(y), + 1, term->cell_height); } else { /* Underline everything before and after the cursor */ int count1 = min(start, cells_left); @@ -2405,6 +2417,9 @@ render_search_box(struct terminal *term) draw_unfocused_block( term, buf->pix[0], &fg, x + start * term->cell_width, y, end - start); } + term_ime_set_cursor_rect(term, + WINDOW_X(x + start * term->cell_width), WINDOW_Y(y), + term->cell_width * (end - start), term->cell_height); } } else if (!have_preedit) #endif @@ -2413,6 +2428,8 @@ render_search_box(struct terminal *term) xassert(cell_idx >= glyph_offset); xassert(cell_idx <= glyph_offset + visible_cells); draw_bar(term, buf->pix[0], font, &fg, x, y); + term_ime_set_cursor_rect( + term, WINDOW_X(x), WINDOW_Y(y), 1, term->cell_height); } } @@ -2465,8 +2482,11 @@ render_search_box(struct terminal *term) /* Already rendered */; else #endif - if (term->search.cursor >= term->search.len) + if (term->search.cursor >= term->search.len) { draw_bar(term, buf->pix[0], font, &fg, x, y); + term_ime_set_cursor_rect( + term, WINDOW_X(x), WINDOW_Y(y), 1, term->cell_height); + } quirk_weston_subsurface_desync_on(term->window->search_sub_surface); @@ -2493,6 +2513,8 @@ render_search_box(struct terminal *term) #if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED free(text); #endif +#undef WINDOW_X +#undef WINDOW_Y } static void @@ -2543,6 +2565,10 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da if (search && term->is_searching) render_search_box(term); + tll_foreach(term->wl->seats, it) + if (it->item.kbd_focus == term) + ime_update_cursor_rect(&it->item, term); + if (grid && (!term->delayed_render_timer.is_armed || csd || search)) grid_render(term); } @@ -2987,6 +3013,9 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data) render_update_title(term); if (search) render_search_box(term); + tll_foreach(term->wl->seats, it) + if (it->item.kbd_focus == term) + ime_update_cursor_rect(&it->item, term); if (grid) grid_render(term); } else { diff --git a/terminal.c b/terminal.c index 5344cc53..6f8130fa 100644 --- a/terminal.c +++ b/terminal.c @@ -2943,3 +2943,19 @@ term_ime_reset(struct terminal *term) } #endif } + +void +term_ime_set_cursor_rect(struct terminal *term, int x, int y, int width, + int height) +{ +#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED + tll_foreach(term->wl->seats, it) { + if (it->item.kbd_focus == term) { + it->item.ime.cursor_rect.pending.x = x; + it->item.ime.cursor_rect.pending.y = y; + it->item.ime.cursor_rect.pending.width = width; + it->item.ime.cursor_rect.pending.height = height; + } + } +#endif +} diff --git a/terminal.h b/terminal.h index 6f1ba1c1..17873c40 100644 --- a/terminal.h +++ b/terminal.h @@ -644,3 +644,5 @@ bool term_ime_is_enabled(const struct terminal *term); void term_ime_enable(struct terminal *term); void term_ime_disable(struct terminal *term); void term_ime_reset(struct terminal *term); +void term_ime_set_cursor_rect( + struct terminal *term, int x, int y, int width, int height); diff --git a/wayland.h b/wayland.h index b849249e..bcd8dcab 100644 --- a/wayland.h +++ b/wayland.h @@ -139,6 +139,13 @@ struct button_tracker { bool send_to_client; /* Only valid when surface is the main grid surface */ }; +struct rect { + int x; + int y; + int width; + int height; +}; + struct seat { struct wayland *wayl; struct wl_seat *wl_seat; @@ -236,6 +243,11 @@ struct seat { /* Input Method Editor */ struct zwp_text_input_v3 *wl_text_input; struct { + struct { + struct rect pending; + struct rect sent; + } cursor_rect; + struct { struct { char *text;