diff --git a/src/console.c b/src/console.c
index 1fbca75b4cf6fcacbeaa2b8948bdf1525faa4036..cc3341948fdd4786577da96bae265e0127db8f8a 100644
--- a/src/console.c
+++ b/src/console.c
@@ -139,7 +139,7 @@ static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, 		{1, "Black"},		{
 
 consvar_t cons_backcolor = {"con_backcolor", "Green", CV_CALL|CV_SAVE, backcolor_cons_t, CONS_backcolor_Change, 0, NULL, NULL, 0, 0, NULL};
 
-static void CON_Print(char *msg);
+static void CON_Print(UINT8 *msg);
 
 //
 //
@@ -173,14 +173,6 @@ static void CONS_Clear_f(void)
 	con_scrollup = 0;
 }
 
-// Choose english keymap
-//
-/*static void CONS_English_f(void)
-{
-	shiftxform = english_shiftxform;
-	CONS_Printf(M_GetText("%s keymap.\n"), M_GetText("English"));
-}*/
-
 static char *bindtable[NUMINPUTS];
 
 static void CONS_Bind_f(void)
@@ -518,7 +510,7 @@ static void CON_RecalcSize(void)
 					conw--;
 				string[conw+1] = '\n';
 				string[conw+2] = '\0';
-				CON_Print(string);
+				CON_Print((UINT8 *)string);
 			}
 		}
 	}
@@ -743,6 +735,11 @@ static void CON_InputDelChar(void)
 // ----
 //
 
+boolean CON_AcceptInput(void)
+{
+	return consoleready;
+}
+
 // Handles console key input
 //
 boolean CON_Responder(event_t *ev)
@@ -760,7 +757,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_textinput && ev->type != ev_console)
 	{
 		if (ev->data1 == gamecontrol[gc_console][0] || ev->data1 == gamecontrol[gc_console][1])
 			consdown = false;
@@ -1074,35 +1071,15 @@ boolean CON_Responder(event_t *ev)
 		return true;
 	}
 
-	// allow people to use keypad in console (good for typing IP addresses) - Calum
-	if (key >= KEY_KEYPAD7 && key <= KEY_KPADDEL)
-	{
-		char keypad_translation[] = {'7','8','9','-',
-		                             '4','5','6','+',
-		                             '1','2','3',
-		                             '0','.'};
-
-		key = keypad_translation[key - KEY_KEYPAD7];
-	}
-	else if (key == KEY_KPADSLASH)
-		key = '/';
-
-	if (key >= 'a' && key <= 'z')
-	{
-		if (capslock ^ shiftdown)
-			key = shiftxform[key];
-	}
-	else if (shiftdown)
-		key = shiftxform[key];
+	// Lactozilla: Ignore caps lock key
+	// or else it turns into a printable character
+	if (ev->type != ev_textinput && key == KEY_CAPSLOCK)
+		return true;
 
 	// enter a char into the command prompt
-	if (key < 32 || key > 127)
+	if (key < 32 || key > 191)
 		return true;
 
-	// add key to cmd line here
-	if (key >= 'A' && key <= 'Z' && !(shiftdown ^ capslock)) //this is only really necessary for dedicated servers
-		key = key + 'a' - 'A';
-
 	if (input_sel != input_cur)
 		CON_InputDelSelection();
 	CON_InputAddChar(key);
@@ -1128,7 +1105,7 @@ static void CON_Linefeed(void)
 }
 
 // Outputs text into the console text buffer
-static void CON_Print(char *msg)
+static void CON_Print(UINT8 *msg)
 {
 	size_t l;
 	INT32 controlchars = 0; // for color changing
@@ -1145,7 +1122,7 @@ static void CON_Print(char *msg)
 		S_StartSound(NULL, sfx_radio);
 	}
 
-	if (!(*msg & 0x80))
+	if (!(HU_IsColorCode(*msg)))
 	{
 		con_line[con_cx++] = '\x80';
 		controlchars = 1;
@@ -1156,7 +1133,7 @@ static void CON_Print(char *msg)
 		// skip non-printable characters and white spaces
 		while (*msg && *msg <= ' ')
 		{
-			if (*msg & 0x80)
+			if (HU_IsColorCode(*msg))
 			{
 				color = con_line[con_cx++] = *(msg++);
 				controlchars++;
@@ -1275,7 +1252,7 @@ void CONS_Printf(const char *fmt, ...)
 	}
 	else
 		// write message in con text buffer
-		CON_Print(txt);
+		CON_Print((UINT8 *)txt);
 
 #ifndef PC_DOS
 	CON_LogMessage(txt);
@@ -1385,7 +1362,7 @@ void CONS_Error(const char *msg)
 static void CON_DrawInput(void)
 {
 	INT32 charwidth = (INT32)con_scalefactor << 3;
-	const char *p = inputlines[inputline];
+	const UINT8 *p = (UINT8 *)inputlines[inputline];
 	size_t c, clen, cend;
 	UINT8 lellip = 0, rellip = 0;
 	INT32 x, y, i;
@@ -1440,32 +1417,32 @@ static void CON_DrawInput(void)
 		if (input_sel < c)
 			V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 77 | V_NOSCALESTART);
 		for (i = 0; i < 3; ++i, x += charwidth)
-			V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, true);
+			V_DrawCharacter(x, y, '.', cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, true);
 	}
 	else
-		V_DrawCharacter(x-charwidth, y, CON_PROMPTCHAR | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, true);
+		V_DrawCharacter(x-charwidth, y, CON_PROMPTCHAR, cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, true);
 
 	for (cend = c + clen; c < cend; ++c, x += charwidth)
 	{
 		if ((input_sel > c && input_cur <= c) || (input_sel <= c && input_cur > c))
 		{
 			V_DrawFill(x, y, charwidth, (10 * con_scalefactor), 77 | V_NOSCALESTART);
-			V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_YELLOWMAP | V_NOSCALESTART, true);
+			V_DrawCharacter(x, y, p[c], cv_constextsize.value | V_YELLOWMAP | V_NOSCALESTART, true);
 		}
 		else
-			V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_NOSCALESTART, true);
+			V_DrawCharacter(x, y, p[c], cv_constextsize.value | V_NOSCALESTART, true);
 
 		if (c == input_cur && con_tick >= 4)
-			V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, true);
+			V_DrawCharacter(x, y + (con_scalefactor*2), '_', cv_constextsize.value | V_NOSCALESTART, true);
 	}
 	if (cend == input_cur && con_tick >= 4)
-		V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, true);
+		V_DrawCharacter(x, y + (con_scalefactor*2), '_', cv_constextsize.value | V_NOSCALESTART, true);
 	if (rellip)
 	{
 		if (input_sel > cend)
 			V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 77 | V_NOSCALESTART);
 		for (i = 0; i < 3; ++i, x += charwidth)
-			V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, true);
+			V_DrawCharacter(x, y, '.', cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, true);
 	}
 }
 
@@ -1501,7 +1478,7 @@ static void CON_DrawHudlines(void)
 
 		for (c = 0, x = 0; c < con_width; c++, x += charwidth, p++)
 		{
-			while (*p & 0x80) // Graue 06-19-2004
+			while (HU_IsColorCode(*p)) // Graue 06-19-2004
 			{
 				charflags = (*p & 0x7f) << V_CHARCOLORSHIFT;
 				p++;
@@ -1511,11 +1488,11 @@ static void CON_DrawHudlines(void)
 			else
 			{
 				//charwidth = SHORT(hu_font['A'-HU_FONTSTART]->width) * con_scalefactor;
-				V_DrawCharacter(x, y, (INT32)(*p) | charflags | cv_constextsize.value | V_NOSCALESTART, true);
+				V_DrawCharacter(x, y, (INT32)(*p), charflags | cv_constextsize.value | V_NOSCALESTART, true);
 			}
 		}
 
-		//V_DrawCharacter(x, y, (p[c]&0xff) | cv_constextsize.value | V_NOSCALESTART, true);
+		//V_DrawCharacter(x, y, (p[c]&0xff), cv_constextsize.value | V_NOSCALESTART, true);
 		y += charheight;
 	}
 
@@ -1581,12 +1558,12 @@ static void CON_DrawConsole(void)
 
 		for (c = 0, x = charwidth; c < con_width; c++, x += charwidth, p++)
 		{
-			while (*p & 0x80)
+			while (HU_IsColorCode(*p))
 			{
 				charflags = (*p & 0x7f) << V_CHARCOLORSHIFT;
 				p++;
 			}
-			V_DrawCharacter(x, y, (INT32)(*p) | charflags | cv_constextsize.value | V_NOSCALESTART, true);
+			V_DrawCharacter(x, y, (INT32)(*p), charflags | cv_constextsize.value | V_NOSCALESTART, true);
 		}
 	}
 
diff --git a/src/console.h b/src/console.h
index 7a55c4b92da51b7c172c9a9749e2d25bf342df3b..51eff11f19a6954a5976b38adcc9c425f5787142 100644
--- a/src/console.h
+++ b/src/console.h
@@ -16,6 +16,7 @@
 void CON_Init(void);
 
 boolean CON_Responder(event_t *ev);
+boolean CON_AcceptInput(void);
 
 // set true when screen size has changed, to adapt console
 extern boolean con_recalc;
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 188304fdaf150ab7d44cf6d7bdeb3c4aa4b00385..a6e720278a00087fced3a057f32e205128b9ec16 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -1306,7 +1306,7 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
 		char *read = mapheaderinfo[gamemap-1]->lvlttl, *writ = netbuffer->u.serverinfo.maptitle;
 		while (writ < (netbuffer->u.serverinfo.maptitle+32) && *read != '\0')
 		{
-			if (!(*read & 0x80))
+			if (!(HU_IsColorCode(*read)))
 			{
 				*writ = toupper(*read);
 				writ++;
diff --git a/src/d_event.h b/src/d_event.h
index c61b9460a37174b01419306c0f7235ecfc090e6e..4185267ee0bd66155c1c6db07bbd0c2fde8758a4 100644
--- a/src/d_event.h
+++ b/src/d_event.h
@@ -27,6 +27,7 @@ typedef enum
 	ev_joystick,
 	ev_mouse2,
 	ev_joystick2,
+	ev_textinput,
 } evtype_t;
 
 // Event structure.
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index e80d07b767b6874cd99d83b7e651446b90b0ef69..1d9ae88861269960dbc2eeb5ea6330d8a61f6ea7 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -916,7 +916,7 @@ boolean EnsurePlayerNameIsGood(char *name, INT32 playernum)
 	// Also, anything over 0x80 is disallowed too, since compilers love to
 	// differ on whether they're printable characters or not.
 	for (ix = 0; name[ix] != '\0'; ix++)
-		if (!isprint(name[ix]) || name[ix] == ';' || (UINT8)(name[ix]) >= 0x80)
+		if (!isprint(name[ix]) || name[ix] == ';' || HU_IsColorCode((UINT8)(name[ix])))
 			return false;
 
 	// Check if a player is currently using the name, case-insensitively.
diff --git a/src/f_finale.c b/src/f_finale.c
index 424fc7f39c86bd2febcd1be60ec5941c29e28520..5344d2d85a807f03770e33d4c61f880f5b8399d0 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -2246,7 +2246,7 @@ void F_EndingDrawer(void)
 		{
 			//colset(linkmap,  164, 165, 169); -- the ideal purple colour to represent a clicked in-game link, but not worth it just for a soundtest-controlled secret
 			V_DrawCenteredString(BASEVIDWIDTH/2, 8, V_ALLOWLOWERCASE|(trans<<V_ALPHASHIFT), str);
-			V_DrawCharacter(32, BASEVIDHEIGHT-16, '>'|(trans<<V_ALPHASHIFT), false);
+			V_DrawCharacter(32, BASEVIDHEIGHT-16, '>', (trans<<V_ALPHASHIFT), false);
 			V_DrawString(40, ((finalecount == STOPPINGPOINT-(20+TICRATE)) ? 1 : 0)+BASEVIDHEIGHT-16, ((timesBeaten || finalecount >= STOPPINGPOINT-TICRATE) ? V_PURPLEMAP : V_BLUEMAP)|(trans<<V_ALPHASHIFT), " [S] ===>");
 		}
 
@@ -2262,7 +2262,7 @@ void F_EndingDrawer(void)
 			else
 				trans2 += 2*trans;
 			if (trans2 < 10)
-				V_DrawCharacter(26, BASEVIDHEIGHT-33, '\x1C'|(trans2<<V_ALPHASHIFT), false);
+				V_DrawCharacter(26, BASEVIDHEIGHT-33, '\x1C', (trans2<<V_ALPHASHIFT), false);
 		}
 	}
 }
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 91a167a60517bfa6043a70328a291eec08a605d9..11b4d8b6464b943ed42748c382234770edd53bc2 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -114,53 +114,6 @@ static void HU_DrawRankings(void);
 static void HU_DrawCoopOverlay(void);
 static void HU_DrawNetplayCoopOverlay(void);
 
-//======================================================================
-//                 KEYBOARD LAYOUTS FOR ENTERING TEXT
-//======================================================================
-
-char *shiftxform;
-
-char english_shiftxform[] =
-{
-	0,
-	1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
-	11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
-	21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
-	31,
-	' ', '!', '"', '#', '$', '%', '&',
-	'"', // shift-'
-	'(', ')', '*', '+',
-	'<', // shift-,
-	'_', // shift--
-	'>', // shift-.
-	'?', // shift-/
-	')', // shift-0
-	'!', // shift-1
-	'@', // shift-2
-	'#', // shift-3
-	'$', // shift-4
-	'%', // shift-5
-	'^', // shift-6
-	'&', // shift-7
-	'*', // shift-8
-	'(', // shift-9
-	':',
-	':', // shift-;
-	'<',
-	'+', // shift-=
-	'>', '?', '@',
-	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
-	'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
-	'{', // shift-[
-	'|', // shift-backslash - OH MY GOD DOES WATCOM SUCK
-	'}', // shift-]
-	'"', '_',
-	'~', // shift-`
-	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
-	'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
-	'{', '|', '}', '~', 127
-};
-
 static char cechotext[1024];
 static tic_t cechotimer = 0;
 static tic_t cechoduration = 5*TICRATE;
@@ -192,6 +145,9 @@ void HU_LoadGraphics(void)
 	{
 		// cache the heads-up font for entire game execution
 		sprintf(buffer, "STCFN%.3d", j);
+		if (j > 127)
+			sprintf(buffer, "UN%06X", j+64);
+
 		if (W_CheckNumForName(buffer) == LUMPERROR)
 			hu_font[i] = NULL;
 		else
@@ -329,9 +285,6 @@ void HU_Init(void)
 	RegisterNetXCmd(XD_SAY, Got_Saycmd);
 #endif
 
-	// set shift translation table
-	shiftxform = english_shiftxform;
-
 	HU_LoadGraphics();
 }
 
@@ -669,13 +622,13 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
 		return;
 	}
 
-	//check for invalid characters (0x80 or above)
+	//check for invalid characters (color codes)
 	{
 		size_t i;
 		const size_t j = strlen(msg);
 		for (i = 0; i < j; i++)
 		{
-			if (msg[i] & 0x80)
+			if (HU_IsColorCode(msg[i]))
 			{
 				CONS_Alert(CONS_WARNING, M_GetText("Illegal say command received from %s containing invalid characters\n"), player_names[playernum]);
 				if (server)
@@ -948,7 +901,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
 
 // Handles key input and string input
 //
-static inline boolean HU_keyInChatString(char *s, char ch)
+static inline boolean HU_keyInChatString(char *s, UINT8 ch)
 {
 	size_t l;
 
@@ -1182,6 +1135,11 @@ static INT16 typelines = 1; // number of drawfill lines we need when drawing the
 // It's up here since it has to be reset when we open the chat.
 #endif
 
+boolean HU_ChatActive(void)
+{
+	return chat_on;
+}
+
 //
 // Returns true if key eaten
 //
@@ -1191,7 +1149,7 @@ boolean HU_Responder(event_t *ev)
 	INT32 c=0;
 #endif
 
-	if (ev->type != ev_keydown)
+	if (!(ev->type == ev_keydown || ev->type == ev_textinput))
 		return false;
 
 	// only KeyDown events now...
@@ -1219,7 +1177,7 @@ boolean HU_Responder(event_t *ev)
 #ifndef NONET
 	c = (INT32)ev->data1;
 
-	if (!chat_on)
+	if (!chat_on && ev->type == ev_keydown)
 	{
 		// enter chat mode
 		if ((ev->data1 == gamecontrol[gc_talkkey][0] || ev->data1 == gamecontrol[gc_talkkey][1])
@@ -1245,30 +1203,17 @@ boolean HU_Responder(event_t *ev)
 	}
 	else // if chat_on
 	{
-
 		// Ignore modifier keys
 		// Note that we do this here so users can still set
 		// their chat keys to one of these, if they so desire.
 		if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT
 		 || ev->data1 == KEY_LCTRL || ev->data1 == KEY_RCTRL
-		 || ev->data1 == KEY_LALT || ev->data1 == KEY_RALT)
+		 || ev->data1 == KEY_LALT || ev->data1 == KEY_RALT
+		 || ev->data1 == KEY_CAPSLOCK)
 			return true;
 
 		c = (INT32)ev->data1;
 
-		// 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) && !CHAT_MUTE)
 		{
@@ -1277,7 +1222,6 @@ boolean HU_Responder(event_t *ev)
 			size_t pastelen;
 
 			// create a dummy string real quickly
-
 			if (paste == NULL)
 				return true;
 
@@ -1314,7 +1258,7 @@ boolean HU_Responder(event_t *ev)
 			}
 		}
 
-		if (!CHAT_MUTE && HU_keyInChatString(w_chat,c))
+		if (!CHAT_MUTE && HU_keyInChatString(w_chat,c) && ev->type == ev_textinput)
 		{
 			HU_queueChatChar(c);
 		}
@@ -1380,7 +1324,7 @@ static char *CHAT_WordWrap(INT32 x, INT32 w, INT32 option, const char *string)
 	for (i = 0; i < slen; ++i)
 	{
 		c = newstring[i];
-		if ((UINT8)c >= 0x80 && (UINT8)c <= 0x89) //color parsing! -Inuyasha 2.16.09
+		if (HU_IsColorCode((UINT8)c))
 			continue;
 
 		if (c == '\n')
@@ -1543,7 +1487,7 @@ static void HU_drawMiniChat(void)
 				if (cv_chatbacktint.value) // on request of wolfy
 					V_DrawFillConsoleMap(x + dx + 2, y+dy, charwidth, charheight, 239|V_SNAPTOBOTTOM|V_SNAPTOLEFT);
 
-				V_DrawChatCharacter(x + dx + 2, y+dy, msg[j++] |V_SNAPTOBOTTOM|V_SNAPTOLEFT|transflag, !cv_allcaps.value, colormap);
+				V_DrawChatCharacter(x + dx + 2, y+dy, msg[j++], V_SNAPTOBOTTOM|V_SNAPTOLEFT|transflag, !cv_allcaps.value, colormap);
 			}
 
 			dx += charwidth;
@@ -1633,7 +1577,7 @@ static void HU_drawChatLog(INT32 offset)
 			else
 			{
 				if ((y+dy+2 >= chat_topy) && (y+dy < (chat_bottomy)))
-					V_DrawChatCharacter(x + dx + 2, y+dy+2, msg[j++] |V_SNAPTOBOTTOM|V_SNAPTOLEFT, !cv_allcaps.value, colormap);
+					V_DrawChatCharacter(x + dx + 2, y+dy+2, msg[j++], V_SNAPTOBOTTOM|V_SNAPTOLEFT, !cv_allcaps.value, colormap);
 				else
 					j++; // don't forget to increment this or we'll get stuck in the limbo.
 			}
@@ -1733,7 +1677,7 @@ static void HU_DrawChat(void)
 			++i;
 		else
 		{
-			V_DrawChatCharacter(chatx + c + 2, y, talk[i] |V_SNAPTOBOTTOM|V_SNAPTOLEFT|cflag, !cv_allcaps.value, V_GetStringColormap(talk[i]|cflag));
+			V_DrawChatCharacter(chatx + c + 2, y, talk[i], V_SNAPTOBOTTOM|V_SNAPTOLEFT|cflag, !cv_allcaps.value, V_GetStringColormap(talk[i]|cflag));
 			i++;
 		}
 
@@ -1751,7 +1695,7 @@ static void HU_DrawChat(void)
 	typelines = 1;
 
 	if ((strlen(w_chat) == 0 || c_input == 0) && hu_tick < 4)
-		V_DrawChatCharacter(chatx + 2 + c, y+1, '_' |V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, !cv_allcaps.value, NULL);
+		V_DrawChatCharacter(chatx + 2 + c, y+1, '_', V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, !cv_allcaps.value, NULL);
 
 	while (w_chat[i])
 	{
@@ -1761,7 +1705,7 @@ static void HU_DrawChat(void)
 			INT32 cursorx = (c+charwidth < boxw-charwidth) ? (chatx + 2 + c+charwidth) : (chatx+1); // we may have to go down.
 			INT32 cursory = (cursorx != chatx+1) ? (y) : (y+charheight);
 			if (hu_tick < 4)
-				V_DrawChatCharacter(cursorx, cursory+1, '_' |V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, !cv_allcaps.value, NULL);
+				V_DrawChatCharacter(cursorx, cursory+1, '_', V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, !cv_allcaps.value, NULL);
 
 			if (cursorx == chatx+1 && saylen == i) // a weirdo hack
 			{
@@ -1774,7 +1718,7 @@ static void HU_DrawChat(void)
 		if (w_chat[i] < HU_FONTSTART)
 			++i;
 		else
-			V_DrawChatCharacter(chatx + c + 2, y, w_chat[i++] | V_SNAPTOBOTTOM|V_SNAPTOLEFT | t, !cv_allcaps.value, NULL);
+			V_DrawChatCharacter(chatx + c + 2, y, w_chat[i++], V_SNAPTOBOTTOM|V_SNAPTOLEFT | t, !cv_allcaps.value, NULL);
 
 		c += charwidth;
 		if (c > boxw-(charwidth*2) && !skippedline)
@@ -1898,13 +1842,13 @@ static void HU_DrawChat_Old(void)
 		else
 		{
 			//charwidth = SHORT(hu_font[talk[i]-HU_FONTSTART]->width) * con_scalefactor;
-			V_DrawCharacter(HU_INPUTX + c, y, talk[i++] | cv_constextsize.value | V_NOSCALESTART, true);
+			V_DrawCharacter(HU_INPUTX + c, y, talk[i++], cv_constextsize.value | V_NOSCALESTART, true);
 		}
 		c += charwidth;
 	}
 
 	if ((strlen(w_chat) == 0 || c_input == 0) && hu_tick < 4)
-		V_DrawCharacter(HU_INPUTX+c, y+2*con_scalefactor, '_' |cv_constextsize.value | V_NOSCALESTART|t, !cv_allcaps.value);
+		V_DrawCharacter(HU_INPUTX+c, y+2*con_scalefactor, '_', cv_constextsize.value | V_NOSCALESTART|t, !cv_allcaps.value);
 
 	i = 0;
 	while (w_chat[i])
@@ -1914,7 +1858,7 @@ static void HU_DrawChat_Old(void)
 		{
 			INT32 cursorx = (HU_INPUTX+c+charwidth < vid.width) ? (HU_INPUTX + c + charwidth) : (HU_INPUTX); // we may have to go down.
 			INT32 cursory = (cursorx != HU_INPUTX) ? (y) : (y+charheight);
-			V_DrawCharacter(cursorx, cursory+2*con_scalefactor, '_' |cv_constextsize.value | V_NOSCALESTART|t, !cv_allcaps.value);
+			V_DrawCharacter(cursorx, cursory+2*con_scalefactor, '_', cv_constextsize.value | V_NOSCALESTART|t, !cv_allcaps.value);
 		}
 
 		//Hurdler: isn't it better like that?
@@ -1926,7 +1870,7 @@ static void HU_DrawChat_Old(void)
 		else
 		{
 			//charwidth = SHORT(hu_font[w_chat[i]-HU_FONTSTART]->width) * con_scalefactor;
-			V_DrawCharacter(HU_INPUTX + c, y, w_chat[i++] | cv_constextsize.value | V_NOSCALESTART | t, true);
+			V_DrawCharacter(HU_INPUTX + c, y, w_chat[i++], cv_constextsize.value | V_NOSCALESTART | t, true);
 		}
 
 		c += charwidth;
@@ -1938,7 +1882,7 @@ static void HU_DrawChat_Old(void)
 	}
 
 	if (hu_tick < 4)
-		V_DrawCharacter(HU_INPUTX + c, y, '_' | cv_constextsize.value |V_NOSCALESTART|t, true);
+		V_DrawCharacter(HU_INPUTX + c, y, '_', cv_constextsize.value |V_NOSCALESTART|t, true);
 }
 #endif
 
diff --git a/src/hu_stuff.h b/src/hu_stuff.h
index 3502a1b2ddfe901a37732a70a0490c3dee5a9541..a68d09dae0cb9a9b76b6cf9143469dc0c7a63b04 100644
--- a/src/hu_stuff.h
+++ b/src/hu_stuff.h
@@ -22,7 +22,12 @@
 //           heads up font
 //------------------------------------
 #define HU_FONTSTART '\x16' // the first font character
-#define HU_FONTEND '~'
+#define HU_FONTEND 0xFF // �
+#define HU_FONTEXT 0xA1 // �
+
+#define HU_COLORSTART 0x80
+#define HU_COLOREND 0x8F
+#define HU_IsColorCode(char) (char >= HU_COLORSTART && char <= HU_COLOREND)
 
 #define HU_FONTSIZE (HU_FONTEND - HU_FONTSTART + 1)
 
@@ -43,9 +48,6 @@
 
 #define HU_CROSSHAIRS 3 // maximum of 9 - see HU_Init();
 
-extern char *shiftxform; // english translation shift table
-extern char english_shiftxform[];
-
 //------------------------------------
 //        sorted player lines
 //------------------------------------
@@ -77,6 +79,7 @@ void HU_AddChatText(const char *text, boolean playsound);
 
 // set true when entering a chat message
 extern boolean chat_on;
+boolean HU_ChatActive(void);
 
 extern patch_t *hu_font[HU_FONTSIZE], *tny_font[HU_FONTSIZE];
 extern patch_t *tallnum[10];
diff --git a/src/m_menu.c b/src/m_menu.c
index e367041e06b35e6f0885c7eeb6ff190d2816c1c1..7b1ebaf70284090e3429d4fba062f0e8feecc439 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -2876,9 +2876,6 @@ static boolean M_ChangeStringCvar(INT32 choice)
 	char buf[MAXSTRINGLENGTH];
 	size_t len;
 
-	if (shiftdown && choice >= 32 && choice <= 127)
-		choice = shiftxform[choice];
-
 	switch (choice)
 	{
 		case KEY_BACKSPACE:
@@ -3185,8 +3182,6 @@ 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];
 		routine(ch);
 		return true;
 	}
@@ -3781,9 +3776,9 @@ static void M_DrawSlider(INT32 x, INT32 y, const consvar_t *cv, boolean ontop)
 	if (ontop)
 	{
 		V_DrawCharacter(x - 6 - (skullAnimCounter/5), y,
-			'\x1C' | V_YELLOWMAP, false);
+			'\x1C', V_YELLOWMAP, false);
 		V_DrawCharacter(x+i*8 + 8 + (skullAnimCounter/5), y,
-			'\x1D' | V_YELLOWMAP, false);
+			'\x1D', V_YELLOWMAP, false);
 	}
 
 	p = W_CachePatchName("M_SLIDER", PU_CACHE);
@@ -4080,7 +4075,7 @@ static void M_DrawGenericMenu(void)
 								V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, cv->string);
 								if (skullAnimCounter < 4 && i == itemOn)
 									V_DrawCharacter(x + 8 + V_StringWidth(cv->string, 0), y + 12,
-										'_' | 0x80, false);
+										'_', 0x80, false);
 								y += 16;
 								break;
 							default:
@@ -4089,9 +4084,9 @@ static void M_DrawGenericMenu(void)
 								if (i == itemOn)
 								{
 									V_DrawCharacter(BASEVIDWIDTH - x - 10 - V_StringWidth(cv->string, 0) - (skullAnimCounter/5), y,
-											'\x1C' | V_YELLOWMAP, false);
+											'\x1C', V_YELLOWMAP, false);
 									V_DrawCharacter(BASEVIDWIDTH - x + 2 + (skullAnimCounter/5), y,
-											'\x1D' | V_YELLOWMAP, false);
+											'\x1D', V_YELLOWMAP, false);
 								}
 								break;
 						}
@@ -4241,14 +4236,14 @@ static void M_DrawGenericScrollMenu(void)
 								V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, cv->string);
 								if (skullAnimCounter < 4 && i == itemOn)
 									V_DrawCharacter(x + 8 + V_StringWidth(cv->string, 0), y + 12,
-										'_' | 0x80, false);
+										'_', 0x80, false);
 #else // cool new string type stuff, not ready for limelight
 								if (i == itemOn)
 								{
 									V_DrawFill(x-2, y-1, MAXSTRINGLENGTH*8 + 4, 8+3, 159);
 									V_DrawString(x, y, V_ALLOWLOWERCASE, cv->string);
 									if (skullAnimCounter < 4)
-										V_DrawCharacter(x + V_StringWidth(cv->string, 0), y, '_' | 0x80, false);
+										V_DrawCharacter(x + V_StringWidth(cv->string, 0), y, '_', 0x80, false);
 								}
 								else
 									V_DrawRightAlignedString(BASEVIDWIDTH - x, y,
@@ -4261,9 +4256,9 @@ static void M_DrawGenericScrollMenu(void)
 								if (i == itemOn)
 								{
 									V_DrawCharacter(BASEVIDWIDTH - x - 10 - V_StringWidth(cv->string, 0) - (skullAnimCounter/5), y,
-											'\x1C' | V_YELLOWMAP, false);
+											'\x1C', V_YELLOWMAP, false);
 									V_DrawCharacter(BASEVIDWIDTH - x + 2 + (skullAnimCounter/5), y,
-											'\x1D' | V_YELLOWMAP, false);
+											'\x1D', V_YELLOWMAP, false);
 								}
 								break;
 						}
@@ -4501,7 +4496,7 @@ static void M_DrawCenteredMenu(void)
 								V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, cv->string);
 								if (skullAnimCounter < 4 && i == itemOn)
 									V_DrawCharacter(x + 8 + V_StringWidth(cv->string, 0), y + 12,
-										'_' | 0x80, false);
+										'_', 0x80, false);
 								y += 16;
 								break;
 							default:
@@ -4510,9 +4505,9 @@ static void M_DrawCenteredMenu(void)
 								if (i == itemOn)
 								{
 									V_DrawCharacter(BASEVIDWIDTH - x - 10 - V_StringWidth(cv->string, 0) - (skullAnimCounter/5), y,
-											'\x1C' | V_YELLOWMAP, false);
+											'\x1C', V_YELLOWMAP, false);
 									V_DrawCharacter(BASEVIDWIDTH - x + 2 + (skullAnimCounter/5), y,
-											'\x1D' | V_YELLOWMAP, false);
+											'\x1D', V_YELLOWMAP, false);
 								}
 								break;
 						}
@@ -5263,9 +5258,9 @@ static void M_DrawLevelPlatterRow(UINT8 row, INT32 y)
 		if (!lsrow)
 		{
 			V_DrawCharacter(lsbasex - 10 - (skullAnimCounter/5), y+25,
-				'\x1C' | V_YELLOWMAP, false);
+				'\x1C', V_YELLOWMAP, false);
 			V_DrawCharacter(lsbasex+282 + 2 + (skullAnimCounter/5), y+25,
-				'\x1D' | V_YELLOWMAP, false);
+				'\x1D', V_YELLOWMAP, false);
 		}
 	}
 	else if (lswide(row))
@@ -6163,7 +6158,7 @@ static void M_DrawAddons(void)
 		V_DrawString(x - 18, y + 8, V_ALLOWLOWERCASE|V_TRANSLUCENT, "Type to search...");
 	if (skullAnimCounter < 4)
 		V_DrawCharacter(x - 18 + V_StringWidth(menusearch+1, 0), y + 8,
-			'_' | 0x80, false);
+			'_', 0x80, false);
 
 	// draw search icon
 	x -= (21 + 5 + 16);
@@ -6189,9 +6184,6 @@ static void M_AddonExec(INT32 ch)
 #define len menusearch[0]
 static boolean M_ChangeStringAddons(INT32 choice)
 {
-	if (shiftdown && choice >= 32 && choice <= 127)
-		choice = shiftxform[choice];
-
 	switch (choice)
 	{
 		case KEY_DEL:
@@ -7015,9 +7007,9 @@ static void M_DrawEmblemHints(void)
 	if (i == itemOn)
 	{
 		V_DrawCharacter(BASEVIDWIDTH - currentMenu->x - 10 - V_StringWidth(cv_soundtest.string, 0) - (skullAnimCounter/5), currentMenu->y + y,
-			'\x1C' | V_YELLOWMAP, false);
+			'\x1C', V_YELLOWMAP, false);
 		V_DrawCharacter(BASEVIDWIDTH - currentMenu->x + 2 + (skullAnimCounter/5), currentMenu->y + y,
-			'\x1D' | V_YELLOWMAP, false);
+			'\x1D', V_YELLOWMAP, false);
 	}
 	if (cv_soundtest.value)
 		V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + y + 8, V_YELLOWMAP, S_sfx[cv_soundtest.value].name);
@@ -7256,9 +7248,9 @@ static void M_DrawSoundTest(void)
 				if (t == st_sel)
 				{
 					V_DrawCharacter(x - 10 - (skullAnimCounter/5), y,
-						'\x1C' | V_YELLOWMAP, false);
+						'\x1C', V_YELLOWMAP, false);
 					V_DrawCharacter(x + 2 + V_StringWidth(sfxstr, 0) + (skullAnimCounter/5), y,
-						'\x1D' | V_YELLOWMAP, false);
+						'\x1D', V_YELLOWMAP, false);
 				}
 
 				if (curplaying == soundtestdefs[t])
@@ -7275,7 +7267,7 @@ static void M_DrawSoundTest(void)
 				if (curplaying == soundtestdefs[t])
 				{
 					V_DrawFill(165+140-9, y-4, 8, 16, 150);
-					//V_DrawCharacter(165+140-8, y, '\x19' | V_YELLOWMAP, false);
+					//V_DrawCharacter(165+140-8, y, '\x19', V_YELLOWMAP, false);
 					V_DrawFixedPatch((165+140-9)<<FRACBITS, (y<<FRACBITS)-(bounce*4), FRACUNIT, 0, hu_font['\x19'-HU_FONTSTART], V_GetStringColormap(V_YELLOWMAP));
 				}
 			}
@@ -7886,7 +7878,7 @@ skiplife:
 			V_DrawScaledPatch(tempx + 9, y + 2, 0, patch);
 			tempx += 16;
 			if (savegameinfo[savetodraw].lives == INFLIVES)
-				V_DrawCharacter(tempx, y + 1, '\x16', false);
+				V_DrawCharacter(tempx, y + 1, '\x16', 0, false);
 			else
 				V_DrawString(tempx, y, 0, va("%d", savegameinfo[savetodraw].lives));
 
@@ -9099,9 +9091,9 @@ void M_DrawTimeAttackMenu(void)
 			if (i == itemOn)
 			{
 				V_DrawCharacter(BASEVIDWIDTH - x - soffset - 10 - V_StringWidth(cv->string, 0) - (skullAnimCounter/5), y,
-					'\x1C' | V_YELLOWMAP, false);
+					'\x1C', V_YELLOWMAP, false);
 				V_DrawCharacter(BASEVIDWIDTH - x - soffset + 2 + (skullAnimCounter/5), y,
-					'\x1D' | V_YELLOWMAP, false);
+					'\x1D', V_YELLOWMAP, false);
 			}
 		}
 	}
@@ -9162,9 +9154,9 @@ void M_DrawTimeAttackMenu(void)
 				/* Draw arrows !! */
 				y = y + 25 - 4;
 				V_DrawCharacter(216 - 10 - (skullAnimCounter/5), y,
-						'\x1C' | V_YELLOWMAP, false);
+						'\x1C', V_YELLOWMAP, false);
 				V_DrawCharacter(216 + 80 + 2 + (skullAnimCounter/5), y,
-						'\x1D' | V_YELLOWMAP, false);
+						'\x1D', V_YELLOWMAP, false);
 			}
 			// Draw press ESC to exit string on main record attack menu
 			V_DrawString(104-72, 180, V_TRANSLUCENT, M_GetText("Press ESC to exit"));
@@ -9375,9 +9367,9 @@ void M_DrawNightsAttackMenu(void)
 			if (i == itemOn)
 			{
 				V_DrawCharacter(BASEVIDWIDTH - x - soffset - 10 - V_StringWidth(cv->string, 0) - (skullAnimCounter/5), y,
-					'\x1C' | V_YELLOWMAP, false);
+					'\x1C', V_YELLOWMAP, false);
 				V_DrawCharacter(BASEVIDWIDTH - x - soffset + 2 + (skullAnimCounter/5), y,
-					'\x1D' | V_YELLOWMAP, false);
+					'\x1D', V_YELLOWMAP, false);
 			}
 		}
 	}
@@ -9422,9 +9414,9 @@ void M_DrawNightsAttackMenu(void)
 				/* Draw arrows !! */
 				y = y + 25 - 4;
 				V_DrawCharacter(208 - 10 - (skullAnimCounter/5), y,
-						'\x1C' | V_YELLOWMAP, false);
+						'\x1C', V_YELLOWMAP, false);
 				V_DrawCharacter(208 + 80 + 2 + (skullAnimCounter/5), y,
-						'\x1D' | V_YELLOWMAP, false);
+						'\x1D', V_YELLOWMAP, false);
 			}
 			// Draw press ESC to exit string on main record attack menu
 			V_DrawString(104-72, 180, V_TRANSLUCENT, M_GetText("Press ESC to exit"));
@@ -10361,7 +10353,7 @@ static void M_DrawMPMainMenu(void)
 	// draw text cursor for name
 	if (itemOn == 2 //0
 	    && skullAnimCounter < 4)   //blink cursor
-		V_DrawCharacter(x+8+V_StringWidth(setupm_ip, V_ALLOWLOWERCASE),y+12,'_',false);
+		V_DrawCharacter(x+8+V_StringWidth(setupm_ip, V_ALLOWLOWERCASE),y+12,'_',0,false);
 }
 
 // Tails 11-19-2002
@@ -10514,7 +10506,7 @@ static void M_DrawSetupMultiPlayerMenu(void)
 	V_DrawString(x + 8, y + 3, V_ALLOWLOWERCASE, setupm_name);
 	if (skullAnimCounter < 4 && itemOn == 0)
 		V_DrawCharacter(x + 8 + V_StringWidth(setupm_name, V_ALLOWLOWERCASE), y + 3,
-			'_' | 0x80, false);
+			'_', 0x80, false);
 
 	y += 20;
 
@@ -10530,9 +10522,9 @@ static void M_DrawSetupMultiPlayerMenu(void)
 	if (itemOn == 1 && (MP_PlayerSetupMenu[1].status & IT_TYPE) != IT_SPACE)
 	{
 		V_DrawCharacter(BASEVIDWIDTH - x - 10 - V_StringWidth(skins[setupm_fakeskin].realname, V_ALLOWLOWERCASE) - (skullAnimCounter/5), y,
-			'\x1C' | V_YELLOWMAP, false);
+			'\x1C', V_YELLOWMAP, false);
 		V_DrawCharacter(BASEVIDWIDTH - x + 2 + (skullAnimCounter/5), y,
-			'\x1D' | V_YELLOWMAP, false);
+			'\x1D', V_YELLOWMAP, false);
 	}
 
 	x = BASEVIDWIDTH/2;
@@ -10607,9 +10599,9 @@ colordraw:
 	if (itemOn == 2 && (MP_PlayerSetupMenu[2].status & IT_TYPE) != IT_SPACE)
 	{
 		V_DrawCharacter(BASEVIDWIDTH - x - 10 - V_StringWidth(Color_Names[setupm_fakecolor], V_ALLOWLOWERCASE) - (skullAnimCounter/5), y,
-			'\x1C' | V_YELLOWMAP, false);
+			'\x1C', V_YELLOWMAP, false);
 		V_DrawCharacter(BASEVIDWIDTH - x + 2 + (skullAnimCounter/5), y,
-			'\x1D' | V_YELLOWMAP, false);
+			'\x1D', V_YELLOWMAP, false);
 	}
 
 	y += 11;
@@ -11683,7 +11675,7 @@ static void M_DrawColorMenu(void)
 								V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, cv->string);
 								if (skullAnimCounter < 4 && i == itemOn)
 									V_DrawCharacter(x + 8 + V_StringWidth(cv->string, 0), y + 12,
-										'_' | 0x80, false);
+										'_', 0x80, false);
 								y += 16;
 								break;
 							default:
@@ -11692,9 +11684,9 @@ static void M_DrawColorMenu(void)
 								if (i == itemOn)
 								{
 									V_DrawCharacter(BASEVIDWIDTH - x - 10 - V_StringWidth(cv->string, 0) - (skullAnimCounter/5), y,
-											'\x1C' | V_YELLOWMAP, false);
+											'\x1C', V_YELLOWMAP, false);
 									V_DrawCharacter(BASEVIDWIDTH - x + 2 + (skullAnimCounter/5), y,
-											'\x1D' | V_YELLOWMAP, false);
+											'\x1D', V_YELLOWMAP, false);
 								}
 								break;
 						}
@@ -11928,7 +11920,7 @@ static void M_OGL_DrawFogMenu(void)
 	// blink cursor on FOG_COLOR_ITEM if selected
 	if (itemOn == FOG_COLOR_ITEM && skullAnimCounter < 4)
 		V_DrawCharacter(BASEVIDWIDTH - mx,
-			my + currentMenu->menuitems[FOG_COLOR_ITEM].alphaKey, '_' | 0x80,false);
+			my + currentMenu->menuitems[FOG_COLOR_ITEM].alphaKey, '_', 0x80,false);
 }
 
 // =====================
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index c876a323f3fbbce1b4a716994cc92a34a09c8913..af5bb29f23f2600e893d7517ae76fe59f551e4e0 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -66,6 +66,7 @@
 #include "../d_main.h"
 #include "../s_sound.h"
 #include "../i_joy.h"
+#include "../hu_stuff.h"
 #include "../st_stuff.h"
 #include "../g_game.h"
 #include "../i_video.h"
@@ -266,90 +267,124 @@ static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen)
 
 static INT32 Impl_SDL_Scancode_To_Keycode(SDL_Scancode code)
 {
-	if (code >= SDL_SCANCODE_A && code <= SDL_SCANCODE_Z)
+	SDL_Keycode keycode = SDL_GetKeyFromScancode(code);
+
+	// Lactozilla
+	// Use keycodes instead of scancodes,
+	// so that non-US keyboards can work! Wow!
+	switch (keycode)
+	{
+		// F11 and F12 are separated from the rest of the function keys
+		case SDLK_F11: return KEY_F11;
+		case SDLK_F12: return KEY_F12;
+
+		case SDLK_KP_0: return KEY_KEYPAD0;
+		case SDLK_KP_1: return KEY_KEYPAD1;
+		case SDLK_KP_2: return KEY_KEYPAD2;
+		case SDLK_KP_3: return KEY_KEYPAD3;
+		case SDLK_KP_4: return KEY_KEYPAD4;
+		case SDLK_KP_5: return KEY_KEYPAD5;
+		case SDLK_KP_6: return KEY_KEYPAD6;
+		case SDLK_KP_7: return KEY_KEYPAD7;
+		case SDLK_KP_8: return KEY_KEYPAD8;
+		case SDLK_KP_9: return KEY_KEYPAD9;
+
+		case SDLK_RETURN:         return KEY_ENTER;
+		case SDLK_ESCAPE:         return KEY_ESCAPE;
+		case SDLK_BACKSPACE:      return KEY_BACKSPACE;
+		case SDLK_TAB:            return KEY_TAB;
+		case SDLK_SPACE:          return KEY_SPACE;
+		case SDLK_CAPSLOCK:       return KEY_CAPSLOCK;
+		case SDLK_PRINTSCREEN:    return 0; // undefined?
+		case SDLK_SCROLLLOCK:     return KEY_SCROLLLOCK;
+		case SDLK_PAUSE:          return KEY_PAUSE;
+		case SDLK_INSERT:         return KEY_INS;
+		case SDLK_HOME:           return KEY_HOME;
+		case SDLK_PAGEUP:         return KEY_PGUP;
+		case SDLK_DELETE:         return KEY_DEL;
+		case SDLK_END:            return KEY_END;
+		case SDLK_PAGEDOWN:       return KEY_PGDN;
+		case SDLK_RIGHT:          return KEY_RIGHTARROW;
+		case SDLK_LEFT:           return KEY_LEFTARROW;
+		case SDLK_DOWN:           return KEY_DOWNARROW;
+		case SDLK_UP:             return KEY_UPARROW;
+		case SDLK_NUMLOCKCLEAR:   return KEY_NUMLOCK;
+		case SDLK_KP_DIVIDE:      return KEY_KPADSLASH;
+		case SDLK_KP_MULTIPLY:    return '*'; // undefined?
+		case SDLK_KP_MINUS:       return KEY_MINUSPAD;
+		case SDLK_KP_PLUS:        return KEY_PLUSPAD;
+		case SDLK_KP_ENTER:       return KEY_ENTER;
+		case SDLK_KP_PERIOD:      return KEY_KPADDEL;
+
+		case SDLK_LSHIFT: return KEY_LSHIFT;
+		case SDLK_RSHIFT: return KEY_RSHIFT;
+		case SDLK_LCTRL:  return KEY_LCTRL;
+		case SDLK_RCTRL:  return KEY_RCTRL;
+		case SDLK_LALT:   return KEY_LALT;
+		case SDLK_RALT:   return KEY_RALT;
+		case SDLK_LGUI:   return KEY_LEFTWIN;
+		case SDLK_RGUI:   return KEY_RIGHTWIN;
+		default:          break;
+	}
+
+	if (keycode >= SDLK_F1 && keycode <= SDLK_F10)
+	{
+		return KEY_F1 + (keycode - SDLK_F1);
+	}
+
+	// Lactozilla: console input
+	if (CON_AcceptInput())
+		return 0;
+	// chat input
+	if (HU_ChatActive())
+		return 0;
+
+	switch (keycode)
+	{
+		case SDLK_MINUS:          return KEY_MINUS;
+		case SDLK_EQUALS:         return KEY_EQUALS;
+		case SDLK_LEFTBRACKET:    return '[';
+		case SDLK_RIGHTBRACKET:   return ']';
+		case SDLK_BACKSLASH:      return '\\';
+		case SDLK_SEMICOLON:      return ';';
+		case SDLK_QUOTE:          return '\'';
+		case SDLK_BACKQUOTE:      return '`';
+		case SDLK_COMMA:          return ',';
+		case SDLK_PERIOD:         return '.';
+		case SDLK_SLASH:          return '/';
+		case SDLK_AMPERSAND:      return '&';
+		case SDLK_ASTERISK:       return '*';
+		case SDLK_AT:             return '@';
+		case SDLK_CARET:          return '^';
+		case SDLK_COLON:          return ':';
+		case SDLK_DOLLAR:         return '$';
+		case SDLK_EXCLAIM:        return '!';
+		case SDLK_GREATER:        return '>';
+		case SDLK_HASH:           return '#';
+		case SDLK_LEFTPAREN:      return '(';
+		case SDLK_LESS:           return '<';
+		case SDLK_PERCENT:        return '%';
+		case SDLK_PLUS:           return '+';
+		case SDLK_QUESTION:       return '?';
+		case SDLK_QUOTEDBL:       return '"';
+		case SDLK_RIGHTPAREN:     return ')';
+		case SDLK_UNDERSCORE:     return '_';
+		default:          break;
+	}
+
+	if (keycode >= SDLK_a && keycode <= SDLK_z)
 	{
 		// get lowercase ASCII
-		return code - SDL_SCANCODE_A + 'a';
+		return keycode - SDLK_a + 'a';
 	}
-	if (code >= SDL_SCANCODE_1 && code <= SDL_SCANCODE_9)
+	if (keycode >= SDLK_1 && keycode <= SDLK_9)
 	{
-		return code - SDL_SCANCODE_1 + '1';
+		return keycode - SDLK_1 + '1';
 	}
-	else if (code == SDL_SCANCODE_0)
+	else if (keycode == SDLK_0)
 	{
 		return '0';
 	}
-	if (code >= SDL_SCANCODE_F1 && code <= SDL_SCANCODE_F10)
-	{
-		return KEY_F1 + (code - SDL_SCANCODE_F1);
-	}
-	switch (code)
-	{
-		// F11 and F12 are separated from the rest of the function keys
-		case SDL_SCANCODE_F11: return KEY_F11;
-		case SDL_SCANCODE_F12: return KEY_F12;
-
-		case SDL_SCANCODE_KP_0: return KEY_KEYPAD0;
-		case SDL_SCANCODE_KP_1: return KEY_KEYPAD1;
-		case SDL_SCANCODE_KP_2: return KEY_KEYPAD2;
-		case SDL_SCANCODE_KP_3: return KEY_KEYPAD3;
-		case SDL_SCANCODE_KP_4: return KEY_KEYPAD4;
-		case SDL_SCANCODE_KP_5: return KEY_KEYPAD5;
-		case SDL_SCANCODE_KP_6: return KEY_KEYPAD6;
-		case SDL_SCANCODE_KP_7: return KEY_KEYPAD7;
-		case SDL_SCANCODE_KP_8: return KEY_KEYPAD8;
-		case SDL_SCANCODE_KP_9: return KEY_KEYPAD9;
-
-		case SDL_SCANCODE_RETURN:         return KEY_ENTER;
-		case SDL_SCANCODE_ESCAPE:         return KEY_ESCAPE;
-		case SDL_SCANCODE_BACKSPACE:      return KEY_BACKSPACE;
-		case SDL_SCANCODE_TAB:            return KEY_TAB;
-		case SDL_SCANCODE_SPACE:          return KEY_SPACE;
-		case SDL_SCANCODE_MINUS:          return KEY_MINUS;
-		case SDL_SCANCODE_EQUALS:         return KEY_EQUALS;
-		case SDL_SCANCODE_LEFTBRACKET:    return '[';
-		case SDL_SCANCODE_RIGHTBRACKET:   return ']';
-		case SDL_SCANCODE_BACKSLASH:      return '\\';
-		case SDL_SCANCODE_NONUSHASH:      return '#';
-		case SDL_SCANCODE_SEMICOLON:      return ';';
-		case SDL_SCANCODE_APOSTROPHE:     return '\'';
-		case SDL_SCANCODE_GRAVE:          return '`';
-		case SDL_SCANCODE_COMMA:          return ',';
-		case SDL_SCANCODE_PERIOD:         return '.';
-		case SDL_SCANCODE_SLASH:          return '/';
-		case SDL_SCANCODE_CAPSLOCK:       return KEY_CAPSLOCK;
-		case SDL_SCANCODE_PRINTSCREEN:    return 0; // undefined?
-		case SDL_SCANCODE_SCROLLLOCK:     return KEY_SCROLLLOCK;
-		case SDL_SCANCODE_PAUSE:          return KEY_PAUSE;
-		case SDL_SCANCODE_INSERT:         return KEY_INS;
-		case SDL_SCANCODE_HOME:           return KEY_HOME;
-		case SDL_SCANCODE_PAGEUP:         return KEY_PGUP;
-		case SDL_SCANCODE_DELETE:         return KEY_DEL;
-		case SDL_SCANCODE_END:            return KEY_END;
-		case SDL_SCANCODE_PAGEDOWN:       return KEY_PGDN;
-		case SDL_SCANCODE_RIGHT:          return KEY_RIGHTARROW;
-		case SDL_SCANCODE_LEFT:           return KEY_LEFTARROW;
-		case SDL_SCANCODE_DOWN:           return KEY_DOWNARROW;
-		case SDL_SCANCODE_UP:             return KEY_UPARROW;
-		case SDL_SCANCODE_NUMLOCKCLEAR:   return KEY_NUMLOCK;
-		case SDL_SCANCODE_KP_DIVIDE:      return KEY_KPADSLASH;
-		case SDL_SCANCODE_KP_MULTIPLY:    return '*'; // undefined?
-		case SDL_SCANCODE_KP_MINUS:       return KEY_MINUSPAD;
-		case SDL_SCANCODE_KP_PLUS:        return KEY_PLUSPAD;
-		case SDL_SCANCODE_KP_ENTER:       return KEY_ENTER;
-		case SDL_SCANCODE_KP_PERIOD:      return KEY_KPADDEL;
-		case SDL_SCANCODE_NONUSBACKSLASH: return '\\';
-
-		case SDL_SCANCODE_LSHIFT: return KEY_LSHIFT;
-		case SDL_SCANCODE_RSHIFT: return KEY_RSHIFT;
-		case SDL_SCANCODE_LCTRL:  return KEY_LCTRL;
-		case SDL_SCANCODE_RCTRL:  return KEY_RCTRL;
-		case SDL_SCANCODE_LALT:   return KEY_LALT;
-		case SDL_SCANCODE_RALT:   return KEY_RALT;
-		case SDL_SCANCODE_LGUI:   return KEY_LEFTWIN;
-		case SDL_SCANCODE_RGUI:   return KEY_RIGHTWIN;
-		default:                  break;
-	}
 #ifdef HWRENDER
 	DBG_Printf("Unknown incoming scancode: %d, represented %c\n",
 				code,
@@ -634,6 +669,22 @@ static void Impl_HandleKeyboardEvent(SDL_KeyboardEvent evt, Uint32 type)
 	if (event.data1) D_PostEvent(&event);
 }
 
+static void Impl_HandleTextInputEvent(char *text)
+{
+	event_t event;
+
+	// yeah
+	UINT16 special = (text[1]+256);
+	if (special == 256)
+		event.data1 = text[0];
+	else
+		event.data1 = special;
+
+	event.type = ev_textinput;
+	if (event.data1 >= 33 && event.data1 <= 191)
+		D_PostEvent(&event);
+}
+
 static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt)
 {
 	if (USE_MOUSEINPUT)
@@ -875,6 +926,9 @@ void I_GetEvent(void)
 			case SDL_KEYDOWN:
 				Impl_HandleKeyboardEvent(evt.key, evt.type);
 				break;
+			case SDL_TEXTINPUT:
+				Impl_HandleTextInputEvent(evt.text.text);
+				break;
 			case SDL_MOUSEMOTION:
 				//if (!mouseMotionOnce)
 				Impl_HandleMouseMotionEvent(evt.motion);
diff --git a/src/st_stuff.c b/src/st_stuff.c
index 1b8107edb89ff57751eadb7f96c869f01efb3f47..dfd022b2c770907f5e9265154ae9a02fd845aab2 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -897,7 +897,7 @@ static void ST_drawLivesArea(void)
 
 		if (livescount == INFLIVES)
 			V_DrawCharacter(hudinfo[HUD_LIVES].x+50, hudinfo[HUD_LIVES].y+8,
-				'\x16' | 0x80 | hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, false);
+				'\x16', 0x80 | hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, false);
 		else
 		{
 			if (stplyr->playerstate == PST_DEAD && !(stplyr->spectator) && (livescount || stplyr->deadtimer < (TICRATE<<1)))
@@ -1105,7 +1105,7 @@ static void ST_drawInput(void)
 		V_DrawFill(x+16+(xoffs), y+9+(yoffs), 10, 1, hudinfo[HUD_LIVES].f|29);\
 	}\
 	V_DrawFill(x+16+(xoffs), y+(yoffs)-offs, 10, 10, col);\
-	V_DrawCharacter(x+16+1+(xoffs), y+1+(yoffs)-offs, hudinfo[HUD_LIVES].f|symb, false)
+	V_DrawCharacter(x+16+1+(xoffs), y+1+(yoffs)-offs, symb, hudinfo[HUD_LIVES].f, false)
 
 	drawbutt( 4,-3, BT_JUMP, 'J');
 	drawbutt(15,-3, BT_USE,  'S');
diff --git a/src/v_video.c b/src/v_video.c
index 0e741df9fc3dc003549aa8b8c1a24d3a4c01b71a..f7ff68b8f8291b140c1e6d9c3243812cb1e8363c 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -2000,17 +2000,23 @@ UINT8 *V_GetStringColormap(INT32 colorflags)
 
 // Writes a single character (draw WHITE if bit 7 set)
 //
-void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed)
+void V_DrawCharacter(INT32 x, INT32 y, INT32 c, INT32 eflags, boolean lowercaseallowed)
 {
 	INT32 w, flags;
-	const UINT8 *colormap = V_GetStringColormap(c);
+	const UINT8 *colormap = V_GetStringColormap(eflags);
 
-	flags = c & ~(V_CHARCOLORMASK | V_PARAMMASK);
-	c &= 0x7f;
-	if (lowercaseallowed)
-		c -= HU_FONTSTART;
+	flags = eflags & ~(V_CHARCOLORMASK | V_PARAMMASK);
+	if (c < HU_FONTEXT)
+	{
+		c &= 0x7f;
+		if (lowercaseallowed)
+			c -= HU_FONTSTART;
+		else
+			c = toupper(c) - HU_FONTSTART;
+	}
 	else
-		c = toupper(c) - HU_FONTSTART;
+		c -= HU_FONTSTART;
+
 	if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
 		return;
 
@@ -2027,17 +2033,23 @@ void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed)
 // Writes a single character for the chat. (draw WHITE if bit 7 set)
 // Essentially the same as the above but it's small or big depending on what resolution you've chosen to huge..
 //
-void V_DrawChatCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed, UINT8 *colormap)
+void V_DrawChatCharacter(INT32 x, INT32 y, INT32 c, INT32 eflags, boolean lowercaseallowed, UINT8 *colormap)
 {
 	INT32 w, flags;
-	//const UINT8 *colormap = V_GetStringColormap(c);
+	//const UINT8 *colormap = V_GetStringColormap(eflags);
 
-	flags = c & ~(V_CHARCOLORMASK | V_PARAMMASK);
-	c &= 0x7f;
-	if (lowercaseallowed)
-		c -= HU_FONTSTART;
+	flags = eflags & ~(V_CHARCOLORMASK | V_PARAMMASK);
+	if (c < HU_FONTEXT)
+	{
+		c &= 0x7f;
+		if (lowercaseallowed)
+			c -= HU_FONTSTART;
+		else
+			c = toupper(c) - HU_FONTSTART;
+	}
 	else
-		c = toupper(c) - HU_FONTSTART;
+		c -= HU_FONTSTART;
+
 	if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
 		return;
 
@@ -2046,8 +2058,6 @@ void V_DrawChatCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed, UI
 		return;
 
 	V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, (vid.width < 640) ? (FRACUNIT) : (FRACUNIT/2), flags, hu_font[c], colormap);
-
-
 }
 
 // Precompile a wordwrapped string to any given width.
diff --git a/src/v_video.h b/src/v_video.h
index 81f410f4087073d23bccb796c779340f9fbd93eb..2243a6cab7c4357042061868f47e837d36fd9adb 100644
--- a/src/v_video.h
+++ b/src/v_video.h
@@ -171,9 +171,9 @@ void V_DrawFadeConsBack(INT32 plines);
 void V_DrawPromptBack(INT32 boxheight, INT32 color);
 
 // draw a single character
-void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed);
+void V_DrawCharacter(INT32 x, INT32 y, INT32 c, INT32 flags, boolean lowercaseallowed);
 // draw a single character, but for the chat
-void V_DrawChatCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed, UINT8 *colormap);
+void V_DrawChatCharacter(INT32 x, INT32 y, INT32 c, INT32 flags, boolean lowercaseallowed, UINT8 *colormap);
 
 UINT8 *V_GetStringColormap(INT32 colorflags);