XIM text always went out through imdkit's COMPOUND_TEXT converter, which already wraps every UTF-8 string in ESC%G; the UTF8_STRING negotiation, the per-client encoding list, ximtext.c's fallback and its wrapper never changed a byte on the wire. The spot location is honoured for every style now (GTK's XIM module sends it with PreeditCallbacks) and an unset focus window means the client window, as the spec says, so those clients get the popup at the caret. readattrs/place kept four transient fields to pass values between them; ximclose tore down state right before die(); the OOM passthrough context was a third policy for one small calloc where emalloc dies like everything else. Both frontends now poll for engine-owner loss only while a preedit shows instead of XIM waking on a pipe the engine had to know about; ibus stops waking five times a second when idle. keymeaningful() is the engine's own predicate. The standalone xim_test moved into the unit suite, so xim/Makefile is gone.
1193 lines
31 KiB
C
1193 lines
31 KiB
C
#include "dat.h"
|
|
#include "fn.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <poll.h>
|
|
#include <dbus/dbus.h>
|
|
#include <xkbcommon/xkbcommon.h>
|
|
|
|
enum
|
|
{
|
|
Maxconns = Maxclients,
|
|
Maxwatches = 2*Maxconns + 1,
|
|
Maxcontexts = Maxclients,
|
|
Relmask = 1<<30,
|
|
Ownerpoll = 200, /* ms between owner checks while a preedit shows */
|
|
/* IBus wire constants; the daemon does not link libibus. */
|
|
Ibuscappreedit = 1<<0,
|
|
Ibuspurposepassword = 8,
|
|
Ibuspurposepin = 9,
|
|
Ibushinthidden = 1<<12,
|
|
Ibusattrunderline = 1,
|
|
Ibusunderlinesingle = 1,
|
|
Ibuspreeditclear = 0,
|
|
};
|
|
|
|
typedef struct Ictx Ictx;
|
|
struct Ictx
|
|
{
|
|
DBusConnection *conn;
|
|
char path[64];
|
|
int focused;
|
|
u32int cap;
|
|
u32int purpose;
|
|
u32int hints;
|
|
int clientcommitpreedit;
|
|
Caret caret;
|
|
};
|
|
|
|
static DBusWatch *watches[Maxwatches];
|
|
static int nwatches;
|
|
static DBusConnection *conns[Maxconns];
|
|
static int nconns;
|
|
static DBusServer *srv;
|
|
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 const char ibusowner[] = ":1.0";
|
|
static Ictx *preowner;
|
|
|
|
static DBusHandlerResult onmsg(DBusConnection*, DBusMessage*, void*);
|
|
static DBusHandlerResult handleerror(DBusConnection*, DBusMessage*,
|
|
const char*, const char*);
|
|
static void checkpreowner(void);
|
|
|
|
static void
|
|
unlinkaddr(void)
|
|
{
|
|
struct stat st;
|
|
|
|
if(addrowned && lstat(addrfile, &st) == 0 &&
|
|
st.st_dev == addrdev && st.st_ino == addrino)
|
|
unlink(addrfile);
|
|
addrowned = 0;
|
|
}
|
|
|
|
static void
|
|
machineidfiles(char *buf, int sz, char **path, int npath)
|
|
{
|
|
int fd, i, n;
|
|
|
|
for(i = 0; i < npath; i++){
|
|
fd = open(path[i], 0);
|
|
if(fd < 0)
|
|
continue;
|
|
n = read(fd, buf, sz - 1);
|
|
close(fd);
|
|
if(n <= 0)
|
|
continue;
|
|
buf[n] = '\0';
|
|
while(n > 0 && (buf[n-1] == '\n' || buf[n-1] == '\r'))
|
|
buf[--n] = '\0';
|
|
if(n > 0)
|
|
return;
|
|
}
|
|
buf[0] = '\0';
|
|
}
|
|
|
|
static void
|
|
machineid(char *buf, int sz)
|
|
{
|
|
char *path[] = {
|
|
"/etc/machine-id",
|
|
"/var/lib/dbus/machine-id",
|
|
};
|
|
|
|
machineidfiles(buf, sz, path, nelem(path));
|
|
}
|
|
|
|
static void
|
|
xdisplay(char *host, int hsz, char *num, int nsz)
|
|
{
|
|
char *d, *colon, *dot, *p;
|
|
int n;
|
|
|
|
strncpy(host, "unix", hsz);
|
|
host[hsz-1] = '\0';
|
|
strncpy(num, "0", nsz);
|
|
num[nsz-1] = '\0';
|
|
d = getenv("DISPLAY");
|
|
if(d == nil || d[0] == '\0')
|
|
return;
|
|
colon = strchr(d, ':');
|
|
if(colon == nil)
|
|
return;
|
|
if(colon > d){
|
|
n = colon - d;
|
|
if(n >= hsz) n = hsz - 1;
|
|
memcpy(host, d, n);
|
|
host[n] = '\0';
|
|
}
|
|
p = colon + 1;
|
|
dot = strchr(p, '.');
|
|
n = dot ? dot - p : (int)strlen(p);
|
|
if(n >= nsz) n = nsz - 1;
|
|
memcpy(num, p, n);
|
|
num[n] = '\0';
|
|
}
|
|
|
|
static int
|
|
buildaddrpath(char *buf, int sz)
|
|
{
|
|
char mid[64], host[64], num[8], *cfg, *home, *explicit;
|
|
struct stat st;
|
|
char dir[512];
|
|
int n;
|
|
|
|
explicit = getenv("IBUS_ADDRESS_FILE");
|
|
if(explicit != nil){
|
|
n = snprintf(buf, sz, "%s", explicit);
|
|
return explicit[0] == '\0' || n < 0 || n >= sz ? -1 : 0;
|
|
}
|
|
machineid(mid, sizeof(mid));
|
|
if(mid[0] == '\0')
|
|
return -1;
|
|
xdisplay(host, sizeof(host), num, sizeof(num));
|
|
cfg = getenv("XDG_CONFIG_HOME");
|
|
if(cfg != nil && cfg[0] != '\0')
|
|
n = snprintf(dir, sizeof(dir), "%s/ibus/bus", cfg);
|
|
else{
|
|
home = getenv("HOME");
|
|
if(home == nil)
|
|
return -1;
|
|
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[512];
|
|
char *p;
|
|
strncpy(tmp, dir, sizeof(tmp));
|
|
tmp[sizeof(tmp)-1] = '\0';
|
|
for(p = tmp+1; *p; p++)
|
|
if(*p == '/'){
|
|
*p = '\0';
|
|
mkdir(tmp, 0700);
|
|
*p = '/';
|
|
}
|
|
mkdir(tmp, 0700);
|
|
}
|
|
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;
|
|
|
|
n = snprintf(tmp, sizeof tmp, "%s.tmp.XXXXXX", path);
|
|
if(n < 0 || n >= (int)sizeof tmp)
|
|
return -1;
|
|
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 ||
|
|
fstat(fileno(fp), &st) < 0)
|
|
goto out;
|
|
if(fclose(fp) < 0){
|
|
fp = nil;
|
|
goto out;
|
|
}
|
|
fp = nil;
|
|
if(rename(tmp, path) < 0)
|
|
goto out;
|
|
addrdev = st.st_dev;
|
|
addrino = st.st_ino;
|
|
addrowned = 1;
|
|
ok = 1;
|
|
out:
|
|
if(fp != nil)
|
|
fclose(fp);
|
|
if(fd >= 0)
|
|
close(fd);
|
|
if(!ok)
|
|
unlink(tmp);
|
|
return ok ? 0 : -1;
|
|
}
|
|
|
|
static dbus_bool_t
|
|
addwatch(DBusWatch *w, void *_)
|
|
{
|
|
int i;
|
|
|
|
USED(_);
|
|
for(i = 0; i < nwatches; i++)
|
|
if(watches[i] == nil){
|
|
watches[i] = w;
|
|
return TRUE;
|
|
}
|
|
if(nwatches >= Maxwatches)
|
|
return FALSE;
|
|
watches[nwatches++] = w;
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
removewatch(DBusWatch *w, void *_)
|
|
{
|
|
int i;
|
|
|
|
USED(_);
|
|
for(i = 0; i < nwatches; i++)
|
|
if(watches[i] == w){
|
|
watches[i] = nil;
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
togglewatch(DBusWatch *w, void *_)
|
|
{
|
|
USED(w);
|
|
USED(_);
|
|
}
|
|
|
|
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;
|
|
strncpy(contexts[i].path, path, sizeof contexts[i].path);
|
|
contexts[i].path[sizeof contexts[i].path-1] = '\0';
|
|
return &contexts[i];
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
static int
|
|
clientpreedit(Ictx *ctx)
|
|
{
|
|
return (ctx->cap & Ibuscappreedit) != 0;
|
|
}
|
|
|
|
static void
|
|
sendrequest(Ictx *ctx, int op, u32int ks, u32int mod, Keyres *res)
|
|
{
|
|
Keyreq kr;
|
|
|
|
memset(&kr, 0, sizeof kr);
|
|
kr.owner = ctx;
|
|
kr.cap = clientpreedit(ctx) ? Cclientpreedit : 0;
|
|
kr.op = op;
|
|
kr.ks = ks;
|
|
kr.mod = mod;
|
|
kr.caret = ctx->caret;
|
|
kr.reply = replyc;
|
|
chansend(keyc, &kr);
|
|
chanrecv(replyc, res);
|
|
}
|
|
|
|
static void
|
|
releasecontext(Ictx *ctx)
|
|
{
|
|
Keyres res;
|
|
|
|
if(ctx->focused)
|
|
sendrequest(ctx, Keyrelease, 0, 0, &res);
|
|
ctx->focused = 0;
|
|
memset(&ctx->caret, 0, sizeof ctx->caret);
|
|
}
|
|
|
|
static void
|
|
dropcontext(Ictx *ctx)
|
|
{
|
|
releasecontext(ctx);
|
|
if(preowner == ctx)
|
|
preowner = nil;
|
|
memset(ctx, 0, sizeof *ctx);
|
|
}
|
|
|
|
static void
|
|
dropconncontexts(DBusConnection *conn)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < nelem(contexts); i++)
|
|
if(contexts[i].conn == conn)
|
|
dropcontext(&contexts[i]);
|
|
}
|
|
|
|
static int
|
|
hidden(Ictx *ctx)
|
|
{
|
|
return ctx->purpose == Ibuspurposepassword ||
|
|
ctx->purpose == Ibuspurposepin ||
|
|
(ctx->hints & Ibushinthidden) != 0;
|
|
}
|
|
|
|
static int
|
|
processkey(Ictx *ctx, u32int sym, u32int state, Keyres *res)
|
|
{
|
|
if(state & Relmask || !ctx->focused || hidden(ctx))
|
|
return 0;
|
|
sendrequest(ctx, Keypress, ipckeysym(sym, xkb_keysym_to_utf32(sym)),
|
|
ipcmod(state), res);
|
|
if(preowner != nil && preowner != ctx)
|
|
checkpreowner();
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
setcursor(Ictx *ctx, int x, int y, int h)
|
|
{
|
|
Keyres res;
|
|
|
|
ctx->caret.valid = 1;
|
|
ctx->caret.x = x;
|
|
ctx->caret.y = y;
|
|
ctx->caret.h = h;
|
|
if(ctx->focused)
|
|
sendrequest(ctx, Keycaret, 0, 0, &res);
|
|
}
|
|
|
|
static void
|
|
appendibustext(DBusMessageIter *it, const char *s, int underline)
|
|
{
|
|
DBusMessageIter v, st, attach, alv, ali, attr, alist;
|
|
DBusMessageIter av, ai, aattach;
|
|
const char *name = "IBusText";
|
|
const char *aname = "IBusAttrList";
|
|
const char *iname = "IBusAttribute";
|
|
dbus_uint32_t type, value, start, end;
|
|
|
|
dbus_message_iter_open_container(it, DBUS_TYPE_VARIANT, "(sa{sv}sv)", &v);
|
|
dbus_message_iter_open_container(&v, DBUS_TYPE_STRUCT, nil, &st);
|
|
dbus_message_iter_append_basic(&st, DBUS_TYPE_STRING, &name);
|
|
dbus_message_iter_open_container(&st, DBUS_TYPE_ARRAY, "{sv}", &attach);
|
|
dbus_message_iter_close_container(&st, &attach);
|
|
dbus_message_iter_append_basic(&st, DBUS_TYPE_STRING, &s);
|
|
dbus_message_iter_open_container(&st, DBUS_TYPE_VARIANT, "(sa{sv}av)", &alv);
|
|
dbus_message_iter_open_container(&alv, DBUS_TYPE_STRUCT, nil, &ali);
|
|
dbus_message_iter_append_basic(&ali, DBUS_TYPE_STRING, &aname);
|
|
dbus_message_iter_open_container(&ali, DBUS_TYPE_ARRAY, "{sv}", &attr);
|
|
dbus_message_iter_close_container(&ali, &attr);
|
|
dbus_message_iter_open_container(&ali, DBUS_TYPE_ARRAY, "v", &alist);
|
|
if(underline && s[0] != '\0'){
|
|
type = Ibusattrunderline;
|
|
value = Ibusunderlinesingle;
|
|
start = 0;
|
|
end = utflen((char*)s);
|
|
dbus_message_iter_open_container(&alist, DBUS_TYPE_VARIANT,
|
|
"(sa{sv}uuuu)", &av);
|
|
dbus_message_iter_open_container(&av, DBUS_TYPE_STRUCT, nil, &ai);
|
|
dbus_message_iter_append_basic(&ai, DBUS_TYPE_STRING, &iname);
|
|
dbus_message_iter_open_container(&ai, DBUS_TYPE_ARRAY, "{sv}",
|
|
&aattach);
|
|
dbus_message_iter_close_container(&ai, &aattach);
|
|
dbus_message_iter_append_basic(&ai, DBUS_TYPE_UINT32, &type);
|
|
dbus_message_iter_append_basic(&ai, DBUS_TYPE_UINT32, &value);
|
|
dbus_message_iter_append_basic(&ai, DBUS_TYPE_UINT32, &start);
|
|
dbus_message_iter_append_basic(&ai, DBUS_TYPE_UINT32, &end);
|
|
dbus_message_iter_close_container(&av, &ai);
|
|
dbus_message_iter_close_container(&alist, &av);
|
|
}
|
|
dbus_message_iter_close_container(&ali, &alist);
|
|
dbus_message_iter_close_container(&alv, &ali);
|
|
dbus_message_iter_close_container(&st, &alv);
|
|
dbus_message_iter_close_container(&v, &st);
|
|
dbus_message_iter_close_container(it, &v);
|
|
}
|
|
|
|
static void
|
|
emitcommit(DBusConnection *c, const char *path, const char *text)
|
|
{
|
|
DBusMessage *sig;
|
|
DBusMessageIter it;
|
|
|
|
sig = dbus_message_new_signal(path, "org.freedesktop.IBus.InputContext", "CommitText");
|
|
if(sig == nil)
|
|
return;
|
|
dbus_message_iter_init_append(sig, &it);
|
|
appendibustext(&it, text, 0);
|
|
dbus_message_set_sender(sig, ibusowner);
|
|
dbus_connection_send(c, sig, nil);
|
|
dbus_message_unref(sig);
|
|
}
|
|
|
|
static void
|
|
emitpreedit(Ictx *ctx, const char *text)
|
|
{
|
|
DBusMessage *sig;
|
|
DBusMessageIter it;
|
|
dbus_uint32_t cursor, mode;
|
|
dbus_bool_t visible;
|
|
const char *name;
|
|
|
|
name = ctx->clientcommitpreedit ?
|
|
"UpdatePreeditTextWithMode" : "UpdatePreeditText";
|
|
sig = dbus_message_new_signal(ctx->path,
|
|
"org.freedesktop.IBus.InputContext", name);
|
|
if(sig == nil)
|
|
return;
|
|
dbus_message_iter_init_append(sig, &it);
|
|
appendibustext(&it, text, 1);
|
|
cursor = utflen((char*)text);
|
|
visible = text[0] != '\0' ? TRUE : FALSE;
|
|
dbus_message_iter_append_basic(&it, DBUS_TYPE_UINT32, &cursor);
|
|
dbus_message_iter_append_basic(&it, DBUS_TYPE_BOOLEAN, &visible);
|
|
if(ctx->clientcommitpreedit){
|
|
mode = Ibuspreeditclear;
|
|
dbus_message_iter_append_basic(&it, DBUS_TYPE_UINT32, &mode);
|
|
}
|
|
dbus_message_set_sender(sig, ibusowner);
|
|
if(dbus_connection_send(ctx->conn, sig, nil)){
|
|
if(text[0] != '\0')
|
|
preowner = ctx;
|
|
else if(preowner == ctx)
|
|
preowner = nil;
|
|
}
|
|
dbus_message_unref(sig);
|
|
}
|
|
|
|
static void
|
|
checkpreowner(void)
|
|
{
|
|
Ictx *ctx;
|
|
Keyres res;
|
|
|
|
ctx = preowner;
|
|
if(ctx == nil)
|
|
return;
|
|
sendrequest(ctx, Keycap, 0, 0, &res);
|
|
if(!res.eaten)
|
|
emitpreedit(ctx, "");
|
|
}
|
|
|
|
static DBusHandlerResult
|
|
handlehello(DBusConnection *c, DBusMessage *m)
|
|
{
|
|
DBusMessage *r;
|
|
char name[32];
|
|
const char *np;
|
|
|
|
if(!dbus_message_has_signature(m, ""))
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"Hello expects no arguments");
|
|
busctr++;
|
|
snprintf(name, sizeof(name), ":1.%d", busctr);
|
|
np = name;
|
|
r = dbus_message_new_method_return(m);
|
|
dbus_message_append_args(r, DBUS_TYPE_STRING, &np, DBUS_TYPE_INVALID);
|
|
dbus_connection_send(c, r, nil);
|
|
dbus_message_unref(r);
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
}
|
|
|
|
static DBusHandlerResult
|
|
handlenameowner(DBusConnection *c, DBusMessage *m)
|
|
{
|
|
DBusError err;
|
|
DBusMessage *r;
|
|
const char *name, *owner;
|
|
|
|
dbus_error_init(&err);
|
|
if(!dbus_message_get_args(m, &err, DBUS_TYPE_STRING, &name,
|
|
DBUS_TYPE_INVALID)){
|
|
dbus_error_free(&err);
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"GetNameOwner expects one bus name");
|
|
}
|
|
if(strcmp(name, "org.freedesktop.IBus") != 0)
|
|
return handleerror(c, m, DBUS_ERROR_NAME_HAS_NO_OWNER,
|
|
"bus name has no owner");
|
|
owner = ibusowner;
|
|
r = dbus_message_new_method_return(m);
|
|
if(r == nil)
|
|
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
|
dbus_message_append_args(r, DBUS_TYPE_STRING, &owner, DBUS_TYPE_INVALID);
|
|
dbus_connection_send(c, r, nil);
|
|
dbus_message_unref(r);
|
|
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;
|
|
|
|
if(!dbus_message_has_signature(m, "s"))
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"CreateInputContext expects one client name");
|
|
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);
|
|
dbus_connection_send(c, r, nil);
|
|
dbus_message_unref(r);
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
}
|
|
|
|
static DBusHandlerResult
|
|
handlekey(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
|
{
|
|
DBusError err;
|
|
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,
|
|
DBUS_TYPE_UINT32, &sym,
|
|
DBUS_TYPE_UINT32, &code,
|
|
DBUS_TYPE_UINT32, &state,
|
|
DBUS_TYPE_INVALID)){
|
|
dbus_error_free(&err);
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"ProcessKeyEvent expects three unsigned integers");
|
|
}
|
|
USED(code);
|
|
if(!processkey(ctx, sym, state, &res))
|
|
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) && (!restart || preedit[0] != '\0'))
|
|
emitpreedit(ctx, preedit);
|
|
return handlebool(c, m, res.eaten);
|
|
}
|
|
|
|
static DBusHandlerResult
|
|
handlenoop(DBusConnection *c, DBusMessage *m)
|
|
{
|
|
DBusMessage *r;
|
|
|
|
r = dbus_message_new_method_return(m);
|
|
dbus_connection_send(c, r, nil);
|
|
dbus_message_unref(r);
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
}
|
|
|
|
static DBusHandlerResult
|
|
handlereset(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
|
{
|
|
Keyres res;
|
|
|
|
sendrequest(ctx, Keyreset, 0, 0, &res);
|
|
if(clientpreedit(ctx))
|
|
emitpreedit(ctx, "");
|
|
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);
|
|
if(clientpreedit(ctx))
|
|
emitpreedit(ctx, "");
|
|
return handlenoop(c, m);
|
|
}
|
|
|
|
static DBusHandlerResult
|
|
handlecap(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
|
{
|
|
DBusError err;
|
|
dbus_uint32_t cap;
|
|
Keyres res;
|
|
char preedit[Maxutf];
|
|
u32int old;
|
|
|
|
dbus_error_init(&err);
|
|
if(!dbus_message_get_args(m, &err, DBUS_TYPE_UINT32, &cap,
|
|
DBUS_TYPE_INVALID)){
|
|
dbus_error_free(&err);
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"SetCapabilities expects one unsigned integer");
|
|
}
|
|
old = ctx->cap;
|
|
ctx->cap = cap;
|
|
if(ctx->focused){
|
|
sendrequest(ctx, Keycap, 0, 0, &res);
|
|
if((old & Ibuscappreedit) != (ctx->cap & Ibuscappreedit)){
|
|
if((ctx->cap & Ibuscappreedit) && res.eaten){
|
|
stoutf(&res.preedit, preedit, sizeof preedit);
|
|
emitpreedit(ctx, preedit);
|
|
}else if(!(ctx->cap & Ibuscappreedit))
|
|
emitpreedit(ctx, "");
|
|
}
|
|
}
|
|
return handlenoop(c, m);
|
|
}
|
|
|
|
static int
|
|
getproperty(DBusMessage *m, const char **iface, const char **name,
|
|
DBusMessageIter *value)
|
|
{
|
|
DBusMessageIter it;
|
|
|
|
if(!dbus_message_has_signature(m, "ssv") ||
|
|
!dbus_message_iter_init(m, &it))
|
|
return 0;
|
|
dbus_message_iter_get_basic(&it, iface);
|
|
dbus_message_iter_next(&it);
|
|
dbus_message_iter_get_basic(&it, name);
|
|
dbus_message_iter_next(&it);
|
|
dbus_message_iter_recurse(&it, value);
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
getcontent(DBusMessageIter *value, u32int *purpose, u32int *hints)
|
|
{
|
|
DBusMessageIter st;
|
|
dbus_uint32_t p, h;
|
|
|
|
if(dbus_message_iter_get_arg_type(value) != DBUS_TYPE_STRUCT)
|
|
return 0;
|
|
dbus_message_iter_recurse(value, &st);
|
|
if(dbus_message_iter_get_arg_type(&st) != DBUS_TYPE_UINT32)
|
|
return 0;
|
|
dbus_message_iter_get_basic(&st, &p);
|
|
if(!dbus_message_iter_next(&st) ||
|
|
dbus_message_iter_get_arg_type(&st) != DBUS_TYPE_UINT32)
|
|
return 0;
|
|
dbus_message_iter_get_basic(&st, &h);
|
|
if(dbus_message_iter_next(&st))
|
|
return 0;
|
|
*purpose = p;
|
|
*hints = h;
|
|
return 1;
|
|
}
|
|
|
|
static DBusHandlerResult
|
|
handlepropertyset(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
|
{
|
|
const char *iface, *name;
|
|
DBusMessageIter value, st;
|
|
dbus_bool_t b;
|
|
u32int purpose, hints;
|
|
Keyres res;
|
|
int washidden;
|
|
|
|
if(!getproperty(m, &iface, &name, &value))
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"Properties.Set expects interface, property, and value");
|
|
if(strcmp(iface, "org.freedesktop.IBus.InputContext") != 0)
|
|
return handleerror(c, m, DBUS_ERROR_UNKNOWN_INTERFACE,
|
|
"unknown property interface");
|
|
if(strcmp(name, "ContentType") == 0){
|
|
if(!getcontent(&value, &purpose, &hints))
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"ContentType expects (uu)");
|
|
washidden = hidden(ctx);
|
|
ctx->purpose = purpose;
|
|
ctx->hints = hints;
|
|
if(ctx->focused && !washidden && hidden(ctx)){
|
|
sendrequest(ctx, Keyreset, 0, 0, &res);
|
|
if(clientpreedit(ctx))
|
|
emitpreedit(ctx, "");
|
|
}
|
|
return handlenoop(c, m);
|
|
}
|
|
if(strcmp(name, "ClientCommitPreedit") == 0){
|
|
if(dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_STRUCT)
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"ClientCommitPreedit expects (b)");
|
|
dbus_message_iter_recurse(&value, &st);
|
|
if(dbus_message_iter_get_arg_type(&st) != DBUS_TYPE_BOOLEAN)
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"ClientCommitPreedit expects (b)");
|
|
dbus_message_iter_get_basic(&st, &b);
|
|
if(dbus_message_iter_next(&st))
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"ClientCommitPreedit expects (b)");
|
|
ctx->clientcommitpreedit = b != FALSE;
|
|
return handlenoop(c, m);
|
|
}
|
|
return handleerror(c, m, DBUS_ERROR_UNKNOWN_PROPERTY,
|
|
"unknown input context property");
|
|
}
|
|
|
|
static DBusHandlerResult
|
|
handlepropertyget(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
|
{
|
|
DBusError err;
|
|
const char *iface, *name;
|
|
|
|
USED(ctx);
|
|
dbus_error_init(&err);
|
|
if(!dbus_message_get_args(m, &err,
|
|
DBUS_TYPE_STRING, &iface, DBUS_TYPE_STRING, &name,
|
|
DBUS_TYPE_INVALID)){
|
|
dbus_error_free(&err);
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"Properties.Get expects interface and property names");
|
|
}
|
|
if(strcmp(iface, "org.freedesktop.IBus.InputContext") != 0)
|
|
return handleerror(c, m, DBUS_ERROR_UNKNOWN_INTERFACE,
|
|
"unknown property interface");
|
|
USED(name);
|
|
return handleerror(c, m, DBUS_ERROR_UNKNOWN_PROPERTY,
|
|
"input context properties are write-only");
|
|
}
|
|
|
|
static DBusHandlerResult
|
|
handlepropertygetall(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
|
{
|
|
DBusMessage *r;
|
|
DBusMessageIter it, a;
|
|
DBusError err;
|
|
const char *iface;
|
|
|
|
USED(ctx);
|
|
dbus_error_init(&err);
|
|
if(!dbus_message_get_args(m, &err, DBUS_TYPE_STRING, &iface,
|
|
DBUS_TYPE_INVALID)){
|
|
dbus_error_free(&err);
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"Properties.GetAll expects an interface name");
|
|
}
|
|
if(strcmp(iface, "org.freedesktop.IBus.InputContext") != 0)
|
|
return handleerror(c, m, DBUS_ERROR_UNKNOWN_INTERFACE,
|
|
"unknown property interface");
|
|
r = dbus_message_new_method_return(m);
|
|
if(r == nil)
|
|
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
|
dbus_message_iter_init_append(r, &it);
|
|
dbus_message_iter_open_container(&it, DBUS_TYPE_ARRAY, "{sv}", &a);
|
|
dbus_message_iter_close_container(&it, &a);
|
|
dbus_connection_send(c, r, nil);
|
|
dbus_message_unref(r);
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
}
|
|
|
|
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;
|
|
|
|
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");
|
|
setcursor(ctx, x, y, h);
|
|
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"
|
|
"<node>\n"
|
|
" <interface name=\"org.freedesktop.IBus\">\n"
|
|
" <method name=\"CreateInputContext\">\n"
|
|
" <arg direction=\"in\" type=\"s\"/>\n"
|
|
" <arg direction=\"out\" type=\"o\"/>\n"
|
|
" </method>\n"
|
|
" </interface>\n"
|
|
" <interface name=\"org.freedesktop.IBus.InputContext\">\n"
|
|
" <method name=\"ProcessKeyEvent\">\n"
|
|
" <arg direction=\"in\" type=\"u\"/>\n"
|
|
" <arg direction=\"in\" type=\"u\"/>\n"
|
|
" <arg direction=\"in\" type=\"u\"/>\n"
|
|
" <arg direction=\"out\" type=\"b\"/>\n"
|
|
" </method>\n"
|
|
" <method name=\"FocusIn\"/>\n"
|
|
" <method name=\"FocusOut\"/>\n"
|
|
" <method name=\"Reset\"/>\n"
|
|
" <method name=\"Destroy\"/>\n"
|
|
" <method name=\"SetCapabilities\"><arg direction=\"in\" type=\"u\"/></method>\n"
|
|
" <method name=\"SetCursorLocation\">"
|
|
"<arg direction=\"in\" type=\"i\"/><arg direction=\"in\" type=\"i\"/>"
|
|
"<arg direction=\"in\" type=\"i\"/><arg direction=\"in\" type=\"i\"/></method>\n"
|
|
" <method name=\"SetEngine\"><arg direction=\"in\" type=\"s\"/></method>\n"
|
|
" <property name=\"ContentType\" type=\"(uu)\" access=\"write\"/>\n"
|
|
" <property name=\"ClientCommitPreedit\" type=\"(b)\" access=\"write\"/>\n"
|
|
" <signal name=\"CommitText\"><arg type=\"v\"/></signal>\n"
|
|
" <signal name=\"UpdatePreeditText\">"
|
|
"<arg type=\"v\"/><arg type=\"u\"/><arg type=\"b\"/></signal>\n"
|
|
" <signal name=\"UpdatePreeditTextWithMode\">"
|
|
"<arg type=\"v\"/><arg type=\"u\"/><arg type=\"b\"/>"
|
|
"<arg type=\"u\"/></signal>\n"
|
|
" </interface>\n"
|
|
" <interface name=\"org.freedesktop.DBus.Properties\">\n"
|
|
" <method name=\"Get\"><arg direction=\"in\" type=\"s\"/>"
|
|
"<arg direction=\"in\" type=\"s\"/><arg direction=\"out\" type=\"v\"/></method>\n"
|
|
" <method name=\"Set\"><arg direction=\"in\" type=\"s\"/>"
|
|
"<arg direction=\"in\" type=\"s\"/><arg direction=\"in\" type=\"v\"/></method>\n"
|
|
" <method name=\"GetAll\"><arg direction=\"in\" type=\"s\"/>"
|
|
"<arg direction=\"out\" type=\"a{sv}\"/></method>\n"
|
|
" </interface>\n"
|
|
"</node>\n";
|
|
|
|
static DBusHandlerResult
|
|
handleintrospect(DBusConnection *c, DBusMessage *m)
|
|
{
|
|
DBusMessage *r;
|
|
const char *xml = introspectxml;
|
|
|
|
if(!dbus_message_has_signature(m, ""))
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"Introspect expects no arguments");
|
|
r = dbus_message_new_method_return(m);
|
|
dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID);
|
|
dbus_connection_send(c, r, nil);
|
|
dbus_message_unref(r);
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
}
|
|
|
|
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)
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
iface = dbus_message_get_interface(m);
|
|
member = dbus_message_get_member(m);
|
|
path = dbus_message_get_path(m);
|
|
if(iface == nil || member == nil || path == nil)
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
if(strcmp(iface, "org.freedesktop.DBus.Introspectable") == 0
|
|
&& strcmp(member, "Introspect") == 0)
|
|
return handleintrospect(c, m);
|
|
if(strcmp(iface, "org.freedesktop.DBus") == 0){
|
|
if(strcmp(member, "Hello") == 0)
|
|
return handlehello(c, m);
|
|
if(strcmp(member, "GetNameOwner") == 0)
|
|
return handlenameowner(c, m);
|
|
if(strcmp(member, "AddMatch") == 0
|
|
|| strcmp(member, "RemoveMatch") == 0){
|
|
if(!dbus_message_has_signature(m, "s"))
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"match rule must be one string");
|
|
return handlenoop(c, m);
|
|
}
|
|
}
|
|
if(strcmp(iface, "org.freedesktop.IBus") == 0){
|
|
if(strcmp(member, "CreateInputContext") == 0)
|
|
return handlecreate(c, m);
|
|
}
|
|
if(strcmp(iface, "org.freedesktop.DBus.Properties") == 0){
|
|
ctx = findcontext(c, path);
|
|
if(ctx == nil)
|
|
return handleerror(c, m, DBUS_ERROR_UNKNOWN_OBJECT,
|
|
"unknown input context");
|
|
if(strcmp(member, "Set") == 0)
|
|
return handlepropertyset(c, m, ctx);
|
|
if(strcmp(member, "Get") == 0)
|
|
return handlepropertyget(c, m, ctx);
|
|
if(strcmp(member, "GetAll") == 0)
|
|
return handlepropertygetall(c, m, ctx);
|
|
}
|
|
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, ctx);
|
|
if(strcmp(member, "FocusIn") == 0){
|
|
if(!dbus_message_has_signature(m, ""))
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"FocusIn expects no arguments");
|
|
return handlefocusin(c, m, ctx);
|
|
}
|
|
if(strcmp(member, "FocusOut") == 0){
|
|
if(!dbus_message_has_signature(m, ""))
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"FocusOut expects no arguments");
|
|
return handlefocusout(c, m, ctx);
|
|
}
|
|
if(strcmp(member, "Reset") == 0){
|
|
if(!dbus_message_has_signature(m, ""))
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"Reset expects no arguments");
|
|
return handlereset(c, m, ctx);
|
|
}
|
|
if(strcmp(member, "Destroy") == 0){
|
|
if(!dbus_message_has_signature(m, ""))
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"Destroy expects no arguments");
|
|
return handledestroy(c, m, ctx);
|
|
}
|
|
if(strcmp(member, "SetCursorLocation") == 0)
|
|
return handlecursor(c, m, ctx);
|
|
if(strcmp(member, "SetCapabilities") == 0)
|
|
return handlecap(c, m, ctx);
|
|
if(strcmp(member, "SetEngine") == 0){
|
|
if(!dbus_message_has_signature(m, "s"))
|
|
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
|
"SetEngine expects one engine name");
|
|
return handlenoop(c, m);
|
|
}
|
|
}
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
}
|
|
|
|
static void
|
|
newconn(DBusServer *s, DBusConnection *c, void *_)
|
|
{
|
|
DBusObjectPathVTable vt;
|
|
|
|
USED(s);
|
|
USED(_);
|
|
if(nconns >= Maxconns){
|
|
fprintf(stderr, "strans: ibus: rejecting client: %d-connection limit reached\n",
|
|
Maxconns);
|
|
dbus_connection_close(c);
|
|
return;
|
|
}
|
|
if(!dbus_connection_set_watch_functions(c, addwatch, removewatch,
|
|
togglewatch, nil, nil)){
|
|
fprintf(stderr, "strans: ibus: cannot watch client connection\n");
|
|
dbus_connection_close(c);
|
|
return;
|
|
}
|
|
memset(&vt, 0, sizeof(vt));
|
|
vt.message_function = onmsg;
|
|
if(!dbus_connection_register_fallback(c, "/", &vt, nil)){
|
|
fprintf(stderr, "strans: ibus: cannot register client handler\n");
|
|
dbus_connection_set_watch_functions(c, nil, nil, nil, nil, nil);
|
|
dbus_connection_close(c);
|
|
return;
|
|
}
|
|
dbus_connection_ref(c);
|
|
conns[nconns++] = c;
|
|
}
|
|
|
|
static void
|
|
pruneconns(void)
|
|
{
|
|
int i, j;
|
|
|
|
j = 0;
|
|
for(i = 0; i < nconns; i++){
|
|
if(dbus_connection_get_is_connected(conns[i])){
|
|
conns[j++] = conns[i];
|
|
continue;
|
|
}
|
|
dropconncontexts(conns[i]);
|
|
dbus_connection_unref(conns[i]);
|
|
}
|
|
nconns = j;
|
|
}
|
|
|
|
static int
|
|
ibusinit(void)
|
|
{
|
|
DBusError err;
|
|
char addr[128];
|
|
char *full;
|
|
int cleanupregistered;
|
|
|
|
full = nil;
|
|
cleanupregistered = 0;
|
|
if(buildaddrpath(addrfile, sizeof(addrfile)) < 0){
|
|
fprintf(stderr, "strans: ibus: cannot build address path\n");
|
|
return -1;
|
|
}
|
|
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){
|
|
fprintf(stderr, "strans: ibus: listen: %s\n", err.message);
|
|
dbus_error_free(&err);
|
|
addrfile[0] = '\0';
|
|
return -1;
|
|
}
|
|
dbus_server_set_new_connection_function(srv, newconn, nil, nil);
|
|
if(!dbus_server_set_watch_functions(srv, addwatch, removewatch,
|
|
togglewatch, nil, nil)){
|
|
fprintf(stderr, "strans: ibus: cannot watch server\n");
|
|
goto fail;
|
|
}
|
|
full = dbus_server_get_address(srv);
|
|
if(full == nil){
|
|
fprintf(stderr, "strans: ibus: cannot get server address\n");
|
|
goto fail;
|
|
}
|
|
if(!atexit(unlinkaddr)){
|
|
fprintf(stderr, "strans: ibus: cannot register address cleanup\n");
|
|
goto fail;
|
|
}
|
|
cleanupregistered = 1;
|
|
if(writeaddr(addrfile, full) < 0){
|
|
fprintf(stderr, "strans: ibus: cannot write %s\n", addrfile);
|
|
goto fail;
|
|
}
|
|
dbus_free(full);
|
|
return 0;
|
|
fail:
|
|
if(cleanupregistered)
|
|
atexitdont(unlinkaddr);
|
|
if(full != nil)
|
|
dbus_free(full);
|
|
dbus_server_disconnect(srv);
|
|
dbus_server_unref(srv);
|
|
srv = nil;
|
|
addrfile[0] = '\0';
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
ibusthread(void *_)
|
|
{
|
|
struct pollfd pfds[Maxwatches];
|
|
DBusWatch *polled[Maxwatches];
|
|
int wi[Maxwatches];
|
|
int i, n, rv;
|
|
unsigned int f;
|
|
|
|
USED(_);
|
|
threadsetname("ibus");
|
|
if(ibusinit() < 0)
|
|
die("ibus: initialization failed");
|
|
replyc = chancreate(sizeof(Keyres), 0);
|
|
if(replyc == nil)
|
|
die("ibus: cannot create reply channel");
|
|
for(;;){
|
|
if(!dbus_server_get_is_connected(srv))
|
|
die("ibus: server disconnected");
|
|
n = 0;
|
|
for(i = 0; i < nwatches && n < Maxwatches; i++){
|
|
if(watches[i] == nil)
|
|
continue;
|
|
if(!dbus_watch_get_enabled(watches[i]))
|
|
continue;
|
|
pfds[n].fd = dbus_watch_get_unix_fd(watches[i]);
|
|
pfds[n].events = 0;
|
|
f = dbus_watch_get_flags(watches[i]);
|
|
if(f & DBUS_WATCH_READABLE) pfds[n].events |= POLLIN;
|
|
if(f & DBUS_WATCH_WRITABLE) pfds[n].events |= POLLOUT;
|
|
wi[n] = i;
|
|
polled[n] = watches[i];
|
|
n++;
|
|
}
|
|
rv = poll(pfds, n, preowner != nil ? Ownerpoll : -1);
|
|
if(rv < 0 && errno == EINTR)
|
|
continue;
|
|
if(rv < 0)
|
|
die("ibus: poll: %s", strerror(errno));
|
|
for(i = 0; i < n; i++){
|
|
if(pfds[i].revents == 0)
|
|
continue;
|
|
if(watches[wi[i]] != polled[i])
|
|
continue;
|
|
f = 0;
|
|
if(pfds[i].revents & POLLIN) f |= DBUS_WATCH_READABLE;
|
|
if(pfds[i].revents & POLLOUT) f |= DBUS_WATCH_WRITABLE;
|
|
if(pfds[i].revents & POLLHUP) f |= DBUS_WATCH_HANGUP;
|
|
if(pfds[i].revents & POLLERR) f |= DBUS_WATCH_ERROR;
|
|
dbus_watch_handle(polled[i], f);
|
|
}
|
|
for(i = 0; i < nconns; i++)
|
|
while(dbus_connection_dispatch(conns[i]) == DBUS_DISPATCH_DATA_REMAINS)
|
|
;
|
|
pruneconns();
|
|
checkpreowner();
|
|
}
|
|
}
|