compose: one Compose table for the XIM and IBus frontends
An IBus client throws away a dead key its engine did not take — the GTK module's own comment says so, and Qt does the same — so é and ü were lost in every IBus application, while XIM composed them with a table of its own. That table moves to compose.c, which both frontends now use; xim.c is the shorter for it. A finished sequence is text, not a key: the engine is asked to hand back what it had pending, and the composed text follows it, so the composed character can no longer land before the syllable typed before it.
This commit is contained in:
4
Makefile
4
Makefile
@@ -19,7 +19,7 @@ DOCKER_IMAGE = strans-build
|
|||||||
DOCKER_RUN = docker run --rm --user "$$(id -u):$$(id -g)" \
|
DOCKER_RUN = docker run --rm --user "$$(id -u):$$(id -g)" \
|
||||||
-v "$(CURDIR):/src" $(DOCKER_IMAGE)
|
-v "$(CURDIR):/src" $(DOCKER_IMAGE)
|
||||||
|
|
||||||
SRCS = dict.c font.c ibus.c ipc.c ko.c main.c popup_layout.c \
|
SRCS = compose.c 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 xim.c
|
srv.c str.c strans.c trie.c vi.c win.c xim.c
|
||||||
OBJS = $(SRCS:.c=.o)
|
OBJS = $(SRCS:.c=.o)
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ $(PROG): $(OBJS)
|
|||||||
$(LD) $(LDFLAGS) -o $@ $(OBJS) $(PROJECT_LDLIBS) $(LDLIBS)
|
$(LD) $(LDFLAGS) -o $@ $(OBJS) $(PROJECT_LDLIBS) $(LDLIBS)
|
||||||
|
|
||||||
$(OBJS): dat.h fn.h ipc.h
|
$(OBJS): dat.h fn.h ipc.h
|
||||||
ibus.o: PROJECT_CPPFLAGS += $(IBUS_CFLAGS)
|
compose.o ibus.o: PROJECT_CPPFLAGS += $(IBUS_CFLAGS)
|
||||||
font.o: PROJECT_CPPFLAGS += $(TEXT_CFLAGS)
|
font.o: PROJECT_CPPFLAGS += $(TEXT_CFLAGS)
|
||||||
win.o: PROJECT_CPPFLAGS += $(POPUP_CFLAGS)
|
win.o: PROJECT_CPPFLAGS += $(POPUP_CFLAGS)
|
||||||
xim.o: PROJECT_CPPFLAGS += $(XIM_CFLAGS)
|
xim.o: PROJECT_CPPFLAGS += $(XIM_CFLAGS)
|
||||||
|
|||||||
@@ -69,6 +69,11 @@ a time. Leaving the field or clicking elsewhere commits what is pending.
|
|||||||
|
|
||||||
Each XIM style is offered with StatusNothing and with StatusNone.
|
Each XIM style is offered with StatusNothing and with StatusNone.
|
||||||
|
|
||||||
|
Dead keys and Compose sequences are composed by strans itself for the XIM
|
||||||
|
and IBus frontends, from the table `XCOMPOSEFILE` or the locale names; the
|
||||||
|
GTK 3 module leaves them to GtkIMContextSimple. Their text follows
|
||||||
|
whatever was pending.
|
||||||
|
|
||||||
XIM text travels as `COMPOUND_TEXT`, which carries any UTF-8. A client that
|
XIM text travels as `COMPOUND_TEXT`, which carries any UTF-8. A client that
|
||||||
sends `XNSpotLocation` gets the popup under that spot whatever its preedit
|
sends `XNSpotLocation` gets the popup under that spot whatever its preedit
|
||||||
style, and above the line when there is no room below; otherwise the popup
|
style, and above the line when there is no room below; otherwise the popup
|
||||||
|
|||||||
54
compose.c
Normal file
54
compose.c
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#include "dat.h"
|
||||||
|
#include "fn.h"
|
||||||
|
|
||||||
|
#include <locale.h>
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
#include <xkbcommon/xkbcommon-compose.h>
|
||||||
|
|
||||||
|
static struct xkb_compose_state *cstate;
|
||||||
|
|
||||||
|
/* Dead keys and Compose: no frontend's client does them for us. */
|
||||||
|
void
|
||||||
|
composeinit(void)
|
||||||
|
{
|
||||||
|
struct xkb_context *ctx;
|
||||||
|
struct xkb_compose_table *table;
|
||||||
|
char *locale;
|
||||||
|
|
||||||
|
locale = setlocale(LC_CTYPE, "");
|
||||||
|
ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
|
if(ctx == nil)
|
||||||
|
return;
|
||||||
|
table = xkb_compose_table_new_from_locale(ctx,
|
||||||
|
locale != nil ? locale : "C", XKB_COMPOSE_COMPILE_NO_FLAGS);
|
||||||
|
if(table != nil)
|
||||||
|
cstate = xkb_compose_state_new(table, XKB_COMPOSE_STATE_NO_FLAGS);
|
||||||
|
xkb_compose_table_unref(table);
|
||||||
|
xkb_context_unref(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Feeds sym to the Compose table. Returns 1 while a sequence is
|
||||||
|
* unfinished: the key belongs to the sequence, not to the engine. A
|
||||||
|
* finished sequence leaves its text in buf, which the caller commits
|
||||||
|
* after whatever the engine had pending.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
composekey(u32int sym, char *buf, int n)
|
||||||
|
{
|
||||||
|
buf[0] = '\0';
|
||||||
|
if(cstate == nil ||
|
||||||
|
xkb_compose_state_feed(cstate, sym) != XKB_COMPOSE_FEED_ACCEPTED)
|
||||||
|
return 0;
|
||||||
|
switch(xkb_compose_state_get_status(cstate)){
|
||||||
|
case XKB_COMPOSE_COMPOSING:
|
||||||
|
case XKB_COMPOSE_CANCELLED:
|
||||||
|
return 1;
|
||||||
|
case XKB_COMPOSE_COMPOSED:
|
||||||
|
xkb_compose_state_get_utf8(cstate, buf, n);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
3
fn.h
3
fn.h
@@ -45,6 +45,9 @@ void ibusthread(void*);
|
|||||||
void ximthread(void*);
|
void ximthread(void*);
|
||||||
int keymeaningful(u32int);
|
int keymeaningful(u32int);
|
||||||
|
|
||||||
|
void composeinit(void);
|
||||||
|
int composekey(u32int, char*, int);
|
||||||
|
|
||||||
void* emalloc(ulong);
|
void* emalloc(ulong);
|
||||||
void* erealloc(void*, ulong);
|
void* erealloc(void*, ulong);
|
||||||
|
|
||||||
|
|||||||
27
ibus.c
27
ibus.c
@@ -531,13 +531,21 @@ dropconncontexts(DBusConnection *conn)
|
|||||||
* focus events of two applications can cross.
|
* focus events of two applications can cross.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
processkey(Ictx *ctx, u32int sym, u32int state, Keyres *res)
|
processkey(Ictx *ctx, u32int sym, u32int state, char *text, int n, Keyres *res)
|
||||||
{
|
{
|
||||||
|
text[0] = '\0';
|
||||||
if(state & Relmask || hidden(ctx))
|
if(state & Relmask || hidden(ctx))
|
||||||
return 0;
|
return 0;
|
||||||
ctx->focused = 1;
|
ctx->focused = 1;
|
||||||
sendrequest(ctx, Keypress, ipckeysym(sym, xkb_keysym_to_utf32(sym)),
|
/* An IBus client drops a dead key we do not take: compose it here. */
|
||||||
ipcmod(state), res);
|
if(composekey(sym, text, n))
|
||||||
|
return -1;
|
||||||
|
if(text[0] != '\0')
|
||||||
|
/* Composed text follows whatever was pending. */
|
||||||
|
sendrequest(ctx, Keyreset, 0, 0, res);
|
||||||
|
else
|
||||||
|
sendrequest(ctx, Keypress, ipckeysym(sym, xkb_keysym_to_utf32(sym)),
|
||||||
|
ipcmod(state), res);
|
||||||
if(preowner != nil && preowner != ctx)
|
if(preowner != nil && preowner != ctx)
|
||||||
checkpreowner();
|
checkpreowner();
|
||||||
return 1;
|
return 1;
|
||||||
@@ -641,14 +649,15 @@ handlekey(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
|||||||
{
|
{
|
||||||
dbus_uint32_t sym, code, state;
|
dbus_uint32_t sym, code, state;
|
||||||
Keyres res;
|
Keyres res;
|
||||||
char commit[Maxutf], preedit[Maxutf];
|
char commit[Maxutf], preedit[Maxutf], text[Maxutf];
|
||||||
int restart;
|
int restart, rv;
|
||||||
|
|
||||||
dbus_message_get_args(m, nil, DBUS_TYPE_UINT32, &sym,
|
dbus_message_get_args(m, nil, DBUS_TYPE_UINT32, &sym,
|
||||||
DBUS_TYPE_UINT32, &code, DBUS_TYPE_UINT32, &state,
|
DBUS_TYPE_UINT32, &code, DBUS_TYPE_UINT32, &state,
|
||||||
DBUS_TYPE_INVALID);
|
DBUS_TYPE_INVALID);
|
||||||
if(!processkey(ctx, sym, state, &res))
|
rv = processkey(ctx, sym, state, text, sizeof text, &res);
|
||||||
return replybool(c, m, 0);
|
if(rv <= 0) /* below zero: a sequence in the making */
|
||||||
|
return replybool(c, m, rv < 0);
|
||||||
stoutf(&res.commit, commit, sizeof commit);
|
stoutf(&res.commit, commit, sizeof commit);
|
||||||
stoutf(&res.preedit, preedit, sizeof preedit);
|
stoutf(&res.preedit, preedit, sizeof preedit);
|
||||||
/* A commit ends the preedit around it, so clients place it right. */
|
/* A commit ends the preedit around it, so clients place it right. */
|
||||||
@@ -658,10 +667,12 @@ handlekey(DBusConnection *c, DBusMessage *m, Ictx *ctx)
|
|||||||
emitpreedit(ctx, "");
|
emitpreedit(ctx, "");
|
||||||
if(commit[0] != '\0')
|
if(commit[0] != '\0')
|
||||||
emitcommit(ctx, commit);
|
emitcommit(ctx, commit);
|
||||||
|
if(text[0] != '\0')
|
||||||
|
emitcommit(ctx, text);
|
||||||
if(clientpreedit(ctx) && (!restart || preedit[0] != '\0'))
|
if(clientpreedit(ctx) && (!restart || preedit[0] != '\0'))
|
||||||
emitpreedit(ctx, preedit);
|
emitpreedit(ctx, preedit);
|
||||||
ctx->keying = 0;
|
ctx->keying = 0;
|
||||||
return replybool(c, m, res.eaten);
|
return replybool(c, m, res.eaten || text[0] != '\0');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Properties.Get PostProcessKeyEvent: what the last key produced. */
|
/* Properties.Get PostProcessKeyEvent: what the last key produced. */
|
||||||
|
|||||||
1
main.c
1
main.c
@@ -55,6 +55,7 @@ threadmain(int argc, char **argv)
|
|||||||
drawc = chancreate(sizeof(Drawcmd), 4);
|
drawc = chancreate(sizeof(Drawcmd), 4);
|
||||||
keyc = chancreate(sizeof(Keyreq), 0);
|
keyc = chancreate(sizeof(Keyreq), 0);
|
||||||
langinit(argv[1]);
|
langinit(argv[1]);
|
||||||
|
composeinit();
|
||||||
srvinit();
|
srvinit();
|
||||||
proccreate(drawthread, nil, 16384);
|
proccreate(drawthread, nil, 16384);
|
||||||
proccreate(srvthread, nil, 16384);
|
proccreate(srvthread, nil, 16384);
|
||||||
|
|||||||
@@ -32,10 +32,11 @@ FAULT = daemon_collision_test daemon_failure_test daemon_restart_test
|
|||||||
LIVE = $(SMOKE) $(FAULT)
|
LIVE = $(SMOKE) $(FAULT)
|
||||||
TESTSRC = test_util.c str_test.c trie_test.c \
|
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 \
|
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 compose_test.c \
|
||||||
|
xim_adapter_test.c
|
||||||
TESTOBJ = $(TESTSRC:.c=.o)
|
TESTOBJ = $(TESTSRC:.c=.o)
|
||||||
PARENTSRC = str.c trie.c dict.c ko.c vi.c ipc.c popup_layout.c \
|
PARENTSRC = str.c trie.c dict.c ko.c vi.c ipc.c popup_layout.c \
|
||||||
font.c
|
font.c compose.c
|
||||||
PARENTOBJ = $(PARENTSRC:%.c=unit_%.o)
|
PARENTOBJ = $(PARENTSRC:%.c=unit_%.o)
|
||||||
COMMONOBJ = $(TESTOBJ) $(PARENTOBJ)
|
COMMONOBJ = $(TESTOBJ) $(PARENTOBJ)
|
||||||
OBJS = unit_test.o stress_test.o $(COMMONOBJ)
|
OBJS = unit_test.o stress_test.o $(COMMONOBJ)
|
||||||
@@ -125,6 +126,7 @@ xim_adapter_test.o: UNIT_CPPFLAGS += $(XIM_CFLAGS)
|
|||||||
xim_adapter_test.o: ../xim.c
|
xim_adapter_test.o: ../xim.c
|
||||||
server_test.o: ../srv.c
|
server_test.o: ../srv.c
|
||||||
unit_font.o: UNIT_CPPFLAGS += $(TEXT_CFLAGS)
|
unit_font.o: UNIT_CPPFLAGS += $(TEXT_CFLAGS)
|
||||||
|
unit_compose.o compose_test.o: UNIT_CPPFLAGS += $(IBUS_CFLAGS)
|
||||||
|
|
||||||
unit_%.o: ../%.c ../dat.h ../fn.h ../ipc.h
|
unit_%.o: ../%.c ../dat.h ../fn.h ../ipc.h
|
||||||
$(CC) $(CPPFLAGS) $(UNIT_CPPFLAGS) $(UNIT_CFLAGS) $(CFLAGS) -c -o $@ $<
|
$(CC) $(CPPFLAGS) $(UNIT_CPPFLAGS) $(UNIT_CFLAGS) $(CFLAGS) -c -o $@ $<
|
||||||
|
|||||||
37
tests/compose_test.c
Normal file
37
tests/compose_test.c
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#include "dat.h"
|
||||||
|
#include "fn.h"
|
||||||
|
#include "test.h"
|
||||||
|
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||||
|
|
||||||
|
/* A sequence swallows its keys and hands its text back at the end. */
|
||||||
|
void
|
||||||
|
compose_sequences(struct ct *t)
|
||||||
|
{
|
||||||
|
static const struct {
|
||||||
|
u32int sym;
|
||||||
|
int composing;
|
||||||
|
char *text;
|
||||||
|
} keys[] = {
|
||||||
|
{ XKB_KEY_a, 0, "" },
|
||||||
|
{ XKB_KEY_dead_acute, 1, "" },
|
||||||
|
{ XKB_KEY_e, 0, "é" },
|
||||||
|
{ XKB_KEY_Multi_key, 1, "" },
|
||||||
|
{ XKB_KEY_o, 1, "" },
|
||||||
|
{ XKB_KEY_c, 0, "©" },
|
||||||
|
{ XKB_KEY_dead_acute, 1, "" },
|
||||||
|
{ XKB_KEY_Escape, 1, "" },
|
||||||
|
{ XKB_KEY_a, 0, "" },
|
||||||
|
};
|
||||||
|
char buf[Maxutf];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(!CT_CHECK(t, setenv("XCOMPOSEFILE", "data/compose", 1) == 0))
|
||||||
|
return;
|
||||||
|
composeinit();
|
||||||
|
for(i = 0; i < nelem(keys); i++){
|
||||||
|
CT_EQ_INT(t, keys[i].composing,
|
||||||
|
composekey(keys[i].sym, buf, sizeof buf));
|
||||||
|
CT_EQ_STR(t, keys[i].text, buf);
|
||||||
|
}
|
||||||
|
unsetenv("XCOMPOSEFILE");
|
||||||
|
}
|
||||||
2
tests/data/compose
Normal file
2
tests/data/compose
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<dead_acute> <e> : "é"
|
||||||
|
<Multi_key> <o> <c> : "©"
|
||||||
@@ -153,9 +153,10 @@ static Keyres
|
|||||||
contextkey(struct ct *t, Ibusfix *f, Ictx *ctx, u32int sym, u32int state)
|
contextkey(struct ct *t, Ibusfix *f, Ictx *ctx, u32int sym, u32int state)
|
||||||
{
|
{
|
||||||
Keyres res;
|
Keyres res;
|
||||||
|
char text[Maxutf];
|
||||||
|
|
||||||
memset(&res, 0, sizeof res);
|
memset(&res, 0, sizeof res);
|
||||||
CT_CHECK(t, processkey(ctx, sym, state, &res));
|
CT_CHECK(t, processkey(ctx, sym, state, text, sizeof text, &res));
|
||||||
nexttrace(t, f, Keypress, ctx);
|
nexttrace(t, f, Keypress, ctx);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -219,6 +220,7 @@ ibus_private_input_policy(struct ct *t)
|
|||||||
Ibusfix f;
|
Ibusfix f;
|
||||||
Ictx *ctx;
|
Ictx *ctx;
|
||||||
Keyres res;
|
Keyres res;
|
||||||
|
char text[Maxutf];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if(!ibusbegin(t, &f))
|
if(!ibusbegin(t, &f))
|
||||||
@@ -239,7 +241,7 @@ ibus_private_input_policy(struct ct *t)
|
|||||||
checkstr(t, "preedit", "k", &res.preedit);
|
checkstr(t, "preedit", "k", &res.preedit);
|
||||||
ctx->purpose = Ibuspurposepassword;
|
ctx->purpose = Ibuspurposepassword;
|
||||||
memset(&res, 0, sizeof res);
|
memset(&res, 0, sizeof res);
|
||||||
CT_CHECK(t, !processkey(ctx, 'x', 0, &res));
|
CT_CHECK(t, !processkey(ctx, 'x', 0, text, sizeof text, &res));
|
||||||
CT_CHECK(t, !res.eaten);
|
CT_CHECK(t, !res.eaten);
|
||||||
notrace(t, &f);
|
notrace(t, &f);
|
||||||
checkenginepreedit(t, "k");
|
checkenginepreedit(t, "k");
|
||||||
@@ -270,6 +272,7 @@ ibus_context_lifecycle(struct ct *t)
|
|||||||
Keyreq req;
|
Keyreq req;
|
||||||
Keyres res;
|
Keyres res;
|
||||||
Caret at;
|
Caret at;
|
||||||
|
char text[Maxutf];
|
||||||
|
|
||||||
if(!ibusbegin(t, &f))
|
if(!ibusbegin(t, &f))
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@@ -285,7 +288,7 @@ ibus_context_lifecycle(struct ct *t)
|
|||||||
notrace(t, &f);
|
notrace(t, &f);
|
||||||
CT_CHECK(t, a->caret.valid);
|
CT_CHECK(t, a->caret.valid);
|
||||||
memset(&res, 0, sizeof res);
|
memset(&res, 0, sizeof res);
|
||||||
CT_CHECK(t, !processkey(a, 'x', Relmask, &res));
|
CT_CHECK(t, !processkey(a, 'x', Relmask, text, sizeof text, &res));
|
||||||
CT_CHECK(t, !res.eaten);
|
CT_CHECK(t, !res.eaten);
|
||||||
notrace(t, &f);
|
notrace(t, &f);
|
||||||
CT_CHECK(t, !a->focused);
|
CT_CHECK(t, !a->focused);
|
||||||
@@ -293,7 +296,7 @@ ibus_context_lifecycle(struct ct *t)
|
|||||||
|
|
||||||
/* A key stands for the FocusIn a client may never send. */
|
/* A key stands for the FocusIn a client may never send. */
|
||||||
memset(&res, 0, sizeof res);
|
memset(&res, 0, sizeof res);
|
||||||
CT_CHECK(t, processkey(a, 'x', 0, &res));
|
CT_CHECK(t, processkey(a, 'x', 0, text, sizeof text, &res));
|
||||||
req = nexttrace(t, &f, Keypress, a);
|
req = nexttrace(t, &f, Keypress, a);
|
||||||
CT_CHECK(t, a->focused);
|
CT_CHECK(t, a->focused);
|
||||||
CT_CHECK(t, !res.eaten);
|
CT_CHECK(t, !res.eaten);
|
||||||
@@ -369,7 +372,7 @@ ibus_context_lifecycle(struct ct *t)
|
|||||||
notrace(t, &f);
|
notrace(t, &f);
|
||||||
|
|
||||||
memset(&res, 0, sizeof res);
|
memset(&res, 0, sizeof res);
|
||||||
CT_CHECK(t, !processkey(b, 'x', Relmask, &res));
|
CT_CHECK(t, !processkey(b, 'x', Relmask, text, sizeof text, &res));
|
||||||
CT_CHECK(t, !res.eaten);
|
CT_CHECK(t, !res.eaten);
|
||||||
notrace(t, &f);
|
notrace(t, &f);
|
||||||
CT_EQ_PTR(t, b, testengineowner());
|
CT_EQ_PTR(t, b, testengineowner());
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ void ibus_capability_policy(struct ct*);
|
|||||||
void ibus_private_input_policy(struct ct*);
|
void ibus_private_input_policy(struct ct*);
|
||||||
void ibus_context_lifecycle(struct ct*);
|
void ibus_context_lifecycle(struct ct*);
|
||||||
void ibus_active_release_lifecycle(struct ct*);
|
void ibus_active_release_lifecycle(struct ct*);
|
||||||
|
void compose_sequences(struct ct*);
|
||||||
void xim_keymap_lookup(struct ct*);
|
void xim_keymap_lookup(struct ct*);
|
||||||
void xim_compound_text(struct ct*);
|
void xim_compound_text(struct ct*);
|
||||||
void xim_adapter_key_contract(struct ct*);
|
void xim_adapter_key_contract(struct ct*);
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ static const struct ct_test tests[] = {
|
|||||||
{ "ibus/private-input-policy", ibus_private_input_policy },
|
{ "ibus/private-input-policy", ibus_private_input_policy },
|
||||||
{ "ibus/context-lifecycle", ibus_context_lifecycle },
|
{ "ibus/context-lifecycle", ibus_context_lifecycle },
|
||||||
{ "ibus/active-release-lifecycle", ibus_active_release_lifecycle },
|
{ "ibus/active-release-lifecycle", ibus_active_release_lifecycle },
|
||||||
|
{ "compose/sequences", compose_sequences },
|
||||||
{ "xim/keymap-lookup", xim_keymap_lookup },
|
{ "xim/keymap-lookup", xim_keymap_lookup },
|
||||||
{ "xim/compound-text", xim_compound_text },
|
{ "xim/compound-text", xim_compound_text },
|
||||||
{ "xim/adapter-key-contract", xim_adapter_key_contract },
|
{ "xim/adapter-key-contract", xim_adapter_key_contract },
|
||||||
|
|||||||
59
xim.c
59
xim.c
@@ -8,7 +8,6 @@
|
|||||||
#include <xcb/xcb_aux.h>
|
#include <xcb/xcb_aux.h>
|
||||||
#include <xcb/xkb.h>
|
#include <xcb/xkb.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
#include <xkbcommon/xkbcommon-compose.h>
|
|
||||||
#include <xkbcommon/xkbcommon-names.h>
|
#include <xkbcommon/xkbcommon-names.h>
|
||||||
#include <xkbcommon/xkbcommon-x11.h>
|
#include <xkbcommon/xkbcommon-x11.h>
|
||||||
#include <xcb-imdkit/imdkit.h>
|
#include <xcb-imdkit/imdkit.h>
|
||||||
@@ -35,7 +34,6 @@ struct Ic
|
|||||||
static xcb_connection_t *conn;
|
static xcb_connection_t *conn;
|
||||||
static xcb_im_t *xim;
|
static xcb_im_t *xim;
|
||||||
static struct xkb_state *kstate;
|
static struct xkb_state *kstate;
|
||||||
static struct xkb_compose_state *compose;
|
|
||||||
static uint8_t xkbevent;
|
static uint8_t xkbevent;
|
||||||
static xcb_window_t rootwin;
|
static xcb_window_t rootwin;
|
||||||
static Ic *ics;
|
static Ic *ics;
|
||||||
@@ -97,26 +95,6 @@ out:
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dead keys and Compose: Xlib leaves them to the input method server. */
|
|
||||||
static void
|
|
||||||
cinit(void)
|
|
||||||
{
|
|
||||||
struct xkb_context *context;
|
|
||||||
struct xkb_compose_table *table;
|
|
||||||
char *locale;
|
|
||||||
|
|
||||||
locale = setlocale(LC_CTYPE, "");
|
|
||||||
context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
|
||||||
if(context == nil)
|
|
||||||
return;
|
|
||||||
table = xkb_compose_table_new_from_locale(context,
|
|
||||||
locale != nil ? locale : "C", XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
||||||
if(table != nil)
|
|
||||||
compose = xkb_compose_state_new(table, XKB_COMPOSE_STATE_NO_FLAGS);
|
|
||||||
xkb_compose_table_unref(table);
|
|
||||||
xkb_context_unref(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
kwatch(void)
|
kwatch(void)
|
||||||
{
|
{
|
||||||
@@ -401,39 +379,27 @@ static void
|
|||||||
kpress(Ic *state, xcb_key_press_event_t *ev)
|
kpress(Ic *state, xcb_key_press_event_t *ev)
|
||||||
{
|
{
|
||||||
Keyres res;
|
Keyres res;
|
||||||
char buf[Maxutf];
|
char buf[Maxutf], text[Maxutf];
|
||||||
u32int key, sym;
|
u32int key, sym;
|
||||||
int composed, n;
|
int n;
|
||||||
|
|
||||||
sym = keymaplookup(kstate, ev->detail, ev->state);
|
sym = keymaplookup(kstate, ev->detail, ev->state);
|
||||||
composed = 0;
|
if(composekey(sym, text, sizeof text))
|
||||||
if(compose != nil &&
|
return;
|
||||||
xkb_compose_state_feed(compose, sym) == XKB_COMPOSE_FEED_ACCEPTED)
|
|
||||||
switch(xkb_compose_state_get_status(compose)){
|
|
||||||
case XKB_COMPOSE_COMPOSING:
|
|
||||||
case XKB_COMPOSE_CANCELLED:
|
|
||||||
return;
|
|
||||||
case XKB_COMPOSE_COMPOSED:
|
|
||||||
sym = xkb_compose_state_get_one_sym(compose);
|
|
||||||
composed = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
key = ipckeysym(sym, xkb_keysym_to_utf32(sym));
|
key = ipckeysym(sym, xkb_keysym_to_utf32(sym));
|
||||||
if(keymeaningful(key))
|
if(keymeaningful(key))
|
||||||
place(state);
|
place(state);
|
||||||
keypress(state, key, ev->state, &res);
|
if(text[0] != '\0')
|
||||||
|
/* Composed text follows whatever was pending. */
|
||||||
|
sendrequest(state, Keyreset, 0, 0, &res);
|
||||||
|
else
|
||||||
|
keypress(state, key, ev->state, &res);
|
||||||
n = stoutf(&res.commit, buf, sizeof buf);
|
n = stoutf(&res.commit, buf, sizeof buf);
|
||||||
commit(state, buf, n);
|
commit(state, buf, n);
|
||||||
updatepreedit(state, &res.preedit);
|
updatepreedit(state, &res.preedit);
|
||||||
if(res.eaten)
|
if(text[0] != '\0')
|
||||||
;
|
commit(state, text, strlen(text));
|
||||||
else if(composed){
|
else if(!res.eaten)
|
||||||
/* The event holds the last key of the sequence, not its result. */
|
|
||||||
n = xkb_compose_state_get_utf8(compose, buf, sizeof buf);
|
|
||||||
commit(state, buf, min(n, (int)sizeof buf - 1));
|
|
||||||
}else
|
|
||||||
xcb_im_forward_event(xim, state->xic, ev);
|
xcb_im_forward_event(xim, state->xic, ev);
|
||||||
xcb_flush(conn);
|
xcb_flush(conn);
|
||||||
}
|
}
|
||||||
@@ -554,7 +520,6 @@ ximinit(void)
|
|||||||
ximlog("cannot read keyboard mapping");
|
ximlog("cannot read keyboard mapping");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
cinit();
|
|
||||||
kwatch();
|
kwatch();
|
||||||
win = xcb_generate_id(conn);
|
win = xcb_generate_id(conn);
|
||||||
xcb_create_window(conn, XCB_COPY_FROM_PARENT, win, screen->root,
|
xcb_create_window(conn, XCB_COPY_FROM_PARENT, win, screen->root,
|
||||||
|
|||||||
Reference in New Issue
Block a user