diff --git a/tests/test.h b/tests/test.h index 57d0a12..7057e8a 100644 --- a/tests/test.h +++ b/tests/test.h @@ -129,4 +129,5 @@ void xim_adapter_callback_owner_loss(struct ct*); void xim_adapter_commit_encoding(struct ct*); void wl_modifier_mask(struct ct*); void wl_forwarded_keys(struct ct*); +void wl_surrounding_text(struct ct*); void wl_repeat_ends(struct ct*); diff --git a/tests/unit_test.c b/tests/unit_test.c index b3b1bd7..d266d7f 100644 --- a/tests/unit_test.c +++ b/tests/unit_test.c @@ -149,6 +149,7 @@ static const struct ct_test tests[] = { { "xim/commit-encoding", xim_adapter_commit_encoding }, { "wl/modifier-mask", wl_modifier_mask }, { "wl/forwarded-keys", wl_forwarded_keys }, + { "wl/surrounding-text", wl_surrounding_text }, { "wl/repeat-ends", wl_repeat_ends }, }; #else diff --git a/tests/wl_adapter_test.c b/tests/wl_adapter_test.c index 35757b7..1fa8a72 100644 --- a/tests/wl_adapter_test.c +++ b/tests/wl_adapter_test.c @@ -107,6 +107,30 @@ wl_forwarded_keys(struct ct *t) * Arming a repeat needs the engine, which is not here; ending one does * not, and a repeat that outlives its key is the worst this can do. */ +/* + * The text before the cursor is what a reading may reach into, and a + * take-back is measured in the bytes those runes hold. + */ +void +wl_surrounding_text(struct ct *t) +{ + sclear(&pendingsurround); + imsurrounding(nil, nil, "가나다라", 9, 9); + checkstr(t, "the text before the cursor", "가나다", &pendingsurround); + surround = pendingsurround; + CT_EQ_INT(t, 3, backbytes(1)); + CT_EQ_INT(t, 6, backbytes(2)); + CT_EQ_INT(t, 9, backbytes(9)); + CT_EQ_INT(t, 0, backbytes(0)); + /* A cursor past the end of the buffer is the whole of it. */ + imsurrounding(nil, nil, "ab", 99, 99); + checkstr(t, "a cursor past the end", "ab", &pendingsurround); + /* An activation starts with none, whatever the last one had. */ + imactivate(nil, nil); + CT_EQ_INT(t, 0, pendingsurround.n); + sclear(&surround); +} + void wl_repeat_ends(struct ct *t) { diff --git a/wl.c b/wl.c index 70b1f40..e0f5e4c 100644 --- a/wl.c +++ b/wl.c @@ -65,6 +65,7 @@ static int active; /* a text input is being served */ static int pendingactive; static int activated; /* an activate arrived since the last done */ static u32int purpose, pendingpurpose; +static Str surround, pendingsurround; /* the client's text before the cursor */ static u32int sent[Maxcode/32]; /* keycodes passed on and still down */ static u32int lasttime; /* the compositor's clock, for a release of our own */ static int reprate; /* repeats a second the seat asks for; 0 is none */ @@ -116,11 +117,26 @@ sendrequest(int op, u32int ks, u32int mod, Keyres *res) kr.op = op; kr.ks = ks; kr.mod = mod; + kr.surround = surround; kr.reply = replyc; chansend(keyc, &kr); chanrecv(replyc, res); } +/* The bytes the last n runes of the client's text take. */ +static int +backbytes(int n) +{ + char utf[Maxutf]; + Str s; + int i; + + sclear(&s); + for(i = max(surround.n - n, 0); i < surround.n; i++) + sputr(&s, surround.r[i]); + return stoutf(&s, utf, sizeof utf); +} + /* The engine's modifier bits, read from the grab's own keyboard state. */ static u32int modmask(void) @@ -187,6 +203,10 @@ answer(Keyres *res, char *tail) char utf[2*Maxutf]; int n; + /* The compositor takes the text back before it inserts ours. */ + if(res->del > 0) + zwp_input_method_v2_delete_surrounding_text(im, + backbytes(res->del), 0); n = stoutf(&res->commit, utf, sizeof utf); if(tail[0] != '\0') n += snprint(utf+n, sizeof utf - n, "%s", tail); @@ -407,6 +427,7 @@ imactivate(void*, struct zwp_input_method_v2*) pendingactive = 1; activated = 1; pendingpurpose = 0; + sclear(&pendingsurround); } static void @@ -415,9 +436,19 @@ imdeactivate(void*, struct zwp_input_method_v2*) pendingactive = 0; } +/* + * The text around the cursor, of which a reading can use what comes + * before it. A text input that sends none leaves this empty, and then + * nothing is ever reached back into or taken away. + */ static void -imsurrounding(void*, struct zwp_input_method_v2*, const char*, u32int, u32int) +imsurrounding(void*, struct zwp_input_method_v2*, const char *text, + u32int cursor, u32int) { + u32int n; + + n = strlen(text); + stail(&pendingsurround, (char*)text, cursor < n ? cursor : n); } static void @@ -444,6 +475,7 @@ imdone(void*, struct zwp_input_method_v2*) serial++; washidden = hidden(); purpose = pendingpurpose; + surround = pendingsurround; if(activated || (active && !pendingactive)) leave(); else if(active && !washidden && hidden())