fix(xim): honor reset and X11 keyboard state

This commit is contained in:
2026-08-14 22:34:37 +09:00
parent 9804c1f55c
commit a637f457f7
10 changed files with 394 additions and 258 deletions

View File

@@ -614,46 +614,6 @@ waitcommit(Display *dpy, XIC ic, char *commit, size_t cap)
return 0;
}
static int
rawtakeover(void)
{
unsigned char req[Ipcreqsz];
char commit[Ipcfieldmax+1], preedit[Ipcfieldmax+1];
Ipcresp resp;
struct timeval tv;
int fd;
fd = ipcconnect();
if(fd < 0)
return fail("connect raw IPC owner: %s", strerror(errno));
tv.tv_sec = Eventtimeout / 1000;
tv.tv_usec = (Eventtimeout % 1000) * 1000;
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
ipcpackreq(req, 0, 0, 'z');
if(ipcsend(fd, req, sizeof req) < 0 ||
ipcreadresp(fd, 0, commit, sizeof commit,
preedit, sizeof preedit, &resp) < 0){
close(fd);
return fail("raw IPC ownership transfer: %s", strerror(errno));
}
return fd;
}
static int
waitclear(Display *dpy, Prelog *p, int empty, int done)
{
int64_t deadline;
deadline = nowms() + Eventtimeout;
while(leftms(deadline) > 0){
pump(dpy, leftms(deadline));
if(p->empty >= empty && p->done >= done)
return 1;
}
return fail("timed out waiting for empty DRAW and DONE");
}
static int
waitpreedit(Display *dpy, Prelog *p)
{
@@ -692,7 +652,6 @@ testcallbacks(Display *dpy, XIM im, Window win)
char commit[Ipcfieldmax+1], *reset;
Prelog log;
XIC ic;
int fd, empty, done;
memset(&log, 0, sizeof log);
ic = callbackic(im, win, &log);
@@ -730,42 +689,33 @@ testcallbacks(Display *dpy, XIM im, Window win)
return fail("bad initial callback sequence: start=%d draw=%d empty=%d done=%d ordered=%d bad=%d",
start, draw, empty, done, ordered, bad);
}
empty = log.empty;
done = log.done;
fd = rawtakeover();
if(fd < 0){
XDestroyIC(ic);
return 0;
}
if(!waitclear(dpy, &log, empty + 1, done + 1)){
close(fd);
XDestroyIC(ic);
return 0;
}
/* Leave the live IC time to expose a delayed duplicate transfer clear. */
pump(dpy, 100);
if(log.empty != empty + 1 || log.done != done + 1 ||
log.doneafterempty != 1 || log.badorder != 0){
int gotempty, gotdone, ordered;
gotempty = log.empty - empty;
gotdone = log.done - done;
ordered = log.doneafterempty;
close(fd);
XDestroyIC(ic);
return fail("owner loss callback counts: empty=%d done=%d ordered=%d",
gotempty, gotdone, ordered);
}
reset = Xutf8ResetIC(ic);
if(reset != NULL)
XFree(reset);
if(reset == NULL || strcmp(reset, "") != 0){
if(reset != NULL)
XFree(reset);
XDestroyIC(ic);
return fail("ResetIC did not return pending preedit");
}
XFree(reset);
if(!sendkey(dpy, ic, win, XK_r, 0, commit, sizeof commit) ||
!sendkey(dpy, ic, win, XK_k, 0, commit, sizeof commit) ||
!sendkey(dpy, ic, win, XK_Return, 0, commit, sizeof commit)){
XDestroyIC(ic);
return 0;
}
if(commit[0] == '\0' && !waitcommit(dpy, ic, commit, sizeof commit)){
XDestroyIC(ic);
return fail("input context did not compose after ResetIC");
}
if(strcmp(commit, "") != 0){
XDestroyIC(ic);
return fail("post-reset composition did not contain 가");
}
XUnsetICFocus(ic);
XDestroyIC(ic);
pump(dpy, 100);
close(fd);
pump(dpy, 100);
if(log.empty != empty + 1 || log.done != done + 1)
return fail("late lifecycle produced duplicate clear callbacks");
if(log.badorder != 0)
return fail("bad callback order after ResetIC");
return 1;
}