ibus: honor preedit and private-input policy
This commit is contained in:
192
ibus.c
192
ibus.c
@@ -17,6 +17,11 @@ enum
|
||||
Maxcontexts = 64,
|
||||
Relmask = 1<<30,
|
||||
Ibussupermask = 1<<26,
|
||||
/* IBus wire constants; the daemon does not link libibus. */
|
||||
Ibuscappreedit = 1<<0,
|
||||
Ibuspurposepassword = 8,
|
||||
Ibuspurposepin = 9,
|
||||
Ibushinthidden = 1<<12,
|
||||
};
|
||||
|
||||
typedef struct Ictx Ictx;
|
||||
@@ -25,6 +30,9 @@ struct Ictx
|
||||
DBusConnection *conn;
|
||||
char path[64];
|
||||
int focused;
|
||||
u32int cap;
|
||||
u32int purpose;
|
||||
u32int hints;
|
||||
Caret caret;
|
||||
};
|
||||
|
||||
@@ -303,7 +311,7 @@ sendrequest(Ictx *ctx, int op, u32int ks, u32int mod, Keyres *res)
|
||||
|
||||
memset(&kr, 0, sizeof kr);
|
||||
kr.owner = ctx;
|
||||
kr.cap = Cclientpreedit;
|
||||
kr.cap = ctx->cap & Ibuscappreedit ? Cclientpreedit : 0;
|
||||
kr.op = op;
|
||||
kr.ks = ks;
|
||||
kr.mod = mod;
|
||||
@@ -341,10 +349,18 @@ dropconncontexts(DBusConnection *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)
|
||||
if(state & Relmask || !ctx->focused || hidden(ctx))
|
||||
return 0;
|
||||
sendrequest(ctx, Keypress, kget(sym), mget(state), res);
|
||||
return 1;
|
||||
@@ -522,7 +538,8 @@ handlekey(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
||||
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);
|
||||
if(ctx->cap & Ibuscappreedit)
|
||||
emitpreedit(c, dbus_message_get_path(m), preedit);
|
||||
return handlebool(c, m, res.eaten);
|
||||
}
|
||||
|
||||
@@ -562,6 +579,149 @@ handlefocusout(DBusConnection *c, DBusMessage *m, Ictx *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(c, ctx->path, preedit);
|
||||
}else if(!(ctx->cap & Ibuscappreedit))
|
||||
emitpreedit(c, ctx->path, "");
|
||||
}
|
||||
}
|
||||
return handlenoop(c, m);
|
||||
}
|
||||
|
||||
static int
|
||||
getcontent(DBusMessage *m, const char **iface, const char **name,
|
||||
u32int *purpose, u32int *hints)
|
||||
{
|
||||
DBusMessageIter it, v, st;
|
||||
dbus_uint32_t p, h;
|
||||
|
||||
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, &v);
|
||||
if(dbus_message_iter_get_arg_type(&v) != DBUS_TYPE_STRUCT)
|
||||
return 0;
|
||||
dbus_message_iter_recurse(&v, &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;
|
||||
u32int purpose, hints;
|
||||
Keyres res;
|
||||
int washidden;
|
||||
|
||||
if(!getcontent(m, &iface, &name, &purpose, &hints))
|
||||
return handleerror(c, m, DBUS_ERROR_INVALID_ARGS,
|
||||
"Properties.Set expects ContentType as (uu)");
|
||||
if(strcmp(iface, "org.freedesktop.IBus.InputContext") != 0)
|
||||
return handleerror(c, m, DBUS_ERROR_UNKNOWN_INTERFACE,
|
||||
"unknown property interface");
|
||||
if(strcmp(name, "ContentType") != 0)
|
||||
return handleerror(c, m, DBUS_ERROR_UNKNOWN_PROPERTY,
|
||||
"unknown input context property");
|
||||
washidden = hidden(ctx);
|
||||
ctx->purpose = purpose;
|
||||
ctx->hints = hints;
|
||||
if(ctx->focused && !washidden && hidden(ctx)){
|
||||
sendrequest(ctx, Keyreset, 0, 0, &res);
|
||||
emitpreedit(c, ctx->path, "");
|
||||
}
|
||||
return handlenoop(c, m);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -617,10 +777,19 @@ static const char introspectxml[] =
|
||||
"<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"
|
||||
" <signal name=\"CommitText\"><arg type=\"v\"/></signal>\n"
|
||||
" <signal name=\"UpdatePreeditText\">"
|
||||
"<arg type=\"v\"/><arg type=\"u\"/><arg type=\"b\"/></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
|
||||
@@ -664,6 +833,18 @@ onmsg(DBusConnection *c, DBusMessage *m, void *_)
|
||||
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)
|
||||
@@ -681,8 +862,9 @@ onmsg(DBusConnection *c, DBusMessage *m, void *_)
|
||||
return handledestroy(c, m, ctx);
|
||||
if(strcmp(member, "SetCursorLocation") == 0)
|
||||
return handlecursor(c, m, ctx);
|
||||
if(strcmp(member, "SetCapabilities") == 0
|
||||
|| strcmp(member, "SetEngine") == 0)
|
||||
if(strcmp(member, "SetCapabilities") == 0)
|
||||
return handlecap(c, m, ctx);
|
||||
if(strcmp(member, "SetEngine") == 0)
|
||||
return handlenoop(c, m);
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
|
||||
Reference in New Issue
Block a user