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); srvclose(im);
return simplefilter(ctx, ev, 0); return simplefilter(ctx, ev, 0);
} }
if(im->usepreedit && commit[0] != '\0' && im->prelen > 0)
setpreedit(im, "", 0);
if(commit[0] != '\0') if(commit[0] != '\0')
g_signal_emit_by_name(ctx, "commit", commit); g_signal_emit_by_name(ctx, "commit", commit);
if(im->usepreedit) 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; dbus_uint32_t sym, code, state;
Keyres res; Keyres res;
char commit[Maxutf], preedit[Maxutf]; char commit[Maxutf], preedit[Maxutf];
int restart;
dbus_error_init(&err); dbus_error_init(&err);
if(!dbus_message_get_args(m, &err, if(!dbus_message_get_args(m, &err,
@@ -645,9 +646,12 @@ handlekey(DBusConnection *c, DBusMessage *m, Ictx *ctx)
return handlebool(c, m, 0); return handlebool(c, m, 0);
stoutf(&res.commit, commit, sizeof commit); stoutf(&res.commit, commit, sizeof commit);
stoutf(&res.preedit, preedit, sizeof preedit); stoutf(&res.preedit, preedit, sizeof preedit);
restart = clientpreedit(ctx) && commit[0] != '\0' && preowner == ctx;
if(restart)
emitpreedit(ctx, "");
if(commit[0] != '\0') if(commit[0] != '\0')
emitcommit(c, dbus_message_get_path(m), commit); emitcommit(c, dbus_message_get_path(m), commit);
if(clientpreedit(ctx)) if(clientpreedit(ctx) && (!restart || preedit[0] != '\0'))
emitpreedit(ctx, preedit); emitpreedit(ctx, preedit);
return handlebool(c, m, res.eaten); return handlebool(c, m, res.eaten);
} }

View File

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

View File

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

View File

@@ -21,8 +21,23 @@ struct Log
int transfer; int transfer;
int atext; int atext;
int aclear; 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 static void
legacy(IBusInputContext *ctx, IBusText *text, guint cursor, legacy(IBusInputContext *ctx, IBusText *text, guint cursor,
gboolean visible, void *arg) gboolean visible, void *arg)
@@ -36,6 +51,28 @@ legacy(IBusInputContext *ctx, IBusText *text, guint cursor,
log = arg; log = arg;
log->legacy++; log->legacy++;
s = ibus_text_get_text(text); 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){ if(strcmp(s, "k") == 0){
attrs = ibus_text_get_attributes(text); attrs = ibus_text_get_attributes(text);
a = attrs == NULL ? NULL : ibus_attr_list_get(attrs, 0); a = attrs == NULL ? NULL : ibus_attr_list_get(attrs, 0);
@@ -97,6 +134,17 @@ commit(IBusInputContext *ctx, IBusText *text, void *arg)
log->commit++; log->commit++;
s = ibus_text_get_text(text); s = ibus_text_get_text(text);
attrs = ibus_text_get_attributes(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 || if(strcmp(s, "") != 0 || attrs == NULL ||
ibus_attr_list_get(attrs, 0) != NULL) ibus_attr_list_get(attrs, 0) != NULL)
log->invalid = 1; log->invalid = 1;
@@ -194,10 +242,22 @@ main(int argc, char **argv)
waitflag(&log, &log.aclear); waitflag(&log, &log.aclear);
ibus_input_context_focus_out(b); 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); 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.invalid || !log.done || !log.atext || !log.aclear ||
log.clearctx != a) log.clearctx != a || !log.rdone)
ok = 0; ok = 0;
if(b != NULL) if(b != NULL)
g_object_unref(b); g_object_unref(b);
@@ -205,9 +265,10 @@ main(int argc, char **argv)
g_object_unref(bus); g_object_unref(bus);
if(!ok){ if(!ok){
fprintf(stderr, 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.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; return 1;
} }
printf("official libibus client preedit, commit, and owner clear: ok\n"); printf("official libibus client preedit, commit, and owner clear: ok\n");