mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -05:00
ime: move preedit state from terminal struct to the seat struct
This ensures different seat’s don’t step on each others IME pre-edit state. It also removes most dependencies on having a valid term pointer for many IME operations. We’re still not all the way, since we support disabling IME with a private mode, which is per terminal, not seat. Thus, we still require the seat to have keyboard focus on one of our windows. Closes #324. But note that *rendering* of multiple seat’s IME pre-edit strings is still broken.
This commit is contained in:
parent
eb3f9f14b0
commit
e8ffb05bc7
8 changed files with 178 additions and 136 deletions
148
ime.c
148
ime.c
|
|
@ -37,10 +37,6 @@ leave(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
|
||||||
struct seat *seat = data;
|
struct seat *seat = data;
|
||||||
LOG_DBG("leave: seat=%s", seat->name);
|
LOG_DBG("leave: seat=%s", seat->name);
|
||||||
|
|
||||||
struct terminal *term = seat->kbd_focus;
|
|
||||||
if (term != NULL)
|
|
||||||
term_ime_reset(term);
|
|
||||||
|
|
||||||
ime_disable(seat);
|
ime_disable(seat);
|
||||||
seat->ime.focused = false;
|
seat->ime.focused = false;
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +49,7 @@ preedit_string(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
|
||||||
|
|
||||||
struct seat *seat = data;
|
struct seat *seat = data;
|
||||||
|
|
||||||
ime_reset_preedit(seat);
|
ime_reset_pending_preedit(seat);
|
||||||
|
|
||||||
if (text != NULL) {
|
if (text != NULL) {
|
||||||
seat->ime.preedit.pending.text = xstrdup(text);
|
seat->ime.preedit.pending.text = xstrdup(text);
|
||||||
|
|
@ -70,7 +66,7 @@ commit_string(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
|
||||||
|
|
||||||
struct seat *seat = data;
|
struct seat *seat = data;
|
||||||
|
|
||||||
ime_reset_commit(seat);
|
ime_reset_pending_commit(seat);
|
||||||
|
|
||||||
if (text != NULL)
|
if (text != NULL)
|
||||||
seat->ime.commit.pending.text = xstrdup(text);
|
seat->ime.commit.pending.text = xstrdup(text);
|
||||||
|
|
@ -107,6 +103,7 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
|
||||||
|
|
||||||
LOG_DBG("done: serial=%u", serial);
|
LOG_DBG("done: serial=%u", serial);
|
||||||
struct seat *seat = data;
|
struct seat *seat = data;
|
||||||
|
struct terminal *term = seat->kbd_focus;
|
||||||
|
|
||||||
if (seat->ime.serial != serial) {
|
if (seat->ime.serial != serial) {
|
||||||
LOG_DBG("IME serial mismatch: expected=0x%08x, got 0x%08x",
|
LOG_DBG("IME serial mismatch: expected=0x%08x, got 0x%08x",
|
||||||
|
|
@ -114,16 +111,16 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
xassert(seat->kbd_focus);
|
|
||||||
struct terminal *term = seat->kbd_focus;
|
|
||||||
|
|
||||||
/* 1. Delete existing pre-edit text */
|
/* 1. Delete existing pre-edit text */
|
||||||
if (term->ime.preedit.cells != NULL) {
|
if (seat->ime.preedit.cells != NULL) {
|
||||||
term_ime_reset(term);
|
ime_reset_preedit(seat);
|
||||||
if (term->is_searching)
|
|
||||||
render_refresh_search(term);
|
if (term != NULL) {
|
||||||
else
|
if (term->is_searching)
|
||||||
render_refresh(term);
|
render_refresh_search(term);
|
||||||
|
else
|
||||||
|
render_refresh(term);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -139,12 +136,14 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
|
||||||
const char *text = seat->ime.commit.pending.text;
|
const char *text = seat->ime.commit.pending.text;
|
||||||
size_t len = strlen(text);
|
size_t len = strlen(text);
|
||||||
|
|
||||||
if (term->is_searching) {
|
if (term != NULL) {
|
||||||
search_add_chars(term, text, len);
|
if (term->is_searching) {
|
||||||
render_refresh_search(term);
|
search_add_chars(term, text, len);
|
||||||
} else
|
render_refresh_search(term);
|
||||||
term_to_slave(term, text, len);
|
} else
|
||||||
ime_reset_commit(seat);
|
term_to_slave(term, text, len);
|
||||||
|
}
|
||||||
|
ime_reset_pending_commit(seat);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 4. Calculate surrounding text to send - not supported */
|
/* 4. Calculate surrounding text to send - not supported */
|
||||||
|
|
@ -155,41 +154,41 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
if (wchars == 0 || wchars == (size_t)-1) {
|
if (wchars == 0 || wchars == (size_t)-1) {
|
||||||
ime_reset_preedit(seat);
|
ime_reset_pending_preedit(seat);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* First, convert to unicode */
|
/* First, convert to unicode */
|
||||||
term->ime.preedit.text = xmalloc((wchars + 1) * sizeof(wchar_t));
|
seat->ime.preedit.text = xmalloc((wchars + 1) * sizeof(wchar_t));
|
||||||
mbstowcs(term->ime.preedit.text, seat->ime.preedit.pending.text, wchars);
|
mbstowcs(seat->ime.preedit.text, seat->ime.preedit.pending.text, wchars);
|
||||||
term->ime.preedit.text[wchars] = L'\0';
|
seat->ime.preedit.text[wchars] = L'\0';
|
||||||
|
|
||||||
/* Next, count number of cells needed */
|
/* Next, count number of cells needed */
|
||||||
size_t cell_count = 0;
|
size_t cell_count = 0;
|
||||||
size_t widths[wchars + 1];
|
size_t widths[wchars + 1];
|
||||||
|
|
||||||
for (size_t i = 0; i < wchars; i++) {
|
for (size_t i = 0; i < wchars; i++) {
|
||||||
int width = max(wcwidth(term->ime.preedit.text[i]), 1);
|
int width = max(wcwidth(seat->ime.preedit.text[i]), 1);
|
||||||
widths[i] = width;
|
widths[i] = width;
|
||||||
cell_count += width;
|
cell_count += width;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate cells */
|
/* Allocate cells */
|
||||||
term->ime.preedit.cells = xmalloc(
|
seat->ime.preedit.cells = xmalloc(
|
||||||
cell_count * sizeof(term->ime.preedit.cells[0]));
|
cell_count * sizeof(seat->ime.preedit.cells[0]));
|
||||||
term->ime.preedit.count = cell_count;
|
seat->ime.preedit.count = cell_count;
|
||||||
|
|
||||||
/* Populate cells */
|
/* Populate cells */
|
||||||
for (size_t i = 0, cell_idx = 0; i < wchars; i++) {
|
for (size_t i = 0, cell_idx = 0; i < wchars; i++) {
|
||||||
struct cell *cell = &term->ime.preedit.cells[cell_idx];
|
struct cell *cell = &seat->ime.preedit.cells[cell_idx];
|
||||||
|
|
||||||
int width = widths[i];
|
int width = widths[i];
|
||||||
|
|
||||||
cell->wc = term->ime.preedit.text[i];
|
cell->wc = seat->ime.preedit.text[i];
|
||||||
cell->attrs = (struct attributes){.clean = 0};
|
cell->attrs = (struct attributes){.clean = 0};
|
||||||
|
|
||||||
for (int j = 1; j < width; j++) {
|
for (int j = 1; j < width; j++) {
|
||||||
cell = &term->ime.preedit.cells[cell_idx + j];
|
cell = &seat->ime.preedit.cells[cell_idx + j];
|
||||||
cell->wc = CELL_MULT_COL_SPACER;
|
cell->wc = CELL_MULT_COL_SPACER;
|
||||||
cell->attrs = (struct attributes){.clean = 1};
|
cell->attrs = (struct attributes){.clean = 1};
|
||||||
}
|
}
|
||||||
|
|
@ -206,18 +205,18 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
|
||||||
/* Note: docs says *both* begin and end should be -1,
|
/* Note: docs says *both* begin and end should be -1,
|
||||||
* but what else can we do if only one is -1? */
|
* but what else can we do if only one is -1? */
|
||||||
LOG_DBG("pre-edit cursor is hidden");
|
LOG_DBG("pre-edit cursor is hidden");
|
||||||
term->ime.preedit.cursor.hidden = true;
|
seat->ime.preedit.cursor.hidden = true;
|
||||||
term->ime.preedit.cursor.start = -1;
|
seat->ime.preedit.cursor.start = -1;
|
||||||
term->ime.preedit.cursor.end = -1;
|
seat->ime.preedit.cursor.end = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (seat->ime.preedit.pending.cursor_begin == byte_len &&
|
else if (seat->ime.preedit.pending.cursor_begin == byte_len &&
|
||||||
seat->ime.preedit.pending.cursor_end == byte_len)
|
seat->ime.preedit.pending.cursor_end == byte_len)
|
||||||
{
|
{
|
||||||
/* Cursor is *after* the entire pre-edit string */
|
/* Cursor is *after* the entire pre-edit string */
|
||||||
term->ime.preedit.cursor.hidden = false;
|
seat->ime.preedit.cursor.hidden = false;
|
||||||
term->ime.preedit.cursor.start = cell_count;
|
seat->ime.preedit.cursor.start = cell_count;
|
||||||
term->ime.preedit.cursor.end = cell_count;
|
seat->ime.preedit.cursor.end = cell_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
|
|
@ -271,7 +270,7 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
|
||||||
|
|
||||||
/* Expand cursor end to end of glyph */
|
/* Expand cursor end to end of glyph */
|
||||||
while (cell_end > cell_begin && cell_end < cell_count &&
|
while (cell_end > cell_begin && cell_end < cell_count &&
|
||||||
term->ime.preedit.cells[cell_end].wc == CELL_MULT_COL_SPACER)
|
seat->ime.preedit.cells[cell_end].wc == CELL_MULT_COL_SPACER)
|
||||||
{
|
{
|
||||||
cell_end++;
|
cell_end++;
|
||||||
}
|
}
|
||||||
|
|
@ -284,50 +283,65 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
|
||||||
xassert(cell_end >= 0);
|
xassert(cell_end >= 0);
|
||||||
xassert(cell_end <= cell_count);
|
xassert(cell_end <= cell_count);
|
||||||
|
|
||||||
term->ime.preedit.cursor.hidden = false;
|
seat->ime.preedit.cursor.hidden = false;
|
||||||
term->ime.preedit.cursor.start = cell_begin;
|
seat->ime.preedit.cursor.start = cell_begin;
|
||||||
term->ime.preedit.cursor.end = cell_end;
|
seat->ime.preedit.cursor.end = cell_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Underline pre-edit string that is *not* covered by the cursor */
|
/* Underline pre-edit string that is *not* covered by the cursor */
|
||||||
bool hidden = term->ime.preedit.cursor.hidden;
|
bool hidden = seat->ime.preedit.cursor.hidden;
|
||||||
int start = term->ime.preedit.cursor.start;
|
int start = seat->ime.preedit.cursor.start;
|
||||||
int end = term->ime.preedit.cursor.end;
|
int end = seat->ime.preedit.cursor.end;
|
||||||
|
|
||||||
for (size_t i = 0, cell_idx = 0; i < wchars; cell_idx += widths[i], i++) {
|
for (size_t i = 0, cell_idx = 0; i < wchars; cell_idx += widths[i], i++) {
|
||||||
if (hidden || start == end || cell_idx < start || cell_idx >= end) {
|
if (hidden || start == end || cell_idx < start || cell_idx >= end) {
|
||||||
struct cell *cell = &term->ime.preedit.cells[cell_idx];
|
struct cell *cell = &seat->ime.preedit.cells[cell_idx];
|
||||||
cell->attrs.underline = true;
|
cell->attrs.underline = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ime_reset_preedit(seat);
|
ime_reset_pending_preedit(seat);
|
||||||
|
|
||||||
if (term->is_searching)
|
if (term != NULL) {
|
||||||
render_refresh_search(term);
|
if (term->is_searching)
|
||||||
else
|
render_refresh_search(term);
|
||||||
render_refresh(term);
|
else
|
||||||
|
render_refresh(term);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ime_reset_preedit(struct seat *seat)
|
ime_reset_pending_preedit(struct seat *seat)
|
||||||
{
|
{
|
||||||
free(seat->ime.preedit.pending.text);
|
free(seat->ime.preedit.pending.text);
|
||||||
seat->ime.preedit.pending.text = NULL;
|
seat->ime.preedit.pending.text = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ime_reset_commit(struct seat *seat)
|
ime_reset_pending_commit(struct seat *seat)
|
||||||
{
|
{
|
||||||
free(seat->ime.commit.pending.text);
|
free(seat->ime.commit.pending.text);
|
||||||
seat->ime.commit.pending.text = NULL;
|
seat->ime.commit.pending.text = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ime_reset(struct seat *seat)
|
ime_reset_pending(struct seat *seat)
|
||||||
{
|
{
|
||||||
ime_reset_preedit(seat);
|
ime_reset_pending_preedit(seat);
|
||||||
ime_reset_commit(seat);
|
ime_reset_pending_commit(seat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ime_reset_preedit(struct seat *seat)
|
||||||
|
{
|
||||||
|
if (seat->ime.preedit.cells == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
free(seat->ime.preedit.text);
|
||||||
|
free(seat->ime.preedit.cells);
|
||||||
|
seat->ime.preedit.text = NULL;
|
||||||
|
seat->ime.preedit.cells = NULL;
|
||||||
|
seat->ime.preedit.count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -339,7 +353,7 @@ ime_send_cursor_rect(struct seat *seat, struct terminal *term)
|
||||||
if (!seat->ime.focused)
|
if (!seat->ime.focused)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!term->ime.enabled)
|
if (!term->ime_enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (seat->ime.cursor_rect.pending.x == seat->ime.cursor_rect.sent.x &&
|
if (seat->ime.cursor_rect.pending.x == seat->ime.cursor_rect.sent.x &&
|
||||||
|
|
@ -370,15 +384,21 @@ ime_enable(struct seat *seat)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
struct terminal *term = seat->kbd_focus;
|
struct terminal *term = seat->kbd_focus;
|
||||||
|
|
||||||
|
/* TODO: we’ve actaully seen text-input::enter without first
|
||||||
|
* seeing keyboard::enter... so perhaps we should check for this,
|
||||||
|
* and... do what? Ignore IME completely, or do we need to call
|
||||||
|
* ime_enable() from keyboard::enter too? */
|
||||||
xassert(term != NULL);
|
xassert(term != NULL);
|
||||||
|
|
||||||
if (!seat->ime.focused)
|
if (!seat->ime.focused)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!term->ime.enabled)
|
if (!term->ime_enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ime_reset(seat);
|
ime_reset_pending(seat);
|
||||||
|
ime_reset_preedit(seat);
|
||||||
|
|
||||||
zwp_text_input_v3_enable(seat->wl_text_input);
|
zwp_text_input_v3_enable(seat->wl_text_input);
|
||||||
zwp_text_input_v3_set_content_type(
|
zwp_text_input_v3_set_content_type(
|
||||||
|
|
@ -408,7 +428,8 @@ ime_disable(struct seat *seat)
|
||||||
if (!seat->ime.focused)
|
if (!seat->ime.focused)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ime_reset(seat);
|
ime_reset_pending(seat);
|
||||||
|
ime_reset_preedit(seat);
|
||||||
|
|
||||||
zwp_text_input_v3_disable(seat->wl_text_input);
|
zwp_text_input_v3_disable(seat->wl_text_input);
|
||||||
zwp_text_input_v3_commit(seat->wl_text_input);
|
zwp_text_input_v3_commit(seat->wl_text_input);
|
||||||
|
|
@ -419,7 +440,7 @@ void
|
||||||
ime_update_cursor_rect(struct seat *seat, struct terminal *term)
|
ime_update_cursor_rect(struct seat *seat, struct terminal *term)
|
||||||
{
|
{
|
||||||
/* Set in render_ime_preedit() */
|
/* Set in render_ime_preedit() */
|
||||||
if (term->ime.preedit.cells != NULL)
|
if (seat->ime.preedit.cells != NULL)
|
||||||
goto update;
|
goto update;
|
||||||
|
|
||||||
/* Set in render_search_box() */
|
/* Set in render_search_box() */
|
||||||
|
|
@ -466,9 +487,10 @@ void ime_enable(struct seat *seat) {}
|
||||||
void ime_disable(struct seat *seat) {}
|
void ime_disable(struct seat *seat) {}
|
||||||
void ime_update_cursor_rect(struct seat *seat, struct terminal *term) {}
|
void ime_update_cursor_rect(struct seat *seat, struct terminal *term) {}
|
||||||
|
|
||||||
|
void ime_reset_pending_preedit(struct seat *seat) {}
|
||||||
|
void ime_reset_pending_commit(struct seat *seat) {}
|
||||||
|
void ime_reset_pending(struct seat *seat) {}
|
||||||
void ime_reset_preedit(struct seat *seat) {}
|
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) {}
|
void ime_send_cursor_rect(struct seat *seat, struct terminal *term) {}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
5
ime.h
5
ime.h
|
|
@ -15,7 +15,8 @@ void ime_enable(struct seat *seat);
|
||||||
void ime_disable(struct seat *seat);
|
void ime_disable(struct seat *seat);
|
||||||
void ime_update_cursor_rect(struct seat *seat, struct terminal *term);
|
void ime_update_cursor_rect(struct seat *seat, struct terminal *term);
|
||||||
|
|
||||||
|
void ime_reset_pending_preedit(struct seat *seat);
|
||||||
|
void ime_reset_pending_commit(struct seat *seat);
|
||||||
|
void ime_reset_pending(struct seat *seat);
|
||||||
void ime_reset_preedit(struct seat *seat);
|
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);
|
void ime_send_cursor_rect(struct seat *seat, struct terminal *term);
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,7 @@ void cmd_scrollback_down(struct terminal *term, int rows) {}
|
||||||
|
|
||||||
void ime_enable(struct seat *seat) {}
|
void ime_enable(struct seat *seat) {}
|
||||||
void ime_disable(struct seat *seat) {}
|
void ime_disable(struct seat *seat) {}
|
||||||
|
void ime_reset_preedit(struct seat *seat) {}
|
||||||
|
|
||||||
void
|
void
|
||||||
notify_notify(const struct terminal *term, const char *title, const char *body)
|
notify_notify(const struct terminal *term, const char *title, const char *body)
|
||||||
|
|
|
||||||
92
render.c
92
render.c
|
|
@ -1114,12 +1114,12 @@ render_sixel_images(struct terminal *term, pixman_image_t *pix,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
render_ime_preedit(struct terminal *term, struct buffer *buf)
|
|
||||||
{
|
|
||||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||||
|
static void
|
||||||
if (likely(term->ime.preedit.cells == NULL))
|
render_ime_preedit_for_seat(struct terminal *term, struct seat *seat,
|
||||||
|
struct buffer *buf)
|
||||||
|
{
|
||||||
|
if (likely(seat->ime.preedit.cells == NULL))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (unlikely(term->is_searching))
|
if (unlikely(term->is_searching))
|
||||||
|
|
@ -1135,10 +1135,10 @@ render_ime_preedit(struct terminal *term, struct buffer *buf)
|
||||||
if (cursor.row < 0 || cursor.row >= term->rows)
|
if (cursor.row < 0 || cursor.row >= term->rows)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int cells_needed = term->ime.preedit.count;
|
int cells_needed = seat->ime.preedit.count;
|
||||||
|
|
||||||
if (term->ime.preedit.cursor.start == cells_needed &&
|
if (seat->ime.preedit.cursor.start == cells_needed &&
|
||||||
term->ime.preedit.cursor.end == cells_needed)
|
seat->ime.preedit.cursor.end == cells_needed)
|
||||||
{
|
{
|
||||||
/* Cursor will be drawn *after* the pre-edit string, i.e. in
|
/* Cursor will be drawn *after* the pre-edit string, i.e. in
|
||||||
* the cell *after*. This means we need to copy, and dirty,
|
* the cell *after*. This means we need to copy, and dirty,
|
||||||
|
|
@ -1160,8 +1160,8 @@ render_ime_preedit(struct terminal *term, struct buffer *buf)
|
||||||
col_idx -= cells_used - cells_left;
|
col_idx -= cells_used - cells_left;
|
||||||
|
|
||||||
if (cells_needed > cells_used) {
|
if (cells_needed > cells_used) {
|
||||||
int start = term->ime.preedit.cursor.start;
|
int start = seat->ime.preedit.cursor.start;
|
||||||
int end = term->ime.preedit.cursor.end;
|
int end = seat->ime.preedit.cursor.end;
|
||||||
|
|
||||||
if (start == end) {
|
if (start == end) {
|
||||||
/* Ensure *end* of pre-edit string is visible */
|
/* Ensure *end* of pre-edit string is visible */
|
||||||
|
|
@ -1177,7 +1177,7 @@ render_ime_preedit(struct terminal *term, struct buffer *buf)
|
||||||
|
|
||||||
/* Make sure we don't start in the middle of a character */
|
/* Make sure we don't start in the middle of a character */
|
||||||
while (ime_ofs < cells_needed &&
|
while (ime_ofs < cells_needed &&
|
||||||
term->ime.preedit.cells[ime_ofs].wc == CELL_MULT_COL_SPACER)
|
seat->ime.preedit.cells[ime_ofs].wc == CELL_MULT_COL_SPACER)
|
||||||
{
|
{
|
||||||
ime_ofs++;
|
ime_ofs++;
|
||||||
}
|
}
|
||||||
|
|
@ -1208,9 +1208,9 @@ render_ime_preedit(struct terminal *term, struct buffer *buf)
|
||||||
row->dirty = true;
|
row->dirty = true;
|
||||||
|
|
||||||
/* Render pre-edit text */
|
/* Render pre-edit text */
|
||||||
xassert(term->ime.preedit.cells[ime_ofs].wc != CELL_MULT_COL_SPACER);
|
xassert(seat->ime.preedit.cells[ime_ofs].wc != CELL_MULT_COL_SPACER);
|
||||||
for (int i = 0, idx = ime_ofs; idx < term->ime.preedit.count; i++, idx++) {
|
for (int i = 0, idx = ime_ofs; idx < seat->ime.preedit.count; i++, idx++) {
|
||||||
const struct cell *cell = &term->ime.preedit.cells[idx];
|
const struct cell *cell = &seat->ime.preedit.cells[idx];
|
||||||
|
|
||||||
if (cell->wc == CELL_MULT_COL_SPACER)
|
if (cell->wc == CELL_MULT_COL_SPACER)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1223,11 +1223,11 @@ render_ime_preedit(struct terminal *term, struct buffer *buf)
|
||||||
render_cell(term, buf->pix[0], row, col_idx + i, row_idx, false);
|
render_cell(term, buf->pix[0], row, col_idx + i, row_idx, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
int start = term->ime.preedit.cursor.start - ime_ofs;
|
int start = seat->ime.preedit.cursor.start - ime_ofs;
|
||||||
int end = term->ime.preedit.cursor.end - ime_ofs;
|
int end = seat->ime.preedit.cursor.end - ime_ofs;
|
||||||
|
|
||||||
if (!term->ime.preedit.cursor.hidden) {
|
if (!seat->ime.preedit.cursor.hidden) {
|
||||||
const struct cell *start_cell = &term->ime.preedit.cells[0];
|
const struct cell *start_cell = &seat->ime.preedit.cells[0];
|
||||||
|
|
||||||
pixman_color_t fg = color_hex_to_pixman(term->colors.fg);
|
pixman_color_t fg = color_hex_to_pixman(term->colors.fg);
|
||||||
pixman_color_t bg = color_hex_to_pixman(term->colors.bg);
|
pixman_color_t bg = color_hex_to_pixman(term->colors.bg);
|
||||||
|
|
@ -1271,6 +1271,17 @@ render_ime_preedit(struct terminal *term, struct buffer *buf)
|
||||||
term->margins.top + row_idx * term->cell_height,
|
term->margins.top + row_idx * term->cell_height,
|
||||||
term->width - term->margins.left - term->margins.right,
|
term->width - term->margins.left - term->margins.right,
|
||||||
1 * term->cell_height);
|
1 * term->cell_height);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void
|
||||||
|
render_ime_preedit(struct terminal *term, struct buffer *buf)
|
||||||
|
{
|
||||||
|
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||||
|
tll_foreach(term->wl->seats, it) {
|
||||||
|
if (it->item.kbd_focus == term)
|
||||||
|
render_ime_preedit_for_seat(term, &it->item, buf);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2238,9 +2249,18 @@ render_search_box(struct terminal *term)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||||
|
/* TODO: do we want to/need to handle multi-seat? */
|
||||||
|
struct seat *ime_seat = NULL;
|
||||||
|
tll_foreach(term->wl->seats, it) {
|
||||||
|
if (it->item.kbd_focus == term) {
|
||||||
|
ime_seat = &it->item;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
size_t text_len = term->search.len;
|
size_t text_len = term->search.len;
|
||||||
if (term->ime.preedit.text != NULL)
|
if (ime_seat != NULL && ime_seat->ime.preedit.text != NULL)
|
||||||
text_len += wcslen(term->ime.preedit.text);
|
text_len += wcslen(ime_seat->ime.preedit.text);
|
||||||
|
|
||||||
wchar_t *text = xmalloc((text_len + 1) * sizeof(wchar_t));
|
wchar_t *text = xmalloc((text_len + 1) * sizeof(wchar_t));
|
||||||
text[0] = L'\0';
|
text[0] = L'\0';
|
||||||
|
|
@ -2250,8 +2270,8 @@ render_search_box(struct terminal *term)
|
||||||
text[term->search.cursor] = L'\0';
|
text[term->search.cursor] = L'\0';
|
||||||
|
|
||||||
/* Insert pre-edit text at cursor */
|
/* Insert pre-edit text at cursor */
|
||||||
if (term->ime.preedit.text != NULL)
|
if (ime_seat != NULL && ime_seat->ime.preedit.text != NULL)
|
||||||
wcscat(text, term->ime.preedit.text);
|
wcscat(text, ime_seat->ime.preedit.text);
|
||||||
|
|
||||||
/* And finally everything after the cursor */
|
/* And finally everything after the cursor */
|
||||||
wcsncat(text, &term->search.buf[term->search.cursor],
|
wcsncat(text, &term->search.buf[term->search.cursor],
|
||||||
|
|
@ -2319,18 +2339,18 @@ render_search_box(struct terminal *term)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
#if (FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
#if (FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||||
if (term->ime.preedit.cells != NULL) {
|
if (ime_seat != NULL && ime_seat->ime.preedit.cells != NULL) {
|
||||||
if (term->ime.preedit.cursor.start == term->ime.preedit.cursor.end) {
|
if (ime_seat->ime.preedit.cursor.start == ime_seat->ime.preedit.cursor.end) {
|
||||||
/* All IME's I've seen so far keeps the cursor at
|
/* All IME's I've seen so far keeps the cursor at
|
||||||
* index 0, so ensure the *end* of the pre-edit string
|
* index 0, so ensure the *end* of the pre-edit string
|
||||||
* is visible */
|
* is visible */
|
||||||
cell_idx += term->ime.preedit.count;
|
cell_idx += ime_seat->ime.preedit.count;
|
||||||
} else {
|
} else {
|
||||||
/* Try to predict in which direction we'll shift the text */
|
/* Try to predict in which direction we'll shift the text */
|
||||||
if (cell_idx + term->ime.preedit.cursor.start > glyph_offset)
|
if (cell_idx + ime_seat->ime.preedit.cursor.start > glyph_offset)
|
||||||
cell_idx += term->ime.preedit.cursor.end;
|
cell_idx += ime_seat->ime.preedit.cursor.end;
|
||||||
else
|
else
|
||||||
cell_idx += term->ime.preedit.cursor.start;
|
cell_idx += ime_seat->ime.preedit.cursor.start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -2383,8 +2403,10 @@ render_search_box(struct terminal *term)
|
||||||
/* Render cursor */
|
/* Render cursor */
|
||||||
if (i == term->search.cursor) {
|
if (i == term->search.cursor) {
|
||||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||||
bool have_preedit = term->ime.preedit.cells != NULL;
|
bool have_preedit =
|
||||||
bool hidden = term->ime.preedit.cursor.hidden;
|
ime_seat != NULL && ime_seat->ime.preedit.cells != NULL;
|
||||||
|
bool hidden =
|
||||||
|
ime_seat != NULL && ime_seat->ime.preedit.cursor.hidden;
|
||||||
|
|
||||||
if (have_preedit && !hidden) {
|
if (have_preedit && !hidden) {
|
||||||
/* Cursor may be outside the visible area:
|
/* Cursor may be outside the visible area:
|
||||||
|
|
@ -2394,13 +2416,13 @@ render_search_box(struct terminal *term)
|
||||||
|
|
||||||
/* If cursor is outside the visible area, we need to
|
/* If cursor is outside the visible area, we need to
|
||||||
* adjust our rectangle's position */
|
* adjust our rectangle's position */
|
||||||
int start = term->ime.preedit.cursor.start
|
int start = ime_seat->ime.preedit.cursor.start
|
||||||
+ min((ssize_t)(cell_idx - glyph_offset), 0);
|
+ min((ssize_t)(cell_idx - glyph_offset), 0);
|
||||||
int end = term->ime.preedit.cursor.end
|
int end = ime_seat->ime.preedit.cursor.end
|
||||||
+ min((ssize_t)(cell_idx - glyph_offset), 0);
|
+ min((ssize_t)(cell_idx - glyph_offset), 0);
|
||||||
|
|
||||||
if (start == end) {
|
if (start == end) {
|
||||||
int count = min(term->ime.preedit.count, cells_left);
|
int count = min(ime_seat->ime.preedit.count, cells_left);
|
||||||
|
|
||||||
/* Underline the entire (visible part of) pre-edit text */
|
/* Underline the entire (visible part of) pre-edit text */
|
||||||
draw_underline(term, buf->pix[0], font, &fg, x, y, count);
|
draw_underline(term, buf->pix[0], font, &fg, x, y, count);
|
||||||
|
|
@ -2415,7 +2437,7 @@ render_search_box(struct terminal *term)
|
||||||
/* Underline everything before and after the cursor */
|
/* Underline everything before and after the cursor */
|
||||||
int count1 = min(start, cells_left);
|
int count1 = min(start, cells_left);
|
||||||
int count2 = max(
|
int count2 = max(
|
||||||
min(term->ime.preedit.count - term->ime.preedit.cursor.end,
|
min(ime_seat->ime.preedit.count - ime_seat->ime.preedit.cursor.end,
|
||||||
cells_left - end),
|
cells_left - end),
|
||||||
0);
|
0);
|
||||||
draw_underline(term, buf->pix[0], font, &fg, x, y, count1);
|
draw_underline(term, buf->pix[0], font, &fg, x, y, count1);
|
||||||
|
|
@ -2487,7 +2509,7 @@ render_search_box(struct terminal *term)
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||||
if (term->ime.preedit.cells != NULL)
|
if (ime_seat != NULL && ime_seat->ime.preedit.cells != NULL)
|
||||||
/* Already rendered */;
|
/* Already rendered */;
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
40
terminal.c
40
terminal.c
|
|
@ -1182,9 +1182,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
.foot_exe = xstrdup(foot_exe),
|
.foot_exe = xstrdup(foot_exe),
|
||||||
.cwd = xstrdup(cwd),
|
.cwd = xstrdup(cwd),
|
||||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||||
.ime = {
|
.ime_enabled = true,
|
||||||
.enabled = true,
|
|
||||||
},
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -2405,10 +2403,8 @@ term_kbd_focus_out(struct terminal *term)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||||
if (term->ime.preedit.cells != NULL) {
|
if (term_ime_reset(term))
|
||||||
term_ime_reset(term);
|
|
||||||
render_refresh(term);
|
render_refresh(term);
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
term->kbd_focus = false;
|
term->kbd_focus = false;
|
||||||
|
|
@ -3039,7 +3035,7 @@ bool
|
||||||
term_ime_is_enabled(const struct terminal *term)
|
term_ime_is_enabled(const struct terminal *term)
|
||||||
{
|
{
|
||||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||||
return term->ime.enabled;
|
return term->ime_enabled;
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -3049,13 +3045,12 @@ void
|
||||||
term_ime_enable(struct terminal *term)
|
term_ime_enable(struct terminal *term)
|
||||||
{
|
{
|
||||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||||
if (term->ime.enabled)
|
if (term->ime_enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
LOG_DBG("IME enabled");
|
LOG_DBG("IME enabled");
|
||||||
|
|
||||||
term->ime.enabled = true;
|
term->ime_enabled = true;
|
||||||
term_ime_reset(term);
|
|
||||||
|
|
||||||
/* IME is per seat - enable on all seat currently focusing us */
|
/* IME is per seat - enable on all seat currently focusing us */
|
||||||
tll_foreach(term->wl->seats, it) {
|
tll_foreach(term->wl->seats, it) {
|
||||||
|
|
@ -3069,13 +3064,12 @@ void
|
||||||
term_ime_disable(struct terminal *term)
|
term_ime_disable(struct terminal *term)
|
||||||
{
|
{
|
||||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||||
if (!term->ime.enabled)
|
if (!term->ime_enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
LOG_DBG("IME disabled");
|
LOG_DBG("IME disabled");
|
||||||
|
|
||||||
term->ime.enabled = false;
|
term->ime_enabled = false;
|
||||||
term_ime_reset(term);
|
|
||||||
|
|
||||||
/* IME is per seat - disable on all seat currently focusing us */
|
/* IME is per seat - disable on all seat currently focusing us */
|
||||||
tll_foreach(term->wl->seats, it) {
|
tll_foreach(term->wl->seats, it) {
|
||||||
|
|
@ -3085,18 +3079,24 @@ term_ime_disable(struct terminal *term)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool
|
||||||
term_ime_reset(struct terminal *term)
|
term_ime_reset(struct terminal *term)
|
||||||
{
|
{
|
||||||
|
bool at_least_one_seat_was_reset = false;
|
||||||
|
|
||||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||||
if (term->ime.preedit.cells != NULL) {
|
tll_foreach(term->wl->seats, it) {
|
||||||
free(term->ime.preedit.text);
|
struct seat *seat = &it->item;
|
||||||
free(term->ime.preedit.cells);
|
|
||||||
term->ime.preedit.text = NULL;
|
if (seat->kbd_focus != term)
|
||||||
term->ime.preedit.cells = NULL;
|
continue;
|
||||||
term->ime.preedit.count = 0;
|
|
||||||
|
ime_reset_preedit(seat);
|
||||||
|
at_least_one_seat_was_reset = true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
return at_least_one_seat_was_reset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
17
terminal.h
17
terminal.h
|
|
@ -567,20 +567,7 @@ struct terminal {
|
||||||
struct grid *url_grid_snapshot;
|
struct grid *url_grid_snapshot;
|
||||||
|
|
||||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||||
struct {
|
bool ime_enabled;
|
||||||
bool enabled;
|
|
||||||
struct {
|
|
||||||
wchar_t *text;
|
|
||||||
struct cell *cells;
|
|
||||||
int count;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
bool hidden;
|
|
||||||
int start; /* Cell index, inclusive */
|
|
||||||
int end; /* Cell index, exclusive */
|
|
||||||
} cursor;
|
|
||||||
} preedit;
|
|
||||||
} ime;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool is_shutting_down;
|
bool is_shutting_down;
|
||||||
|
|
@ -713,7 +700,7 @@ bool term_view_to_text(
|
||||||
bool term_ime_is_enabled(const struct terminal *term);
|
bool term_ime_is_enabled(const struct terminal *term);
|
||||||
void term_ime_enable(struct terminal *term);
|
void term_ime_enable(struct terminal *term);
|
||||||
void term_ime_disable(struct terminal *term);
|
void term_ime_disable(struct terminal *term);
|
||||||
void term_ime_reset(struct terminal *term);
|
bool term_ime_reset(struct terminal *term);
|
||||||
void term_ime_set_cursor_rect(
|
void term_ime_set_cursor_rect(
|
||||||
struct terminal *term, int x, int y, int width, int height);
|
struct terminal *term, int x, int y, int width, int height);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,7 @@ seat_destroy(struct seat *seat)
|
||||||
if (seat->wl_seat != NULL)
|
if (seat->wl_seat != NULL)
|
||||||
wl_seat_release(seat->wl_seat);
|
wl_seat_release(seat->wl_seat);
|
||||||
|
|
||||||
ime_reset(seat);
|
ime_reset_pending(seat);
|
||||||
free(seat->clipboard.text);
|
free(seat->clipboard.text);
|
||||||
free(seat->primary.text);
|
free(seat->primary.text);
|
||||||
free(seat->name);
|
free(seat->name);
|
||||||
|
|
|
||||||
|
|
@ -256,6 +256,15 @@ struct seat {
|
||||||
int32_t cursor_begin;
|
int32_t cursor_begin;
|
||||||
int32_t cursor_end;
|
int32_t cursor_end;
|
||||||
} pending;
|
} pending;
|
||||||
|
|
||||||
|
wchar_t *text;
|
||||||
|
struct cell *cells;
|
||||||
|
int count;
|
||||||
|
struct {
|
||||||
|
bool hidden;
|
||||||
|
int start; /* Cell index, inclusive */
|
||||||
|
int end; /* Cell index, exclusive */
|
||||||
|
} cursor;
|
||||||
} preedit;
|
} preedit;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue