fix: repair XIM context lifecycle
This commit is contained in:
127
xim/main.c
127
xim/main.c
@@ -9,15 +9,21 @@
|
||||
#include <xcb-imdkit/imdkit.h>
|
||||
#include <xcb-imdkit/encoding.h>
|
||||
#include "ipc.h"
|
||||
#include "keymap.h"
|
||||
|
||||
typedef struct Ic Ic;
|
||||
struct Ic
|
||||
{
|
||||
int fd;
|
||||
};
|
||||
|
||||
static xcb_connection_t *conn;
|
||||
static xcb_im_t *xim;
|
||||
static int srvfd = -1;
|
||||
static xcb_keysym_t *kmap;
|
||||
static uint8_t minkc, maxkc;
|
||||
static uint8_t symsper;
|
||||
|
||||
static char *encs[] = {"COMPOUND_TEXT", "en_US.UTF-8", ""};
|
||||
static char *encs[] = {"COMPOUND_TEXT"};
|
||||
static uint32_t styles[] = {
|
||||
XCB_IM_PreeditNothing | XCB_IM_StatusNothing,
|
||||
XCB_IM_PreeditNone | XCB_IM_StatusNone,
|
||||
@@ -37,7 +43,7 @@ kinit(void)
|
||||
xcb_get_keyboard_mapping_reply_t *r;
|
||||
const xcb_setup_t *setup;
|
||||
xcb_keysym_t *syms;
|
||||
int i, n;
|
||||
int n;
|
||||
|
||||
setup = xcb_get_setup(conn);
|
||||
if(setup == NULL)
|
||||
@@ -50,26 +56,22 @@ kinit(void)
|
||||
die("keyboard mapping failed");
|
||||
symsper = r->keysyms_per_keycode;
|
||||
n = xcb_get_keyboard_mapping_keysyms_length(r);
|
||||
kmap = malloc(n * sizeof(xcb_keysym_t));
|
||||
if(kmap == NULL)
|
||||
syms = malloc(n * sizeof(xcb_keysym_t));
|
||||
if(syms == NULL)
|
||||
die("malloc failed");
|
||||
syms = xcb_get_keyboard_mapping_keysyms(r);
|
||||
for(i = 0; i < n; i++)
|
||||
kmap[i] = syms[i];
|
||||
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)
|
||||
{
|
||||
int col;
|
||||
|
||||
if(kmap == NULL || kc < minkc || kc > maxkc)
|
||||
return 0;
|
||||
col = (state & Mshift) ? 1 : 0;
|
||||
if(col >= symsper)
|
||||
col = 0;
|
||||
return kmap[(kc - minkc) * symsper + col];
|
||||
return keymaplookup(kmap + (kc - minkc) * symsper, symsper, state);
|
||||
}
|
||||
|
||||
static xcb_screen_t*
|
||||
@@ -106,21 +108,21 @@ commit(xcb_im_input_context_t *ic, char *s, int len)
|
||||
}
|
||||
|
||||
static void
|
||||
srvclose(void)
|
||||
srvclose(Ic *state)
|
||||
{
|
||||
if(srvfd < 0)
|
||||
if(state == NULL || state->fd < 0)
|
||||
return;
|
||||
close(srvfd);
|
||||
srvfd = -1;
|
||||
close(state->fd);
|
||||
state->fd = -1;
|
||||
}
|
||||
|
||||
static int
|
||||
srvconnect(void)
|
||||
srvconnect(Ic *state)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
int fd;
|
||||
|
||||
if(srvfd >= 0)
|
||||
if(state->fd >= 0)
|
||||
return 0;
|
||||
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if(fd < 0)
|
||||
@@ -132,25 +134,25 @@ srvconnect(void)
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
srvfd = fd;
|
||||
state->fd = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
readresp(xcb_im_input_context_t *ic)
|
||||
readresp(Ic *state, xcb_im_input_context_t *ic)
|
||||
{
|
||||
char buf[Ipcfieldmax+1];
|
||||
Ipcresp resp;
|
||||
|
||||
if(ipcreadresp(srvfd, 0, buf, sizeof buf, NULL, 0, &resp) < 0)
|
||||
if(ipcreadresp(state->fd, 0, buf, sizeof buf, NULL, 0, &resp) < 0)
|
||||
return -1;
|
||||
if(buf[0] != '\0')
|
||||
commit(ic, buf, strlen(buf));
|
||||
if(resp.ncommit > 0)
|
||||
commit(ic, buf, resp.ncommit);
|
||||
return resp.eaten;
|
||||
}
|
||||
|
||||
static void
|
||||
kpress(xcb_im_input_context_t *ic, xcb_key_press_event_t *ev)
|
||||
kpress(Ic *state, xcb_im_input_context_t *ic, xcb_key_press_event_t *ev)
|
||||
{
|
||||
unsigned char buf[Ipcreqsz];
|
||||
uint32_t key, rune;
|
||||
@@ -166,10 +168,10 @@ kpress(xcb_im_input_context_t *ic, xcb_key_press_event_t *ev)
|
||||
key = rune;
|
||||
ipcpackreq(buf, 0, ev->state, key);
|
||||
eaten = 0;
|
||||
if(srvconnect() == 0){
|
||||
if(ipcsend(srvfd, buf, sizeof buf) < 0 ||
|
||||
(eaten = readresp(ic)) < 0){
|
||||
srvclose();
|
||||
if(srvconnect(state) == 0){
|
||||
if(ipcsend(state->fd, buf, sizeof buf) < 0 ||
|
||||
(eaten = readresp(state, ic)) < 0){
|
||||
srvclose(state);
|
||||
eaten = 0;
|
||||
}
|
||||
}
|
||||
@@ -179,15 +181,43 @@ kpress(xcb_im_input_context_t *ic, xcb_key_press_event_t *ev)
|
||||
}
|
||||
|
||||
static void
|
||||
reset(xcb_im_input_context_t *ic)
|
||||
reset(Ic *state, xcb_im_input_context_t *ic, int release)
|
||||
{
|
||||
unsigned char buf[Ipcreqsz];
|
||||
|
||||
ipcpackreset(buf, 0);
|
||||
if(srvconnect() < 0)
|
||||
if(state == NULL || state->fd < 0)
|
||||
return;
|
||||
if(ipcsend(srvfd, buf, sizeof buf) < 0 || readresp(ic) < 0)
|
||||
srvclose();
|
||||
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
|
||||
@@ -195,20 +225,33 @@ 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(ic, ev);
|
||||
kpress(state, ic, ev);
|
||||
break;
|
||||
case XCB_XIM_RESET_IC:
|
||||
reset(state, ic, 0);
|
||||
break;
|
||||
case XCB_XIM_UNSET_IC_FOCUS:
|
||||
reset(ic);
|
||||
reset(state, ic, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -224,7 +267,7 @@ ximinit(void)
|
||||
|
||||
st.nStyles = 2;
|
||||
st.styles = styles;
|
||||
enc.nEncodings = 3;
|
||||
enc.nEncodings = 1;
|
||||
enc.encodings = encs;
|
||||
xcb_compound_text_init();
|
||||
conn = xcb_connect(NULL, &scr);
|
||||
@@ -247,15 +290,19 @@ int
|
||||
main(void)
|
||||
{
|
||||
xcb_generic_event_t *ev;
|
||||
uint8_t type;
|
||||
|
||||
ximinit();
|
||||
for(;;){
|
||||
ev = xcb_wait_for_event(conn);
|
||||
if(ev == NULL)
|
||||
break;
|
||||
xcb_im_filter_event(xim, ev);
|
||||
type = ev->response_type & ~0x80;
|
||||
if(type == XCB_MAPPING_NOTIFY)
|
||||
kinit();
|
||||
else
|
||||
xcb_im_filter_event(xim, ev);
|
||||
free(ev);
|
||||
}
|
||||
srvclose();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user