diff --git a/.gitignore b/.gitignore index b8182d5..dff0a64 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,6 @@ /tests/daemon_failure_test /tests/daemon_restart_test /xim/*.o -/xim/xim_test /gtk/im-strans.so /bench/bench /bench/perf.data* diff --git a/Makefile b/Makefile index 0c6527b..63ea71c 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ DOCKER_RUN = docker run --rm --user "$$(id -u):$$(id -g)" \ SRCS = dict.c font.c ibus.c ipc.c ko.c main.c popup_layout.c \ srv.c str.c strans.c trie.c vi.c win.c -XIMSRCS = xim/xim.c xim/keymap.c xim/ximtext.c +XIMSRCS = xim/xim.c xim/keymap.c XIMOBJS = $(XIMSRCS:.c=.o) OBJS = $(SRCS:.c=.o) $(XIMOBJS) @@ -42,7 +42,6 @@ $(XIMOBJS): PROJECT_CPPFLAGS += -I. $(XIM_CFLAGS) clean: rm -f $(OBJS) $(PROG) $(MAKE) -C tests clean - $(MAKE) -C xim/ clean $(MAKE) -C gtk/ clean $(MAKE) -C bench/ clean @@ -76,7 +75,6 @@ docker: docker-image check: verify-map $(MAKE) -C tests check UNITARGS="$(UNITARGS)" - $(MAKE) -C xim check check-live: $(PROG) gtk $(MAKE) -C tests check-live diff --git a/fn.h b/fn.h index 72acf20..641d6bb 100644 --- a/fn.h +++ b/fn.h @@ -39,7 +39,7 @@ void srvinit(void); void srvthread(void*); void ibusthread(void*); void ximthread(void*); -void ximownernotify(void); +int keymeaningful(u32int); void* emalloc(ulong); void* erealloc(void*, ulong); diff --git a/ibus.c b/ibus.c index 911d852..e906480 100644 --- a/ibus.c +++ b/ibus.c @@ -17,6 +17,7 @@ enum 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, @@ -1165,7 +1166,7 @@ ibusthread(void *_) polled[n] = watches[i]; n++; } - rv = poll(pfds, n, 200); + rv = poll(pfds, n, preowner != nil ? Ownerpoll : -1); if(rv < 0 && errno == EINTR) continue; if(rv < 0) diff --git a/strans.c b/strans.c index 50beb6b..42ed367 100644 --- a/strans.c +++ b/strans.c @@ -438,6 +438,13 @@ ismodkey(u32int ks) return ks >= Kmodfirst && ks <= Kmodlast; } +/* Modifier presses and unmapped keys never engage the engine. */ +int +keymeaningful(u32int ks) +{ + return ks != 0 && !ismodkey(ks); +} + static void foldascii(Str *s) { @@ -765,12 +772,6 @@ init(void) memset(&lastdraw, 0, sizeof lastdraw); } -static int -meaningful(Keyreq *kr) -{ - return kr->ks != 0 && !ismodkey(kr->ks); -} - /* * The engine belongs to whichever context last typed a real key; only * the owner's requests change state. Every request ends in redraw(), @@ -780,9 +781,7 @@ static void imhandlekey(Keyreq *kr) { Keyres res; - void *oldowner; - oldowner = activeowner; sclear(&res.commit); sclear(&res.preedit); res.eaten = 1; @@ -811,7 +810,7 @@ imhandlekey(Keyreq *kr) res.eaten = 0; if(activeowner != nil && kr->owner == activeowner) activecap = kr->cap & Cclientpreedit; - if(meaningful(kr)){ + if(keymeaningful(kr->ks)){ if(kr->owner != activeowner){ reset(); activeowner = kr->owner; @@ -824,8 +823,6 @@ imhandlekey(Keyreq *kr) break; } redraw(); - if(activeowner != oldowner) - ximownernotify(); if(kr->owner == activeowner){ if(search.lang) res.preedit = search.text; diff --git a/tests/Makefile b/tests/Makefile index e7544c7..aa4b8be 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -30,12 +30,13 @@ FAULT = daemon_collision_test daemon_failure_test daemon_restart_test LIVE = $(SMOKE) $(FAULT) TESTSRC = test_util.c str_test.c trie_test.c \ ko_test.c vi_test.c engine_test.c dict_test.c ipc_test.c \ - popup_test.c font_test.c ibus_test.c server_test.c xim_adapter_test.c + popup_test.c font_test.c ibus_test.c server_test.c keymap_test.c \ + xim_adapter_test.c TESTOBJ = $(TESTSRC:.c=.o) PARENTSRC = str.c trie.c dict.c ko.c vi.c ipc.c popup_layout.c \ font.c PARENTOBJ = $(PARENTSRC:%.c=unit_%.o) -XIMOBJ = unit_xim_keymap.o unit_ximtext.o +XIMOBJ = unit_xim_keymap.o COMMONOBJ = $(TESTOBJ) $(PARENTOBJ) $(XIMOBJ) OBJS = unit_test.o stress_test.o $(COMMONOBJ) @@ -116,7 +117,7 @@ unit_test.o stress_test.o $(TESTOBJ): test.h ../dat.h ../fn.h ../ipc.h \ engine_test.o: ../strans.c ibus_test.o: UNIT_CPPFLAGS += $(IBUS_CFLAGS) ibus_test.o: ../ibus.c -xim_adapter_test.o: UNIT_CPPFLAGS += $(XIM_CFLAGS) +keymap_test.o xim_adapter_test.o: UNIT_CPPFLAGS += $(XIM_CFLAGS) xim_adapter_test.o: ../xim/xim.c server_test.o: ../srv.c unit_font.o: UNIT_CPPFLAGS += $(TEXT_CFLAGS) @@ -125,10 +126,6 @@ unit_xim_keymap.o: ../xim/keymap.c $(CC) $(CPPFLAGS) $(UNIT_CPPFLAGS) $(XIM_CFLAGS) $(UNIT_CFLAGS) \ $(CFLAGS) -c -o $@ $< -unit_ximtext.o: ../xim/ximtext.c - $(CC) $(CPPFLAGS) $(UNIT_CPPFLAGS) $(XIM_CFLAGS) $(UNIT_CFLAGS) \ - $(CFLAGS) -c -o $@ $< - unit_%.o: ../%.c ../dat.h ../fn.h ../ipc.h $(CC) $(CPPFLAGS) $(UNIT_CPPFLAGS) $(UNIT_CFLAGS) $(CFLAGS) -c -o $@ $< diff --git a/tests/keymap_test.c b/tests/keymap_test.c new file mode 100644 index 0000000..f05ab5d --- /dev/null +++ b/tests/keymap_test.c @@ -0,0 +1,71 @@ +#include "test.h" +#include +#include +#include + +uint32_t keymaplookup(struct xkb_state*, uint8_t, uint16_t); + +enum +{ + ShiftMask = 1<<0, + LockMask = 1<<1, + Mod2Mask = 1<<4, + Mod5Mask = 1<<7, + Group1 = 1<<13, +}; + +void +xim_keymap_lookup(struct ct *t) +{ + struct xkb_context *context; + struct xkb_keymap *keymap; + struct xkb_rule_names names; + struct xkb_state *state; + + memset(&names, 0, sizeof names); + names.layout = "de,ru"; + context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + keymap = xkb_keymap_new_from_names(context, &names, + XKB_KEYMAP_COMPILE_NO_FLAGS); + if(!CT_CHECK(t, keymap != nil)){ + xkb_context_unref(context); + return; + } + state = xkb_state_new(keymap); + CT_EQ_UINT(t, XKB_KEY_a, keymaplookup(state, 38, 0)); + CT_EQ_UINT(t, XKB_KEY_A, keymaplookup(state, 38, ShiftMask)); + CT_EQ_UINT(t, XKB_KEY_A, keymaplookup(state, 38, LockMask)); + CT_EQ_UINT(t, XKB_KEY_a, keymaplookup(state, 38, LockMask|ShiftMask)); + CT_EQ_UINT(t, XKB_KEY_EuroSign, keymaplookup(state, 26, Mod5Mask)); + CT_EQ_UINT(t, XKB_KEY_Cyrillic_u, keymaplookup(state, 26, Group1)); + CT_EQ_UINT(t, XKB_KEY_KP_End, keymaplookup(state, 87, 0)); + CT_EQ_UINT(t, XKB_KEY_KP_1, keymaplookup(state, 87, Mod2Mask)); + xkb_state_unref(state); + xkb_keymap_unref(keymap); + xkb_context_unref(context); +} + +/* imdkit carries any UTF-8 text through COMPOUND_TEXT unchanged. */ +void +xim_compound_text(struct ct *t) +{ + static char *text[] = { "AπŸ˜€ν•œ", "ν•œκΈ€", "𠀋", "πŸ‘©β€πŸ’»" }; + char *ct, *utf8; + size_t clen, len, ulen; + int i; + + xcb_compound_text_init(); + for(i = 0; i < nelem(text); i++){ + len = strlen(text[i]); + ct = xcb_utf8_to_compound_text(text[i], len, &clen); + if(!CT_CHECK(t, ct != nil)) + continue; + utf8 = xcb_compound_text_to_utf8(ct, clen, &ulen); + if(CT_CHECK(t, utf8 != nil)){ + CT_EQ_SIZE(t, len, ulen); + CT_EQ_MEM(t, text[i], utf8, len); + } + free(utf8); + free(ct); + } +} diff --git a/tests/test.h b/tests/test.h index da7cf0c..1917664 100644 --- a/tests/test.h +++ b/tests/test.h @@ -82,13 +82,14 @@ void ibus_capability_policy(struct ct*); void ibus_private_input_policy(struct ct*); void ibus_context_lifecycle(struct ct*); void ibus_active_release_lifecycle(struct ct*); +void xim_keymap_lookup(struct ct*); +void xim_compound_text(struct ct*); void xim_adapter_key_contract(struct ct*); void xim_adapter_release_lifecycle(struct ct*); void xim_adapter_free_waits_for_release(struct ct*); void xim_adapter_styles(struct ct*); void xim_adapter_placement(struct ct*); void xim_adapter_placement_updates(struct ct*); -void xim_adapter_encoding_negotiation(struct ct*); void xim_adapter_callback_replacement(struct ct*); void xim_adapter_callback_unicode(struct ct*); void xim_adapter_callback_cleanup(struct ct*); diff --git a/tests/unit_test.c b/tests/unit_test.c index 7cb33eb..abb80dc 100644 --- a/tests/unit_test.c +++ b/tests/unit_test.c @@ -134,13 +134,14 @@ static const struct ct_test tests[] = { { "ibus/private-input-policy", ibus_private_input_policy }, { "ibus/context-lifecycle", ibus_context_lifecycle }, { "ibus/active-release-lifecycle", ibus_active_release_lifecycle }, + { "xim/keymap-lookup", xim_keymap_lookup }, + { "xim/compound-text", xim_compound_text }, { "xim/adapter-key-contract", xim_adapter_key_contract }, { "xim/adapter-release-lifecycle", xim_adapter_release_lifecycle }, { "xim/adapter-free-waits-release", xim_adapter_free_waits_for_release }, { "xim/styles", xim_adapter_styles }, { "xim/placement", xim_adapter_placement }, { "xim/placement-updates", xim_adapter_placement_updates }, - { "xim/encoding-negotiation", xim_adapter_encoding_negotiation }, { "xim/callback-replacement", xim_adapter_callback_replacement }, { "xim/callback-unicode", xim_adapter_callback_unicode }, { "xim/callback-cleanup", xim_adapter_callback_cleanup }, diff --git a/tests/xim_adapter_test.c b/tests/xim_adapter_test.c index 5ef8317..65a8db0 100644 --- a/tests/xim_adapter_test.c +++ b/tests/xim_adapter_test.c @@ -6,6 +6,7 @@ struct Icbinding { xcb_im_input_context_t *ic; void *data; + uint32_t style; xcb_window_t clientwin; xcb_window_t focuswin; uint32_t preattrmask; @@ -51,6 +52,8 @@ static int wireflush(xcb_connection_t*); #define xcb_im_input_context_set_data wiresetdata #define xcb_im_input_context_get_data(ic) \ (wirebinding(ic) == nil ? nil : wirebinding(ic)->data) +#define xcb_im_input_context_get_input_style(ic) \ + (wirebinding(ic) == nil ? 0 : wirebinding(ic)->style) #define xcb_im_input_context_get_client_window(ic) \ (wirebinding(ic) == nil ? XCB_NONE : wirebinding(ic)->clientwin) #define xcb_im_input_context_get_focus_window(ic) \ @@ -72,6 +75,7 @@ static int wireflush(xcb_connection_t*); #undef xcb_im_forward_event #undef xcb_im_input_context_set_data #undef xcb_im_input_context_get_data +#undef xcb_im_input_context_get_input_style #undef xcb_im_input_context_get_client_window #undef xcb_im_input_context_get_focus_window #undef xcb_im_input_context_get_preedit_attr_mask @@ -314,6 +318,26 @@ wireclear(void) preowner = nil; } +/* Wire text is the COMPOUND_TEXT of what the engine produced. */ +static int +checkct(struct ct *t, char *where, uchar *got, size_t ngot, char *utf8) +{ + char *want; + size_t nwant; + int ok; + + xcb_compound_text_init(); + want = xcb_utf8_to_compound_text(utf8, strlen(utf8), &nwant); + if(!CT_CHECK(t, want != nil)) + return 0; + ok = ngot == nwant && memcmp(got, want, nwant) == 0; + if(!ok) + CT_ERRORF(t, "%s: wire text differs from COMPOUND_TEXT of %s", + where, utf8); + free(want); + return ok; +} + static void wirebind(int n, xcb_im_input_context_t *ic, Ic *state) { @@ -681,35 +705,39 @@ xim_adapter_styles(struct ct *t) XCB_IM_PreeditCallbacks | XCB_IM_StatusNothing, XCB_IM_PreeditNothing | XCB_IM_StatusNothing, }; - Ic state; - int cap, i; + xcb_im_input_context_t *ic; + Ic *state; + int i; + CT_EQ_SIZE(t, 1, nelem(encs)); + CT_EQ_STR(t, "COMPOUND_TEXT", encs[0]); CT_EQ_SIZE(t, nelem(want), nelem(styles)); + /* Only PreeditCallbacks clients draw the preedit themselves. */ for(i = 0; i < nelem(want); i++){ CT_EQ_UINT(t, want[i], styles[i]); - CT_CHECK(t, stylecap(want[i], &cap)); - CT_EQ_INT(t, i == 1 ? Cclientpreedit : 0, cap); + wireclear(); + ic = (xcb_im_input_context_t*)(uintptr)(80+i); + wirebind(0, ic, nil); + icbindings[0].style = want[i]; + iccreate(nil, ic); + state = icbindings[0].data; + if(!CT_CHECK(t, state != nil && state == ics)) + continue; + CT_EQ_INT(t, i == 1 ? Cclientpreedit : 0, state->cap); + CT_EQ_PTR(t, ic, state->xic); + icunlink(state); + free(state); } - CT_CHECK(t, !stylecap(XCB_IM_PreeditNone | XCB_IM_StatusNothing, - &cap)); - CT_CHECK(t, !stylecap(XCB_IM_PreeditCallbacks | XCB_IM_StatusCallbacks, - &cap)); - CT_EQ_INT(t, 0, cap); - memset(&state, 0, sizeof state); - setstyle(&state, XCB_IM_PreeditNone | XCB_IM_StatusNothing); - CT_EQ_UINT(t, XCB_IM_PreeditNothing | XCB_IM_StatusNothing, - state.style); - CT_EQ_INT(t, 0, state.cap); - setstyle(&state, XCB_IM_PreeditCallbacks | XCB_IM_StatusNothing); - CT_EQ_INT(t, Cclientpreedit, state.cap); } void xim_adapter_placement(struct ct *t) { + /* Spot location first, then the bottom of the focus window, then of + * the client window; an unset focus window is the client window. */ static const struct { - u32int style; u32int mask; + int focuswin; int fgeo; int ftrans; int cgeo; @@ -718,18 +746,13 @@ xim_adapter_placement(struct ct *t) int x; int y; } cases[] = { - {XCB_IM_PreeditPosition | XCB_IM_StatusNothing, - XCB_XIM_XNSpotLocation_MASK, 1, 1, 1, 1, 1, 103, 204}, - {XCB_IM_PreeditPosition | XCB_IM_StatusNothing, - XCB_XIM_XNSpotLocation_MASK, 1, 0, 1, 1, 1, 300, 440}, - {XCB_IM_PreeditPosition | XCB_IM_StatusNothing, - 0, 1, 1, 1, 1, 1, 100, 230}, - {XCB_IM_PreeditNothing | XCB_IM_StatusNothing, - XCB_XIM_XNSpotLocation_MASK, 1, 1, 1, 1, 1, 100, 230}, - {XCB_IM_PreeditPosition | XCB_IM_StatusNothing, - 0, 0, 1, 1, 1, 1, 300, 440}, - {XCB_IM_PreeditPosition | XCB_IM_StatusNothing, - 0, 0, 1, 0, 1, 0, 0, 0}, + {XCB_XIM_XNSpotLocation_MASK, 10, 1, 1, 1, 1, 1, 103, 204}, + {XCB_XIM_XNSpotLocation_MASK, 10, 1, 0, 1, 1, 1, 300, 440}, + {XCB_XIM_XNSpotLocation_MASK, 0, 1, 1, 1, 1, 1, 303, 404}, + {0, 10, 1, 1, 1, 1, 1, 100, 230}, + {0, 0, 1, 1, 1, 1, 1, 300, 440}, + {0, 10, 0, 1, 1, 1, 1, 300, 440}, + {0, 10, 0, 1, 0, 1, 0, 0, 0}, }; xcb_im_input_context_t *ic; Icbinding *b; @@ -742,12 +765,11 @@ xim_adapter_placement(struct ct *t) wireclear(); memset(&state, 0, sizeof state); state.xic = ic; - state.style = cases[i].style; state.caret.valid = 1; state.caret.x = state.caret.y = 9; wirebind(0, ic, &state); b = &icbindings[0]; - b->focuswin = 10; + b->focuswin = cases[i].focuswin; b->clientwin = 20; b->preattrmask = cases[i].mask; b->preattr.spot_location.x = 3; @@ -758,12 +780,7 @@ xim_adapter_placement(struct ct *t) focus->translateok = cases[i].ftrans; client->geometryok = cases[i].cgeo; client->translateok = cases[i].ctrans; - refreshplace(&state); - CT_EQ_UINT(t, 10, state.focuswin); - CT_EQ_UINT(t, 20, state.clientwin); - CT_EQ_INT(t, cases[i].style == - (XCB_IM_PreeditPosition | XCB_IM_StatusNothing) && - cases[i].mask != 0, state.hasspot); + place(&state); CT_EQ_INT(t, cases[i].valid, state.caret.valid); CT_EQ_INT(t, cases[i].x, state.caret.x); CT_EQ_INT(t, cases[i].y, state.caret.y); @@ -793,7 +810,6 @@ xim_adapter_placement_updates(struct ct *t) goto cleanup; wireclear(); state.xic = ic; - state.style = XCB_IM_PreeditPosition | XCB_IM_StatusNothing; wirebind(0, ic, &state); b = &icbindings[0]; b->focuswin = 11; @@ -825,8 +841,6 @@ xim_adapter_placement_updates(struct ct *t) CT_CHECK(t, req.caret.valid); CT_EQ_INT(t, 165, req.caret.x); CT_EQ_INT(t, 266, req.caret.y); - CT_EQ_INT(t, 15, state.spot.x); - CT_EQ_INT(t, 16, state.spot.y); b->preattrmask = 0; wirewins[0].geometryok = 0; hdr.major_opcode = XCB_XIM_SET_IC_FOCUS; @@ -841,33 +855,6 @@ cleanup: ximend(&f); } -void -xim_adapter_encoding_negotiation(struct ct *t) -{ - xcb_im_packet_header_fr_t hdr; - xcb_im_client_t *clients[Maxclients+1]; - u16int selected; - int i; - - clearencodings(); - CT_EQ_SIZE(t, 2, nelem(encs)); - CT_EQ_STR(t, "COMPOUND_TEXT", encs[0]); - CT_EQ_STR(t, "UTF8_STRING", encs[1]); - memset(&hdr, 0, sizeof hdr); - hdr.major_opcode = XCB_XIM_ENCODING_NEGOTIATION; - for(i = 0; i < nelem(clients); i++){ - clients[i] = (xcb_im_client_t*)(uintptr)(i+1); - selected = i == 0 ? 0 : 1; - callback(nil, clients[i], nil, &hdr, nil, &selected, nil); - } - CT_EQ_INT(t, Ecompound, getencoding(clients[0])); - CT_EQ_INT(t, Eutf8, getencoding(clients[Maxclients])); - hdr.major_opcode = XCB_XIM_DISCONNECT; - for(i = 0; i < nelem(clients); i++) - callback(nil, clients[i], nil, &hdr, nil, nil, nil); - CT_EQ_PTR(t, nil, clientenc); -} - void xim_adapter_callback_replacement(struct ct *t) { @@ -879,8 +866,7 @@ xim_adapter_callback_replacement(struct ct *t) ic = (xcb_im_input_context_t*)(uintptr)3; memset(&state, 0, sizeof state); state.xic = ic; - state.style = XCB_IM_PreeditCallbacks | XCB_IM_StatusNothing; - state.encoding = Eutf8; + state.cap = Cclientpreedit; wireclear(); sinit(&pre, "k", strlen("k")); updatepreedit(&state, &pre); @@ -899,8 +885,7 @@ xim_adapter_callback_replacement(struct ct *t) CT_EQ_UINT(t, 0, wirecalls[2].first); CT_EQ_UINT(t, 1, wirecalls[2].changed); CT_EQ_UINT(t, 2, wirecalls[2].caret); - CT_EQ_INT(t, strlen("ν•œπŸ˜€"), wirecalls[2].nbyte); - CT_EQ_MEM(t, "ν•œπŸ˜€", wirecalls[2].text, wirecalls[2].nbyte); + checkct(t, "replacement", wirecalls[2].text, wirecalls[2].nbyte, "ν•œπŸ˜€"); CT_EQ_INT(t, 2, wirecalls[2].nfeedback); for(i = 0; i < wirecalls[2].nfeedback; i++) CT_EQ_UINT(t, XCB_XIM_UNDERLINE, wirecalls[2].feedback[i]); @@ -925,16 +910,12 @@ xim_adapter_callback_unicode(struct ct *t) Ic state; Str pre; Wirecall *call; - char *want; - size_t nwant; int i; - xcb_compound_text_init(); for(i = 0; i < nelem(text); i++){ memset(&state, 0, sizeof state); state.xic = (xcb_im_input_context_t*)(uintptr)(10+i); - state.style = XCB_IM_PreeditCallbacks | XCB_IM_StatusNothing; - state.encoding = Eutf8; + state.cap = Cclientpreedit; sinit(&pre, text[i], strlen(text[i])); wireclear(); updatepreedit(&state, &pre); @@ -943,24 +924,7 @@ xim_adapter_callback_unicode(struct ct *t) CT_EQ_INT(t, Wdraw, call->op); CT_EQ_UINT(t, pre.n, call->caret); CT_EQ_INT(t, pre.n, call->nfeedback); - CT_EQ_INT(t, strlen(text[i]), call->nbyte); - CT_EQ_MEM(t, text[i], call->text, call->nbyte); - - memset(&state, 0, sizeof state); - state.xic = (xcb_im_input_context_t*)(uintptr)(20+i); - state.style = XCB_IM_PreeditCallbacks | XCB_IM_StatusNothing; - state.encoding = Ecompound; - wireclear(); - updatepreedit(&state, &pre); - want = ximcompound(text[i], strlen(text[i]), &nwant); - if(!CT_CHECK(t, want != nil)) - continue; - call = &wirecalls[1]; - CT_EQ_SIZE(t, nwant, call->nbyte); - CT_EQ_MEM(t, want, call->text, nwant); - CT_EQ_UINT(t, pre.n, call->caret); - CT_EQ_INT(t, pre.n, call->nfeedback); - free(want); + checkct(t, text[i], call->text, call->nbyte, text[i]); } } @@ -971,8 +935,7 @@ startstate(Ic *state, xcb_im_input_context_t *ic) memset(state, 0, sizeof *state); state->xic = ic; - state->style = XCB_IM_PreeditCallbacks | XCB_IM_StatusNothing; - state->encoding = Eutf8; + state->cap = Cclientpreedit; sinit(&pre, "あ", strlen("あ")); updatepreedit(state, &pre); } @@ -986,7 +949,6 @@ xim_adapter_callback_cleanup(struct ct *t) }; xcb_im_packet_header_fr_t hdr; xcb_im_reset_ic_reply_fr_t reply; - xcb_key_press_event_t ev; xcb_im_client_t *client; xcb_im_input_context_t *ic; Ximfix f; @@ -1001,9 +963,7 @@ xim_adapter_callback_cleanup(struct ct *t) wireclear(); wirebind(0, ic, &state); state.xic = ic; - state.style = XCB_IM_PreeditCallbacks | XCB_IM_StatusNothing; state.cap = Cclientpreedit; - state.encoding = Eutf8; keypress(&state, 'k', 0, &res); nexttrace(t, &f, Keypress, &state); keypress(&state, 'a', 0, &res); @@ -1016,9 +976,8 @@ xim_adapter_callback_cleanup(struct ct *t) nexttrace(t, &f, Keycap, &state); nexttrace(t, &f, Keyreset, &state); notrace(t, &f); - CT_EQ_INT(t, strlen("か"), reply.byte_length_of_committed_string); - CT_EQ_MEM(t, "か", reply.committed_string, - reply.byte_length_of_committed_string); + checkct(t, "reset reply", reply.committed_string, + reply.byte_length_of_committed_string, "か"); free(reply.committed_string); CT_CHECK(t, state.engaged); CT_EQ_PTR(t, &state, testengineowner()); @@ -1075,23 +1034,6 @@ xim_adapter_callback_cleanup(struct ct *t) CT_EQ_INT(t, Wdone, wirecalls[3].op); CT_EQ_PTR(t, nil, preowner); - ic = (xcb_im_input_context_t*)(uintptr)42; - wireclear(); - wirebind(0, ic, nil); - icinstall(nil, ic, nil); - CT_EQ_PTR(t, &passthrough, icbindings[0].data); - memset(&ev, 0, sizeof ev); - ev.response_type = XCB_KEY_PRESS; - ev.detail = 38; - hdr.major_opcode = XCB_XIM_FORWARD_EVENT; - callback(nil, nil, ic, &hdr, nil, &ev, nil); - CT_EQ_INT(t, 1, nwirecalls); - CT_EQ_INT(t, Wforward, wirecalls[0].op); - CT_EQ_UINT(t, ev.detail, wirecalls[0].keysym); - ev.response_type = XCB_KEY_RELEASE; - callback(nil, nil, ic, &hdr, nil, &ev, nil); - CT_EQ_INT(t, 2, nwirecalls); - CT_EQ_INT(t, Wforward, wirecalls[1].op); } void @@ -1110,8 +1052,7 @@ xim_adapter_callback_transfer(struct ct *t) goto cleanup; wireclear(); a.xic = aic; - a.style = XCB_IM_PreeditCallbacks | XCB_IM_StatusNothing; - a.encoding = Eutf8; + a.cap = Cclientpreedit; sinit(&pre, "あ", strlen("あ")); updatepreedit(&a, &pre); keypress(&b, 'x', 0, &res); @@ -1138,33 +1079,30 @@ xim_adapter_callback_owner_loss(struct ct *t) Keyreq req; Keyreq trace; Keyres res; - uchar token; - int foreign, i, n; + int foreign; ic = (xcb_im_input_context_t*)(uintptr)51; memset(&state, 0, sizeof state); foreign = 0; if(!ximbegin(t, &f, LangJP)) goto cleanup; - if(!CT_CHECK(t, wakeinit() == 0)) - goto cleanup; - wakedrain(); wireclear(); wirebind(0, ic, &state); state.xic = ic; - state.style = XCB_IM_PreeditCallbacks | XCB_IM_StatusNothing; state.cap = Cclientpreedit; - state.encoding = Eutf8; keypress(&state, 'k', 0, &res); nexttrace(t, &f, Keypress, &state); updatepreedit(&state, &res.preedit); CT_EQ_INT(t, Wstart, wirecalls[0].op); CT_EQ_INT(t, Wdraw, wirecalls[1].op); CT_EQ_PTR(t, &state, preowner); - checkownerwake(); + /* While we still own the engine, the periodic check changes nothing. */ + checkpreowner(); nexttrace(t, &f, Keycap, &state); CT_EQ_PTR(t, &state, preowner); + CT_EQ_INT(t, 2, nwirecalls); + /* Another frontend takes the engine; the next check clears our preedit. */ memset(&req, 0, sizeof req); req.owner = &foreign; req.op = Keypress; @@ -1177,21 +1115,6 @@ xim_adapter_callback_owner_loss(struct ct *t) CT_EQ_INT(t, Keypress, trace.op); CT_EQ_PTR(t, &foreign, trace.owner); CT_EQ_PTR(t, &foreign, testengineowner()); - /* A full pipe cannot block imthread; all bytes coalesce into one probe. */ - token = 0; - for(i = 0; i < 1<<20; i++){ - n = write(wakefd[1], &token, 1); - if(n < 0 && errno == EINTR){ - i--; - continue; - } - if(n < 0) - break; - } - CT_CHECK(t, i < 1<<20); - CT_CHECK(t, errno == EAGAIN || errno == EWOULDBLOCK); - ximownernotify(); - CT_CHECK(t, wakedrain() > 0); checkpreowner(); nexttrace(t, &f, Keycap, &state); CT_EQ_PTR(t, &foreign, testengineowner()); @@ -1201,6 +1124,8 @@ xim_adapter_callback_owner_loss(struct ct *t) CT_EQ_INT(t, Wdone, wirecalls[3].op); CT_CHECK(t, !state.prestarted); CT_EQ_PTR(t, nil, preowner); + checkpreowner(); + notrace(t, &f); /* Late XIM lifecycle messages remain idempotent after owner cleanup. */ release(&state); @@ -1226,7 +1151,6 @@ xim_adapter_callback_owner_loss(struct ct *t) CT_CHECK(t, channbrecv(f.trace, &trace) > 0); CT_EQ_INT(t, Keyrelease, trace.op); CT_EQ_PTR(t, &foreign, trace.owner); - wakedrain(); cleanup: preowner = nil; ximend(&f); @@ -1238,32 +1162,18 @@ xim_adapter_commit_encoding(struct ct *t) static char text[] = "ν•œπ €‹β€οΈπŸ‘©β€πŸ’»"; Ic state; xcb_im_input_context_t *ic; - char *want; - size_t nwant; ic = (xcb_im_input_context_t*)(uintptr)60; memset(&state, 0, sizeof state); state.xic = ic; - state.encoding = Eutf8; wireclear(); commit(&state, text, strlen(text)); CT_EQ_INT(t, 1, nwirecalls); CT_EQ_INT(t, Wcommit, wirecalls[0].op); CT_EQ_UINT(t, XCB_XIM_LOOKUP_CHARS, wirecalls[0].flag); CT_EQ_UINT(t, 0, wirecalls[0].keysym); - CT_EQ_INT(t, strlen(text), wirecalls[0].nbyte); - CT_EQ_MEM(t, text, wirecalls[0].text, wirecalls[0].nbyte); - - xcb_compound_text_init(); - state.encoding = Ecompound; + checkct(t, "commit", wirecalls[0].text, wirecalls[0].nbyte, text); wireclear(); - commit(&state, text, strlen(text)); - want = ximcompound(text, strlen(text), &nwant); - if(!CT_CHECK(t, want != nil)) - return; - CT_EQ_INT(t, 1, nwirecalls); - CT_EQ_INT(t, Wcommit, wirecalls[0].op); - CT_EQ_SIZE(t, nwant, wirecalls[0].nbyte); - CT_EQ_MEM(t, want, wirecalls[0].text, nwant); - free(want); + commit(&state, text, 0); + CT_EQ_INT(t, 0, nwirecalls); } diff --git a/tests/xim_test.c b/tests/xim_test.c deleted file mode 100644 index d6e44ad..0000000 --- a/tests/xim_test.c +++ /dev/null @@ -1,118 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -uint32_t keymaplookup(struct xkb_state*, uint8_t, uint16_t); -char *ximcompound(const char*, size_t, size_t*); - -enum -{ - ShiftMask = 1<<0, - LockMask = 1<<1, - Mod2Mask = 1<<4, - Mod5Mask = 1<<7, - Group1 = 1<<13, -}; - -static struct xkb_state* -keystate(const char *layout) -{ - struct xkb_context *context; - struct xkb_keymap *keymap; - struct xkb_rule_names names; - struct xkb_state *state; - - memset(&names, 0, sizeof names); - names.layout = layout; - context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - assert(context != NULL); - keymap = xkb_keymap_new_from_names(context, &names, - XKB_KEYMAP_COMPILE_NO_FLAGS); - assert(keymap != NULL); - state = xkb_state_new(keymap); - assert(state != NULL); - xkb_keymap_unref(keymap); - xkb_context_unref(context); - return state; -} - -static void -checkkeys(void) -{ - struct xkb_state *state; - - state = keystate("de,ru"); - assert(keymaplookup(state, 38, 0) == XKB_KEY_a); - assert(keymaplookup(state, 38, ShiftMask) == XKB_KEY_A); - assert(keymaplookup(state, 38, LockMask) == XKB_KEY_A); - assert(keymaplookup(state, 38, LockMask|ShiftMask) == XKB_KEY_a); - assert(keymaplookup(state, 26, Mod5Mask) == XKB_KEY_EuroSign); - assert(keymaplookup(state, 26, Group1) == XKB_KEY_Cyrillic_u); - assert(keymaplookup(state, 87, 0) == XKB_KEY_KP_End); - assert(keymaplookup(state, 87, Mod2Mask) == XKB_KEY_KP_1); - xkb_state_unref(state); - assert(keymaplookup(NULL, 38, 0) == XKB_KEY_NoSymbol); -} - -static void -checktext(const char *s) -{ - char *ct, *utf8; - size_t clen, len, ulen; - - len = strlen(s); - ct = ximcompound(s, len, &clen); - assert(ct != NULL); - utf8 = xcb_compound_text_to_utf8(ct, clen, &ulen); - assert(utf8 != NULL); - assert(ulen == len); - assert(memcmp(utf8, s, len) == 0); - free(utf8); - free(ct); -} - -static void -checkutf8run(void) -{ - static const char s[] = "πŸ˜€"; - char *ct; - size_t n; - - ct = ximcompound(s, sizeof s - 1, &n); - assert(ct != NULL); - assert(n >= 6); - assert(memcmp(ct, "\033%G", 3) == 0); - assert(memcmp(ct+n-3, "\033%@", 3) == 0); - free(ct); -} - -static void -checkcorpus(void) -{ - static const char *text[] = { - "ν•œκΈ€", - "𠀋", - "πŸ‘©β€πŸ’»", - }; - size_t i; - - for(i = 0; i < sizeof text / sizeof text[0]; i++) - checktext(text[i]); -} - -int -main(void) -{ - xcb_compound_text_init(); - - checkkeys(); - checkcorpus(); - checktext("AπŸ˜€ν•œ"); - checkutf8run(); - return 0; -} diff --git a/xim/Makefile b/xim/Makefile deleted file mode 100644 index 0078586..0000000 --- a/xim/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -CC = cc -PKG_CONFIG ?= pkg-config -CFLAGS ?= -O2 -XIM_CFLAGS = $(shell $(PKG_CONFIG) --cflags xcb-imdkit xkbcommon-x11) -XIM_LIBS = $(shell $(PKG_CONFIG) --libs xcb-imdkit xkbcommon-x11) -TEST = xim_test - -all: $(TEST) - -$(TEST): ../tests/xim_test.c keymap.c ximtext.c - $(CC) $(CPPFLAGS) -I.. $(XIM_CFLAGS) -Wall -Wextra $(CFLAGS) \ - $(LDFLAGS) -o $@ ../tests/xim_test.c keymap.c ximtext.c \ - $(XIM_LIBS) $(LDLIBS) - -check: $(TEST) - ./$(TEST) - -clean: - rm -f main.o strans-xim $(TEST) - -.PHONY: all check clean diff --git a/xim/keymap.c b/xim/keymap.c index 935877f..4ab2c32 100644 --- a/xim/keymap.c +++ b/xim/keymap.c @@ -3,6 +3,7 @@ #include #include +/* The keysym for an X core key event, with its modifier and group state. */ uint32_t keymaplookup(struct xkb_state *state, uint8_t keycode, uint16_t corestate) { @@ -21,15 +22,13 @@ keymaplookup(struct xkb_state *state, uint8_t keycode, uint16_t corestate) xkb_mod_mask_t mods; int i; - if(state == NULL) - return XKB_KEY_NoSymbol; keymap = xkb_state_get_keymap(state); mods = 0; for(i = 0; i < (int)(sizeof modname / sizeof modname[0]); i++){ if(!(corestate & (1U << i))) continue; index = xkb_keymap_mod_get_index(keymap, modname[i]); - if(index != XKB_MOD_INVALID && index < 8 * sizeof mods) + if(index != XKB_MOD_INVALID) mods |= (xkb_mod_mask_t)1 << index; } xkb_state_update_mask(state, mods, 0, 0, 0, 0, diff --git a/xim/xim.c b/xim/xim.c index 6f20482..977da11 100644 --- a/xim/xim.c +++ b/xim/xim.c @@ -2,9 +2,7 @@ #include "fn.h" #include -#include #include -#include #include #include #include @@ -12,8 +10,12 @@ #include u32int keymaplookup(struct xkb_state*, uint8_t, u16int); -char *ximcompound(const char*, size_t, size_t*); +/* + * One XIM input context. Only PreeditCallbacks clients draw the preedit + * themselves (cap); the popup shows it for the others. engaged means the + * engine has seen a real key from this context and owes it a release. + */ typedef struct Ic Ic; struct Ic { @@ -21,188 +23,31 @@ struct Ic xcb_im_input_context_t *xic; xcb_im_client_t *client; int engaged; - u32int style; int cap; - int encoding; int prestarted; int nprerune; - xcb_window_t clientwin; - xcb_window_t focuswin; - xcb_point_t spot; - int hasspot; Caret caret; }; -typedef struct Clientenc Clientenc; -struct Clientenc -{ - Clientenc *next; - xcb_im_client_t *client; - int encoding; -}; - enum { - Ecompound, - Eutf8, + Ownerpoll = 200, /* ms between owner checks while a preedit shows */ }; static xcb_connection_t *conn; static xcb_im_t *xim; static struct xkb_state *kstate; static xcb_window_t rootwin; -static int opened; static Ic *ics; static Ic *preowner; -static Ic passthrough; -static Clientenc *clientenc; static Channel *replyc; -static int wakefd[2] = {-1, -1}; -static char *encs[] = {"COMPOUND_TEXT", "UTF8_STRING"}; +static char *encs[] = {"COMPOUND_TEXT"}; static u32int styles[] = { XCB_IM_PreeditPosition | XCB_IM_StatusNothing, XCB_IM_PreeditCallbacks | XCB_IM_StatusNothing, XCB_IM_PreeditNothing | XCB_IM_StatusNothing, }; -static int stylecaps[] = {0, Cclientpreedit, 0}; - -static int -stylecap(u32int style, int *cap) -{ - int i; - - for(i = 0; i < nelem(styles); i++) - if(styles[i] == style){ - *cap = stylecaps[i]; - return 1; - } - *cap = 0; - return 0; -} - -static int -wakeinit(void) -{ - int fd[2]; - int flags, i; - - if(wakefd[0] >= 0) - return 0; - if(pipe(fd) < 0) - return -1; - for(i = 0; i < nelem(fd); i++){ - flags = fcntl(fd[i], F_GETFL); - if(flags < 0 || fcntl(fd[i], F_SETFL, - flags | O_NONBLOCK) < 0) - goto fail; - flags = fcntl(fd[i], F_GETFD); - if(flags < 0 || fcntl(fd[i], F_SETFD, - flags | FD_CLOEXEC) < 0) - goto fail; - } - wakefd[0] = fd[0]; - wakefd[1] = fd[1]; - return 0; -fail: - close(fd[0]); - close(fd[1]); - return -1; -} - -/* imthread only leaves a wake token; XIM ownership stays on the XIM thread. */ -void -ximownernotify(void) -{ - uchar token; - int n; - - if(wakefd[1] < 0) - return; - token = 0; - do - n = write(wakefd[1], &token, 1); - while(n < 0 && errno == EINTR); - /* EAGAIN means a pending byte will already wake the XIM poll loop. */ -} - -static int -wakedrain(void) -{ - uchar buf[32]; - int n, total; - - total = 0; - for(;;){ - n = read(wakefd[0], buf, sizeof buf); - if(n > 0){ - total += n; - continue; - } - if(n < 0 && errno == EINTR) - continue; - break; - } - return total; -} - -static int -getencoding(xcb_im_client_t *client) -{ - Clientenc *p; - - for(p = clientenc; p != nil; p = p->next) - if(p->client == client) - return p->encoding; - return Ecompound; -} - -static int -setencoding(xcb_im_client_t *client, int encoding) -{ - Clientenc *p; - - if(client == nil) - return -1; - for(p = clientenc; p != nil; p = p->next) - if(p->client == client){ - p->encoding = encoding; - return 0; - } - p = calloc(1, sizeof *p); - if(p == nil) - return -1; - p->client = client; - p->encoding = encoding; - p->next = clientenc; - clientenc = p; - return 0; -} - -static void -clearencoding(xcb_im_client_t *client) -{ - Clientenc *p, **link; - - for(link = &clientenc; (p = *link) != nil; link = &p->next) - if(p->client == client){ - *link = p->next; - free(p); - return; - } -} - -static void -clearencodings(void) -{ - Clientenc *next, *p; - - for(p = clientenc; p != nil; p = next){ - next = p->next; - free(p); - } - clientenc = nil; -} static void ximlog(char *msg) @@ -219,7 +64,6 @@ kinit(void) int32_t device; int ok; - context = nil; keymap = nil; state = nil; ok = -1; @@ -253,45 +97,14 @@ static xcb_screen_t* getscreen(int scr) { xcb_screen_iterator_t iter; - const xcb_setup_t *setup; - setup = xcb_get_setup(conn); - if(setup == nil) - return nil; - iter = xcb_setup_roots_iterator(setup); + iter = xcb_setup_roots_iterator(xcb_get_setup(conn)); for(; iter.rem; scr--, xcb_screen_next(&iter)) if(scr == 0) return iter.data; return nil; } -static void -readattrs(Ic *state) -{ - const xcb_im_preedit_attr_t *attr; - xcb_window_t clientwin, focuswin; - xcb_point_t spot; - int hasspot; - - clientwin = xcb_im_input_context_get_client_window(state->xic); - focuswin = xcb_im_input_context_get_focus_window(state->xic); - spot = state->spot; - hasspot = 0; - if(state->style == (XCB_IM_PreeditPosition | XCB_IM_StatusNothing) && - (xcb_im_input_context_get_preedit_attr_mask(state->xic) & - XCB_XIM_XNSpotLocation_MASK)){ - attr = xcb_im_input_context_get_preedit_attr(state->xic); - if(attr != nil){ - spot = attr->spot_location; - hasspot = 1; - } - } - state->clientwin = clientwin; - state->focuswin = focuswin; - state->spot = spot; - state->hasspot = hasspot; -} - static int translate(xcb_window_t win, int x, int y, Caret *caret) { @@ -335,32 +148,36 @@ placebottom(Ic *state, xcb_window_t win) return 1; } +/* + * The popup goes at the client's spot location when it sends one, else + * under the focus window, else under the client window. An unset focus + * window means the client window, as the XIM spec says. + */ static void place(Ic *state) { - xcb_window_t win[2]; - int i, nwin; + const xcb_im_preedit_attr_t *attr; + xcb_window_t clientwin, focuswin; memset(&state->caret, 0, sizeof state->caret); - win[0] = state->focuswin; - nwin = 1; - if(state->clientwin != state->focuswin) - win[nwin++] = state->clientwin; - if(state->hasspot && translate(state->focuswin, - state->spot.x, state->spot.y, &state->caret)) - return; - for(i = 0; i < nwin; i++) - if(placebottom(state, win[i])) + clientwin = xcb_im_input_context_get_client_window(state->xic); + focuswin = xcb_im_input_context_get_focus_window(state->xic); + if(focuswin == XCB_NONE) + focuswin = clientwin; + if(xcb_im_input_context_get_preedit_attr_mask(state->xic) & + XCB_XIM_XNSpotLocation_MASK){ + attr = xcb_im_input_context_get_preedit_attr(state->xic); + if(translate(focuswin, attr->spot_location.x, + attr->spot_location.y, &state->caret)) return; + } + if(placebottom(state, focuswin)) + return; + if(clientwin != focuswin) + placebottom(state, clientwin); } -static void -refreshplace(Ic *state) -{ - readattrs(state); - place(state); -} - +/* XIM text goes out as COMPOUND_TEXT; imdkit wraps UTF-8 in ESC%G. */ static void commit(Ic *state, char *s, int nbyte) { @@ -369,17 +186,12 @@ commit(Ic *state, char *s, int nbyte) if(nbyte == 0) return; - wire = s; - nwire = nbyte; - if(state->encoding == Ecompound){ - wire = ximcompound(s, nbyte, &nwire); - if(wire == nil) - return; - } + wire = xcb_utf8_to_compound_text(s, nbyte, &nwire); + if(wire == nil) + return; xcb_im_commit_string(xim, state->xic, XCB_XIM_LOOKUP_CHARS, wire, (u32int)nwire, 0); - if(wire != s) - free(wire); + free(wire); } static void @@ -387,7 +199,7 @@ clearpreedit(Ic *state) { xcb_im_preedit_draw_fr_t frame; - if(state == nil || !state->prestarted) + if(!state->prestarted) return; memset(&frame, 0, sizeof frame); frame.chg_length = state->nprerune; @@ -410,20 +222,16 @@ updatepreedit(Ic *state, Str *pre) size_t nwire; int i, nbyte; - if(!(state->style & XCB_IM_PreeditCallbacks)) + if(!(state->cap & Cclientpreedit)) return; if(pre->n == 0){ clearpreedit(state); return; } nbyte = stoutf(pre, utf, sizeof utf); - wire = utf; - nwire = nbyte; - if(state->encoding == Ecompound){ - wire = ximcompound(utf, nbyte, &nwire); - if(wire == nil) - return; - } + wire = xcb_utf8_to_compound_text(utf, nbyte, &nwire); + if(wire == nil) + return; for(i = 0; i < pre->n; i++) feedback[i] = XCB_XIM_UNDERLINE; if(!state->prestarted){ @@ -440,14 +248,7 @@ updatepreedit(Ic *state, Str *pre) xcb_im_preedit_draw_callback(xim, state->xic, &frame); state->nprerune = pre->n; preowner = state; - if(wire != utf) - free(wire); -} - -static int -meaningful(u32int key) -{ - return key != 0 && (key < Kmodfirst || key > Kmodlast); + free(wire); } static void @@ -467,6 +268,7 @@ sendrequest(Ic *state, int op, u32int key, u32int mod, Keyres *res) chanrecv(replyc, res); } +/* Another frontend may have taken the engine; then our preedit is stale. */ static void checkpreowner(void) { @@ -479,17 +281,10 @@ checkpreowner(void) clearpreedit(preowner); } -static void -checkownerwake(void) -{ - if(wakedrain() > 0) - checkpreowner(); -} - static void keypress(Ic *state, u32int key, u32int mod, Keyres *res) { - if(meaningful(key)){ + if(keymeaningful(key)){ if(preowner != nil && preowner != state) clearpreedit(preowner); state->engaged = 1; @@ -502,8 +297,6 @@ release(Ic *state) { Keyres res; - if(state == nil) - return; clearpreedit(state); if(!state->engaged) return; @@ -511,6 +304,7 @@ release(Ic *state) state->engaged = 0; } +/* XIM reset hands the pending preedit back to the client as committed text. */ static void resetic(Ic *state, xcb_im_reset_ic_reply_fr_t *reply) { @@ -519,9 +313,6 @@ resetic(Ic *state, xcb_im_reset_ic_reply_fr_t *reply) size_t nwire; int nbyte; - if(state == nil) - return; - sclear(&pending.preedit); if(!state->engaged){ clearpreedit(state); return; @@ -532,14 +323,7 @@ resetic(Ic *state, xcb_im_reset_ic_reply_fr_t *reply) if(reply == nil || !pending.eaten || pending.preedit.n == 0) return; nbyte = stoutf(&pending.preedit, utf, sizeof utf); - if(state->encoding == Ecompound) - wire = ximcompound(utf, nbyte, &nwire); - else{ - nwire = nbyte; - wire = malloc(nwire); - if(wire != nil) - memmove(wire, utf, nwire); - } + wire = xcb_utf8_to_compound_text(utf, nbyte, &nwire); if(wire == nil) return; reply->committed_string = (uchar*)wire; @@ -556,9 +340,9 @@ kpress(Ic *state, xcb_key_press_event_t *ev) key = keymaplookup(kstate, ev->detail, ev->state); key = ipckeysym(key, xkb_keysym_to_utf32(key)); - if(meaningful(key)){ + if(keymeaningful(key)){ wasvalid = state->caret.valid; - refreshplace(state); + place(state); if(state->engaged && wasvalid && !state->caret.valid) sendrequest(state, Keycaret, 0, 0, &res); } @@ -596,67 +380,39 @@ icfree(void *p) } static void -setstyle(Ic *state, u32int style) +iccreate(xcb_im_client_t *client, xcb_im_input_context_t *ic) { - int cap; + Ic *state; - if(!stylecap(style, &cap)) - style = XCB_IM_PreeditNothing | XCB_IM_StatusNothing; - state->style = style; - state->cap = cap; -} - -static void -icinstall(xcb_im_client_t *client, xcb_im_input_context_t *ic, Ic *state) -{ - if(state == nil){ - ximlog("out of memory; passing input context through"); - xcb_im_input_context_set_data(ic, &passthrough, nil); - return; - } + state = emalloc(sizeof(Ic)); state->xic = ic; state->client = client; - state->encoding = getencoding(client); - setstyle(state, xcb_im_input_context_get_input_style(ic)); - refreshplace(state); + if(xcb_im_input_context_get_input_style(ic) == + (XCB_IM_PreeditCallbacks | XCB_IM_StatusNothing)) + state->cap = Cclientpreedit; state->next = ics; ics = state; xcb_im_input_context_set_data(ic, state, icfree); } -static void -iccreate(xcb_im_client_t *client, xcb_im_input_context_t *ic) -{ - if(ic == nil || xcb_im_input_context_get_data(ic) != nil) - return; - icinstall(client, ic, calloc(1, sizeof(Ic))); -} - static void callback(xcb_im_t *im, xcb_im_client_t *client, xcb_im_input_context_t *ic, const xcb_im_packet_header_fr_t *hdr, void *frame, void *arg, void *user) { xcb_key_press_event_t *ev; + Keyres res; Ic *state; USED(im); USED(frame); USED(user); - if(hdr->major_opcode == XCB_XIM_ENCODING_NEGOTIATION){ - if(arg != nil && setencoding(client, - *(u16int*)arg == 1 ? Eutf8 : Ecompound) < 0) - die("xim: cannot remember client encoding"); - return; - } if(hdr->major_opcode == XCB_XIM_DISCONNECT || hdr->major_opcode == XCB_XIM_CLOSE){ for(state = ics; state != nil; state = state->next) if(state->client == client) release(state); - clearencoding(client); return; } - if(hdr->major_opcode == XCB_XIM_CREATE_IC){ iccreate(client, ic); return; @@ -664,16 +420,6 @@ callback(xcb_im_t *im, xcb_im_client_t *client, xcb_im_input_context_t *ic, if(ic == nil) return; state = xcb_im_input_context_get_data(ic); - if(state == nil) - return; - if(state == &passthrough){ - if(hdr->major_opcode == XCB_XIM_FORWARD_EVENT){ - ev = arg; - if(ev != nil) - xcb_im_forward_event(xim, ic, ev); - } - return; - } switch(hdr->major_opcode){ case XCB_XIM_FORWARD_EVENT: ev = arg; @@ -682,12 +428,9 @@ callback(xcb_im_t *im, xcb_im_client_t *client, xcb_im_input_context_t *ic, break; case XCB_XIM_SET_IC_VALUES: case XCB_XIM_SET_IC_FOCUS: - refreshplace(state); - if(state->engaged){ - Keyres res; - + place(state); + if(state->engaged) sendrequest(state, Keycaret, 0, 0, &res); - } break; case XCB_XIM_DESTROY_IC: case XCB_XIM_UNSET_IC_FOCUS: @@ -699,40 +442,6 @@ callback(xcb_im_t *im, xcb_im_client_t *client, xcb_im_input_context_t *ic, } } -static void -ximclose(void) -{ - Ic *state, *next; - - for(state = ics; state != nil; state = state->next) - release(state); - if(xim != nil){ - if(opened) - xcb_im_close_im(xim); - xcb_im_destroy(xim); - xim = nil; - } - opened = 0; - preowner = nil; - clearencodings(); - for(state = ics; state != nil; state = next){ - next = state->next; - free(state); - } - ics = nil; - rootwin = XCB_NONE; - xkb_state_unref(kstate); - kstate = nil; - if(conn != nil){ - xcb_disconnect(conn); - conn = nil; - } - if(replyc != nil){ - chanfree(replyc); - replyc = nil; - } -} - static int ximinit(void) { @@ -743,21 +452,13 @@ ximinit(void) int scr; replyc = chancreate(sizeof(Keyres), 0); - if(replyc == nil){ - ximlog("cannot create reply channel"); - return -1; - } - if(wakeinit() < 0){ - ximlog("cannot create owner wake pipe"); - return -1; - } st.nStyles = nelem(styles); st.styles = styles; enc.nEncodings = nelem(encs); enc.encodings = encs; xcb_compound_text_init(); conn = xcb_connect(nil, &scr); - if(conn == nil || xcb_connection_has_error(conn)){ + if(xcb_connection_has_error(conn)){ ximlog("cannot connect to X server"); return -1; } @@ -786,57 +487,42 @@ ximinit(void) ximlog("cannot claim XIM selection"); return -1; } - opened = 1; return 0; } void ximthread(void *arg) { - struct pollfd pfd[2]; + struct pollfd pfd; xcb_generic_event_t *ev; uint8_t type; - int fd, n; + int n; USED(arg); threadsetname("xim"); - if(ximinit() < 0){ - ximclose(); + if(ximinit() < 0) die("xim: initialization failed"); - } - fd = xcb_get_file_descriptor(conn); - pfd[0].fd = fd; - pfd[0].events = POLLIN; - pfd[1].fd = wakefd[0]; - pfd[1].events = POLLIN; + pfd.fd = xcb_get_file_descriptor(conn); + pfd.events = POLLIN; for(;;){ - pfd[0].revents = pfd[1].revents = 0; - n = poll(pfd, nelem(pfd), -1); - if(n < 0){ - if(errno == EINTR) - continue; + pfd.revents = 0; + n = poll(&pfd, 1, preowner != nil ? Ownerpoll : -1); + if(n < 0 && errno != EINTR) break; + while((ev = xcb_poll_for_event(conn)) != nil){ + type = ev->response_type & ~0x80; + if(type == XCB_MAPPING_NOTIFY){ + if(kinit() < 0) + ximlog("cannot refresh keyboard mapping"); + }else + xcb_im_filter_event(xim, ev); + free(ev); } - if(pfd[1].revents & POLLIN) - checkownerwake(); - if(pfd[0].revents) - while((ev = xcb_poll_for_event(conn)) != nil){ - type = ev->response_type & ~0x80; - if(type == XCB_MAPPING_NOTIFY){ - if(kinit() < 0) - ximlog("cannot refresh keyboard mapping"); - }else - xcb_im_filter_event(xim, ev); - free(ev); - /* Ownership changes outrank the next queued X event. */ - checkownerwake(); - } - if(pfd[0].revents & (POLLERR|POLLHUP|POLLNVAL)) + checkpreowner(); + if(pfd.revents & (POLLERR|POLLHUP|POLLNVAL)) break; } - n = xcb_connection_has_error(conn); - ximclose(); - if(n) + if(xcb_connection_has_error(conn)) die("xim: X server disconnected"); die("xim: frontend stopped"); } diff --git a/xim/ximtext.c b/xim/ximtext.c deleted file mode 100644 index 4adddbf..0000000 --- a/xim/ximtext.c +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include -#include -#include - -static char* -ximutf8compound(const char *s, size_t len, size_t *outlen) -{ - static const char begin[] = "\033%G"; - static const char end[] = "\033%@"; - char *ct; - size_t n; - - if(outlen != NULL) - *outlen = 0; - if(s == NULL || len > SIZE_MAX - (sizeof begin + sizeof end - 1)) - return NULL; - n = len + sizeof begin + sizeof end - 2; - ct = malloc(n + 1); - if(ct == NULL) - return NULL; - memcpy(ct, begin, sizeof begin - 1); - memcpy(ct + sizeof begin - 1, s, len); - memcpy(ct + sizeof begin - 1 + len, end, sizeof end - 1); - ct[n] = '\0'; - if(outlen != NULL) - *outlen = n; - return ct; -} - -char* -ximcompound(const char *s, size_t len, size_t *outlen) -{ - char *ct; - - if(outlen != NULL) - *outlen = 0; - if(s == NULL) - return NULL; - ct = xcb_utf8_to_compound_text(s, len, outlen); - if(ct != NULL) - return ct; - /* X.Org Compound Text extension for characters in no legacy charset. */ - return ximutf8compound(s, len, outlen); -}