ibus: honor preedit and private-input policy
This commit is contained in:
@@ -26,6 +26,12 @@ enum
|
||||
Stoptimeout = 3000,
|
||||
Relmask = 1<<30,
|
||||
Ctrlmask = 1<<2,
|
||||
Cappreedit = 1<<0,
|
||||
Purposefree = 0,
|
||||
Purposepassword = 8,
|
||||
Purposepin = 9,
|
||||
Hintprivate = 1<<11,
|
||||
Hinthidden = 1<<12,
|
||||
};
|
||||
|
||||
typedef struct Daemon Daemon;
|
||||
@@ -837,6 +843,66 @@ emptycall(DBusConnection *conn, Siglog *log, char *path, char *member)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
capcall(DBusConnection *conn, Siglog *log, char *path, dbus_uint32_t cap)
|
||||
{
|
||||
DBusMessage *m, *reply;
|
||||
|
||||
resetsig(log);
|
||||
m = method(path, "org.freedesktop.IBus.InputContext", "SetCapabilities");
|
||||
if(m == NULL || !dbus_message_append_args(m, DBUS_TYPE_UINT32, &cap,
|
||||
DBUS_TYPE_INVALID)){
|
||||
if(m != NULL) dbus_message_unref(m);
|
||||
return fail("build SetCapabilities call");
|
||||
}
|
||||
reply = sendcall(conn, m);
|
||||
if(reply == NULL)
|
||||
return 0;
|
||||
if(!dbus_message_has_signature(reply, "")){
|
||||
dbus_message_unref(reply);
|
||||
return fail("SetCapabilities returned a nonempty reply");
|
||||
}
|
||||
dbus_message_unref(reply);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
contentcall(DBusConnection *conn, Siglog *log, char *path,
|
||||
dbus_uint32_t purpose, dbus_uint32_t hints)
|
||||
{
|
||||
DBusMessage *m, *reply;
|
||||
DBusMessageIter it, v, st;
|
||||
const char *iface, *property;
|
||||
|
||||
resetsig(log);
|
||||
iface = "org.freedesktop.IBus.InputContext";
|
||||
property = "ContentType";
|
||||
m = method(path, "org.freedesktop.DBus.Properties", "Set");
|
||||
if(m == NULL)
|
||||
return 0;
|
||||
dbus_message_iter_init_append(m, &it);
|
||||
if(!dbus_message_iter_append_basic(&it, DBUS_TYPE_STRING, &iface) ||
|
||||
!dbus_message_iter_append_basic(&it, DBUS_TYPE_STRING, &property) ||
|
||||
!dbus_message_iter_open_container(&it, DBUS_TYPE_VARIANT, "(uu)", &v) ||
|
||||
!dbus_message_iter_open_container(&v, DBUS_TYPE_STRUCT, NULL, &st) ||
|
||||
!dbus_message_iter_append_basic(&st, DBUS_TYPE_UINT32, &purpose) ||
|
||||
!dbus_message_iter_append_basic(&st, DBUS_TYPE_UINT32, &hints) ||
|
||||
!dbus_message_iter_close_container(&v, &st) ||
|
||||
!dbus_message_iter_close_container(&it, &v)){
|
||||
dbus_message_unref(m);
|
||||
return fail("build Properties.Set ContentType call");
|
||||
}
|
||||
reply = sendcall(conn, m);
|
||||
if(reply == NULL)
|
||||
return 0;
|
||||
if(!dbus_message_has_signature(reply, "")){
|
||||
dbus_message_unref(reply);
|
||||
return fail("Properties.Set ContentType returned a nonempty reply");
|
||||
}
|
||||
dbus_message_unref(reply);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
keycall(DBusConnection *conn, Siglog *log, char *path, dbus_uint32_t sym,
|
||||
dbus_uint32_t state, int *eaten)
|
||||
@@ -1037,18 +1103,18 @@ runconnectioncapacity(Daemon *d)
|
||||
!createcontext(conn[i], paths[i], sizeof paths[i]) ||
|
||||
!emptycall(conn[i], &logs[i], paths[i], "FocusIn") ||
|
||||
!nosignal(&logs[i], "connection capacity FocusIn") ||
|
||||
!expectkey(conn[i], &logs[i], paths[i], 0xffe1, 0, 0, "",
|
||||
!expectkey(conn[i], &logs[i], paths[i], 0xffe1, 0, 0, NULL,
|
||||
"connection capacity key"))
|
||||
goto out;
|
||||
}
|
||||
if(!overflowrejected(d->address))
|
||||
goto out;
|
||||
for(i = 0; i < Maxconnections; i++)
|
||||
if(!expectkey(conn[i], &logs[i], paths[i], 0xffe1, 0, 0, "",
|
||||
if(!expectkey(conn[i], &logs[i], paths[i], 0xffe1, 0, 0, NULL,
|
||||
"accepted connection after rejection"))
|
||||
goto out;
|
||||
closebus(&conn[Maxconnections-1], &logs[Maxconnections-1]);
|
||||
if(!expectkey(conn[0], &logs[0], paths[0], 0xffe1, 0, 0, "",
|
||||
if(!expectkey(conn[0], &logs[0], paths[0], 0xffe1, 0, 0, NULL,
|
||||
"connection close barrier"))
|
||||
goto out;
|
||||
conn[Maxconnections-1] = openbus(d->address,
|
||||
@@ -1061,6 +1127,9 @@ runconnectioncapacity(Daemon *d)
|
||||
!emptycall(conn[Maxconnections-1], &logs[Maxconnections-1],
|
||||
paths[Maxconnections-1], "FocusIn") ||
|
||||
!nosignal(&logs[Maxconnections-1], "recovered connection FocusIn") ||
|
||||
!capcall(conn[Maxconnections-1], &logs[Maxconnections-1],
|
||||
paths[Maxconnections-1], Cappreedit) ||
|
||||
!nosignal(&logs[Maxconnections-1], "recovered connection capability") ||
|
||||
!expectkey(conn[Maxconnections-1], &logs[Maxconnections-1],
|
||||
paths[Maxconnections-1], 'n', Ctrlmask, 1, "",
|
||||
"recovered connection select Japanese") ||
|
||||
@@ -1069,7 +1138,7 @@ runconnectioncapacity(Daemon *d)
|
||||
"recovered connection key"))
|
||||
goto out;
|
||||
for(i = 0; i < Maxconnections-1; i++)
|
||||
if(!expectkey(conn[i], &logs[i], paths[i], 0xffe1, 0, 0, "",
|
||||
if(!expectkey(conn[i], &logs[i], paths[i], 0xffe1, 0, 0, NULL,
|
||||
"accepted connection after recovery"))
|
||||
goto out;
|
||||
ok = 1;
|
||||
@@ -1107,9 +1176,11 @@ runlifecycle(Daemon *d)
|
||||
goto out;
|
||||
if(!expectkey(c1, &s1, a, 'x', 0, 0, NULL, "unfocused key"))
|
||||
goto out;
|
||||
if(!cursorcall(c1, &s1, a, 10, 20, 0, 14) ||
|
||||
if(!cursorcall(c1, &s1, a, -10, -20, 0, 14) ||
|
||||
!nosignal(&s1, "pre-focus cursor") ||
|
||||
!emptycall(c1, &s1, a, "FocusIn") || !nosignal(&s1, "FocusIn") ||
|
||||
!capcall(c1, &s1, a, Cappreedit) ||
|
||||
!nosignal(&s1, "A capability") ||
|
||||
!expectkey(c1, &s1, a, 'x', Relmask, 0, NULL, "physical release"))
|
||||
goto out;
|
||||
if(!expectkey(c1, &s1, a, 'n', Ctrlmask, 1, "", "select Japanese") ||
|
||||
@@ -1117,6 +1188,8 @@ runlifecycle(Daemon *d)
|
||||
!expectkey(c1, &s1, a, 'a', 0, 1, "か", "A key a"))
|
||||
goto out;
|
||||
if(!emptycall(c2, &s2, b, "FocusIn") || !nosignal(&s2, "B FocusIn") ||
|
||||
!capcall(c2, &s2, b, Cappreedit) ||
|
||||
!nosignal(&s2, "inactive B capability") ||
|
||||
!expectkey(c2, &s2, b, 'n', 0, 1, "ん", "B takeover") ||
|
||||
!expectkey(c1, &s1, a, 0xffe1, 0, 0, "", "stale A state") ||
|
||||
!expectkey(c2, &s2, b, 0xffe1, 0, 0, "ん", "active B state"))
|
||||
@@ -1138,6 +1211,8 @@ runlifecycle(Daemon *d)
|
||||
goto out;
|
||||
if(!createcontext(c1, a2, sizeof a2) ||
|
||||
!emptycall(c1, &s1, a2, "FocusIn") || !nosignal(&s1, "A2 FocusIn") ||
|
||||
!capcall(c1, &s1, a2, Cappreedit) ||
|
||||
!nosignal(&s1, "A2 capability") ||
|
||||
!cursorcall(c1, &s1, a2, 120, 130, 0, 18) ||
|
||||
!nosignal(&s1, "A2 stale cursor"))
|
||||
goto out;
|
||||
@@ -1150,7 +1225,7 @@ runlifecycle(Daemon *d)
|
||||
!expectkey(c2, &s2, b, 'k', 0, 1, "k", "reset owner key k") ||
|
||||
!expectkey(c2, &s2, b, 'a', 0, 1, "か", "reset owner key a"))
|
||||
goto out;
|
||||
if(!keycall(c2, &s2, b, 0xff0d, 0, &eaten) || !eaten ||
|
||||
if(!keycall(c2, &s2, b, '0', 0, &eaten) || !eaten ||
|
||||
!committed(&s2, b, "か", "active commit"))
|
||||
goto out;
|
||||
if(!expectkey(c2, &s2, b, 'k', 0, 1, "k", "pre-FocusOut key") ||
|
||||
@@ -1173,6 +1248,8 @@ runlifecycle(Daemon *d)
|
||||
goto out;
|
||||
if(!emptycall(c2, &s2, active, "FocusIn") ||
|
||||
!nosignal(&s2, "capacity owner FocusIn") ||
|
||||
!capcall(c2, &s2, active, Cappreedit) ||
|
||||
!nosignal(&s2, "capacity owner capability") ||
|
||||
!expectkey(c2, &s2, active, 'k', 0, 1, "k", "capacity owner key"))
|
||||
goto out;
|
||||
closebus(&c2, &s2);
|
||||
@@ -1188,6 +1265,8 @@ runlifecycle(Daemon *d)
|
||||
if(!limitscall(c3) ||
|
||||
!emptycall(c3, &s3, first, "FocusIn") ||
|
||||
!nosignal(&s3, "reused context FocusIn") ||
|
||||
!capcall(c3, &s3, first, Cappreedit) ||
|
||||
!nosignal(&s3, "reused context capability") ||
|
||||
!expectkey(c3, &s3, first, 'k', 0, 1, "k", "reused context key"))
|
||||
goto out;
|
||||
ok = 1;
|
||||
@@ -1198,6 +1277,106 @@ out:
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int
|
||||
runpolicy(Daemon *d)
|
||||
{
|
||||
DBusConnection *c1, *c2;
|
||||
Siglog s1, s2;
|
||||
char n1[32], n2[32], a[96], b[96], typed[16];
|
||||
char *query;
|
||||
int eaten, i, ok;
|
||||
|
||||
c1 = c2 = NULL;
|
||||
memset(&s1, 0, sizeof s1);
|
||||
memset(&s2, 0, sizeof s2);
|
||||
ok = 0;
|
||||
c1 = openbus(d->address, &s1);
|
||||
c2 = openbus(d->address, &s2);
|
||||
if(c1 == NULL || c2 == NULL ||
|
||||
!hello(c1, n1, sizeof n1) || !hello(c2, n2, sizeof n2) ||
|
||||
!createcontext(c1, a, sizeof a) || !createcontext(c2, b, sizeof b) ||
|
||||
!emptycall(c1, &s1, a, "FocusIn") || !nosignal(&s1, "policy FocusIn"))
|
||||
goto out;
|
||||
|
||||
/* Capability starts at zero; only advertised bit zero affects routing. */
|
||||
if(!expectkey(c1, &s1, a, 'n', Ctrlmask, 1, NULL,
|
||||
"zero-capability language") ||
|
||||
!expectkey(c1, &s1, a, 'k', 0, 1, NULL, "zero-capability key") ||
|
||||
!capcall(c1, &s1, a, 1<<1) || !nosignal(&s1, "non-preedit cap") ||
|
||||
!capcall(c1, &s1, a, Cappreedit|(1<<1)) ||
|
||||
!preedit(&s1, a, "k", 1, 1, "enable inline") ||
|
||||
!capcall(c1, &s1, a, Cappreedit|(1<<1)) ||
|
||||
!nosignal(&s1, "repeat inline capability") ||
|
||||
!capcall(c1, &s1, a, 1<<1) ||
|
||||
!preedit(&s1, a, "", 0, 0, "disable inline") ||
|
||||
!capcall(c1, &s1, a, 1<<1) ||
|
||||
!nosignal(&s1, "repeat popup capability") ||
|
||||
!expectkey(c1, &s1, a, 'a', 0, 1, NULL, "popup-preedit key") ||
|
||||
!capcall(c1, &s1, a, Cappreedit|(1<<1)) ||
|
||||
!preedit(&s1, a, "か", 1, 1, "restore inline"))
|
||||
goto out;
|
||||
|
||||
/* A focused but inactive context cannot acquire ownership through Keycap. */
|
||||
if(!emptycall(c2, &s2, b, "FocusIn") || !nosignal(&s2, "inactive FocusIn") ||
|
||||
!capcall(c2, &s2, b, Cappreedit) ||
|
||||
!nosignal(&s2, "inactive capability") ||
|
||||
!expectkey(c1, &s1, a, 0xffe1, 0, 0, "か", "owner after inactive cap"))
|
||||
goto out;
|
||||
|
||||
/* Toggling inline display leaves the engine-owned candidate list intact. */
|
||||
if(!emptycall(c1, &s1, a, "Reset") ||
|
||||
!preedit(&s1, a, "", 0, 0, "candidate reset") ||
|
||||
!expectkey(c1, &s1, a, 'e', Ctrlmask, 1, "", "begin emoji search"))
|
||||
goto out;
|
||||
query = "smile";
|
||||
typed[0] = '\0';
|
||||
for(i = 0; query[i] != '\0'; i++){
|
||||
typed[i] = query[i];
|
||||
typed[i+1] = '\0';
|
||||
if(!keycall(c1, &s1, a, query[i], 0, &eaten) || !eaten ||
|
||||
!preedit(&s1, a, typed, i+1, 1, "emoji query"))
|
||||
goto out;
|
||||
}
|
||||
if(!capcall(c1, &s1, a, 1<<1) ||
|
||||
!preedit(&s1, a, "", 0, 0, "candidate popup policy") ||
|
||||
!capcall(c1, &s1, a, Cappreedit|(1<<1)) ||
|
||||
!preedit(&s1, a, "smile", 5, 1, "candidate inline policy") ||
|
||||
!keycall(c1, &s1, a, '1', 0, &eaten) || !eaten ||
|
||||
!committed(&s1, a, "😀", "candidate after capability toggle"))
|
||||
goto out;
|
||||
|
||||
/* PRIVATE preserves composition; hidden purposes and hints reset it. */
|
||||
if(!expectkey(c1, &s1, a, 'n', Ctrlmask, 1, "", "private select Japanese") ||
|
||||
!expectkey(c1, &s1, a, 'n', 0, 1, "ん", "private preedit") ||
|
||||
!contentcall(c1, &s1, a, Purposefree, Hintprivate) ||
|
||||
!nosignal(&s1, "PRIVATE property") ||
|
||||
!expectkey(c1, &s1, a, 'a', 0, 1, "な", "PRIVATE key") ||
|
||||
!contentcall(c1, &s1, a, Purposepassword, 0) ||
|
||||
!preedit(&s1, a, "", 0, 0, "PASSWORD transition") ||
|
||||
!expectkey(c1, &s1, a, 'x', 0, 0, NULL, "PASSWORD key") ||
|
||||
!contentcall(c1, &s1, a, Purposefree, 0) ||
|
||||
!nosignal(&s1, "leave PASSWORD") ||
|
||||
!expectkey(c1, &s1, a, 'k', 0, 1, "k", "after PASSWORD") ||
|
||||
!contentcall(c1, &s1, a, Purposepin, 0) ||
|
||||
!preedit(&s1, a, "", 0, 0, "PIN transition") ||
|
||||
!expectkey(c1, &s1, a, 'x', 0, 0, NULL, "PIN key") ||
|
||||
!contentcall(c1, &s1, a, Purposefree, Hintprivate) ||
|
||||
!nosignal(&s1, "leave PIN with PRIVATE") ||
|
||||
!expectkey(c1, &s1, a, 'n', 0, 1, "ん", "PRIVATE resumes") ||
|
||||
!contentcall(c1, &s1, a, Purposefree, Hinthidden) ||
|
||||
!preedit(&s1, a, "", 0, 0, "HIDDEN_TEXT transition") ||
|
||||
!expectkey(c1, &s1, a, 'x', 0, 0, NULL, "HIDDEN_TEXT key") ||
|
||||
!contentcall(c1, &s1, a, Purposefree, 0) ||
|
||||
!nosignal(&s1, "free-form property") ||
|
||||
!expectkey(c1, &s1, a, 'n', 0, 1, "ん", "free-form resumes"))
|
||||
goto out;
|
||||
ok = 1;
|
||||
out:
|
||||
closebus(&c1, &s1);
|
||||
closebus(&c2, &s2);
|
||||
return ok;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
@@ -1213,12 +1392,14 @@ main(int argc, char **argv)
|
||||
ok = runconnectioncapacity(&daemon);
|
||||
if(ok)
|
||||
ok = runlifecycle(&daemon);
|
||||
if(ok)
|
||||
ok = runpolicy(&daemon);
|
||||
if(!ok)
|
||||
showerrors(&daemon);
|
||||
if(!stopdaemon(&daemon))
|
||||
ok = 0;
|
||||
if(!ok)
|
||||
return 1;
|
||||
printf("ibus live lifecycle and connection capacity: ok\n");
|
||||
printf("ibus live lifecycle, policy, and connection capacity: ok\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user