xim: run server inside strans

This commit is contained in:
2026-08-14 16:16:27 +09:00
parent 08369e718e
commit 4e07699460
9 changed files with 394 additions and 341 deletions

1
.gitignore vendored
View File

@@ -8,7 +8,6 @@
/tests/daemon_failure_test /tests/daemon_failure_test
/tests/daemon_restart_test /tests/daemon_restart_test
/xim/*.o /xim/*.o
/xim/strans-xim
/xim/xim_test /xim/xim_test
/gtk/im-strans.so /gtk/im-strans.so
/bench/bench /bench/bench

View File

@@ -2,8 +2,10 @@ CC = 9c
LD = 9l LD = 9l
PKG_CFLAGS = $(shell pkg-config --cflags dbus-1 xkbcommon) PKG_CFLAGS = $(shell pkg-config --cflags dbus-1 xkbcommon)
PKG_LIBS = $(shell pkg-config --libs dbus-1 xkbcommon) PKG_LIBS = $(shell pkg-config --libs dbus-1 xkbcommon)
TEXT_CFLAGS = $(shell pkg-config --cflags 'pangocairo >= 1.56' 'cairo >= 1.18') XIM_CFLAGS = $(shell pkg-config --cflags xcb-imdkit xkbcommon)
TEXT_LIBS = $(shell pkg-config --libs 'pangocairo >= 1.56' 'cairo >= 1.18') XIM_LIBS = $(shell pkg-config --libs xcb-imdkit xkbcommon)
TEXT_CFLAGS = $(shell pkg-config --cflags pangocairo cairo)
TEXT_LIBS = $(shell pkg-config --libs pangocairo cairo)
CFLAGS = -Wall -Wextra -O2 -g $(PKG_CFLAGS) CFLAGS = -Wall -Wextra -O2 -g $(PKG_CFLAGS)
PROG = strans PROG = strans
DOCKER_IMAGE = strans-build DOCKER_IMAGE = strans-build
@@ -12,15 +14,19 @@ DOCKER_RUN = docker run --rm --user "$$(id -u):$$(id -g)" \
SRCS = dict.c font.c hash.c ibus.c ipc.c ko.c main.c popup_layout.c \ SRCS = dict.c font.c hash.c ibus.c ipc.c ko.c main.c popup_layout.c \
srv.c str.c strans.c trie.c vi.c win.c srv.c str.c strans.c trie.c vi.c win.c
OBJS = $(SRCS:.c=.o) XIMSRCS = xim/xim.c xim/keymap.c xim/ximtext.c
XIMOBJS = $(XIMSRCS:.c=.o)
OBJS = $(SRCS:.c=.o) $(XIMOBJS)
all: $(PROG) xim gtk all: $(PROG) gtk
$(PROG): $(OBJS) $(PROG): $(OBJS)
$(LD) -o $@ $(OBJS) -lthread -lbio -lxcb $(PKG_LIBS) $(TEXT_LIBS) $(LD) -o $@ $(OBJS) -lthread -lbio -lxcb $(PKG_LIBS) $(TEXT_LIBS) \
$(XIM_LIBS)
$(OBJS): dat.h fn.h ipc.h $(OBJS): dat.h fn.h ipc.h
font.o: CFLAGS += $(TEXT_CFLAGS) font.o: CFLAGS += $(TEXT_CFLAGS)
$(XIMOBJS): CFLAGS += -I. $(XIM_CFLAGS)
clean: clean:
rm -f $(OBJS) $(PROG) rm -f $(OBJS) $(PROG)
@@ -29,9 +35,6 @@ clean:
$(MAKE) -C gtk/ clean $(MAKE) -C gtk/ clean
$(MAKE) -C bench/ clean $(MAKE) -C bench/ clean
xim:
$(MAKE) -C xim/
gtk: gtk:
$(MAKE) -C gtk/ $(MAKE) -C gtk/
@@ -68,5 +71,5 @@ verify-map:
python3 -B tests/mkhanja_test.py python3 -B tests/mkhanja_test.py
python3 -B tests/skk2ktrans_test.py python3 -B tests/skk2ktrans_test.py
.PHONY: all check test verify-map clean xim gtk bench docker docker-image \ .PHONY: all check test verify-map clean gtk bench docker docker-image \
docker-build docker-check docker-bench docker-build docker-check docker-bench

View File

@@ -40,9 +40,8 @@ make docker-check
Build output: Build output:
```text ```text
strans daemon and IBus frontend strans daemon with IBus and XIM frontends
xim/strans-xim XIM frontend gtk/im-strans.so GTK 3 frontend module
gtk/im-strans.so GTK 3 frontend
``` ```
Use `make docker-check TESTARGS=hangul` to run selected C tests. Native Use `make docker-check TESTARGS=hangul` to run selected C tests. Native
@@ -57,16 +56,16 @@ from the build tree:
```sh ```sh
./run.sh ./run.sh
# daemon only # equivalently
./strans map ./strans map
``` ```
Pango shapes the complete string and selects glyph fallback from the system Pango shapes the complete string and selects glyph fallback from the system
font set. font set.
The popup renderer requires Pango/PangoCairo 1.56 or newer and Cairo 1.18 or The popup renderer uses Pango/PangoCairo and Cairo. When `DISPLAY` is set,
newer. `run.sh` starts the daemon and, when `DISPLAY` is set, the XIM frontend. `strans` starts its XIM worker in the same process. `run.sh` restarts the
It restarts existing `strans` processes owned by the current user. existing `strans` process owned by the current user.
Configure clients as needed: Configure clients as needed:
@@ -82,8 +81,8 @@ GLFW_IM_MODULE=ibus kitty
``` ```
strans provides its own IBus endpoint; `ibus-daemon` and fcitx are not strans provides its own IBus endpoint; `ibus-daemon` and fcitx are not
required. Without `DISPLAY`, the daemon and IBus frontend still work, but the required. Without `DISPLAY`, the daemon and IBus frontend still work, but XIM
popup is disabled. is not started and the popup is disabled.
## Dictionary data ## Dictionary data

1
fn.h
View File

@@ -39,6 +39,7 @@ void backvi(Im*);
void srvinit(void); void srvinit(void);
void srvthread(void*); void srvthread(void*);
void ibusthread(void*); void ibusthread(void*);
void ximthread(void*);
void* emalloc(ulong); void* emalloc(ulong);
void* erealloc(void*, ulong); void* erealloc(void*, ulong);

6
main.c
View File

@@ -49,6 +49,8 @@ erealloc(void *p, ulong n)
void void
threadmain(int argc, char **argv) threadmain(int argc, char **argv)
{ {
char *display;
if(argc != 2) if(argc != 2)
usage(); usage();
@@ -65,6 +67,10 @@ threadmain(int argc, char **argv)
die("can't create server worker"); die("can't create server worker");
if(proccreate(ibusthread, nil, 32768) < 0) if(proccreate(ibusthread, nil, 32768) < 0)
die("can't create IBus worker"); die("can't create IBus worker");
display = getenv("DISPLAY");
if(display != nil && display[0] != '\0' &&
proccreate(ximthread, nil, 32768) < 0)
fprint(2, "strans: xim: cannot create worker\n");
if(threadcreate(dictthread, nil, 16384) < 0) if(threadcreate(dictthread, nil, 16384) < 0)
die("can't create dictionary worker"); die("can't create dictionary worker");
imthread(nil); imthread(nil);

14
run.sh
View File

@@ -11,28 +11,16 @@ if ! test -x ./strans; then
echo "run.sh: ./strans is not executable; run make first" >&2 echo "run.sh: ./strans is not executable; run make first" >&2
exit 1 exit 1
fi fi
if test -n "${DISPLAY-}" && ! test -x xim/strans-xim; then
echo "run.sh: xim/strans-xim is not executable; run make first" >&2
exit 1
fi
if ! command -v pkill >/dev/null 2>&1; then if ! command -v pkill >/dev/null 2>&1; then
echo "run.sh: pkill is required" >&2 echo "run.sh: pkill is required" >&2
exit 1 exit 1
fi fi
uid=$(id -u) || exit 1 uid=$(id -u) || exit 1
pkill -TERM -u "$uid" -x strans-xim 2>/dev/null || :
pkill -TERM -u "$uid" -x strans 2>/dev/null || : pkill -TERM -u "$uid" -x strans 2>/dev/null || :
sleep 0.1 sleep 0.1
pkill -KILL -u "$uid" -x strans-xim 2>/dev/null || :
pkill -KILL -u "$uid" -x strans 2>/dev/null || : pkill -KILL -u "$uid" -x strans 2>/dev/null || :
./strans map </dev/null & ./strans map </dev/null &
strans_pid=$! strans_pid=$!
if test -n "${DISPLAY-}"; then echo "run.sh: started strans $strans_pid"
xim/strans-xim </dev/null &
xim_pid=$!
echo "run.sh: started daemon $strans_pid and XIM $xim_pid"
else
echo "run.sh: started daemon $strans_pid; DISPLAY is unset"
fi

View File

@@ -2,18 +2,9 @@ CC = cc
XIM_CFLAGS = $(shell pkg-config --cflags xcb-imdkit xkbcommon) XIM_CFLAGS = $(shell pkg-config --cflags xcb-imdkit xkbcommon)
XIM_LIBS = $(shell pkg-config --libs xcb-imdkit xkbcommon) XIM_LIBS = $(shell pkg-config --libs xcb-imdkit xkbcommon)
CFLAGS = -Wall -Wextra -O2 -I.. $(XIM_CFLAGS) CFLAGS = -Wall -Wextra -O2 -I.. $(XIM_CFLAGS)
PROG = strans-xim
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
TEST = xim_test TEST = xim_test
all: $(PROG) all: $(TEST)
$(PROG): $(OBJS) ../ipc.c ../ipc.h
$(CC) $(CFLAGS) -o $@ $(OBJS) ../ipc.c $(XIM_LIBS)
$(OBJS): ../ipc.h
$(TEST): ../tests/xim_test.c keymap.c ximtext.c $(TEST): ../tests/xim_test.c keymap.c ximtext.c
$(CC) $(CFLAGS) -o $@ ../tests/xim_test.c keymap.c ximtext.c $(XIM_LIBS) $(CC) $(CFLAGS) -o $@ ../tests/xim_test.c keymap.c ximtext.c $(XIM_LIBS)
@@ -22,6 +13,6 @@ check: $(TEST)
./$(TEST) ./$(TEST)
clean: clean:
rm -f $(OBJS) $(PROG) $(TEST) rm -f main.o strans-xim $(TEST)
.PHONY: all check clean .PHONY: all check clean

View File

@@ -1,298 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <xcb/xcb.h>
#include <xkbcommon/xkbcommon.h>
#include <xcb-imdkit/imdkit.h>
#include <xcb-imdkit/encoding.h>
#include "ipc.h"
uint32_t keymaplookup(const uint32_t*, int, uint16_t);
char *ximcompound(const char*, size_t, size_t*);
typedef struct Ic Ic;
struct Ic
{
int fd;
};
static xcb_connection_t *conn;
static xcb_im_t *xim;
static xcb_keysym_t *kmap;
static uint8_t minkc, maxkc;
static uint8_t symsper;
static char *encs[] = {"COMPOUND_TEXT"};
static uint32_t styles[] = {
XCB_IM_PreeditNothing | XCB_IM_StatusNothing,
XCB_IM_PreeditNone | XCB_IM_StatusNone,
};
static void
die(char *msg)
{
fprintf(stderr, "strans-xim: %s\n", msg);
exit(1);
}
static void
kinit(void)
{
xcb_get_keyboard_mapping_cookie_t c;
xcb_get_keyboard_mapping_reply_t *r;
const xcb_setup_t *setup;
xcb_keysym_t *syms;
int n;
setup = xcb_get_setup(conn);
if(setup == NULL)
die("xcb_get_setup failed");
minkc = setup->min_keycode;
maxkc = setup->max_keycode;
c = xcb_get_keyboard_mapping(conn, minkc, maxkc - minkc + 1);
r = xcb_get_keyboard_mapping_reply(conn, c, NULL);
if(r == NULL)
die("keyboard mapping failed");
symsper = r->keysyms_per_keycode;
n = xcb_get_keyboard_mapping_keysyms_length(r);
syms = malloc(n * sizeof(xcb_keysym_t));
if(syms == NULL)
die("malloc failed");
memcpy(syms, xcb_get_keyboard_mapping_keysyms(r),
n * sizeof(xcb_keysym_t));
free(kmap);
kmap = syms;
free(r);
}
static uint32_t
kget(uint8_t kc, uint16_t state)
{
if(kmap == NULL || kc < minkc || kc > maxkc)
return 0;
return keymaplookup(kmap + (kc - minkc) * symsper, symsper, state);
}
static xcb_screen_t*
getscreen(int scr)
{
xcb_screen_iterator_t iter;
const xcb_setup_t *setup;
setup = xcb_get_setup(conn);
if(setup == NULL)
die("xcb_get_setup failed");
iter = xcb_setup_roots_iterator(setup);
for(; iter.rem; scr--, xcb_screen_next(&iter))
if(scr == 0)
return iter.data;
die("no screen");
return NULL;
}
static void
commit(xcb_im_input_context_t *ic, char *s, int len)
{
char *ct;
size_t clen;
if(len == 0)
return;
ct = ximcompound(s, len, &clen);
if(ct == NULL)
return;
xcb_im_commit_string(xim, ic, XCB_XIM_LOOKUP_CHARS, ct, clen, 0);
xcb_flush(conn);
free(ct);
}
static void
srvclose(Ic *state)
{
if(state == NULL || state->fd < 0)
return;
close(state->fd);
state->fd = -1;
}
static int
srvconnect(Ic *state)
{
if(state->fd >= 0)
return 0;
state->fd = ipcconnect();
if(state->fd < 0)
return -1;
return 0;
}
static int
readresp(Ic *state, xcb_im_input_context_t *ic)
{
char buf[Ipcfieldmax+1];
Ipcresp resp;
if(ipcreadresp(state->fd, 0, buf, sizeof buf, NULL, 0, &resp) < 0)
return -1;
if(resp.ncommit > 0)
commit(ic, buf, resp.ncommit);
return resp.eaten;
}
static void
kpress(Ic *state, xcb_im_input_context_t *ic, xcb_key_press_event_t *ev)
{
unsigned char buf[Ipcreqsz];
uint32_t key, rune;
int eaten;
key = kget(ev->detail, ev->state);
rune = xkb_keysym_to_utf32(key);
if(rune >= ' ' && rune != 0x7f)
key = rune;
else if(key >= 0xff00 && key <= 0xffff)
key = Kspec + (key - 0xff00);
else
key = rune;
ipcpackreq(buf, 0, ev->state, key);
eaten = 0;
if(srvconnect(state) == 0){
if(ipcsend(state->fd, buf, sizeof buf) < 0 ||
(eaten = readresp(state, ic)) < 0){
srvclose(state);
eaten = 0;
}
}
if(eaten == 0)
xcb_im_forward_event(xim, ic, ev);
xcb_flush(conn);
}
static void
reset(Ic *state, xcb_im_input_context_t *ic, int release)
{
unsigned char buf[Ipcreqsz];
if(state == NULL || state->fd < 0)
return;
ipcpackreset(buf, 0);
if(ipcsend(state->fd, buf, sizeof buf) < 0 ||
readresp(state, ic) < 0)
release = 1;
if(release)
srvclose(state);
}
static void
icfree(void *p)
{
Ic *state;
state = p;
/* imdkit frees this data on context destruction and client loss. */
srvclose(state);
free(state);
}
static void
iccreate(xcb_im_input_context_t *ic)
{
Ic *state;
if(ic == NULL || xcb_im_input_context_get_data(ic) != NULL)
return;
state = calloc(1, sizeof *state);
if(state == NULL)
return;
state->fd = -1;
xcb_im_input_context_set_data(ic, state, icfree);
}
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;
Ic *state;
(void)im;
(void)client;
(void)frame;
(void)user;
if(hdr->major_opcode == XCB_XIM_CREATE_IC){
iccreate(ic);
return;
}
if(ic == NULL)
return;
state = xcb_im_input_context_get_data(ic);
if(state == NULL)
return;
switch(hdr->major_opcode){
case XCB_XIM_FORWARD_EVENT:
ev = arg;
if(ev != NULL && (ev->response_type & ~0x80) == XCB_KEY_PRESS)
kpress(state, ic, ev);
break;
case XCB_XIM_RESET_IC:
/* ResetIC ends this context's ownership, like focus loss. */
reset(state, ic, 1);
break;
case XCB_XIM_UNSET_IC_FOCUS:
reset(state, ic, 1);
break;
}
}
static void
ximinit(void)
{
xcb_screen_t *screen;
xcb_window_t win;
xcb_im_styles_t st;
xcb_im_encodings_t enc;
int scr;
st.nStyles = 2;
st.styles = styles;
enc.nEncodings = 1;
enc.encodings = encs;
xcb_compound_text_init();
conn = xcb_connect(NULL, &scr);
if(conn == NULL || xcb_connection_has_error(conn))
die("xcb_connect failed");
screen = getscreen(scr);
kinit();
win = xcb_generate_id(conn);
xcb_create_window(conn, XCB_COPY_FROM_PARENT, win, screen->root,
0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual, 0, NULL);
xim = xcb_im_create(conn, scr, win, "strans",
XCB_IM_ALL_LOCALES, &st, NULL, NULL, &enc,
XCB_EVENT_MASK_KEY_PRESS, callback, NULL);
if(xim == NULL || !xcb_im_open_im(xim))
die("xcb_im failed");
}
int
main(void)
{
xcb_generic_event_t *ev;
uint8_t type;
ximinit();
for(;;){
ev = xcb_wait_for_event(conn);
if(ev == NULL)
break;
type = ev->response_type & ~0x80;
if(type == XCB_MAPPING_NOTIFY)
kinit();
else
xcb_im_filter_event(xim, ev);
free(ev);
}
return 0;
}

364
xim/xim.c Normal file
View File

@@ -0,0 +1,364 @@
#include "dat.h"
#include "fn.h"
#include <xcb/xcb.h>
#include <xkbcommon/xkbcommon.h>
#include <xcb-imdkit/imdkit.h>
#include <xcb-imdkit/encoding.h>
u32int keymaplookup(const u32int*, int, u16int);
char *ximcompound(const char*, size_t, size_t*);
typedef struct Ic Ic;
struct Ic
{
Ic *next;
int engaged;
};
static xcb_connection_t *conn;
static xcb_im_t *xim;
static xcb_keysym_t *kmap;
static uint8_t minkc, maxkc;
static uint8_t symsper;
static int opened;
static Ic *ics;
static Channel *replyc;
static char *encs[] = {"COMPOUND_TEXT"};
static u32int styles[] = {
XCB_IM_PreeditNothing | XCB_IM_StatusNothing,
XCB_IM_PreeditNone | XCB_IM_StatusNone,
};
static void
ximlog(char *msg)
{
fprint(2, "strans: xim: %s\n", msg);
}
static int
kinit(void)
{
xcb_get_keyboard_mapping_cookie_t c;
xcb_get_keyboard_mapping_reply_t *r;
const xcb_setup_t *setup;
xcb_keysym_t *syms;
uint8_t newmin, newmax, newper;
int n;
setup = xcb_get_setup(conn);
if(setup == nil)
return -1;
newmin = setup->min_keycode;
newmax = setup->max_keycode;
c = xcb_get_keyboard_mapping(conn, newmin, newmax - newmin + 1);
r = xcb_get_keyboard_mapping_reply(conn, c, nil);
if(r == nil)
return -1;
newper = r->keysyms_per_keycode;
n = xcb_get_keyboard_mapping_keysyms_length(r);
syms = malloc(n * sizeof(xcb_keysym_t));
if(syms == nil){
free(r);
return -1;
}
memcpy(syms, xcb_get_keyboard_mapping_keysyms(r),
n * sizeof(xcb_keysym_t));
free(kmap);
kmap = syms;
minkc = newmin;
maxkc = newmax;
symsper = newper;
free(r);
return 0;
}
static u32int
kget(uint8_t kc, u16int state)
{
if(kmap == nil || kc < minkc || kc > maxkc)
return 0;
return keymaplookup(kmap + (kc - minkc) * symsper, symsper, state);
}
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);
for(; iter.rem; scr--, xcb_screen_next(&iter))
if(scr == 0)
return iter.data;
return nil;
}
static void
commit(xcb_im_input_context_t *ic, char *s, int len)
{
char *ct;
size_t clen;
if(len == 0)
return;
ct = ximcompound(s, len, &clen);
if(ct == nil)
return;
xcb_im_commit_string(xim, ic, XCB_XIM_LOOKUP_CHARS, ct, clen, 0);
xcb_flush(conn);
free(ct);
}
static int
meaningful(u32int key)
{
return key != 0 && (key < Kmodfirst || key > Kmodlast);
}
static void
sendrequest(Ic *state, int op, u32int key, u32int mod, Keyres *res)
{
Keyreq kr;
memset(&kr, 0, sizeof kr);
kr.owner = state;
kr.op = op;
kr.ks = key;
kr.mod = mod & Mmask;
kr.reply = replyc;
chansend(keyc, &kr);
chanrecv(replyc, res);
}
static void
keypress(Ic *state, u32int key, u32int mod, Keyres *res)
{
if(meaningful(key))
state->engaged = 1;
sendrequest(state, Keypress, key, mod, res);
}
static void
release(Ic *state)
{
Keyres res;
if(state == nil || !state->engaged)
return;
sendrequest(state, Keyrelease, 0, 0, &res);
state->engaged = 0;
}
static void
kpress(Ic *state, xcb_im_input_context_t *ic, xcb_key_press_event_t *ev)
{
Keyres res;
char buf[Maxutf];
u32int key, rune;
int n;
key = kget(ev->detail, ev->state);
rune = xkb_keysym_to_utf32(key);
if(rune >= ' ' && rune != 0x7f)
key = rune;
else if(key >= 0xff00 && key <= 0xffff)
key = Kspec + (key - 0xff00);
else
key = rune;
keypress(state, key, ev->state, &res);
n = stoutf(&res.commit, buf, sizeof buf);
commit(ic, buf, n);
if(!res.eaten)
xcb_im_forward_event(xim, ic, ev);
xcb_flush(conn);
}
static void
icunlink(Ic *state)
{
Ic **p;
for(p = &ics; *p != nil; p = &(*p)->next)
if(*p == state){
*p = state->next;
return;
}
}
static void
icfree(void *p)
{
Ic *state;
state = p;
/* The owner remains valid until imthread acknowledges its release. */
release(state);
icunlink(state);
free(state);
}
static void
iccreate(xcb_im_input_context_t *ic)
{
Ic *state;
if(ic == nil || xcb_im_input_context_get_data(ic) != nil)
return;
state = calloc(1, sizeof *state);
if(state == nil)
return;
state->next = ics;
ics = state;
xcb_im_input_context_set_data(ic, state, icfree);
}
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;
Ic *state;
USED(im);
USED(client);
USED(frame);
USED(user);
if(hdr->major_opcode == XCB_XIM_CREATE_IC){
iccreate(ic);
return;
}
if(ic == nil)
return;
state = xcb_im_input_context_get_data(ic);
if(state == nil)
return;
switch(hdr->major_opcode){
case XCB_XIM_FORWARD_EVENT:
ev = arg;
if(ev != nil && (ev->response_type & ~0x80) == XCB_KEY_PRESS)
kpress(state, ic, ev);
break;
case XCB_XIM_RESET_IC:
case XCB_XIM_UNSET_IC_FOCUS:
release(state);
break;
}
}
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;
for(state = ics; state != nil; state = next){
next = state->next;
free(state);
}
ics = nil;
free(kmap);
kmap = nil;
if(conn != nil){
xcb_disconnect(conn);
conn = nil;
}
if(replyc != nil){
chanfree(replyc);
replyc = nil;
}
}
static int
ximinit(void)
{
xcb_screen_t *screen;
xcb_window_t win;
xcb_im_styles_t st;
xcb_im_encodings_t enc;
int scr;
replyc = chancreate(sizeof(Keyres), 0);
if(replyc == nil){
ximlog("cannot create reply channel");
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)){
ximlog("cannot connect to X server");
return -1;
}
screen = getscreen(scr);
if(screen == nil){
ximlog("cannot find X screen");
return -1;
}
if(kinit() < 0){
ximlog("cannot read keyboard mapping");
return -1;
}
win = xcb_generate_id(conn);
xcb_create_window(conn, XCB_COPY_FROM_PARENT, win, screen->root,
0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual, 0, nil);
xim = xcb_im_create(conn, scr, win, "strans",
XCB_IM_ALL_LOCALES, &st, nil, nil, &enc,
XCB_EVENT_MASK_KEY_PRESS, callback, nil);
if(xim == nil){
ximlog("cannot create XIM server");
return -1;
}
if(!xcb_im_open_im(xim)){
ximlog("cannot claim XIM selection");
return -1;
}
opened = 1;
return 0;
}
void
ximthread(void *arg)
{
xcb_generic_event_t *ev;
uint8_t type;
USED(arg);
threadsetname("xim");
if(ximinit() < 0){
ximclose();
return;
}
for(;;){
ev = xcb_wait_for_event(conn);
if(ev == nil)
break;
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(xcb_connection_has_error(conn))
ximlog("X server disconnected");
ximclose();
}