xim: clear callback preedit on owner loss
This commit is contained in:
1
fn.h
1
fn.h
@@ -40,6 +40,7 @@ void srvinit(void);
|
|||||||
void srvthread(void*);
|
void srvthread(void*);
|
||||||
void ibusthread(void*);
|
void ibusthread(void*);
|
||||||
void ximthread(void*);
|
void ximthread(void*);
|
||||||
|
void ximownernotify(void);
|
||||||
|
|
||||||
void* emalloc(ulong);
|
void* emalloc(ulong);
|
||||||
void* erealloc(void*, ulong);
|
void* erealloc(void*, ulong);
|
||||||
|
|||||||
4
strans.c
4
strans.c
@@ -798,7 +798,9 @@ static void
|
|||||||
imhandlekey(Keyreq *kr)
|
imhandlekey(Keyreq *kr)
|
||||||
{
|
{
|
||||||
Keyres res;
|
Keyres res;
|
||||||
|
void *oldowner;
|
||||||
|
|
||||||
|
oldowner = activeowner;
|
||||||
sclear(&res.commit);
|
sclear(&res.commit);
|
||||||
sclear(&res.preedit);
|
sclear(&res.preedit);
|
||||||
res.eaten = 1;
|
res.eaten = 1;
|
||||||
@@ -845,6 +847,8 @@ imhandlekey(Keyreq *kr)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if(activeowner != oldowner)
|
||||||
|
ximownernotify();
|
||||||
if(kr->owner == activeowner){
|
if(kr->owner == activeowner){
|
||||||
if(search.lang)
|
if(search.lang)
|
||||||
res.preedit = search.text;
|
res.preedit = search.text;
|
||||||
|
|||||||
@@ -91,4 +91,5 @@ void xim_adapter_callback_replacement(struct ct*);
|
|||||||
void xim_adapter_callback_unicode(struct ct*);
|
void xim_adapter_callback_unicode(struct ct*);
|
||||||
void xim_adapter_callback_cleanup(struct ct*);
|
void xim_adapter_callback_cleanup(struct ct*);
|
||||||
void xim_adapter_callback_transfer(struct ct*);
|
void xim_adapter_callback_transfer(struct ct*);
|
||||||
|
void xim_adapter_callback_owner_loss(struct ct*);
|
||||||
void xim_adapter_commit_encoding(struct ct*);
|
void xim_adapter_commit_encoding(struct ct*);
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ static const struct ct_test tests[] = {
|
|||||||
{ "xim/callback-unicode", xim_adapter_callback_unicode },
|
{ "xim/callback-unicode", xim_adapter_callback_unicode },
|
||||||
{ "xim/callback-cleanup", xim_adapter_callback_cleanup },
|
{ "xim/callback-cleanup", xim_adapter_callback_cleanup },
|
||||||
{ "xim/callback-transfer", xim_adapter_callback_transfer },
|
{ "xim/callback-transfer", xim_adapter_callback_transfer },
|
||||||
|
{ "xim/callback-owner-loss", xim_adapter_callback_owner_loss },
|
||||||
{ "xim/commit-encoding", xim_adapter_commit_encoding },
|
{ "xim/commit-encoding", xim_adapter_commit_encoding },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -659,7 +659,7 @@ xim_adapter_placement(struct ct *t)
|
|||||||
{XCB_IM_PreeditPosition | XCB_IM_StatusNothing,
|
{XCB_IM_PreeditPosition | XCB_IM_StatusNothing,
|
||||||
XCB_XIM_XNSpotLocation_MASK, 1, 1, 1, 1, 1, 103, 204},
|
XCB_XIM_XNSpotLocation_MASK, 1, 1, 1, 1, 1, 103, 204},
|
||||||
{XCB_IM_PreeditPosition | XCB_IM_StatusNothing,
|
{XCB_IM_PreeditPosition | XCB_IM_StatusNothing,
|
||||||
XCB_XIM_XNSpotLocation_MASK, 1, 0, 1, 1, 1, 303, 404},
|
XCB_XIM_XNSpotLocation_MASK, 1, 0, 1, 1, 1, 300, 440},
|
||||||
{XCB_IM_PreeditPosition | XCB_IM_StatusNothing,
|
{XCB_IM_PreeditPosition | XCB_IM_StatusNothing,
|
||||||
0, 1, 1, 1, 1, 1, 100, 230},
|
0, 1, 1, 1, 1, 1, 100, 230},
|
||||||
{XCB_IM_PreeditNothing | XCB_IM_StatusNothing,
|
{XCB_IM_PreeditNothing | XCB_IM_StatusNothing,
|
||||||
@@ -1022,6 +1022,110 @@ cleanup:
|
|||||||
ximend(&f);
|
ximend(&f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
xim_adapter_callback_owner_loss(struct ct *t)
|
||||||
|
{
|
||||||
|
Ximfix f;
|
||||||
|
Ic state;
|
||||||
|
xcb_im_input_context_t *ic;
|
||||||
|
xcb_im_packet_header_fr_t hdr;
|
||||||
|
Keyreq req;
|
||||||
|
Keyreq trace;
|
||||||
|
Keyres res;
|
||||||
|
uchar token;
|
||||||
|
int foreign, i, n;
|
||||||
|
|
||||||
|
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();
|
||||||
|
nexttrace(t, &f, Keycap, &state);
|
||||||
|
CT_EQ_PTR(t, &state, preowner);
|
||||||
|
|
||||||
|
memset(&req, 0, sizeof req);
|
||||||
|
req.owner = &foreign;
|
||||||
|
req.op = Keypress;
|
||||||
|
req.ks = 'n';
|
||||||
|
req.reply = replyc;
|
||||||
|
chansend(keyc, &req);
|
||||||
|
chanrecv(replyc, &res);
|
||||||
|
memset(&trace, 0, sizeof trace);
|
||||||
|
CT_CHECK(t, channbrecv(f.trace, &trace) > 0);
|
||||||
|
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());
|
||||||
|
CT_EQ_INT(t, 4, nwirecalls);
|
||||||
|
CT_EQ_INT(t, Wdraw, wirecalls[2].op);
|
||||||
|
CT_EQ_UINT(t, 1, wirecalls[2].changed);
|
||||||
|
CT_EQ_INT(t, Wdone, wirecalls[3].op);
|
||||||
|
CT_CHECK(t, !state.prestarted);
|
||||||
|
CT_EQ_PTR(t, nil, preowner);
|
||||||
|
|
||||||
|
/* Late XIM lifecycle messages remain idempotent after owner cleanup. */
|
||||||
|
release(&state);
|
||||||
|
nexttrace(t, &f, Keyrelease, &state);
|
||||||
|
release(&state);
|
||||||
|
notrace(t, &f);
|
||||||
|
memset(&hdr, 0, sizeof hdr);
|
||||||
|
hdr.major_opcode = XCB_XIM_RESET_IC;
|
||||||
|
callback(nil, nil, ic, &hdr, nil, nil, nil);
|
||||||
|
notrace(t, &f);
|
||||||
|
hdr.major_opcode = XCB_XIM_DESTROY_IC;
|
||||||
|
callback(nil, nil, ic, &hdr, nil, nil, nil);
|
||||||
|
notrace(t, &f);
|
||||||
|
CT_EQ_INT(t, 4, nwirecalls);
|
||||||
|
|
||||||
|
memset(&req, 0, sizeof req);
|
||||||
|
req.owner = &foreign;
|
||||||
|
req.op = Keyrelease;
|
||||||
|
req.reply = replyc;
|
||||||
|
chansend(keyc, &req);
|
||||||
|
chanrecv(replyc, &res);
|
||||||
|
memset(&trace, 0, sizeof trace);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
xim_adapter_commit_encoding(struct ct *t)
|
xim_adapter_commit_encoding(struct ct *t)
|
||||||
{
|
{
|
||||||
|
|||||||
139
xim/xim.c
139
xim/xim.c
@@ -1,6 +1,10 @@
|
|||||||
#include "dat.h"
|
#include "dat.h"
|
||||||
#include "fn.h"
|
#include "fn.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
#include <xcb-imdkit/imdkit.h>
|
#include <xcb-imdkit/imdkit.h>
|
||||||
@@ -52,6 +56,7 @@ static Ic *ics;
|
|||||||
static Ic *preowner;
|
static Ic *preowner;
|
||||||
static Clientenc clientenc[Maxclients];
|
static Clientenc clientenc[Maxclients];
|
||||||
static Channel *replyc;
|
static Channel *replyc;
|
||||||
|
static int wakefd[2] = {-1, -1};
|
||||||
|
|
||||||
static char *encs[] = {"COMPOUND_TEXT", "UTF8_STRING"};
|
static char *encs[] = {"COMPOUND_TEXT", "UTF8_STRING"};
|
||||||
static u32int styles[] = {
|
static u32int styles[] = {
|
||||||
@@ -75,6 +80,71 @@ stylecap(u32int style, int *cap)
|
|||||||
return 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
|
static int
|
||||||
getencoding(xcb_im_client_t *client)
|
getencoding(xcb_im_client_t *client)
|
||||||
{
|
{
|
||||||
@@ -272,11 +342,9 @@ place(Ic *state)
|
|||||||
nwin = 1;
|
nwin = 1;
|
||||||
if(state->clientwin != state->focuswin)
|
if(state->clientwin != state->focuswin)
|
||||||
win[nwin++] = state->clientwin;
|
win[nwin++] = state->clientwin;
|
||||||
if(state->hasspot)
|
if(state->hasspot && translate(state->focuswin,
|
||||||
for(i = 0; i < nwin; i++)
|
state->spot.x, state->spot.y, &state->caret))
|
||||||
if(translate(win[i], state->spot.x, state->spot.y,
|
return;
|
||||||
&state->caret))
|
|
||||||
return;
|
|
||||||
for(i = 0; i < nwin; i++)
|
for(i = 0; i < nwin; i++)
|
||||||
if(placebottom(state, win[i]))
|
if(placebottom(state, win[i]))
|
||||||
return;
|
return;
|
||||||
@@ -395,6 +463,25 @@ sendrequest(Ic *state, int op, u32int key, u32int mod, Keyres *res)
|
|||||||
chanrecv(replyc, res);
|
chanrecv(replyc, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
checkpreowner(void)
|
||||||
|
{
|
||||||
|
Keyres res;
|
||||||
|
|
||||||
|
if(preowner == nil)
|
||||||
|
return;
|
||||||
|
sendrequest(preowner, Keycap, 0, 0, &res);
|
||||||
|
if(!res.eaten)
|
||||||
|
clearpreedit(preowner);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
checkownerwake(void)
|
||||||
|
{
|
||||||
|
if(wakedrain() > 0)
|
||||||
|
checkpreowner();
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
keypress(Ic *state, u32int key, u32int mod, Keyres *res)
|
keypress(Ic *state, u32int key, u32int mod, Keyres *res)
|
||||||
{
|
{
|
||||||
@@ -611,6 +698,10 @@ ximinit(void)
|
|||||||
ximlog("cannot create reply channel");
|
ximlog("cannot create reply channel");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if(wakeinit() < 0){
|
||||||
|
ximlog("cannot create owner wake pipe");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
st.nStyles = nelem(styles);
|
st.nStyles = nelem(styles);
|
||||||
st.styles = styles;
|
st.styles = styles;
|
||||||
enc.nEncodings = nelem(encs);
|
enc.nEncodings = nelem(encs);
|
||||||
@@ -653,8 +744,10 @@ ximinit(void)
|
|||||||
void
|
void
|
||||||
ximthread(void *arg)
|
ximthread(void *arg)
|
||||||
{
|
{
|
||||||
|
struct pollfd pfd[2];
|
||||||
xcb_generic_event_t *ev;
|
xcb_generic_event_t *ev;
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
|
int fd, n;
|
||||||
|
|
||||||
USED(arg);
|
USED(arg);
|
||||||
threadsetname("xim");
|
threadsetname("xim");
|
||||||
@@ -662,17 +755,35 @@ ximthread(void *arg)
|
|||||||
ximclose();
|
ximclose();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
fd = xcb_get_file_descriptor(conn);
|
||||||
|
pfd[0].fd = fd;
|
||||||
|
pfd[0].events = POLLIN;
|
||||||
|
pfd[1].fd = wakefd[0];
|
||||||
|
pfd[1].events = POLLIN;
|
||||||
for(;;){
|
for(;;){
|
||||||
ev = xcb_wait_for_event(conn);
|
pfd[0].revents = pfd[1].revents = 0;
|
||||||
if(ev == nil)
|
n = poll(pfd, nelem(pfd), -1);
|
||||||
|
if(n < 0){
|
||||||
|
if(errno == EINTR)
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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))
|
||||||
break;
|
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))
|
if(xcb_connection_has_error(conn))
|
||||||
ximlog("X server disconnected");
|
ximlog("X server disconnected");
|
||||||
|
|||||||
Reference in New Issue
Block a user