Index | Thread | Search

From:
Walter Alejandro Iglesias <wai@roquesor.com>
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

Download raw body.

Thread
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