From: Walter Alejandro Iglesias Subject: Re: Proper UTF-8 support for cwm(1) menu search (PING) To: tech@openbsd.org Date: Mon, 19 Jan 2026 11:06:59 +0100 On Tue, Dec 23, 2025 at 01:09:23PM +0100, Walter Alejandro Iglesias wrote: > Currently UTF-8 support in in cwm(1) menu search is incomplete. Dead > keys don't work as expected, for example, 'aacute' will print first the > tilde, then the 'a' by separate. This is because cwm uses > XLookupString(3) which handles keyboard input for Latin-1. > > There is no direct way to replace that function for > Xutf8LookupString(3). I had to add XIM and XIC definitions. > > In case anyone is concerned about portability to systems that still > support Latin-1. I compiled the Debian version with my patch and tested > it with the en_US.ISO-8859-15 locale, it works perfectly. > Index: menu.c =================================================================== RCS file: /cvs/xenocara/app/cwm/menu.c,v diff -u -p -u -p -r1.110 menu.c --- menu.c 15 Oct 2022 16:06:07 -0000 1.110 +++ menu.c 23 Dec 2025 10:59:04 -0000 @@ -65,7 +65,7 @@ struct menu_ctx { void (*match)(struct menu_q *, struct menu_q *, char *); void (*print)(struct menu *, int); }; -static struct menu *menu_handle_key(XEvent *, struct menu_ctx *, +static struct menu *menu_handle_key(XIC, XEvent *, struct menu_ctx *, struct menu_q *, struct menu_q *); static void menu_handle_move(struct menu_ctx *, struct menu_q *, int, int); @@ -77,7 +77,8 @@ static void menu_draw_entry(struct me int, int); static int menu_calc_entry(struct menu_ctx *, int, int); static struct menu *menu_complete_path(struct menu_ctx *); -static int menu_keycode(XKeyEvent *, enum ctltype *, char *); +static int menu_keycode(XIC, XKeyEvent *, enum ctltype *, + char *); struct menu * menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const char *prompt, @@ -124,6 +125,12 @@ menu_filter(struct screen_ctx *sc, struc XSelectInput(X_Dpy, mc.win, MENUMASK); XMapRaised(X_Dpy, mc.win); + XIM im = XOpenIM(X_Dpy, NULL, NULL, NULL); + XIC ic = XCreateIC(im, XNInputStyle, + XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, mc.win, NULL); + XSetICFocus(ic); + if (XGrabPointer(X_Dpy, mc.win, False, MENUGRABMASK, GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_QUESTION], CurrentTime) != GrabSuccess) { @@ -144,12 +151,15 @@ menu_filter(struct screen_ctx *sc, struc XWindowEvent(X_Dpy, mc.win, MENUMASK, &e); + if (XFilterEvent(&e, None)) + continue; + switch (e.type) { case KeyPress: - if ((mi = menu_handle_key(&e, &mc, menuq, &resultq)) - != NULL) + if ((mi = menu_handle_key(ic, &e, &mc, menuq, + &resultq)) != NULL) goto out; - /* FALLTHROUGH */ + /* FALLTHROUGH */ case Expose: menu_draw(&mc, menuq, &resultq); break; @@ -217,8 +227,8 @@ menu_complete_path(struct menu_ctx *mc) } static struct menu * -menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq, - struct menu_q *resultq) +menu_handle_key(XIC ic, XEvent *e, struct menu_ctx *mc, + struct menu_q *menuq, struct menu_q *resultq) { struct menu *mi; enum ctltype ctl; @@ -227,7 +237,7 @@ menu_handle_key(XEvent *e, struct menu_c int clen, i; wchar_t wc; - if (menu_keycode(&e->xkey, &ctl, chr) < 0) + if (menu_keycode(ic, &e->xkey, &ctl, chr) < 0) return NULL; switch (ctl) { @@ -501,9 +511,11 @@ menu_calc_entry(struct menu_ctx *mc, int } static int -menu_keycode(XKeyEvent *ev, enum ctltype *ctl, char *chr) +menu_keycode(XIC ic, XKeyEvent *ev, enum ctltype *ctl, char *chr) { - KeySym ks; + KeySym ks; + Status status; + size_t len = sizeof(chr); *ctl = CTL_NONE; chr[0] = '\0'; @@ -582,7 +594,10 @@ menu_keycode(XKeyEvent *ev, enum ctltype if (*ctl != CTL_NONE) return 0; - if (XLookupString(ev, chr, 32, &ks, NULL) < 0) + len = Xutf8LookupString(ic, ev, chr, len - 1, &ks, &status); + chr[len] = 0; + + if (status == XBufferOverflow) return -1; return 0; -- Walter