fix(frontends): restart preedit around commits

This commit is contained in:
2026-08-15 15:42:35 +09:00
parent 67ed2ef478
commit a1ef8a4744
5 changed files with 136 additions and 30 deletions

View File

@@ -345,6 +345,8 @@ kpress(GtkIMContext *ctx, GdkEventKey *ev)
srvclose(im);
return simplefilter(ctx, ev, 0);
}
if(im->usepreedit && commit[0] != '\0' && im->prelen > 0)
setpreedit(im, "", 0);
if(commit[0] != '\0')
g_signal_emit_by_name(ctx, "commit", commit);
if(im->usepreedit)

6
ibus.c
View File

@@ -629,6 +629,7 @@ handlekey(DBusConnection *c, DBusMessage *m, Ictx *ctx)
dbus_uint32_t sym, code, state;
Keyres res;
char commit[Maxutf], preedit[Maxutf];
int restart;
dbus_error_init(&err);
if(!dbus_message_get_args(m, &err,
@@ -645,9 +646,12 @@ handlekey(DBusConnection *c, DBusMessage *m, Ictx *ctx)
return handlebool(c, m, 0);
stoutf(&res.commit, commit, sizeof commit);
stoutf(&res.preedit, preedit, sizeof preedit);
restart = clientpreedit(ctx) && commit[0] != '\0' && preowner == ctx;
if(restart)
emitpreedit(ctx, "");
if(commit[0] != '\0')
emitcommit(c, dbus_message_get_path(m), commit);
if(clientpreedit(ctx))
if(clientpreedit(ctx) && (!restart || preedit[0] != '\0'))
emitpreedit(ctx, preedit);
return handlebool(c, m, res.eaten);
}

View File

@@ -420,25 +420,38 @@ engine_active_owner_caret(struct ct *t)
void
engine_commit_contract(struct ct *t)
{
static const struct {
Rune key;
char *one;
char *all;
} cases[] = {
{ 'z', "", "ㅋㅋㅋ" },
{ 'r', "", "ㄱㄱㄱ" },
{ 'd', "", "ㅇㅇㅇ" },
{ 'k', "", "ㅏㅏㅏ" },
};
Keyres res;
Str all, com;
char owner;
int i;
int i, j;
for(j = 0; j < nelem(cases); j++){
init();
im.l = getlang(LangKO);
sclear(&all);
for(i = 0; i < 3; i++){
res = ownerrequestcap(&owner, Cclientpreedit,
Keypress, 'z', 0);
Keypress, cases[j].key, 0);
CT_CHECK(t, res.eaten);
checkstr(t, "repeated consonant commit",
i == 0 ? "" : "", &res.commit);
checkstr(t, "repeated consonant preedit", "", &res.preedit);
checkstr(t, "repeated jamo commit",
i == 0 ? "" : cases[j].one, &res.commit);
checkstr(t, "repeated jamo preedit", cases[j].one,
&res.preedit);
sappend(&all, &res.commit);
}
sappend(&all, &res.preedit);
checkstr(t, "repeated consonant total", "ㅋㅋㅋ", &all);
checkstr(t, "repeated jamo total", cases[j].all, &all);
}
init();
im.l = getlang(LangJP);

View File

@@ -80,7 +80,9 @@ struct Siglog
char event[128];
int n;
char pre[Ipcfieldmax+1];
char shown[Ipcfieldmax+1];
char commit[Ipcfieldmax+1];
int prechanged;
};
static int
@@ -568,6 +570,8 @@ prechange(GtkIMContext *ctx, Siglog *l)
gchar *s;
gtk_im_context_get_preedit_string(ctx, &s, NULL, NULL);
if(strcmp(l->pre, s) != 0)
l->prechanged = 1;
snprintf(l->pre, sizeof l->pre, "%s", s);
g_free(s);
logsig(l, 'C');
@@ -585,10 +589,27 @@ commit(GtkIMContext *ctx, gchar *s, Siglog *l)
{
(void)ctx;
snprintf(l->commit, sizeof l->commit, "%s", s);
l->pre[0] = '\0';
logsig(l, 'K');
}
static void
clearevent(Siglog *l)
{
l->event[0] = '\0';
l->n = 0;
l->commit[0] = '\0';
l->prechanged = 0;
}
static void
applyclient(Siglog *l)
{
if(l->commit[0] != '\0')
l->shown[0] = '\0';
if(l->prechanged)
snprintf(l->shown, sizeof l->shown, "%s", l->pre);
}
static void
clearlog(Siglog *l)
{
@@ -748,18 +769,23 @@ main(int argc, char **argv)
setpre(&srv, "");
clearlog(&log);
Check(key(ctx, win, GDK_KEY_z), "first preedit key was not eaten");
Check(strcmp(log.event, "SC") == 0 && strcmp(log.pre, "") == 0,
"first preedit signals were %s (%s)", log.event, log.pre);
clearlog(&log);
applyclient(&log);
Check(strcmp(log.event, "SC") == 0 && strcmp(log.pre, "") == 0 &&
strcmp(log.shown, "") == 0,
"first preedit signals were %s (preedit %s, shown %s)",
log.event, log.pre, log.shown);
clearevent(&log);
setreply(&srv, 1, Rcommitpre, 0);
Check(key(ctx, win, GDK_KEY_z), "repeated preedit key was not eaten");
Check(strcmp(log.event, "KC") == 0 &&
strcmp(log.commit, "") == 0 && strcmp(log.pre, "") == 0,
"repeated preedit signals were %s (commit %s, preedit %s)",
log.event, log.commit, log.pre);
Check(g_utf8_strlen(log.commit, -1) + g_utf8_strlen(log.pre, -1) == 2,
"two physical keys produced commit %s and preedit %s",
log.commit, log.pre);
applyclient(&log);
Check(strcmp(log.event, "CEKSC") == 0 &&
strcmp(log.commit, "") == 0 && strcmp(log.pre, "") == 0 &&
strcmp(log.shown, "") == 0,
"repeated preedit signals were %s (commit %s, preedit %s, shown %s)",
log.event, log.commit, log.pre, log.shown);
Check(g_utf8_strlen(log.commit, -1) + g_utf8_strlen(log.shown, -1) == 2,
"two physical keys produced commit %s and shown preedit %s",
log.commit, log.shown);
/* Unconsumed input uses GtkIMContextSimple, including its stateful forms. */
setpre(&srv, "");

View File

@@ -21,8 +21,23 @@ struct Log
int transfer;
int atext;
int aclear;
char revent[16];
int rn;
int repeat;
int rfirst;
int rcommit;
int rdone;
};
static void
revent(Log *log, char c)
{
if(log->rn + 1 < (int)sizeof log->revent){
log->revent[log->rn++] = c;
log->revent[log->rn] = '\0';
}
}
static void
legacy(IBusInputContext *ctx, IBusText *text, guint cursor,
gboolean visible, void *arg)
@@ -36,6 +51,28 @@ legacy(IBusInputContext *ctx, IBusText *text, guint cursor,
log = arg;
log->legacy++;
s = ibus_text_get_text(text);
if(log->repeat != 0){
revent(log, 'P');
if(log->repeat == 1){
if(s[0] == '\0'){
if(cursor != 0 || visible)
log->invalid = 1;
}else if(strcmp(s, "") != 0 || cursor != 1 || !visible)
log->invalid = 1;
else
log->rfirst = 1;
}else if(s[0] == '\0'){
if(cursor != 0 || visible || log->rcommit)
log->invalid = 1;
}else if(strcmp(s, "") != 0 || cursor != 1 || !visible ||
!log->rcommit)
log->invalid = 1;
else
log->rdone = 1;
if(log->loop != NULL && log->waiting != NULL && *log->waiting)
g_main_loop_quit(log->loop);
return;
}
if(strcmp(s, "k") == 0){
attrs = ibus_text_get_attributes(text);
a = attrs == NULL ? NULL : ibus_attr_list_get(attrs, 0);
@@ -97,6 +134,17 @@ commit(IBusInputContext *ctx, IBusText *text, void *arg)
log->commit++;
s = ibus_text_get_text(text);
attrs = ibus_text_get_attributes(text);
if(log->repeat != 0){
revent(log, 'K');
if(log->repeat != 2 || strcmp(s, "") != 0 || attrs == NULL ||
ibus_attr_list_get(attrs, 0) != NULL)
log->invalid = 1;
else
log->rcommit = 1;
if(log->loop != NULL && log->waiting != NULL && *log->waiting)
g_main_loop_quit(log->loop);
return;
}
if(strcmp(s, "") != 0 || attrs == NULL ||
ibus_attr_list_get(attrs, 0) != NULL)
log->invalid = 1;
@@ -194,10 +242,22 @@ main(int argc, char **argv)
waitflag(&log, &log.aclear);
ibus_input_context_focus_out(b);
}
ibus_input_context_focus_in(a);
ok = ok && ibus_input_context_process_key_event(a, 's', 0,
IBUS_CONTROL_MASK);
log.repeat = 1;
ok = ok && ibus_input_context_process_key_event(a, 'z', 0, 0) &&
waitflag(&log, &log.rfirst);
log.repeat = 2;
log.revent[0] = '\0';
log.rn = 0;
ok = ok && ibus_input_context_process_key_event(a, 'z', 0, 0) &&
waitflag(&log, &log.rdone) && strcmp(log.revent, "PKP") == 0;
log.repeat = 0;
ibus_input_context_focus_out(a);
if(log.legacy == 0 || log.modern != 0 || log.commit != 1 ||
if(log.legacy == 0 || log.modern != 0 || log.commit != 2 ||
log.invalid || !log.done || !log.atext || !log.aclear ||
log.clearctx != a)
log.clearctx != a || !log.rdone)
ok = 0;
if(b != NULL)
g_object_unref(b);
@@ -205,9 +265,10 @@ main(int argc, char **argv)
g_object_unref(bus);
if(!ok){
fprintf(stderr,
"ibus_client_smoke: legacy=%d modern=%d commit=%d invalid=%d preedit=%d committed=%d transfer-text=%d a-clear=%d\n",
"ibus_client_smoke: legacy=%d modern=%d commit=%d invalid=%d preedit=%d committed=%d transfer-text=%d a-clear=%d repeat=%s\n",
log.legacy, log.modern, log.commit, log.invalid,
log.sawpreedit, log.sawcommit, log.atext, log.aclear);
log.sawpreedit, log.sawcommit, log.atext, log.aclear,
log.revent);
return 1;
}
printf("official libibus client preedit, commit, and owner clear: ok\n");