[PATCH] Rewrite of keymap handling to support SDL & ST modifiers

[ Thread Index | Date Index | More lists.tuxfamily.org/hatari-devel Archives ]


This differs significantly from Vincent's patch:
* It's compatible with current keymap format:
  - Numbers are decimal unless 0x prefix is used
  - Supports symbolic SDL key names
* Instead of allowing user to specify sequence of multiple keys per
  shortcut, one can specify single key with multiple modifiers
* Additional features (Alt-XXX) and fixes

Changes:
* Keymap_LoadRemapFile() refactor / rewrite
* Switch to using KeyMapping struct and KeysDown array
  with new SDLKey & STKey types
  - KeyMapping type is used in LoadedKeymap[] array,
    for PC -> ST key mappings loaded from external keymap file
  - KeysDown[] array (indexed by PC / SDL scancode) items are
    set when a key is pressed, and used when it is released
* Clear KeysDown[] key mapping state on reset
* Add defines for (some) of the ST scancode values
* Support for specifying ST key modifiers in the mapping file
  - Single PC key can map into ST scancode with multiple ST modifiers
* Add ALT-XXX sequence insert support for keymaps
* Add trace output for ST key modifiers
* Support for specifying PC/SDL key modifiers in the mapping file
* Accept also (0x prefixed) hex values for scancodes
---
 src/includes/keymap.h |   1 +
 src/keymap.c          | 646 +++++++++++++++++++++++++++++++++++-------
 src/reset.c           |   2 +
 3 files changed, 543 insertions(+), 106 deletions(-)

diff --git a/src/includes/keymap.h b/src/includes/keymap.h
index abf7586e..0ba2ac9e 100644
--- a/src/includes/keymap.h
+++ b/src/includes/keymap.h
@@ -11,6 +11,7 @@
 #include <SDL_keyboard.h>
 
 extern void Keymap_Init(void);
+extern void Keymap_Reset(void);
 extern void Keymap_LoadRemapFile(const char *pszFileName);
 extern void Keymap_DebounceAllKeys(void);
 extern void Keymap_KeyDown(const SDL_Keysym *sdlkey);
diff --git a/src/keymap.c b/src/keymap.c
index 83d03934..e934be63 100644
--- a/src/keymap.c
+++ b/src/keymap.c
@@ -10,6 +10,7 @@
 */
 const char Keymap_fileid[] = "Hatari keymap.c";
 
+#include <assert.h>
 #include <ctype.h>
 #include "main.h"
 #include "keymap.h"
@@ -26,20 +27,67 @@ const char Keymap_fileid[] = "Hatari keymap.c";
 /* if not able to map */
 #define ST_NO_SCANCODE 0xff
 
-/* Table for loaded keys: */
-static int LoadedKeymap[KBD_MAX_SCANCODE][2];
+/* Some ST keyboard scancodes */
+#define ST_ESC		0x01
+#define ST_CONTROL	0x1d
+#define ST_LSHIFT	0x2a
+#define ST_RSHIFT	0x36
+#define ST_ALTERNATE	0x38
+#define ST_CAPSLOCK	0x3a
 
 /* List of ST scan codes to NOT de-bounce when running in maximum speed */
 static const uint8_t DebounceExtendedKeys[] =
 {
-	0x1d,  /* CTRL */
-	0x2a,  /* Left SHIFT */
-	0x01,  /* ESC */
-	0x38,  /* ALT */
-	0x36,  /* Right SHIFT */
-	0      /* term */
+	ST_CONTROL,
+	ST_LSHIFT,
+	ST_ESC,
+	ST_ALTERNATE,
+	ST_RSHIFT,
+	0  /* End of list */
 };
 
+typedef struct
+{
+	SDL_Scancode scancode;
+	SDL_Keymod mods;
+} SDLKey;
+
+static const struct
+{
+	const char *name;
+	uint8_t scancode;
+	uint8_t mod;
+} ST_Modifiers[] = {
+	{"CONTROL", ST_CONTROL,   0x01},
+	{"LSHIFT",  ST_LSHIFT,    0x02},
+	{"RSHIFT",  ST_RSHIFT,    0x04},
+	{"ALT",     ST_ALTERNATE, 0x08},
+	{"ALT_XXX", ST_ALTERNATE, 0x10},
+};
+
+#define ALT_XXX_BIT 0x10
+
+typedef struct
+{
+	uint8_t scancode;
+	/* key modifiers matching ST_Modifiers[].mod bit values */
+	uint8_t mods;
+} ST_Key;
+
+/* Key mappings: pair SDL key definition with ST key */
+typedef struct
+{
+	/* Input on PC keyboard */
+	SDLKey pc;
+	/* Output on the ST keyboard */
+	ST_Key st;
+} KeyMapping;
+
+/* Loaded PC->ST key mappings */
+static KeyMapping LoadedKeymap[KBD_MAX_SCANCODE];
+
+/* ST keys being kept down, indexed by SDL scancode */
+static ST_Key KeysDown[SDL_NUM_SCANCODES];
 
 
 /*-----------------------------------------------------------------------*/
@@ -51,6 +99,15 @@ void Keymap_Init(void)
 	Keymap_LoadRemapFile(ConfigureParams.Keyboard.szMappingFileName);
 }
 
+/**
+ * Clear key states on reset, like IKBD does
+ */
+void Keymap_Reset(void)
+{
+	memset(KeysDown, 0, sizeof(KeysDown));
+}
+
+
 /**
  * Map SDL symbolic key to ST scan code.
  * This assumes a QWERTY ST keyboard.
@@ -65,7 +122,7 @@ static uint8_t Keymap_SymbolicToStScanCode(const SDL_Keysym* pKeySym)
 	 case SDLK_TAB: code = 0x0F; break;
 	 case SDLK_CLEAR: code = 0x47; break;
 	 case SDLK_RETURN: code = 0x1C; break;
-	 case SDLK_ESCAPE: code = 0x01; break;
+	 case SDLK_ESCAPE: code = ST_ESC; break;
 	 case SDLK_SPACE: code = 0x39; break;
 	 case SDLK_EXCLAIM: code = 0x09; break;     /* on azerty? */
 	 case SDLK_QUOTEDBL: code = 0x04; break;    /* on azerty? */
@@ -180,14 +237,14 @@ static uint8_t Keymap_SymbolicToStScanCode(const SDL_Keysym* pKeySym)
 	 case SDLK_F12: code = 0x61; break;
 	 case SDLK_F13: code = 0x62; break;
 	 /* Key state modifier keys */
-	 case SDLK_CAPSLOCK: code = 0x3A; break;
+	 case SDLK_CAPSLOCK: code = ST_CAPSLOCK; break;
 	 case SDLK_SCROLLLOCK: code = 0x61; break;
-	 case SDLK_RSHIFT: code = 0x36; break;
-	 case SDLK_LSHIFT: code = 0x2A; break;
-	 case SDLK_RCTRL: code = 0x1D; break;
-	 case SDLK_LCTRL: code = 0x1D; break;
-	 case SDLK_RALT: code = 0x38; break;
-	 case SDLK_LALT: code = 0x38; break;
+	 case SDLK_RSHIFT: code = ST_RSHIFT; break;
+	 case SDLK_LSHIFT: code = ST_LSHIFT; break;
+	 case SDLK_RCTRL: code = ST_CONTROL; break;
+	 case SDLK_LCTRL: code = ST_CONTROL; break;
+	 case SDLK_RALT: code = ST_ALTERNATE; break;
+	 case SDLK_LALT: code = ST_ALTERNATE; break;
 	 /* Miscellaneous function keys */
 	 case SDLK_HELP: code = 0x62; break;
 	 case SDLK_PRINTSCREEN: code = 0x62; break;
@@ -243,7 +300,7 @@ static uint8_t Keymap_PcToStScanCode(const SDL_Keysym* pKeySym)
 	 case SDL_SCANCODE_9: return 0x0a;
 	 case SDL_SCANCODE_0: return 0x0b;
 	 case SDL_SCANCODE_RETURN: return 0x1c;
-	 case SDL_SCANCODE_ESCAPE: return 0x01;
+	 case SDL_SCANCODE_ESCAPE: return ST_ESC;
 	 case SDL_SCANCODE_BACKSPACE: return 0x0e;
 	 case SDL_SCANCODE_TAB: return 0x0f;
 	 case SDL_SCANCODE_SPACE: return 0x39;
@@ -259,7 +316,7 @@ static uint8_t Keymap_PcToStScanCode(const SDL_Keysym* pKeySym)
 	 case SDL_SCANCODE_COMMA: return 0x33;
 	 case SDL_SCANCODE_PERIOD: return 0x34;
 	 case SDL_SCANCODE_SLASH: return 0x35;
-	 case SDL_SCANCODE_CAPSLOCK: return 0x3a;
+	 case SDL_SCANCODE_CAPSLOCK: return ST_CAPSLOCK;
 	 case SDL_SCANCODE_F1: return 0x3b;
 	 case SDL_SCANCODE_F2: return 0x3c;
 	 case SDL_SCANCODE_F3: return 0x3d;
@@ -322,11 +379,11 @@ static uint8_t Keymap_PcToStScanCode(const SDL_Keysym* pKeySym)
 	 case SDL_SCANCODE_KP_HASH: return 0x0c;
 	 case SDL_SCANCODE_KP_SPACE: return 0x39;
 	 case SDL_SCANCODE_KP_CLEAR: return 0x47;
-	 case SDL_SCANCODE_LCTRL: return 0x1d;
-	 case SDL_SCANCODE_LSHIFT: return 0x2a;
-	 case SDL_SCANCODE_LALT: return 0x38;
-	 case SDL_SCANCODE_RCTRL: return 0x1d;
-	 case SDL_SCANCODE_RSHIFT: return 0x36;
+	 case SDL_SCANCODE_LCTRL: return ST_CONTROL;
+	 case SDL_SCANCODE_LSHIFT: return ST_LSHIFT;
+	 case SDL_SCANCODE_LALT: return ST_ALTERNATE;
+	 case SDL_SCANCODE_RCTRL: return ST_CONTROL;
+	 case SDL_SCANCODE_RSHIFT: return ST_RSHIFT;
 	 default:
 		if (!pKeySym->scancode && pKeySym->sym)
 		{
@@ -385,40 +442,299 @@ static uint8_t Keymap_GetKeyPadScanCode(const SDL_Keysym* pKeySym)
 	return ST_NO_SCANCODE;
 }
 
+/**
+ * If given ST scancode is not set, return NULL, otherwise add scancode
+ * to pressed keys array, output trace of the SDL->ST key mapping and
+ * return pointer to the mapping
+ */
+static ST_Key* UpdateMapping(const char *maptype, SDL_Scancode pcscan, uint8_t stscan)
+{
+	ST_Key *key = &(KeysDown[pcscan]);
+
+	if (stscan == ST_NO_SCANCODE)
+		return NULL;
+	key->scancode = stscan;
+
+	LOG_TRACE(TRACE_KEYMAP, "key mapping: %02x (%s)\n", stscan, maptype);
+	return key;
+}
 
 /**
- * Remap SDL Key to ST Scan code
+ * Remap SDL Key to ST Key (when key is pressed).
+ * Receives the pressed key from SDL, and returns matching ST_Key,
+ * or NULL if no mapping could be found
  */
-static uint8_t Keymap_RemapKeyToSTScanCode(const SDL_Keysym* pKeySym)
+static ST_Key* Keymap_RemapKeyToSTKey(const SDL_Keysym* pKeySym)
 {
+	const SDL_Keycode sym = pKeySym->sym;
+	const SDL_Scancode scancode = pKeySym->scancode;
+
+	/* check for UpdateMapping() */
+	assert(scancode < ARRAY_SIZE(KeysDown));
+	memset(KeysDown+scancode, 0, sizeof(*KeysDown));
+
 	/* Check for keypad first so we can handle numlock */
-	if (ConfigureParams.Keyboard.nKeymapType != KEYMAP_LOADED)
+	if (ConfigureParams.Keyboard.nKeymapType != KEYMAP_LOADED &&
+	    sym >= SDLK_KP_1 && sym <= SDLK_KP_9)
 	{
-		if (pKeySym->sym >= SDLK_KP_1 && pKeySym->sym <= SDLK_KP_9)
-		{
-			return Keymap_GetKeyPadScanCode(pKeySym);
-		}
+		return UpdateMapping("keypad/symbolic", scancode,
+				     Keymap_GetKeyPadScanCode(pKeySym));
 	}
 
 	/* Remap from PC scancodes? */
 	if (ConfigureParams.Keyboard.nKeymapType == KEYMAP_SCANCODE)
 	{
-		return Keymap_PcToStScanCode(pKeySym);
+		return UpdateMapping("scancode/symbolic", scancode,
+				     Keymap_PcToStScanCode(pKeySym));
 	}
 
 	/* Use loaded keymap? */
 	if (ConfigureParams.Keyboard.nKeymapType == KEYMAP_LOADED)
 	{
 		int i;
-		for (i = 0; i < KBD_MAX_SCANCODE && LoadedKeymap[i][1] != 0; i++)
+		SDL_Keymod mods = pKeySym->mod;
+		for (i = 0; i < ARRAY_SIZE(LoadedKeymap); i++)
+		{
+			KeyMapping *mapping = &LoadedKeymap[i];
+			SDL_Keymod pcmod;
+
+			if (mapping->pc.scancode == 0)
+				break; /* End of table */
+
+			if (mapping->pc.scancode != scancode)
+				continue;
+
+			pcmod = mapping->pc.mods;
+			/* (at least) one shift used? */
+			if (pcmod == KMOD_SHIFT)
+			{
+				if (!(mods & pcmod))
+					continue;
+			}
+			/* (at least) specified mod used? */
+			else if ((mods & pcmod) != pcmod)
+				continue;
+
+			KeysDown[scancode].mods = mapping->st.mods;
+			return UpdateMapping("keymap", scancode,
+					     mapping->st.scancode);
+		}
+	}
+
+	/* Fall back to symbolic mapping */
+	return UpdateMapping("symbolic", scancode,
+			     Keymap_SymbolicToStScanCode(pKeySym));
+}
+
+
+/*-----------------------------------------------------------------------*/
+/**
+ * Parse SDL modifier name from 'name' and return its value, or
+ * zero for error.
+ */
+static SDL_Keymod GetSdlModifier(const char *name) {
+	struct {
+		SDL_Keymod mod;
+		const char *name;
+	} const keymodNames[] = {
+		{ KMOD_LSHIFT, "LSHIFT" },
+		{ KMOD_RSHIFT, "RSHIFT" },
+		{ KMOD_SHIFT,  "SHIFT" }, /* special case: either of above */
+
+		{ KMOD_LCTRL, "LCTRL" },
+		{ KMOD_RCTRL, "RCTRL" },
+		{ KMOD_CTRL,  "CTRL" },   /* both of above */
+
+		{ KMOD_LALT, "LALT" },
+		{ KMOD_RALT, "RALT" },
+		{ KMOD_ALT,  "ALT" },     /* both of above */
+
+		{ KMOD_LGUI, "LGUI" },
+		{ KMOD_RGUI, "RGUI" },
+		{ KMOD_GUI,  "GUI" },     /* both of above */
+
+		{ KMOD_CAPS, "CAPS" },
+		{ KMOD_MODE, "MODE" },
+		{ KMOD_NUM,  "NUM" },
+	};
+	int i;
+	for (i = 0; i < ARRAY_SIZE(keymodNames); i++)
+	{
+		if (strcmp(name, keymodNames[i].name) == 0)
+			return keymodNames[i].mod;
+	};
+	return 0;
+}
+
+
+/*-----------------------------------------------------------------------*/
+/**
+ * Fill "mapping" KeyMapping host part based on host "spec" string.
+ * Return true on success
+ */
+static bool HostSpecToKeymap(const char *spec, KeyMapping* mapping)
+{
+	char buf[64], *token, *saveptr, *endptr;
+	SDL_Scancode scancode = 0;
+	SDL_Keymod mods = 0;
+
+	if (!spec)
+		return false;
+	if (strlcpy(buf, spec, sizeof(buf)) >= sizeof(buf))
+	{
+		Log_Printf(LOG_ERROR, "PC/SDL scancode spec '%s' too long\n", spec);
+		return false;
+	}
+
+	scancode = mods = 0;
+	for (token = strtok_r(buf, "|", &saveptr);
+	     token;
+	     token = strtok_r(NULL, "|", &saveptr))
+	{
+		token = Str_Trim(token);
+		/* not a number? */
+		if (!isdigit(token[0]))
+		{
+			SDL_Scancode code;
+			SDL_Keymod mod;
+
+			/* is it modifier? */
+			mod = GetSdlModifier(token);
+			if (mod)
+			{
+				if (mods)
+				{
+					Log_Printf(LOG_ERROR, "extra '%s', PC/SDL modifier already set\n", token);
+					return false;
+				}
+				mods = mod;
+				continue;
+			}
+			/* is it non-modifier, symbolic key name? */
+			code = SDL_GetScancodeFromKey(SDL_GetKeyFromName(token));
+			if (code)
+			{
+				if (scancode)
+				{
+					Log_Printf(LOG_ERROR, "extra '%s', PC/SDL scancode already set\n", token);
+					return false;
+				}
+				scancode = code;
+				continue;
+			}
+			Log_Printf(LOG_ERROR, "unknown PC/SDL key/modifier name '%s'\n", token);
+			return false;
+		}
+		/* no => numerical scancode */
+		if (scancode)
+		{
+			Log_Printf(LOG_WARN, "extra '%s', PC/SDL scancode already set\n", token);
+			return false;
+		}
+		scancode = strtol(token, &endptr, 0);
+		if (!scancode)
+		{
+			Log_Printf(LOG_ERROR, "invalid PC/SDL scancode '%s'\n", token);
+			return false;
+		}
+		if (*endptr)
+		{
+			Log_Printf(LOG_WARN, "'%s' garbage at end of '%s' PC/SDL scancode\n",
+				   endptr, token);
+			return false;
+		}
+	}
+	mapping->pc.scancode = scancode;
+	mapping->pc.mods = mods;
+	return (scancode > 0);
+}
+
+
+/*-----------------------------------------------------------------------*/
+/**
+ * Parse modifier name from 'name' and return 'mods' value with
+ * corresponding bit added to it, or return zero if there was no match
+ */
+static uint8_t AddSTModifier(uint8_t mods, const char *name)
+{
+	uint8_t mod, i;
+	for (i = 0; i < ARRAY_SIZE(ST_Modifiers); i++)
+	{
+		if (strcmp(ST_Modifiers[i].name, name) != 0)
+			continue;
+
+		mod = ST_Modifiers[i].mod;
+		if (mod & mods)
+		{
+			Log_Printf(LOG_WARN, "ST modifier '%s' specified twice\n", name);
+			return 0;
+		}
+		return (mods | mod);
+	}
+	Log_Printf(LOG_ERROR, "unknown/unsupported ST modifier '%s'\n", name);
+	return 0;
+}
+
+
+/*-----------------------------------------------------------------------*/
+/**
+ * Fill "mapping" KeyMapping guest (ST) part based on guest "spec" string.
+ * Return true on success
+ */
+static bool GuestSpecToKeymap(const char *spec, KeyMapping* mapping)
+{
+	char buf[64], *token, *saveptr, *endptr;
+	uint8_t scancode, mods;
+
+	if (!spec)
+		return false;
+	if (strlcpy(buf, spec, sizeof(buf)) >= sizeof(buf))
+	{
+		Log_Printf(LOG_ERROR, "ST scancode spec '%s' too long\n", spec);
+		return false;
+	}
+
+	scancode = mods = 0;
+	for (token = strtok_r(buf, "|", &saveptr);
+	     token;
+	     token = strtok_r(NULL, "|", &saveptr))
+	{
+		token = Str_Trim(token);
+		/* not a number? */
+		if (!isdigit(token[0]))
+		{
+			mods = AddSTModifier(mods, token);
+			if (!mods)
+				return false;
+			continue;
+		}
+		if (scancode)
+		{
+			Log_Printf(LOG_WARN, "extra '%s', ST scancode already set\n", token);
+			return false;
+		}
+		scancode = strtol(token, &endptr, 0);
+		if (!scancode)
+		{
+			Log_Printf(LOG_ERROR, "invalid ST scancode '%s'\n", token);
+			return false;
+		}
+		if (*endptr)
 		{
-			if (pKeySym->sym == (SDL_Keycode)LoadedKeymap[i][0])
-				return LoadedKeymap[i][1];
+			Log_Printf(LOG_WARN, "'%s' garbage at end of '%s' ST scancode\n",
+				   endptr, token);
+			return false;
 		}
 	}
+	if ((mods & ALT_XXX_BIT) && (mods & ~ALT_XXX_BIT))
+	{
+		Log_Printf(LOG_ERROR, "'ALT_XXX' ST modifier specified with other modifier(s)\n");
+		return false;
+	}
+	mapping->st.scancode = scancode;
+	mapping->st.mods = mods;
 
-	/* Use symbolic mapping */
-	return Keymap_SymbolicToStScanCode(pKeySym);
+	return (scancode > 0);
 }
 
 
@@ -428,86 +744,85 @@ static uint8_t Keymap_RemapKeyToSTScanCode(const SDL_Keysym* pKeySym)
  */
 void Keymap_LoadRemapFile(const char *pszFileName)
 {
-	char szString[1024];
-	int STScanCode, PCKeyCode;
 	FILE *in;
-	int idx = 0;
+	int idx, linenro, fails;
 
 	/* Initialize table with default values */
 	memset(LoadedKeymap, 0, sizeof(LoadedKeymap));
 
-	if (!*pszFileName)
+	if (strlen(pszFileName) == 0)
 		return;
 
-	/* Attempt to load file */
+	/* Attempt to load mapping file */
 	if (!File_Exists(pszFileName))
 	{
-		Log_Printf(LOG_DEBUG, "Keymap_LoadRemapFile: '%s' not a file\n", pszFileName);
+		Log_Printf(LOG_WARN, "Keymap_LoadRemapFile: keymap file '%s' missing\n", pszFileName);
 		return;
 	}
 	in = fopen(pszFileName, "r");
 	if (!in)
 	{
-		Log_Printf(LOG_ERROR, "Keymap_LoadRemapFile: failed to "
-			   " open keymap file '%s'\n", pszFileName);
+		Log_Printf(LOG_ERROR, "Keymap_LoadRemapFile: failed to open keymap file '%s'\n", pszFileName);
 		return;
 	}
 
-	while (!feof(in) && idx < KBD_MAX_SCANCODE)
+	Log_Printf(LOG_DEBUG, "Keymap_LoadRemapFile: Loading '%s' keymap file\n", pszFileName);
+
+	idx = linenro = fails = 0;
+	while (!feof(in))
 	{
+		char *line, *saveptr, buf[1024];
+		const char *host, *guest;
+
+		if (idx >= ARRAY_SIZE(LoadedKeymap))
+		{
+			Log_Printf(LOG_WARN, "Mappings specified already for"
+				   "supported number (%d) of keys, skipping"
+				   "rest of '%s' at line %d\n",
+				   ARRAY_SIZE(LoadedKeymap), pszFileName, linenro);
+			fails++;
+			break;
+		}
+
 		/* Read line from file */
-		if (fgets(szString, sizeof(szString), in) == NULL)
+		if (fgets(buf, sizeof(buf), in) == NULL)
 			break;
+		linenro++;
+
 		/* Remove white-space from start of line */
-		Str_Trim(szString);
-		if (strlen(szString)>0)
+		line = Str_Trim(buf);
+
+		/* Ignore empty line and comments */
+		if (strlen(line) == 0 || line[0] == ';' || line[0] == '#')
+			continue;
+
+		/* get the host (PC) key spec */
+		host = Str_Trim(strtok_r(line, ",", &saveptr));
+		if (!HostSpecToKeymap(host, &LoadedKeymap[idx]))
 		{
-			char *p;
-			/* Is a comment? */
-			if (szString[0] == ';' || szString[0] == '#')
-				continue;
-			/* Cut out the values */
-			p = strtok(szString, ",");
-			if (!p)
-				continue;
-			Str_Trim(szString);
-			PCKeyCode = atoi(szString);    /* Direct key code? */
-			if (PCKeyCode < 10)
-			{
-				/* If it's not a valid number >= 10, then
-				 * assume we've got a symbolic key name
-				 */
-				int offset = 0;
-				/* quoted character (e.g. comment line char)? */
-				if (*szString == '\\' && strlen(szString) == 2)
-					offset = 1;
-				PCKeyCode = Keymap_GetKeyFromName(szString+offset);
-			}
-			p = strtok(NULL, "\n");
-			if (!p)
-				continue;
-			STScanCode = atoi(p);
-			/* Store into remap table, check both value within range */
-			if (STScanCode > 0 && STScanCode <= KBD_MAX_SCANCODE
-			    && PCKeyCode >= 8)
-			{
-				LOG_TRACE(TRACE_KEYMAP,
-				          "keymap from file: sym=%i --> scan=%i\n",
-				          PCKeyCode, STScanCode);
-				LoadedKeymap[idx][0] = PCKeyCode;
-				LoadedKeymap[idx][1] = STScanCode;
-				idx += 1;
-			}
-			else
-			{
-				Log_Printf(LOG_WARN, "Could not parse keymap file:"
-				           " '%s' (%d >= 8), '%s' (0 > %d <= %d)\n",
-					   szString, PCKeyCode, p, STScanCode, KBD_MAX_SCANCODE);
-			}
+			Log_Printf(LOG_WARN, "Failed to parse host (PC/SDL) part '%s' of line %d in: %s\n",
+				   host, linenro, pszFileName);
+			fails++;
+			continue;
 		}
-	}
 
+		/* Get the guest (ST) key spec */
+		guest = Str_Trim(strtok_r(NULL, "\n", &saveptr));
+		if (!GuestSpecToKeymap(guest, &LoadedKeymap[idx]))
+		{
+			Log_Printf(LOG_WARN, "Failed to parse guest (ST) part '%s' of line %d in: %s\n",
+				   guest, linenro, pszFileName);
+			fails++;
+			continue;
+		}
+		LOG_TRACE(TRACE_KEYMAP, "key mapping from file: host %s => guest %s\n",
+			  host, guest);
+		idx += 1;
+	}
 	fclose(in);
+
+	if (fails)
+		Log_AlertDlg(LOG_ERROR, "%d keymap file parsing failures\n(see console log for details)", fails);
 }
 
 
@@ -596,6 +911,98 @@ static bool IsKeyTranslatable(SDL_Keycode symkey)
 }
 
 
+/*-----------------------------------------------------------------------*/
+/**
+ * Set modifiers keys indicated by bits in 'mods' up or down
+ * based on 'down'.
+ *
+ * Return true if modifiers were present and trace output
+ * requires newline, false otherwise.
+ */
+static bool InsertModifiers(uint8_t mods, bool down)
+{
+	const char *separator;
+	uint8_t i, scancode;
+	if (!mods)
+		return false;
+
+	separator = "key mod(s):";
+	for (i = 0; i < ARRAY_SIZE(ST_Modifiers); i++)
+	{
+		if (!(mods & ST_Modifiers[i].mod))
+			continue;
+
+		scancode = ST_Modifiers[i].scancode;
+		LOG_TRACE(TRACE_KEYMAP, "%s %s (0x%02x)",
+			  separator, ST_Modifiers[i].name, scancode);
+		separator = " |";
+
+		if (down)
+		{
+			/* because modifiers may already be down
+			 * due to normal key events (or mapped
+			 * scancodes that happen to be modifiers),
+			 * KeyStates are counted
+			 */
+			if (Keyboard.KeyStates[scancode]++ > 0)
+				continue;
+		}
+		else
+		{
+			if (--Keyboard.KeyStates[scancode] > 0)
+				continue;
+		}
+		IKBD_PressSTKey(scancode, down);
+	}
+	return true;
+}
+
+
+/*-----------------------------------------------------------------------*/
+/**
+ * Insert key presses and releases for ALT_XXX digits
+ * when relevant modifier bit is set.
+ *
+ * Return true if scancode should be ignored and trace output
+ * requires newline, false otherwise.
+ */
+static bool InsertAltXXXDigits(ST_Key *stkey)
+{
+	/* map 0, 1-3, 5-6, 7-9 to corresponding numpad scancodes */
+	static const uint8_t keypad[] = {
+		112,
+		109, 110, 111,
+		106, 107, 108,
+		103, 104, 105
+	};
+	char buf[8], *xxx;
+
+	if (!(stkey->mods & ALT_XXX_BIT))
+		return false;
+
+	sprintf(buf, "%d", stkey->scancode);
+	for (xxx = buf; *xxx; xxx++)
+	{
+		uint8_t scancode;
+		int digit;
+
+		digit = *xxx - '0';
+		assert(digit >= 0 && digit <= 9);
+		scancode = keypad[digit];
+		LOG_TRACE(TRACE_KEYMAP, " + '%d' (0x%02x)",
+			  digit, scancode);
+
+		/* press and release each keycode
+		 * without checking whether they
+		 * are already pressed
+		 */
+		IKBD_PressSTKey(scancode, true);
+		IKBD_PressSTKey(scancode, false);
+	}
+	return true;
+}
+
+
 /*-----------------------------------------------------------------------*/
 /**
  * User pressed a key down
@@ -603,6 +1010,7 @@ static bool IsKeyTranslatable(SDL_Keycode symkey)
 void Keymap_KeyDown(const SDL_Keysym *sdlkey)
 {
 	uint8_t STScanCode;
+	ST_Key* stkey;
 	int symkey = sdlkey->sym;
 	int modkey = sdlkey->mod;
 
@@ -621,16 +1029,26 @@ void Keymap_KeyDown(const SDL_Keysym *sdlkey)
 	if (!IsKeyTranslatable(symkey))
 		return;
 
-	STScanCode = Keymap_RemapKeyToSTScanCode(sdlkey);
+	stkey = Keymap_RemapKeyToSTKey(sdlkey);
+	if (stkey == NULL)
+		return;
+
+	STScanCode = stkey->scancode;
 	LOG_TRACE(TRACE_KEYMAP, "key map: sym=0x%x to ST-scan=0x%02x\n", symkey, STScanCode);
-	if (STScanCode != ST_NO_SCANCODE)
+
+	assert(Keyboard.KeyStates[scancode] == 0);
+	if (InsertModifiers(stkey->mods, true))
 	{
-		if (!Keyboard.KeyStates[STScanCode])
-		{
-			/* Set down */
-			Keyboard.KeyStates[STScanCode] = true;
-			IKBD_PressSTKey(STScanCode, true);
-		}
+		bool done = InsertAltXXXDigits(stkey);
+		LOG_TRACE(TRACE_KEYMAP, "\n");
+		if (done)
+			return;
+	}
+	if (!Keyboard.KeyStates[STScanCode])
+	{
+		/* Set down */
+		Keyboard.KeyStates[STScanCode]++;
+		IKBD_PressSTKey(STScanCode, true);
 	}
 }
 
@@ -642,6 +1060,7 @@ void Keymap_KeyDown(const SDL_Keysym *sdlkey)
 void Keymap_KeyUp(const SDL_Keysym *sdlkey)
 {
 	uint8_t STScanCode;
+	ST_Key* stkey;
 	int symkey = sdlkey->sym;
 	int modkey = sdlkey->mod;
 
@@ -661,16 +1080,31 @@ void Keymap_KeyUp(const SDL_Keysym *sdlkey)
 	if (!IsKeyTranslatable(symkey))
 		return;
 
-	STScanCode = Keymap_RemapKeyToSTScanCode(sdlkey);
+	stkey = &KeysDown[sdlkey->scancode];
+	if (stkey == NULL)
+	{
+		Log_Printf(LOG_ERROR, "No mapping for key %d found!\n", sdlkey->scancode);
+		return;
+	}
+
 	/* Release key (only if was pressed) */
-	if (STScanCode != ST_NO_SCANCODE)
+	STScanCode = stkey->scancode;
+	assert(Keyboard.KeyStates[STScanCode] > 0);
+	if (Keyboard.KeyStates[STScanCode])
 	{
-		if (Keyboard.KeyStates[STScanCode])
-		{
-			IKBD_PressSTKey(STScanCode, false);
-			Keyboard.KeyStates[STScanCode] = false;
-		}
+		IKBD_PressSTKey(STScanCode, false);
+		Keyboard.KeyStates[STScanCode]--;
+	}
+	if (InsertModifiers(stkey->mods, false))
+	{
+		LOG_TRACE(TRACE_KEYMAP, "\n");
 	}
+	/* Trace state of ST modifiers keys */
+	LOG_TRACE(TRACE_KEYMAP,"  LSHIFT:%d RSHIFT:%d CTRL:%d ALT:%d\n",
+		  Keyboard.KeyStates[ST_LSHIFT],
+		  Keyboard.KeyStates[ST_RSHIFT],
+		  Keyboard.KeyStates[ST_CONTROL],
+		  Keyboard.KeyStates[ST_ALTERNATE]);
 }
 
 /*-----------------------------------------------------------------------*/
diff --git a/src/reset.c b/src/reset.c
index 0a244c50..10946540 100644
--- a/src/reset.c
+++ b/src/reset.c
@@ -22,6 +22,7 @@ const char Reset_fileid[] = "Hatari reset.c";
 #include "ioMem.h"
 #include "cycles.h"
 #include "cycInt.h"
+#include "keymap.h"
 #include "m68000.h"
 #include "mfp.h"
 #include "midi.h"
@@ -103,6 +104,7 @@ static int Reset_ST(bool bCold)
 	Sound_Reset();                /* Reset Sound */
 	ACIA_Reset( ACIA_Array );     /* ACIA */
 	IKBD_Reset(bCold);            /* Keyboard (after ACIA) */
+	Keymap_Reset();               /* Key mapping state */
 	SCC_Reset();
 	if (Config_IsMachineFalcon() && !bUseVDIRes)
 		VIDEL_reset();
-- 
2.30.2


--------------15BD487FF3B90B84C931453F--



Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/