fix(xim): follow keyboard layout changes

kinit() sets up the XKB extension, after which the server stops sending
core MappingNotify to this client, so the refresh branch never fired and
strans kept its startup keymap; setxkbmap after launch left every XIM
key resolved against the old layout. Select XKB NewKeyboardNotify and
MapNotify (with the map parts, or MapNotify is never delivered) and
rebuild the keymap on those events instead.
This commit is contained in:
2026-08-16 16:16:44 +09:00
parent a70cfc42e3
commit c2baa47026
3 changed files with 26 additions and 6 deletions

View File

@@ -5,8 +5,8 @@ CFLAGS ?= -O2 -g
WARN_CFLAGS = -Wall -Wextra WARN_CFLAGS = -Wall -Wextra
IBUS_CFLAGS = $(shell $(PKG_CONFIG) --cflags dbus-1 xkbcommon) IBUS_CFLAGS = $(shell $(PKG_CONFIG) --cflags dbus-1 xkbcommon)
IBUS_LIBS = $(shell $(PKG_CONFIG) --libs dbus-1 xkbcommon) IBUS_LIBS = $(shell $(PKG_CONFIG) --libs dbus-1 xkbcommon)
XIM_CFLAGS = $(shell $(PKG_CONFIG) --cflags xcb-imdkit xkbcommon-x11) XIM_CFLAGS = $(shell $(PKG_CONFIG) --cflags xcb-imdkit xcb-xkb xkbcommon-x11)
XIM_LIBS = $(shell $(PKG_CONFIG) --libs xcb-imdkit xkbcommon-x11) XIM_LIBS = $(shell $(PKG_CONFIG) --libs xcb-imdkit xcb-xkb xkbcommon-x11)
TEXT_CFLAGS = $(shell $(PKG_CONFIG) --cflags pangocairo cairo) TEXT_CFLAGS = $(shell $(PKG_CONFIG) --cflags pangocairo cairo)
TEXT_LIBS = $(shell $(PKG_CONFIG) --libs pangocairo cairo) TEXT_LIBS = $(shell $(PKG_CONFIG) --libs pangocairo cairo)
POPUP_CFLAGS = $(shell $(PKG_CONFIG) --cflags xcb-randr) POPUP_CFLAGS = $(shell $(PKG_CONFIG) --cflags xcb-randr)

View File

@@ -18,8 +18,8 @@ IBUS_CLIENT_CFLAGS = $(shell $(PKG_CONFIG) --cflags ibus-1.0)
IBUS_CLIENT_LIBS = $(shell $(PKG_CONFIG) --libs ibus-1.0) IBUS_CLIENT_LIBS = $(shell $(PKG_CONFIG) --libs ibus-1.0)
X11_CFLAGS = $(shell $(PKG_CONFIG) --cflags x11) X11_CFLAGS = $(shell $(PKG_CONFIG) --cflags x11)
X11_LIBS = $(shell $(PKG_CONFIG) --libs x11) X11_LIBS = $(shell $(PKG_CONFIG) --libs x11)
XIM_CFLAGS = $(shell $(PKG_CONFIG) --cflags xcb-imdkit xkbcommon-x11) XIM_CFLAGS = $(shell $(PKG_CONFIG) --cflags xcb-imdkit xcb-xkb xkbcommon-x11)
XIM_LIBS = $(shell $(PKG_CONFIG) --libs xcb-imdkit xkbcommon-x11) XIM_LIBS = $(shell $(PKG_CONFIG) --libs xcb-imdkit xcb-xkb xkbcommon-x11)
UNIT_LDLIBS = -lthread -lbio $(TEXT_LIBS) $(IBUS_LIBS) $(XIM_LIBS) UNIT_LDLIBS = -lthread -lbio $(TEXT_LIBS) $(IBUS_LIBS) $(XIM_LIBS)
PROG = unit_test PROG = unit_test

View File

@@ -4,6 +4,7 @@
#include <errno.h> #include <errno.h>
#include <poll.h> #include <poll.h>
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xkb.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-x11.h> #include <xkbcommon/xkbcommon-x11.h>
#include <xcb-imdkit/imdkit.h> #include <xcb-imdkit/imdkit.h>
@@ -37,6 +38,7 @@ enum
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 uint8_t xkbevent;
static xcb_window_t rootwin; static xcb_window_t rootwin;
static Ic *ics; static Ic *ics;
static Ic *preowner; static Ic *preowner;
@@ -55,6 +57,7 @@ ximlog(char *msg)
fprint(2, "strans: xim: %s\n", msg); fprint(2, "strans: xim: %s\n", msg);
} }
/* Reads the server's keymap; XKB-aware clients get XKB events, not MappingNotify. */
static int static int
kinit(void) kinit(void)
{ {
@@ -70,7 +73,7 @@ kinit(void)
context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if(context == nil || !xkb_x11_setup_xkb_extension(conn, if(context == nil || !xkb_x11_setup_xkb_extension(conn,
XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION, XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION,
XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, nil, nil, nil, nil)) XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, nil, nil, &xkbevent, nil))
goto out; goto out;
device = xkb_x11_get_core_keyboard_device_id(conn); device = xkb_x11_get_core_keyboard_device_id(conn);
if(device < 0) if(device < 0)
@@ -93,6 +96,22 @@ out:
return ok; return ok;
} }
static void
kwatch(void)
{
u16int events, parts;
events = XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY |
XCB_XKB_EVENT_TYPE_MAP_NOTIFY;
parts = XCB_XKB_MAP_PART_KEY_TYPES | XCB_XKB_MAP_PART_KEY_SYMS |
XCB_XKB_MAP_PART_MODIFIER_MAP |
XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS |
XCB_XKB_MAP_PART_KEY_ACTIONS | XCB_XKB_MAP_PART_VIRTUAL_MODS |
XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP;
xcb_xkb_select_events(conn, XCB_XKB_ID_USE_CORE_KBD, events, 0,
events, parts, parts, nil);
}
static xcb_screen_t* static xcb_screen_t*
getscreen(int scr) getscreen(int scr)
{ {
@@ -472,6 +491,7 @@ ximinit(void)
ximlog("cannot read keyboard mapping"); ximlog("cannot read keyboard mapping");
return -1; return -1;
} }
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,
0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
@@ -511,7 +531,7 @@ ximthread(void *arg)
break; break;
while((ev = xcb_poll_for_event(conn)) != nil){ while((ev = xcb_poll_for_event(conn)) != nil){
type = ev->response_type & ~0x80; type = ev->response_type & ~0x80;
if(type == XCB_MAPPING_NOTIFY){ if(type == xkbevent){
if(kinit() < 0) if(kinit() < 0)
ximlog("cannot refresh keyboard mapping"); ximlog("cannot refresh keyboard mapping");
}else }else