ibus: the text before the cursor, and the runes to take back
RequireSurroundingText asks the client to send its text; it arrives through SetSurroundingText as an IBusText and a cursor counted in runes, and DeleteSurroundingText asks for runes back before the commit that replaces them. The official libibus client now proves the whole turn: 한자 typed, the Hanja key, Enter, and 漢字 arrives with the 한 taken away. A client that has set EffectivePostProcessKeyEvent is never asked for its text. It reads the key's commits back after ProcessKeyEvent returns, and a DeleteSurroundingText signal is not one of the things that reply can carry, so the deletion would land after the text it was meant to make room for and eat the wrong runes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
92
ibus.c
92
ibus.c
@@ -16,6 +16,7 @@ enum
|
||||
Relmask = 1<<30,
|
||||
/* IBus wire constants; the daemon does not link libibus. */
|
||||
Ibuscappreedit = 1<<0,
|
||||
Ibuscapsurround = 1<<5,
|
||||
Ibusattrunderline = 1,
|
||||
Ibusunderlinesingle = 1,
|
||||
Ibuspreeditclear = 0,
|
||||
@@ -51,6 +52,7 @@ struct Ictx
|
||||
int npost;
|
||||
Post post[Maxpost];
|
||||
Caret caret;
|
||||
Str surround; /* the client's own text just before the cursor */
|
||||
};
|
||||
|
||||
static DBusWatch *watches[Maxwatches];
|
||||
@@ -304,6 +306,11 @@ sendrequest(Ictx *ctx, int op, u32int ks, u32int mod, Keyres *res)
|
||||
kr.ks = ks;
|
||||
kr.mod = mod;
|
||||
kr.caret = ctx->caret;
|
||||
/* A client that reads the key's effects back afterwards takes a
|
||||
* DeleteSurroundingText signal after them, which is too late to be
|
||||
* a taking back: it is never asked for text it cannot lose. */
|
||||
if(!ctx->postprocess)
|
||||
kr.surround = ctx->surround;
|
||||
kr.reply = replyc;
|
||||
chansend(keyc, &kr);
|
||||
chanrecv(replyc, res);
|
||||
@@ -464,6 +471,34 @@ emitpreedit(Ictx *ctx, const char *text)
|
||||
preowner = nil;
|
||||
}
|
||||
|
||||
/* The client is to take back the runes the reading reached into. */
|
||||
static void
|
||||
emitdelete(Ictx *ctx, int n)
|
||||
{
|
||||
DBusMessage *sig;
|
||||
dbus_int32_t off;
|
||||
dbus_uint32_t nrune;
|
||||
|
||||
off = -n;
|
||||
nrune = n;
|
||||
sig = newsignal(ctx->path, "DeleteSurroundingText");
|
||||
dbus_message_append_args(sig, DBUS_TYPE_INT32, &off,
|
||||
DBUS_TYPE_UINT32, &nrune, DBUS_TYPE_INVALID);
|
||||
dbus_connection_send(ctx->conn, sig, nil);
|
||||
dbus_message_unref(sig);
|
||||
}
|
||||
|
||||
/* Nothing arrives until the client is asked to send it. */
|
||||
static void
|
||||
emitrequiresurround(Ictx *ctx)
|
||||
{
|
||||
DBusMessage *sig;
|
||||
|
||||
sig = newsignal(ctx->path, "RequireSurroundingText");
|
||||
dbus_connection_send(ctx->conn, sig, nil);
|
||||
dbus_message_unref(sig);
|
||||
}
|
||||
|
||||
/* Another frontend may have taken the engine; then our preedit is stale. */
|
||||
static void
|
||||
checkpreowner(void)
|
||||
@@ -665,6 +700,8 @@ handlekey(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
||||
ctx->keying = 1;
|
||||
if(restart)
|
||||
emitpreedit(ctx, "");
|
||||
if(res.del > 0)
|
||||
emitdelete(ctx, res.del);
|
||||
if(commit[0] != '\0')
|
||||
emitcommit(ctx, commit);
|
||||
if(text[0] != '\0')
|
||||
@@ -732,6 +769,7 @@ handlefocusout(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
||||
flushcontext(ctx, Keyrelease);
|
||||
ctx->focused = 0;
|
||||
memset(&ctx->caret, 0, sizeof ctx->caret);
|
||||
sclear(&ctx->surround);
|
||||
return reply(c, m, DBUS_TYPE_INVALID, nil);
|
||||
}
|
||||
|
||||
@@ -746,6 +784,8 @@ handlecap(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
||||
dbus_message_get_args(m, nil, DBUS_TYPE_UINT32, &cap, DBUS_TYPE_INVALID);
|
||||
old = ctx->cap;
|
||||
ctx->cap = cap;
|
||||
if((cap & ~old) & Ibuscapsurround)
|
||||
emitrequiresurround(ctx);
|
||||
if(ctx->focused){
|
||||
sendrequest(ctx, Keycap, 0, 0, &res);
|
||||
if((old & Ibuscappreedit) != (ctx->cap & Ibuscappreedit)){
|
||||
@@ -759,6 +799,57 @@ handlecap(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
||||
return reply(c, m, DBUS_TYPE_INVALID, nil);
|
||||
}
|
||||
|
||||
/* IBusText is (sa{sv}sv); the string in it is all we want. */
|
||||
static int
|
||||
getibustext(DBusMessageIter *value, const char **s)
|
||||
{
|
||||
DBusMessageIter st;
|
||||
int i;
|
||||
|
||||
if(dbus_message_iter_get_arg_type(value) != DBUS_TYPE_STRUCT)
|
||||
return 0;
|
||||
dbus_message_iter_recurse(value, &st);
|
||||
for(i = 0; i < 2; i++)
|
||||
if(!dbus_message_iter_next(&st))
|
||||
return 0;
|
||||
if(dbus_message_iter_get_arg_type(&st) != DBUS_TYPE_STRING)
|
||||
return 0;
|
||||
dbus_message_iter_get_basic(&st, s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The bytes the first n runes of s take; IBus counts a cursor in runes. */
|
||||
static int
|
||||
runebytes(const char *s, int n)
|
||||
{
|
||||
Rune r;
|
||||
char *p;
|
||||
|
||||
for(p = (char*)s; n > 0 && *p != '\0'; n--)
|
||||
p += chartorune(&r, p);
|
||||
return p - s;
|
||||
}
|
||||
|
||||
/* The text around the cursor; a reading can use what comes before it. */
|
||||
static DBusHandlerResult
|
||||
handlesurround(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
||||
{
|
||||
DBusMessageIter it, v;
|
||||
dbus_uint32_t cursor;
|
||||
const char *s;
|
||||
|
||||
if(!dbus_message_iter_init(m, &it))
|
||||
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
||||
"SetSurroundingText expects text, cursor, and anchor");
|
||||
dbus_message_iter_recurse(&it, &v);
|
||||
if(!getibustext(&v, &s) || !dbus_message_iter_next(&it))
|
||||
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
||||
"SetSurroundingText expects an IBusText");
|
||||
dbus_message_iter_get_basic(&it, &cursor);
|
||||
stail(&ctx->surround, (char*)s, runebytes(s, cursor));
|
||||
return reply(c, m, DBUS_TYPE_INVALID, nil);
|
||||
}
|
||||
|
||||
static int
|
||||
getproperty(DBusMessage *m, const char **iface, const char **name,
|
||||
DBusMessageIter *value)
|
||||
@@ -913,6 +1004,7 @@ static struct {
|
||||
{"SetCursorLocation", "iiii", handlecursor},
|
||||
{"SetCursorLocationRelative", "iiii", handleok},
|
||||
{"SetCapabilities", "u", handlecap},
|
||||
{"SetSurroundingText", "vuu", handlesurround},
|
||||
{"SetEngine", "s", handleok},
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user