xim: drop what no client uses; poll for owner loss like IBus
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.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -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*
|
||||
|
||||
4
Makefile
4
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
|
||||
|
||||
2
fn.h
2
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);
|
||||
|
||||
3
ibus.c
3
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)
|
||||
|
||||
19
strans.c
19
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;
|
||||
|
||||
@@ -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 $@ $<
|
||||
|
||||
|
||||
71
tests/keymap_test.c
Normal file
71
tests/keymap_test.c
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "test.h"
|
||||
#include <xcb-imdkit/encoding.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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*);
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
118
tests/xim_test.c
118
tests/xim_test.c
@@ -1,118 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <xcb-imdkit/encoding.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||
|
||||
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;
|
||||
}
|
||||
21
xim/Makefile
21
xim/Makefile
@@ -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
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <xkbcommon/xkbcommon-names.h>
|
||||
|
||||
/* 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,
|
||||
|
||||
464
xim/xim.c
464
xim/xim.c
@@ -2,9 +2,7 @@
|
||||
#include "fn.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <xkbcommon/xkbcommon-x11.h>
|
||||
@@ -12,8 +10,12 @@
|
||||
#include <xcb-imdkit/encoding.h>
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <xcb-imdkit/encoding.h>
|
||||
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user