fix: repair IBus context lifecycle
This commit is contained in:
302
ibus.c
302
ibus.c
@@ -14,7 +14,9 @@ enum
|
||||
{
|
||||
Maxwatches = 32,
|
||||
Maxconns = 16,
|
||||
Maxcontexts = 64,
|
||||
Relmask = 1<<30,
|
||||
Ibussupermask = 1<<26,
|
||||
};
|
||||
|
||||
typedef struct Watch Watch;
|
||||
@@ -23,24 +25,41 @@ struct Watch
|
||||
DBusWatch *w;
|
||||
};
|
||||
|
||||
typedef struct Ictx Ictx;
|
||||
struct Ictx
|
||||
{
|
||||
DBusConnection *conn;
|
||||
char path[64];
|
||||
uvlong owner;
|
||||
int focused;
|
||||
Caret caret;
|
||||
};
|
||||
|
||||
static Watch watches[Maxwatches];
|
||||
static int nwatches;
|
||||
static DBusConnection *conns[Maxconns];
|
||||
static int nconns;
|
||||
static DBusServer *srv;
|
||||
static char addrfile[256];
|
||||
static Ictx contexts[Maxcontexts];
|
||||
static char addrfile[512];
|
||||
static dev_t addrdev;
|
||||
static ino_t addrino;
|
||||
static int addrowned;
|
||||
static int icctr;
|
||||
static int busctr;
|
||||
static Channel *replyc;
|
||||
static uvlong owner;
|
||||
|
||||
static DBusHandlerResult onmsg(DBusConnection*, DBusMessage*, void*);
|
||||
|
||||
static void
|
||||
unlinkaddr(void)
|
||||
{
|
||||
if(addrfile[0] != '\0')
|
||||
struct stat st;
|
||||
|
||||
if(addrowned && lstat(addrfile, &st) == 0 &&
|
||||
st.st_dev == addrdev && st.st_ino == addrino)
|
||||
unlink(addrfile);
|
||||
addrowned = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -98,7 +117,8 @@ buildaddrpath(char *buf, int sz)
|
||||
{
|
||||
char mid[64], host[64], num[8], *cfg, *home;
|
||||
struct stat st;
|
||||
char dir[256];
|
||||
char dir[512];
|
||||
int n;
|
||||
|
||||
machineid(mid, sizeof(mid));
|
||||
if(mid[0] == '\0')
|
||||
@@ -106,15 +126,17 @@ buildaddrpath(char *buf, int sz)
|
||||
xdisplay(host, sizeof(host), num, sizeof(num));
|
||||
cfg = getenv("XDG_CONFIG_HOME");
|
||||
if(cfg != nil && cfg[0] != '\0')
|
||||
snprintf(dir, sizeof(dir), "%s/ibus/bus", cfg);
|
||||
n = snprintf(dir, sizeof(dir), "%s/ibus/bus", cfg);
|
||||
else{
|
||||
home = getenv("HOME");
|
||||
if(home == nil)
|
||||
return -1;
|
||||
snprintf(dir, sizeof(dir), "%s/.config/ibus/bus", home);
|
||||
n = snprintf(dir, sizeof(dir), "%s/.config/ibus/bus", home);
|
||||
}
|
||||
if(n < 0 || n >= (int)sizeof dir)
|
||||
return -1;
|
||||
if(stat(dir, &st) < 0){
|
||||
char tmp[256];
|
||||
char tmp[512];
|
||||
char *p;
|
||||
strncpy(tmp, dir, sizeof(tmp));
|
||||
tmp[sizeof(tmp)-1] = '\0';
|
||||
@@ -126,23 +148,55 @@ buildaddrpath(char *buf, int sz)
|
||||
}
|
||||
mkdir(tmp, 0700);
|
||||
}
|
||||
snprintf(buf, sz, "%s/%s-%s-%s", dir, mid, host, num);
|
||||
return 0;
|
||||
n = snprintf(buf, sz, "%s/%s-%s-%s", dir, mid, host, num);
|
||||
return n < 0 || n >= sz ? -1 : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
writeaddr(char *path, char *addr)
|
||||
{
|
||||
char tmp[576];
|
||||
FILE *fp;
|
||||
struct stat st;
|
||||
int fd, n, ok;
|
||||
|
||||
fp = fopen(path, "w");
|
||||
if(fp == nil)
|
||||
n = snprintf(tmp, sizeof tmp, "%s.tmp.XXXXXX", path);
|
||||
if(n < 0 || n >= (int)sizeof tmp)
|
||||
return -1;
|
||||
fprintf(fp, "IBUS_ADDRESS=%s\n", addr);
|
||||
fprintf(fp, "IBUS_DAEMON_PID=%d\n", (int)getpid());
|
||||
fd = mkstemp(tmp);
|
||||
if(fd < 0)
|
||||
return -1;
|
||||
ok = 0;
|
||||
fp = nil;
|
||||
if(fchmod(fd, 0600) < 0)
|
||||
goto out;
|
||||
fp = fdopen(fd, "w");
|
||||
if(fp == nil)
|
||||
goto out;
|
||||
fd = -1;
|
||||
if(fprintf(fp, "IBUS_ADDRESS=%s\n", addr) < 0 ||
|
||||
fprintf(fp, "IBUS_DAEMON_PID=%d\n", (int)getpid()) < 0 ||
|
||||
fflush(fp) < 0 || fsync(fileno(fp)) < 0)
|
||||
goto out;
|
||||
if(fclose(fp) < 0){
|
||||
fp = nil;
|
||||
goto out;
|
||||
}
|
||||
fp = nil;
|
||||
if(rename(tmp, path) < 0 || lstat(path, &st) < 0)
|
||||
goto out;
|
||||
addrdev = st.st_dev;
|
||||
addrino = st.st_ino;
|
||||
addrowned = 1;
|
||||
ok = 1;
|
||||
out:
|
||||
if(fp != nil)
|
||||
fclose(fp);
|
||||
chmod(path, 0600);
|
||||
return 0;
|
||||
if(fd >= 0)
|
||||
close(fd);
|
||||
if(!ok)
|
||||
unlink(tmp);
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
static dbus_bool_t
|
||||
@@ -204,38 +258,72 @@ mget(u32int state)
|
||||
if(state & (1<<0)) m |= Mshift;
|
||||
if(state & (1<<2)) m |= Mctrl;
|
||||
if(state & (1<<3)) m |= Malt;
|
||||
if(state & (1<<6)) m |= Msuper;
|
||||
if(state & ((1<<6)|Ibussupermask)) m |= Msuper;
|
||||
return m;
|
||||
}
|
||||
|
||||
static Ictx*
|
||||
findcontext(DBusConnection *conn, const char *path)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < nelem(contexts); i++)
|
||||
if(contexts[i].conn == conn && strcmp(contexts[i].path, path) == 0)
|
||||
return &contexts[i];
|
||||
return nil;
|
||||
}
|
||||
|
||||
static Ictx*
|
||||
newcontext(DBusConnection *conn, const char *path)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < nelem(contexts); i++)
|
||||
if(contexts[i].conn == nil){
|
||||
memset(&contexts[i], 0, sizeof contexts[i]);
|
||||
contexts[i].conn = conn;
|
||||
contexts[i].owner = ownernew();
|
||||
strncpy(contexts[i].path, path, sizeof contexts[i].path);
|
||||
contexts[i].path[sizeof contexts[i].path-1] = '\0';
|
||||
return &contexts[i];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
static void
|
||||
sendkey(u32int ks, u32int mod, Keyres *res)
|
||||
sendrequest(Ictx *ctx, int op, u32int ks, u32int mod, Keyres *res)
|
||||
{
|
||||
Keyreq kr;
|
||||
|
||||
memset(&kr, 0, sizeof kr);
|
||||
kr.owner = owner;
|
||||
kr.op = Keypress;
|
||||
kr.owner = ctx->owner;
|
||||
kr.op = op;
|
||||
kr.ks = ks;
|
||||
kr.mod = mod;
|
||||
kr.want = 1;
|
||||
kr.caret = ctx->caret;
|
||||
kr.want = op == Keypress;
|
||||
kr.reply = replyc;
|
||||
chansend(keyc, &kr);
|
||||
chanrecv(replyc, res);
|
||||
}
|
||||
|
||||
static void
|
||||
sendreset(void)
|
||||
releasecontext(Ictx *ctx)
|
||||
{
|
||||
Keyreq kr;
|
||||
Keyres res;
|
||||
|
||||
memset(&kr, 0, sizeof kr);
|
||||
kr.owner = owner;
|
||||
kr.op = Keyreset;
|
||||
kr.reply = replyc;
|
||||
chansend(keyc, &kr);
|
||||
chanrecv(replyc, &res);
|
||||
if(replyc != nil)
|
||||
sendrequest(ctx, Keyrelease, 0, 0, &res);
|
||||
ctx->focused = 0;
|
||||
memset(&ctx->caret, 0, sizeof ctx->caret);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
dropcontext(Ictx *ctx)
|
||||
{
|
||||
releasecontext(ctx);
|
||||
memset(ctx, 0, sizeof *ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -318,15 +406,53 @@ handlehello(DBusConnection *c, DBusMessage *m)
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
static DBusHandlerResult
|
||||
handleerror(DBusConnection *c, DBusMessage *m, const char *name,
|
||||
const char *text)
|
||||
{
|
||||
DBusMessage *r;
|
||||
|
||||
r = dbus_message_new_error(m, name, text);
|
||||
if(r != nil){
|
||||
dbus_connection_send(c, r, nil);
|
||||
dbus_message_unref(r);
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
static DBusHandlerResult
|
||||
handlebool(DBusConnection *c, DBusMessage *m, int value)
|
||||
{
|
||||
DBusMessage *r;
|
||||
dbus_bool_t b;
|
||||
|
||||
r = dbus_message_new_method_return(m);
|
||||
b = value ? TRUE : FALSE;
|
||||
dbus_message_append_args(r, DBUS_TYPE_BOOLEAN, &b, DBUS_TYPE_INVALID);
|
||||
dbus_connection_send(c, r, nil);
|
||||
dbus_message_unref(r);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
static DBusHandlerResult
|
||||
handlecreate(DBusConnection *c, DBusMessage *m)
|
||||
{
|
||||
DBusMessage *r;
|
||||
char path[64];
|
||||
const char *pp;
|
||||
Ictx *ctx;
|
||||
int n;
|
||||
|
||||
icctr++;
|
||||
snprintf(path, sizeof(path), "/org/freedesktop/IBus/InputContext_%d", icctr);
|
||||
n = snprintf(path, sizeof(path),
|
||||
"/org/freedesktop/IBus/InputContext_%d", icctr);
|
||||
if(n < 0 || n >= (int)sizeof path)
|
||||
return handleerror(c, m, DBUS_ERROR_LIMITS_EXCEEDED,
|
||||
"input context path exhausted");
|
||||
ctx = newcontext(c, path);
|
||||
if(ctx == nil)
|
||||
return handleerror(c, m, DBUS_ERROR_LIMITS_EXCEEDED,
|
||||
"too many input contexts");
|
||||
pp = path;
|
||||
r = dbus_message_new_method_return(m);
|
||||
dbus_message_append_args(r, DBUS_TYPE_OBJECT_PATH, &pp, DBUS_TYPE_INVALID);
|
||||
@@ -336,14 +462,12 @@ handlecreate(DBusConnection *c, DBusMessage *m)
|
||||
}
|
||||
|
||||
static DBusHandlerResult
|
||||
handlekey(DBusConnection *c, DBusMessage *m)
|
||||
handlekey(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
||||
{
|
||||
DBusMessage *r;
|
||||
DBusError err;
|
||||
dbus_uint32_t sym, code, state;
|
||||
Keyres res;
|
||||
char commit[Maxutf], preedit[Maxutf];
|
||||
dbus_bool_t b;
|
||||
u32int ks, mod;
|
||||
|
||||
dbus_error_init(&err);
|
||||
@@ -356,28 +480,17 @@ handlekey(DBusConnection *c, DBusMessage *m)
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
USED(code);
|
||||
if(state & Relmask){
|
||||
r = dbus_message_new_method_return(m);
|
||||
b = FALSE;
|
||||
dbus_message_append_args(r, DBUS_TYPE_BOOLEAN, &b, DBUS_TYPE_INVALID);
|
||||
dbus_connection_send(c, r, nil);
|
||||
dbus_message_unref(r);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
if(state & Relmask || !ctx->focused)
|
||||
return handlebool(c, m, 0);
|
||||
ks = kget(sym);
|
||||
mod = mget(state);
|
||||
sendkey(ks, mod, &res);
|
||||
sendrequest(ctx, Keypress, ks, mod, &res);
|
||||
stoutf(&res.commit, commit, sizeof commit);
|
||||
stoutf(&res.preedit, preedit, sizeof preedit);
|
||||
if(commit[0] != '\0')
|
||||
emitcommit(c, dbus_message_get_path(m), commit);
|
||||
emitpreedit(c, dbus_message_get_path(m), preedit);
|
||||
r = dbus_message_new_method_return(m);
|
||||
b = res.eaten ? TRUE : FALSE;
|
||||
dbus_message_append_args(r, DBUS_TYPE_BOOLEAN, &b, DBUS_TYPE_INVALID);
|
||||
dbus_connection_send(c, r, nil);
|
||||
dbus_message_unref(r);
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
return handlebool(c, m, res.eaten);
|
||||
}
|
||||
|
||||
static DBusHandlerResult
|
||||
@@ -392,13 +505,66 @@ handlenoop(DBusConnection *c, DBusMessage *m)
|
||||
}
|
||||
|
||||
static DBusHandlerResult
|
||||
handlereset(DBusConnection *c, DBusMessage *m)
|
||||
handlereset(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
||||
{
|
||||
sendreset();
|
||||
Keyres res;
|
||||
|
||||
sendrequest(ctx, Keyreset, 0, 0, &res);
|
||||
emitpreedit(c, dbus_message_get_path(m), "");
|
||||
return handlenoop(c, m);
|
||||
}
|
||||
|
||||
static DBusHandlerResult
|
||||
handlefocusin(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
||||
{
|
||||
ctx->focused = 1;
|
||||
return handlenoop(c, m);
|
||||
}
|
||||
|
||||
static DBusHandlerResult
|
||||
handlefocusout(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
||||
{
|
||||
releasecontext(ctx);
|
||||
emitpreedit(c, dbus_message_get_path(m), "");
|
||||
return handlenoop(c, m);
|
||||
}
|
||||
|
||||
static DBusHandlerResult
|
||||
handledestroy(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
||||
{
|
||||
dropcontext(ctx);
|
||||
return handlenoop(c, m);
|
||||
}
|
||||
|
||||
static DBusHandlerResult
|
||||
handlecursor(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
||||
{
|
||||
DBusError err;
|
||||
dbus_int32_t x, y, w, h;
|
||||
Keyres res;
|
||||
|
||||
dbus_error_init(&err);
|
||||
if(!dbus_message_get_args(m, &err,
|
||||
DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y,
|
||||
DBUS_TYPE_INT32, &w, DBUS_TYPE_INT32, &h,
|
||||
DBUS_TYPE_INVALID)){
|
||||
dbus_error_free(&err);
|
||||
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
||||
"SetCursorLocation expects four integers");
|
||||
}
|
||||
if(w < 0 || h < 0)
|
||||
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
||||
"cursor dimensions must not be negative");
|
||||
ctx->caret.valid = 1;
|
||||
ctx->caret.x = x;
|
||||
ctx->caret.y = y;
|
||||
ctx->caret.w = w;
|
||||
ctx->caret.h = h;
|
||||
if(ctx->focused)
|
||||
sendrequest(ctx, Keycaret, 0, 0, &res);
|
||||
return handlenoop(c, m);
|
||||
}
|
||||
|
||||
static const char introspectxml[] =
|
||||
"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
|
||||
" \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
|
||||
@@ -448,6 +614,7 @@ static DBusHandlerResult
|
||||
onmsg(DBusConnection *c, DBusMessage *m, void *_)
|
||||
{
|
||||
const char *iface, *member, *path;
|
||||
Ictx *ctx;
|
||||
|
||||
USED(_);
|
||||
if(dbus_message_get_type(m) != DBUS_MESSAGE_TYPE_METHOD_CALL)
|
||||
@@ -472,15 +639,23 @@ onmsg(DBusConnection *c, DBusMessage *m, void *_)
|
||||
return handlecreate(c, m);
|
||||
}
|
||||
if(strcmp(iface, "org.freedesktop.IBus.InputContext") == 0){
|
||||
ctx = findcontext(c, path);
|
||||
if(ctx == nil)
|
||||
return handleerror(c, m, DBUS_ERROR_UNKNOWN_OBJECT,
|
||||
"unknown input context");
|
||||
if(strcmp(member, "ProcessKeyEvent") == 0)
|
||||
return handlekey(c, m);
|
||||
if(strcmp(member, "FocusOut") == 0
|
||||
|| strcmp(member, "Reset") == 0)
|
||||
return handlereset(c, m);
|
||||
if(strcmp(member, "FocusIn") == 0
|
||||
|| strcmp(member, "Destroy") == 0
|
||||
|| strcmp(member, "SetCapabilities") == 0
|
||||
|| strcmp(member, "SetCursorLocation") == 0
|
||||
return handlekey(c, m, ctx);
|
||||
if(strcmp(member, "FocusIn") == 0)
|
||||
return handlefocusin(c, m, ctx);
|
||||
if(strcmp(member, "FocusOut") == 0)
|
||||
return handlefocusout(c, m, ctx);
|
||||
if(strcmp(member, "Reset") == 0)
|
||||
return handlereset(c, m, ctx);
|
||||
if(strcmp(member, "Destroy") == 0)
|
||||
return handledestroy(c, m, ctx);
|
||||
if(strcmp(member, "SetCursorLocation") == 0)
|
||||
return handlecursor(c, m, ctx);
|
||||
if(strcmp(member, "SetCapabilities") == 0
|
||||
|| strcmp(member, "SetEngine") == 0)
|
||||
return handlenoop(c, m);
|
||||
}
|
||||
@@ -507,7 +682,7 @@ newconn(DBusServer *s, DBusConnection *c, void *_)
|
||||
static void
|
||||
pruneconns(void)
|
||||
{
|
||||
int i, j;
|
||||
int i, j, k;
|
||||
|
||||
j = 0;
|
||||
for(i = 0; i < nconns; i++){
|
||||
@@ -515,6 +690,9 @@ pruneconns(void)
|
||||
conns[j++] = conns[i];
|
||||
continue;
|
||||
}
|
||||
for(k = 0; k < nelem(contexts); k++)
|
||||
if(contexts[k].conn == conns[i])
|
||||
dropcontext(&contexts[k]);
|
||||
dbus_connection_unref(conns[i]);
|
||||
}
|
||||
nconns = j;
|
||||
@@ -531,8 +709,11 @@ ibusinit(void)
|
||||
fprintf(stderr, "strans: ibus: cannot build address path\n");
|
||||
return -1;
|
||||
}
|
||||
unlink(addrfile);
|
||||
snprintf(addr, sizeof(addr), "unix:abstract=strans-%d", (int)getpid());
|
||||
if(snprintf(addr, sizeof(addr), "unix:abstract=strans-%d",
|
||||
(int)getpid()) >= (int)sizeof addr){
|
||||
fprintf(stderr, "strans: ibus: address is too long\n");
|
||||
return -1;
|
||||
}
|
||||
dbus_error_init(&err);
|
||||
srv = dbus_server_listen(addr, &err);
|
||||
if(srv == nil){
|
||||
@@ -571,7 +752,6 @@ ibusthread(void *_)
|
||||
if(ibusinit() < 0)
|
||||
return;
|
||||
replyc = chancreate(sizeof(Keyres), 0);
|
||||
owner = ownernew();
|
||||
for(;;){
|
||||
n = 0;
|
||||
for(i = 0; i < nwatches && n < Maxwatches; i++){
|
||||
|
||||
Reference in New Issue
Block a user