From c101f493676126e2a4cec2002b6dd55cfc055ea3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Deuc=D0=B5?= <shurd@sasktel.net>
Date: Tue, 31 Dec 2024 18:03:17 -0500
Subject: [PATCH] In X11 mode, support arbitrary character entry.

You still cannot enter a NUL.

Holding down ALT and entering an 8-bit decimal number on the keypad
starting with a digit from 1-9 will pass that byte to the input
buffer as entered.

If you prefix the decimal number with the keypad zero, you can enter
a Unicode codepoint to be translated and sent (if it translates).
---
 src/conio/x_cio.c    |  4 ++
 src/conio/x_events.c | 87 +++++++++++++++++++++++++++++++++++++++++++-
 src/conio/x_events.h |  1 +
 3 files changed, 91 insertions(+), 1 deletion(-)

diff --git a/src/conio/x_cio.c b/src/conio/x_cio.c
index 51a8daabd5..fd37e0fda8 100644
--- a/src/conio/x_cio.c
+++ b/src/conio/x_cio.c
@@ -514,6 +514,10 @@ int x_initciolib(int mode)
 		xp_dlclose(dl);
 		return(-1);
 	}
+	if((x11.XLookupKeysym=xp_dlsym(dl,XLookupKeysym))==NULL) {
+		xp_dlclose(dl);
+		return(-1);
+	}
 #ifdef WITH_XRENDER
 	xrender_found = true;
 	if ((dl2 = xp_dlopen(libnames2,RTLD_LAZY,1)) == NULL)
diff --git a/src/conio/x_events.c b/src/conio/x_events.c
index 8cd43e6198..21535d4c23 100644
--- a/src/conio/x_events.c
+++ b/src/conio/x_events.c
@@ -1106,7 +1106,7 @@ static int init_window()
 	gcv.graphics_exposures = False;
 	gc=x11.XCreateGC(dpy, win, GCFunction | GCForeground | GCBackground | GCGraphicsExposures, &gcv);
 
-	x11.XSelectInput(dpy, win, KeyReleaseMask | KeyPressMask
+	x11.XSelectInput(dpy, win, KeyReleaseMask | KeyPressMask | KeyReleaseMask
 		     | ExposureMask | ButtonPressMask | PropertyChangeMask
 		     | ButtonReleaseMask | PointerMotionMask
 		     | StructureNotifyMask | FocusChangeMask);
@@ -1651,9 +1651,42 @@ handle_configuration(int w, int h, bool map, bool se)
 		got_first_resize = true;
 }
 
+static void
+handle_bios_key(uint32_t *bios_key, bool *bios_key_parsing, bool *zero_first)
+{
+	uint8_t ch;
+
+	if (*bios_key > 0 && *bios_key_parsing) {
+		if (*zero_first) {
+			// Unicode character
+			ch = cpchar_from_unicode_cpoint(getcodepage(), *bios_key, 0);
+			if (ch == 0)
+				x11.XBell(dpy, 100);
+			else {
+				write(key_pipe[1], &ch, 1);
+				if (ch == 0xe0)
+					write(key_pipe[1], &ch, 1);
+			}
+		}
+		else {
+			// Codepage character
+			ch = *bios_key;
+			write(key_pipe[1], &ch, 1);
+			if (ch == 0xe0)
+				write(key_pipe[1], &ch, 1);
+		}
+	}
+	*bios_key = 0;
+	*bios_key_parsing = false;
+	*zero_first = false;
+}
+
 static void
 x11_event(XEvent *ev)
 {
+	static uint32_t bios_key = 0;
+	static bool bios_key_parsing = false;
+	static bool zero_first = false;
 	bool resize;
 	int x, y, w, h;
 
@@ -1959,6 +1992,18 @@ x11_event(XEvent *ev)
 			break;
 
 		/* Keyboard Events */
+		case KeyRelease:
+			{
+				if (bios_key_parsing) {
+					KeySym ks = x11.XLookupKeysym((XKeyEvent *)ev, 0);
+					// If Mod1 (ie: ALT) is released, *and* the only bytes were KP numbers, do the BIOS thing.
+					if (ks == XK_Alt_L || ks == XK_Alt_R) {
+						handle_bios_key(&bios_key, &bios_key_parsing, &zero_first);
+					}
+				}
+			}
+			break;
+
 		case KeyPress:
 			{
 				static char buf[128];
@@ -1970,6 +2015,7 @@ x11_event(XEvent *ev)
 				int cnt;
 				int i;
 				uint8_t ch;
+				bool terminate_bios = false;
 
 				if (ic)
 					cnt = x11.XwcLookupString(ic, (XKeyPressedEvent *)ev, wbuf, sizeof(wbuf)/sizeof(wbuf[0]), &ks, &lus);
@@ -1978,6 +2024,45 @@ x11_event(XEvent *ev)
 					lus = XLookupKeySym;
 				}
 
+				if (bios_key_parsing) {
+					if (ks >= XK_KP_0 && ks <= XK_KP_9) {
+						if (bios_key == 0 && ks == XK_KP_0)
+							zero_first = true;
+						else {
+							if (zero_first) {
+								if (bios_key >= 429496730 ||
+								    (bios_key == 429496729 && ks > XK_KP_5)) {
+									terminate_bios = true;
+								}
+							}
+							else {
+								if (bios_key >= 26 ||
+								    (bios_key == 25 && ks > XK_KP_5)) {
+									terminate_bios = true;
+								}
+							}
+							if (terminate_bios) {
+								handle_bios_key(&bios_key, &bios_key_parsing, &zero_first);
+							}
+							else {
+								bios_key *= 10;
+								bios_key += (ks - XK_KP_0);
+								break;
+							}
+						}
+					}
+					else {
+						handle_bios_key(&bios_key, &bios_key_parsing, &zero_first);
+					}
+				}
+
+				if (ks == XK_Alt_L || ks == XK_Alt_R) {
+					bios_key = 0;
+					bios_key_parsing = true;
+					zero_first = false;
+					break;
+				}
+
 				switch(lus) {
 					case XLookupNone:
 						ks = 0xffff;
diff --git a/src/conio/x_events.h b/src/conio/x_events.h
index 51b5b3cd22..6e4c6b8bed 100644
--- a/src/conio/x_events.h
+++ b/src/conio/x_events.h
@@ -119,6 +119,7 @@ struct x11 {
 	int (*XMoveWindow)(Display *, Window, int, int);
 	Status (*XGetWMNormalHints)(Display*, Window, XSizeHints*, long*);
 	int (*XMoveResizeWindow)(Display*, Window, int, int, unsigned int, unsigned int);
+	KeySym (*XLookupKeysym)(XKeyEvent *, int);
 #ifndef DefaultDepth
 	int (*DefaultDepth)(Display *, int);
 #endif
-- 
GitLab