fix(gtk): restore compose fallback and clean lifecycle

This commit is contained in:
2026-08-14 22:29:14 +09:00
parent 534ddcd9bb
commit 9804c1f55c
2 changed files with 285 additions and 224 deletions

View File

@@ -1,3 +1,4 @@
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
@@ -12,12 +13,14 @@
typedef struct Im Im;
struct Im
{
GtkIMContext parent;
GtkIMContextSimple parent;
int fd;
int usepreedit;
int effective;
int ext;
int private;
int simpleactive;
int insimple;
int simpledone;
char pre[Ipcfieldmax+1];
int prelen;
GdkWindow *win;
@@ -33,10 +36,12 @@ struct Im
typedef struct ImClass ImClass;
struct ImClass
{
GtkIMContextClass parent;
GtkIMContextSimpleClass parent;
};
static GType imtype;
static GObjectClass *parentobject;
static GtkIMContextClass *parentim;
static void
setpreedit(Im *im, const char *s, int n)
@@ -60,15 +65,32 @@ setpreedit(Im *im, const char *s, int n)
}
static void
srvclose(Im *im)
srvdrop(Im *im, int notify)
{
if(im->fd >= 0)
close(im->fd);
im->fd = -1;
im->ext = 0;
im->effective = 1;
im->caretsent = 0;
setpreedit(im, "", 0);
if(notify)
setpreedit(im, "", 0);
else{
im->pre[0] = '\0';
im->prelen = 0;
}
}
static void
srvclose(Im *im)
{
srvdrop(im, 1);
}
static int
validtext(const char *s, size_t n)
{
return s[n] == '\0' && memchr(s, '\0', n) == NULL &&
g_utf8_validate(s, n, NULL);
}
static int
@@ -80,6 +102,11 @@ readresp(Im *im, int want, int update, char *commit, int ncommit,
if(ipcreadresp(im->fd, want, commit, ncommit, pre, sizeof pre,
resp) < 0)
return -1;
if(!validtext(commit, resp->commitlen) ||
(want && !validtext(pre, resp->preeditlen))){
errno = EPROTO;
return -1;
}
if(update)
setpreedit(im, pre, resp->preeditlen);
return 0;
@@ -188,8 +215,7 @@ srvconnect(Im *im)
return -1;
}
im->ext = resp.eaten != 0;
im->effective = im->ext ? im->usepreedit : 1;
if(!im->effective)
if(!im->usepreedit)
setpreedit(im, "", 0);
if(im->ext && sendcaret(im) < 0){
srvclose(im);
@@ -228,6 +254,40 @@ mget(uint32_t state)
return m;
}
static void
simplecommit(GtkIMContext *ctx, const char *s, Im *im)
{
(void)ctx;
(void)s;
if(im->insimple)
im->simpledone = 1;
}
static void
simpleend(GtkIMContext *ctx, Im *im)
{
(void)ctx;
if(im->insimple)
im->simpledone = 1;
}
static gboolean
simplefilter(GtkIMContext *ctx, GdkEventKey *ev, int release)
{
Im *im;
gboolean r;
im = (Im*)ctx;
im->simpleactive = 1;
im->simpledone = 0;
im->insimple = 1;
r = parentim->filter_keypress(ctx, ev);
im->insimple = 0;
if((!r && !release) || im->simpledone)
im->simpleactive = 0;
return r;
}
static void
sendreset(Im *im)
{
@@ -239,9 +299,9 @@ sendreset(Im *im)
setpreedit(im, "", 0);
return;
}
ipcpackreset(buf, im->effective);
ipcpackreset(buf, im->usepreedit);
if(ipcsend(im->fd, buf, sizeof buf) < 0 ||
readresp(im, im->effective, 0, commit,
readresp(im, im->usepreedit, 0, commit,
sizeof commit, &resp) < 0){
srvclose(im);
return;
@@ -249,22 +309,6 @@ sendreset(Im *im)
setpreedit(im, "", 0);
}
static gboolean
plaincommit(GtkIMContext *ctx, uint32_t key, uint32_t mod)
{
char u[8];
int n;
if(mod & (Mctrl|Malt|Msuper))
return FALSE;
if(key < 0x20 || key >= Kspec)
return FALSE;
n = g_unichar_to_utf8(key, u);
u[n] = '\0';
g_signal_emit_by_name(ctx, "commit", u);
return TRUE;
}
static gboolean
kpress(GtkIMContext *ctx, GdkEventKey *ev)
{
@@ -275,33 +319,36 @@ kpress(GtkIMContext *ctx, GdkEventKey *ev)
Ipcresp resp;
im = (Im*)ctx;
if(ev->type != GDK_KEY_PRESS)
if(ev->type != GDK_KEY_PRESS){
if(im->simpleactive)
return simplefilter(ctx, ev, 1);
return FALSE;
}
if(im->simpleactive)
return simplefilter(ctx, ev, 0);
key = kget(ev->keyval);
if(key == 0)
return FALSE;
mod = mget(ev->state);
if(im->private)
return plaincommit(ctx, key, mod);
if(im->private || key == 0)
return simplefilter(ctx, ev, 0);
if(srvconnect(im) < 0)
return plaincommit(ctx, key, mod);
return simplefilter(ctx, ev, 0);
/* A retained GDK window may have moved since the last cursor report. */
if(sendcaret(im) < 0){
srvclose(im);
return plaincommit(ctx, key, mod);
return simplefilter(ctx, ev, 0);
}
ipcpackreq(buf, im->effective, mod, key);
ipcpackreq(buf, im->usepreedit, mod, key);
if(ipcsend(im->fd, buf, sizeof buf) < 0 ||
readresp(im, im->effective, im->effective, commit,
readresp(im, im->usepreedit, im->usepreedit, commit,
sizeof commit, &resp) < 0){
srvclose(im);
return plaincommit(ctx, key, mod);
return simplefilter(ctx, ev, 0);
}
if(commit[0] != '\0')
g_signal_emit_by_name(ctx, "commit", commit);
if(resp.eaten)
return TRUE;
return plaincommit(ctx, key, mod);
return simplefilter(ctx, ev, 0);
}
static void
@@ -312,6 +359,10 @@ getpreedit(GtkIMContext *ctx, gchar **str, PangoAttrList **attrs,
PangoAttribute *u;
im = (Im*)ctx;
if(im->simpleactive){
parentim->get_preedit_string(ctx, str, attrs, cursor_pos);
return;
}
if(str)
*str = g_strdup(im->pre);
if(attrs){
@@ -330,6 +381,9 @@ getpreedit(GtkIMContext *ctx, gchar **str, PangoAttrList **attrs,
static void
reset(GtkIMContext *ctx)
{
if(parentim->reset != NULL)
parentim->reset(ctx);
((Im*)ctx)->simpleactive = 0;
sendreset((Im*)ctx);
}
@@ -339,6 +393,9 @@ focusout(GtkIMContext *ctx)
Im *im;
im = (Im*)ctx;
if(parentim->focus_out != NULL)
parentim->focus_out(ctx);
im->simpleactive = 0;
sendreset(im);
/* Closing the context connection releases engine ownership. */
srvclose(im);
@@ -354,13 +411,15 @@ setusepreedit(GtkIMContext *ctx, gboolean use)
im = (Im*)ctx;
use = use != FALSE;
if(parentim->set_use_preedit != NULL)
parentim->set_use_preedit(ctx, use);
if(im->usepreedit == use)
return;
im->usepreedit = use;
if(im->fd < 0 || !im->ext){
im->effective = im->ext ? use : 1;
if(!use)
setpreedit(im, "", 0);
if(im->fd < 0 || !im->ext)
return;
}
ipcpackcap(buf, use);
if(ipcsend(im->fd, buf, sizeof buf) < 0 ||
readresp(im, use, use, commit, sizeof commit, &resp) < 0 ||
@@ -368,9 +427,6 @@ setusepreedit(GtkIMContext *ctx, gboolean use)
srvclose(im);
return;
}
im->effective = use;
if(!use)
setpreedit(im, "", 0);
}
static void
@@ -381,6 +437,8 @@ setclientwindow(GtkIMContext *ctx, GdkWindow *win)
im = (Im*)ctx;
if(win != NULL && gdk_window_is_destroyed(win))
win = NULL;
if(parentim->set_client_window != NULL)
parentim->set_client_window(ctx, win);
if(im->win == win)
return;
if(win != NULL)
@@ -397,6 +455,8 @@ setcursorlocation(GtkIMContext *ctx, GdkRectangle *area)
Im *im;
im = (Im*)ctx;
if(parentim->set_cursor_location != NULL)
parentim->set_cursor_location(ctx, area);
if(im->win != NULL && gdk_window_is_destroyed(im->win))
dropwindow(im);
if(area == NULL || im->win == NULL)
@@ -432,18 +492,18 @@ purposechanged(GObject *obj, GParamSpec *pspec, gpointer data)
return;
im->private = private;
if(private)
sendreset(im);
reset(GTK_IM_CONTEXT(im));
}
static void
finalize(GObject *obj)
dispose(GObject *obj)
{
Im *im;
im = (Im*)obj;
srvclose(im);
srvdrop(im, 0);
dropwindow(im);
G_OBJECT_CLASS(g_type_class_peek_parent(G_OBJECT_GET_CLASS(obj)))->finalize(obj);
parentobject->dispose(obj);
}
static void
@@ -453,12 +513,13 @@ init(Im *im)
im->fd = -1;
im->usepreedit = 1;
im->effective = 1;
im->pre[0] = '\0';
g_object_get(im, "input-purpose", &purpose, NULL);
im->private = isprivate(purpose);
g_signal_connect(im, "notify::input-purpose",
G_CALLBACK(purposechanged), NULL);
g_signal_connect(im, "commit", G_CALLBACK(simplecommit), im);
g_signal_connect(im, "preedit-end", G_CALLBACK(simpleend), im);
}
static void
@@ -469,6 +530,8 @@ classinit(ImClass *klass)
ic = GTK_IM_CONTEXT_CLASS(klass);
oc = G_OBJECT_CLASS(klass);
parentim = g_type_class_peek_parent(klass);
parentobject = G_OBJECT_CLASS(parentim);
ic->filter_keypress = kpress;
ic->get_preedit_string = getpreedit;
ic->reset = reset;
@@ -476,7 +539,7 @@ classinit(ImClass *klass)
ic->set_use_preedit = setusepreedit;
ic->set_client_window = setclientwindow;
ic->set_cursor_location = setcursorlocation;
oc->finalize = finalize;
oc->dispose = dispose;
}
static const GtkIMContextInfo info = {
@@ -501,7 +564,8 @@ im_module_init(GTypeModule *mod)
0,
(GInstanceInitFunc)init,
};
imtype = g_type_module_register_type(mod, GTK_TYPE_IM_CONTEXT, "strans-gtk", &ti, 0);
imtype = g_type_module_register_type(mod, GTK_TYPE_IM_CONTEXT_SIMPLE,
"strans-gtk", &ti, 0);
}
G_MODULE_EXPORT void