From 95d7a486eeb37d3feef16fd29892c62cf966d01d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sat, 25 Mar 2023 16:26:31 +0100
Subject: [PATCH 1/5] Implement support for native keyboard layout

---
 src/console.c     | 19 ++++++++-----------
 src/d_event.h     |  1 +
 src/d_main.c      |  6 +++---
 src/hu_stuff.c    | 45 ++++++++++++++++++++++++++++++++-------------
 src/sdl/i_video.c | 16 ++++++++++++++++
 5 files changed, 60 insertions(+), 27 deletions(-)

diff --git a/src/console.c b/src/console.c
index 33d59046e..2a48ad80b 100644
--- a/src/console.c
+++ b/src/console.c
@@ -924,7 +924,7 @@ boolean CON_Responder(event_t *ev)
 		return false;
 
 	// let go keyup events, don't eat them
-	if (ev->type != ev_keydown && ev->type != ev_console)
+	if (ev->type != ev_keydown && ev->type != ev_text && ev->type != ev_console)
 	{
 		if (ev->key == gamecontrol[GC_CONSOLE][0] || ev->key == gamecontrol[GC_CONSOLE][1])
 			consdown = false;
@@ -951,7 +951,7 @@ boolean CON_Responder(event_t *ev)
 		// check other keys only if console prompt is active
 		if (!consoleready && key < NUMINPUTS) // metzgermeister: boundary check!!
 		{
-			if (! menuactive && bindtable[key])
+			if (!menuactive && bindtable[key])
 			{
 				COM_BufAddText(bindtable[key]);
 				COM_BufAddText("\n");
@@ -968,6 +968,12 @@ boolean CON_Responder(event_t *ev)
 		}
 	}
 
+	if (ev->type == ev_text)
+	{
+		CON_InputAddChar(key);
+		return true;
+	}
+
 	// Always eat ctrl/shift/alt if console open, so the menu doesn't get ideas
 	if (key == KEY_LSHIFT || key == KEY_RSHIFT
 	 || key == KEY_LCTRL || key == KEY_RCTRL
@@ -1304,21 +1310,12 @@ boolean CON_Responder(event_t *ev)
 	else if (key == KEY_KPADSLASH)
 		key = '/';
 
-	if (key >= 'a' && key <= 'z')
-	{
-		if (capslock ^ shiftdown)
-			key = shiftxform[key];
-	}
-	else if (shiftdown)
-		key = shiftxform[key];
-
 	// enter a char into the command prompt
 	if (key < 32 || key > 127)
 		return true;
 
 	if (input_sel != input_cur)
 		CON_InputDelSelection();
-	CON_InputAddChar(key);
 
 	return true;
 }
diff --git a/src/d_event.h b/src/d_event.h
index 5aa435060..7743d8609 100644
--- a/src/d_event.h
+++ b/src/d_event.h
@@ -22,6 +22,7 @@ typedef enum
 {
 	ev_keydown,
 	ev_keyup,
+	ev_text,
 	ev_console,
 	ev_mouse,
 	ev_joystick,
diff --git a/src/d_main.c b/src/d_main.c
index 32f16a282..d660b732e 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -190,19 +190,19 @@ void D_ProcessEvents(void)
 		ev = &events[eventtail];
 
 		// Set mouse buttons early in case event is eaten later
-		if (ev->type == ev_keydown || ev->type == ev_keyup)
+		if (ev->type == ev_keydown || ev->type == ev_keyup || ev->type == ev_text)
 		{
 			// Mouse buttons
 			if ((UINT32)(ev->key - KEY_MOUSE1) < MOUSEBUTTONS)
 			{
-				if (ev->type == ev_keydown)
+				if (ev->type == ev_keydown || ev->type == ev_text)
 					mouse.buttons |= 1 << (ev->key - KEY_MOUSE1);
 				else
 					mouse.buttons &= ~(1 << (ev->key - KEY_MOUSE1));
 			}
 			else if ((UINT32)(ev->key - KEY_2MOUSE1) < MOUSEBUTTONS)
 			{
-				if (ev->type == ev_keydown)
+				if (ev->type == ev_keydown || ev->type == ev_text)
 					mouse2.buttons |= 1 << (ev->key - KEY_2MOUSE1);
 				else
 					mouse2.buttons &= ~(1 << (ev->key - KEY_2MOUSE1));
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 23c63e98f..04d1642a1 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -77,6 +77,7 @@ patch_t *nto_font[NT_FONTSIZE];
 
 static player_t *plr;
 boolean chat_on; // entering a chat message?
+boolean chat_on_first_event; // blocker for first chat input event
 static char w_chat[HU_MAXMSGLEN + 1];
 static size_t c_input = 0; // let's try to make the chat input less shitty.
 static boolean headsupactive = false;
@@ -1047,7 +1048,7 @@ boolean HU_Responder(event_t *ev)
 	INT32 c=0;
 #endif
 
-	if (ev->type != ev_keydown)
+	if (ev->type != ev_keydown && ev->type != ev_text)
 		return false;
 
 	// only KeyDown events now...
@@ -1077,11 +1078,15 @@ boolean HU_Responder(event_t *ev)
 
 	if (!chat_on)
 	{
+		if (ev->type == ev_text)
+			return false;
+
 		// enter chat mode
 		if ((ev->key == gamecontrol[GC_TALKKEY][0] || ev->key == gamecontrol[GC_TALKKEY][1])
 			&& netgame && !OLD_MUTE) // check for old chat mute, still let the players open the chat incase they want to scroll otherwise.
 		{
 			chat_on = true;
+			chat_on_first_event = false;
 			w_chat[0] = 0;
 			teamtalk = false;
 			chat_scrollmedown = true;
@@ -1092,6 +1097,7 @@ boolean HU_Responder(event_t *ev)
 			&& netgame && !OLD_MUTE)
 		{
 			chat_on = true;
+			chat_on_first_event = false;
 			w_chat[0] = 0;
 			teamtalk = G_GametypeHasTeams(); // Don't teamtalk if we don't have teams.
 			chat_scrollmedown = true;
@@ -1101,6 +1107,31 @@ boolean HU_Responder(event_t *ev)
 	}
 	else // if chat_on
 	{
+		if (!chat_on_first_event)
+		{
+			// since the text event is sent immediately after the keydown event,
+			// we need to make sure that nothing is displayed once the chat
+			// opens, otherwise a 't' would be outputted.
+			chat_on_first_event = true;
+			return true;
+		}
+
+		if (ev->type == ev_text)
+		{
+			if ((c < HU_FONTSTART || c > HU_FONTEND || !hu_font[c-HU_FONTSTART])
+				&& c != ' ') // Allow spaces, of course
+			{
+				return false;
+			}
+
+			if (CHAT_MUTE || strlen(w_chat) >= HU_MAXMSGLEN)
+				return true;
+
+			memmove(&w_chat[c_input + 1], &w_chat[c_input], strlen(w_chat) - c_input + 1);
+			w_chat[c_input] = c;
+			c_input++;
+			return true;
+		}
 
 		// Ignore modifier keys
 		// Note that we do this here so users can still set
@@ -1110,8 +1141,6 @@ boolean HU_Responder(event_t *ev)
 		 || ev->key == KEY_LALT || ev->key == KEY_RALT)
 			return true;
 
-		c = (INT32)ev->key;
-
 		// I know this looks very messy but this works. If it ain't broke, don't fix it!
 		// shift LETTERS to uppercase if we have capslock or are holding shift
 		if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
@@ -1194,16 +1223,6 @@ boolean HU_Responder(event_t *ev)
 			else
 				c_input++;
 		}
-		else if ((c >= HU_FONTSTART && c <= HU_FONTEND && hu_font[c-HU_FONTSTART])
-			|| c == ' ') // Allow spaces, of course
-		{
-			if (CHAT_MUTE || strlen(w_chat) >= HU_MAXMSGLEN)
-				return true;
-
-			memmove(&w_chat[c_input + 1], &w_chat[c_input], strlen(w_chat) - c_input + 1);
-			w_chat[c_input] = c;
-			c_input++;
-		}
 		else if (c == KEY_BACKSPACE)
 		{
 			if (CHAT_MUTE || c_input <= 0)
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index d2fbb9006..c25de5fd3 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -686,6 +686,19 @@ static void Impl_HandleKeyboardEvent(SDL_KeyboardEvent evt, Uint32 type)
 	if (event.key) D_PostEvent(&event);
 }
 
+static void Impl_HandleTextEvent(SDL_TextInputEvent evt)
+{
+	event_t event;
+	event.type = ev_text;
+	if (evt.text[1] != '\0')
+	{
+		// limit ourselves to ASCII for now, we can add UTF-8 support later
+		return;
+	}
+	event.key = evt.text[0];
+	D_PostEvent(&event);
+}
+
 static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt)
 {
 	static boolean firstmove = true;
@@ -934,6 +947,9 @@ void I_GetEvent(void)
 			case SDL_KEYDOWN:
 				Impl_HandleKeyboardEvent(evt.key, evt.type);
 				break;
+			case SDL_TEXTINPUT:
+				Impl_HandleTextEvent(evt.text);
+				break;
 			case SDL_MOUSEMOTION:
 				//if (!mouseMotionOnce)
 				Impl_HandleMouseMotionEvent(evt.motion);

From 6896080f4fdca25a764516c66692d52dd8c9063c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sat, 25 Mar 2023 23:08:13 +0100
Subject: [PATCH 2/5] fixup! Implement support for native keyboard layout

---
 src/hu_stuff.c | 15 +---------
 src/m_menu.c   | 76 ++++++++++++++++++++++++++++----------------------
 2 files changed, 44 insertions(+), 47 deletions(-)

diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 04d1642a1..a5268f821 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -1141,21 +1141,8 @@ boolean HU_Responder(event_t *ev)
 		 || ev->key == KEY_LALT || ev->key == KEY_RALT)
 			return true;
 
-		// I know this looks very messy but this works. If it ain't broke, don't fix it!
-		// shift LETTERS to uppercase if we have capslock or are holding shift
-		if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
-		{
-			if (shiftdown ^ capslock)
-				c = shiftxform[c];
-		}
-		else // if we're holding shift we should still shift non letter symbols
-		{
-			if (shiftdown)
-				c = shiftxform[c];
-		}
-
 		// pasting. pasting is cool. chat is a bit limited, though :(
-		if ((c == 'v' || c == 'V') && ctrldown)
+		if (c == 'v' && ctrldown)
 		{
 			const char *paste;
 			size_t chatlen;
diff --git a/src/m_menu.c b/src/m_menu.c
index e879e9c14..71e47bc69 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -3230,40 +3230,42 @@ boolean M_Responder(event_t *ev)
 	}
 	else if (menuactive)
 	{
-		if (ev->type == ev_keydown)
+		if (ev->type == ev_keydown || ev->type == ev_text)
 		{
-			keydown++;
 			ch = ev->key;
-
-			// added 5-2-98 remap virtual keys (mouse & joystick buttons)
-			switch (ch)
+			if (ev->type == ev_keydown)
 			{
-				case KEY_MOUSE1:
-				case KEY_JOY1:
-					ch = KEY_ENTER;
-					break;
-				case KEY_JOY1 + 3:
-					ch = 'n';
-					break;
-				case KEY_MOUSE1 + 1:
-				case KEY_JOY1 + 1:
-					ch = KEY_ESCAPE;
-					break;
-				case KEY_JOY1 + 2:
-					ch = KEY_BACKSPACE;
-					break;
-				case KEY_HAT1:
-					ch = KEY_UPARROW;
-					break;
-				case KEY_HAT1 + 1:
-					ch = KEY_DOWNARROW;
-					break;
-				case KEY_HAT1 + 2:
-					ch = KEY_LEFTARROW;
-					break;
-				case KEY_HAT1 + 3:
-					ch = KEY_RIGHTARROW;
-					break;
+				keydown++;
+				// added 5-2-98 remap virtual keys (mouse & joystick buttons)
+				switch (ch)
+				{
+					case KEY_MOUSE1:
+					case KEY_JOY1:
+						ch = KEY_ENTER;
+						break;
+					case KEY_JOY1 + 3:
+						ch = 'n';
+						break;
+					case KEY_MOUSE1 + 1:
+					case KEY_JOY1 + 1:
+						ch = KEY_ESCAPE;
+						break;
+					case KEY_JOY1 + 2:
+						ch = KEY_BACKSPACE;
+						break;
+					case KEY_HAT1:
+						ch = KEY_UPARROW;
+						break;
+					case KEY_HAT1 + 1:
+						ch = KEY_DOWNARROW;
+						break;
+					case KEY_HAT1 + 2:
+						ch = KEY_LEFTARROW;
+						break;
+					case KEY_HAT1 + 3:
+						ch = KEY_RIGHTARROW;
+						break;
+				}
 			}
 		}
 		else if (ev->type == ev_joystick  && ev->key == 0 && joywait < I_GetTime())
@@ -3425,8 +3427,11 @@ boolean M_Responder(event_t *ev)
 	// Handle menuitems which need a specific key handling
 	if (routine && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_KEYHANDLER)
 	{
-		if (shiftdown && ch >= 32 && ch <= 127)
-			ch = shiftxform[ch];
+		// ignore ev_keydown events if the key maps to a character, since
+		// the ev_text event will follow immediately after in that case.
+		if (ev->type == ev_keydown && ch >= 32 && ch <= 127)
+			return false;
+
 		routine(ch);
 		return true;
 	}
@@ -3468,6 +3473,11 @@ boolean M_Responder(event_t *ev)
 	{
 		if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_STRING)
 		{
+			// ignore ev_keydown events if the key maps to a character, since
+			// the ev_text event will follow immediately after in that case.
+			if (ev->type == ev_keydown && ch >= 32 && ch <= 127)
+				return false;
+
 			if (M_ChangeStringCvar(ch))
 				return true;
 			else

From b8f8cc145104d41298585a2c5c84abee7211ade7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Wed, 29 Mar 2023 18:58:18 +0200
Subject: [PATCH 3/5] fixup! fixup! Implement support for native keyboard
 layout

---
 src/console.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/console.c b/src/console.c
index 2a48ad80b..5571f6bb9 100644
--- a/src/console.c
+++ b/src/console.c
@@ -951,7 +951,7 @@ boolean CON_Responder(event_t *ev)
 		// check other keys only if console prompt is active
 		if (!consoleready && key < NUMINPUTS) // metzgermeister: boundary check!!
 		{
-			if (!menuactive && bindtable[key])
+			if (ev->type == ev_keydown && !menuactive && bindtable[key])
 			{
 				COM_BufAddText(bindtable[key]);
 				COM_BufAddText("\n");

From afe8432c1e5e0fcf40cb5fee9e1e4593352821b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Tue, 23 May 2023 17:41:10 +0200
Subject: [PATCH 4/5] fixup! fixup! fixup! Implement support for native
 keyboard layout

---
 src/sdl/i_system.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index e328bedc2..65f16f0bd 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -601,6 +601,7 @@ void I_GetConsoleEvents(void)
 	else
 	{
 		// push regular character
+		ev.type = ev_text;
 		ev.key = tty_con.buffer[tty_con.cursor] = key;
 		tty_con.cursor++;
 		// print the current line (this is differential)

From 5deee87bd293430a201e2ced5748d07791c18a30 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sat, 4 Nov 2023 19:12:23 +0100
Subject: [PATCH 5/5] Fix keyhandler menus not blocking in-game movement

---
 src/m_menu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index 56d82eed2..68974d61a 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -3376,7 +3376,7 @@ boolean M_Responder(event_t *ev)
 		// ignore ev_keydown events if the key maps to a character, since
 		// the ev_text event will follow immediately after in that case.
 		if (ev->type == ev_keydown && ch >= 32 && ch <= 127)
-			return false;
+			return true;
 
 		routine(ch);
 		return true;