Re: [hatari-devel] Suggestion for enhanced keymap table format

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


Hi,

I did a diff between your and current keymap.c
version (attached).

For your intended change to be reviewable, please
split all non-functional & unrelated code changes
(white space/indenting, const, ! -> ==0, type
changes, commenting out trace outputs) to
a separate patch, or remove them.

Then either rebase your changes to latest Hatari
git version of keymap.c, or otherwise make sure
they apply and still work with it, before posting
a patch / "git diff" against it.


	- Eero

On 11/3/20 12:25 AM, Vincent Barrilliot wrote:
Hello


I have made further enhancement and bug corrections to my code. Now the keyboard mapping for French keyboard is quite good, all keys work. However there is a problem if you press 2 keys with emulated modifiers at the same time, but I guess that's a rare situation.

Now I can code with Pure C in Hatari because I can make curly braces ! :D

Anybody wants to try it ?

Just drop the keymap.c in src and use the attached keymap.


Vinz


On 31/10/2020 00:22, Eero Tamminen wrote:
Hi,

Thomas is probably best person to comment on this,
but until we've removed support for SDL v1, these
kind of changes should support both SDL v1 and v2.

Regardless of that, could you send your PoC changes as patch to list?

(Just the relevant changes, no extra white space
changes etc.)


Keymap file format change isn't necessarily much
of a problem, if you could provide also (shell or
python) script that can convert between the
formats.

    - Eero

On 10/31/20 12:28 AM, Vincent Barrilliot wrote:
Dear Hatari developers,

I am new to Hatari development. I am French, 43 and I also follow EmuTOS and FreeMiNT development although I listen and learn at the moment.

You have been doing a fantastic job with Hatari, thank you for such a great piece of software !

As a French keyboard user, I am suffering from the inconvenience of the "alt-shift" keys which are not well supported currently, and I have decided to do something about it.

I have made a proof of concept where I modified keymap.c with the following improvement:

The keymap is now an array of KeyMappings structs which have the SDL_keysym on one hand, and a list of ST scancodes on the other hand.

By pressing AltGr-' on my French PC keyboard, Hatari sends scancodes of shift-alt-# to its "ikbd", and an opening curly brace is correctly output in e.g. STeno or EmuCON.

So this works and should enable to bind any key+modifiers from the PC to any sequence of scancodes on the ST side, solving that old problem for good.

Now I need to be able to make that configurable and enhance the format of the keymap configuration file to support this and I would your advice for this.

Obviously I could make something that works with scancodes numbers only, but it would break the current "symbolic" mapping which although not perfect, is easy to use and understand.

I've had the following ideas:

1 Use some "section" delimiter in the config file so to indicate to the parser how to interpret following the lines.

SYMBOLIC

Y,44

Z,21

SCANCODE

21|200,2a|38|1a

(we could also allow usage of modifier names or ST modifier names to make things more friendly, ie 21|KMOD_RALT,LSHIFT|ALTERNATE|1a)

2 Introduce some prefix to indicate to the parser what is we're describing. Eg s for symbolic, r for raw (scancode).

sY,44

r21|KMOD_RALT,LSHIFT|ALTERNATE|r1a

But that would break backward compatibility with exiting keymaps...


None of that sounds great... By default I'd go with the scancode because it's the most robust. There is only a handful of keys to "override" anyway so we wouldn't compete with /etc/termcap's complexity. We can also ship a few standard keymaps. But then what do we do with existing user keymaps...?

Thanks for any idea!


Vincent B






--- ../src/keymap.c	2020-10-16 13:17:04.851049785 +0300
+++ keymap.c	2020-11-03 15:14:07.063674154 +0200
@@ -6,7 +6,7 @@
 
   Here we process a key press and the remapping of the scancodes.
 */
-const char Keymap_fileid[] = "Hatari keymap.c";
+const char Keymap_fileid[] = "Hatari keymap.c : " __DATE__ " " __TIME__;
 
 #include <ctype.h>
 #include "main.h"
@@ -22,13 +22,39 @@
 #include "log.h"
 
 
+/* ST keyboard modifiers, as would be returned by Kbshift (why reinvent the wheel ?) */
+#define K_RSHIFT	0x0001
+#define K_LSHIFT	0x0002
+#define K_CTRL		0x0004
+#define K_ALTERNATE	0x0008
+#define K_CAPSLOCK	0x0010
+
 #if !WITH_SDL2
 /* This table is used to translate a symbolic keycode to the (SDL) scancode */
 static Uint8 SdlSymToSdlScan[SDLK_LAST];
 #endif
 
-/* Table for loaded keys: */
-static int LoadedKeymap[KBD_MAX_SCANCODE][2];
+
+/* Key mappings: pair a SDL definition with a list of keys we have to press on the ST */
+#define MAX_ST_SCANCODES 4 /* Max is alt-a-b-c, so 4 chars */
+struct KeyMapping
+{
+	/* Input on PC keyboard */
+	SDL_keysym SdlKeysym;
+	SDL_Keymod modmask;
+	/* What we should press on the ST's keyboard */
+	uint8_t	   STScanCodesLength;
+	uint8_t    STScanCodes[MAX_ST_SCANCODES];
+	/* What modifiers we effectively had to press (and will have to release when the key is up.
+	 * WARNING: this assumes the keyboard doesn't let you press a key that is already pressed. */
+	uint8_t	   PressedModifiers[MAX_ST_SCANCODES];
+};
+static struct KeyMapping LoadedKeyMap[KBD_MAX_SCANCODE];
+static struct KeyMapping DummyMapping; /* For when the mapping doesn't come from the user-defined table */
+
+
+/* Current ST modifiers, as would be returned by KbShift(0) */
+uint32_t currentSTModifiers;
 
 /* List of ST scan codes to NOT de-bounce when running in maximum speed */
 static const char DebounceExtendedKeys[] =
@@ -41,7 +67,11 @@
 	0      /* term */
 };
 
-
+/* Helper functions for parsing the keymap file */
+static int HostSpecToSDLKeysym(const char *spec, struct KeyMapping* result);
+static int GuestSpecToSTScanCodes(const char *spec, struct KeyMapping *result);
+static SDL_Keymod SDLKeymodFromName(const char *name);
+static void UpdateSTModifiers(uint8_t scancode, bool isPressed);
 
 /*-----------------------------------------------------------------------*/
 /**
@@ -53,12 +83,14 @@
 	memset(SdlSymToSdlScan, 0, sizeof(SdlSymToSdlScan));      /* Clear array */
 #endif
 	Keymap_LoadRemapFile(ConfigureParams.Keyboard.szMappingFileName);
+
+	DummyMapping.STScanCodesLength = 1;
 }
 
 /**
  * Map SDL symbolic key to ST scan code
  */
-static char Keymap_SymbolicToStScanCode(SDL_keysym* pKeySym)
+static uint8_t Keymap_SymbolicToStScanCode(const SDL_keysym* pKeySym)
 {
 	char code;
 
@@ -220,7 +252,7 @@
 /**
  * Remap SDL scancode key to ST Scan code - this is the version for SDL2
  */
-static char Keymap_PcToStScanCode(SDL_keysym* pKeySym)
+static uint8_t Keymap_PcToStScanCode(const SDL_keysym* pKeySym)
 {
 	switch (pKeySym->scancode)
 	{
@@ -368,12 +400,13 @@
  * locations on "qwertz" and "azerty" keyboards.
  * This clever code has originally been taken from the emulator Aranym. (cheers!)
  */
-static int Keymap_FindScanCodeOffset(SDL_keysym* keysym)
+static int Keymap_FindScanCodeOffset(const SDL_keysym* keysym)
 {
 	int offset = -1;    /* uninitialized scancode offset */
 	int scanPC = keysym->scancode;
 
-	if (scanPC == 0)  return -1; /* Ignore illegal scancode */
+	if (scanPC == 0)
+		return -1; /* Ignore illegal scancode */
 
 	switch (keysym->sym)
 	{
@@ -449,7 +482,7 @@
  * Map PC scancode to ST scancode.
  * This code was heavily inspired by the emulator Aranym. (cheers!)
  */
-static char Keymap_PcToStScanCode(SDL_keysym* keysym)
+static uint8_t Keymap_PcToStScanCode(const SDL_keysym* keysym)
 {
 	static int offset = -1;    /* uninitialized scancode offset */
 
@@ -479,23 +512,23 @@
 	 case SDLK_KP_ENTER:    return 0x72;  /* NumPad Enter */
 
 	 /* Special Keys */
-	 case SDLK_PRINT:    return 0x62;  /* Help */
-	 case SDLK_SCROLLOCK: return 0x61; /* Undo */
-	 case SDLK_PAGEUP:   return 0x63;  /* Keypad ( */
-	 case SDLK_PAGEDOWN: return 0x64;  /* Keypad ) */
-	 case SDLK_HOME:     return 0x47;  /* Home */
-	 case SDLK_END:      return 0x60;  /* End => "<>" on German Atari kbd */
-	 case SDLK_UP:       return 0x48;  /* Arrow Up */
-	 case SDLK_LEFT:     return 0x4b;  /* Arrow Left */
-	 case SDLK_RIGHT:    return 0x4d;  /* Arrow Right */
-	 case SDLK_DOWN:     return 0x50;  /* Arrow Down */
-	 case SDLK_INSERT:   return 0x52;  /* Insert */
-	 case SDLK_DELETE:   return 0x53;  /* Delete */
-	 case SDLK_LESS:     return 0x60;  /* "<" */
+	 case SDLK_PRINT:    	return 0x62;  /* Help */
+	 case SDLK_SCROLLOCK: 	return 0x61; /* Undo */
+	 case SDLK_PAGEUP:   	return 0x63;  /* Keypad ( */
+	 case SDLK_PAGEDOWN: 	return 0x64;  /* Keypad ) */
+	 case SDLK_HOME:     	return 0x47;  /* Home */
+	 case SDLK_END:      	return 0x60;  /* End => "<>" on German Atari kbd */
+	 case SDLK_UP:       	return 0x48;  /* Arrow Up */
+	 case SDLK_LEFT:    	return 0x4b;  /* Arrow Left */
+	 case SDLK_RIGHT:    	return 0x4d;  /* Arrow Right */
+	 case SDLK_DOWN:     	return 0x50;  /* Arrow Down */
+	 case SDLK_INSERT:   	return 0x52;  /* Insert */
+	 case SDLK_DELETE:   	return 0x53;  /* Delete */
+	 case SDLK_LESS:     	return 0x60;  /* "<" */
 
 	 /* Map Right Alt/Alt Gr/Control to the Atari keys */
-	 case SDLK_RCTRL:  return 0x1d;  /* Control */
-	 case SDLK_RALT:   return 0x38;  /* Alternate */
+	 case SDLK_RCTRL:  	return 0x1d;  /* Control */
+	 case SDLK_RALT:   	return 0x38;  /* Alternate */
 
 	 default:
 		/* Process remaining keys: assume that it's PC101 keyboard
@@ -534,7 +567,7 @@
  * so that we can easily toggle between number and cursor mode with the
  * numlock key.
  */
-static char Keymap_GetKeyPadScanCode(SDL_keysym* pKeySym)
+static char Keymap_GetKeyPadScanCode(const SDL_keysym* pKeySym)
 {
 	if (SDL_GetModState() & KMOD_NUM)
 	{
@@ -573,39 +606,65 @@
 }
 
 
+static int InputMatchesKeyMapping(const SDL_keysym* keySym, const struct KeyMapping *mapping)
+{
+	//if (keySym->scancode == mapping->SdlKeysym.scancode)
+		// LOG_TRACE(TRACE_KEYMAP,"matching 0x%04x and 0x%04x\n", keySym->mod & mapping->modmask, mapping->SdlKeysym.mod);
+	return keySym->scancode == mapping->SdlKeysym.scancode
+		&& (keySym->mod & mapping->modmask) == mapping->SdlKeysym.mod;
+}
+
+
 /**
  * Remap SDL Key to ST Scan code
+ * Receives the pressed key from SDL, and a buffer to fill with ST keyboard scan codes to simulate.
+ * Returns the number of scan codes filled in the buffer.
  */
-static char Keymap_RemapKeyToSTScanCode(SDL_keysym* pKeySym)
+static struct KeyMapping* Keymap_RemapKeyToSTScanCodes(SDL_keysym* pKeySym, bool enableTrace)
 {
 	/* Check for keypad first so we can handle numlock */
 	if (ConfigureParams.Keyboard.nKeymapType != KEYMAP_LOADED)
 	{
 		if (pKeySym->sym >= SDLK_KP1 && pKeySym->sym <= SDLK_KP9)
 		{
-			return Keymap_GetKeyPadScanCode(pKeySym);
+			DummyMapping.STScanCodes[0] = Keymap_GetKeyPadScanCode(pKeySym);
+			return &DummyMapping;
 		}
 	}
 
 	/* Remap from PC scancodes? */
 	if (ConfigureParams.Keyboard.nKeymapType == KEYMAP_SCANCODE)
 	{
-		return Keymap_PcToStScanCode(pKeySym);
+		DummyMapping.STScanCodes[0] = Keymap_PcToStScanCode(pKeySym);
+		return &DummyMapping;
 	}
 
 	/* Use loaded keymap? */
 	if (ConfigureParams.Keyboard.nKeymapType == KEYMAP_LOADED)
 	{
 		int i;
-		for (i = 0; i < KBD_MAX_SCANCODE && LoadedKeymap[i][1] != 0; i++)
+		for (i = 0; i < KBD_MAX_SCANCODE; i++)
 		{
-			if (pKeySym->sym == (SDLKey)LoadedKeymap[i][0])
-				return LoadedKeymap[i][1];
+			struct KeyMapping *mapping = &LoadedKeyMap[i];
+
+			if (mapping->SdlKeysym.scancode == 0)
+				break; /* End of table */
+
+			if (!InputMatchesKeyMapping(pKeySym, mapping))
+				continue;
+
+			if (enableTrace)
+				LOG_TRACE(TRACE_KEYMAP,"Keymap found: %02x %02x %02x (%d bytes)\n",
+					mapping->STScanCodes[0],mapping->STScanCodes[1],mapping->STScanCodes[2],mapping->STScanCodesLength);
+			return mapping;
 		}
 	}
 
-	/* Use symbolic mapping */
-	return Keymap_SymbolicToStScanCode(pKeySym);
+	/* Fall back to symbolic mapping */
+	DummyMapping.STScanCodes[0] = Keymap_SymbolicToStScanCode(pKeySym);
+	if (enableTrace)
+		LOG_TRACE(TRACE_KEYMAP,"Symbolic mapping: %02x\n",DummyMapping.STScanCodes[0]);
+	return &DummyMapping;
 }
 
 
@@ -613,17 +672,25 @@
 /**
  * Load keyboard remap file
  */
-void Keymap_LoadRemapFile(char *pszFileName)
+void Keymap_LoadRemapFile(const char *pszFileName)
 {
-	char szString[1024];
-	int STScanCode, PCKeyCode;
+	char mapLine[1024];
+	int hostSpecIsOk, guestSpecIsOk;
 	FILE *in;
 	int idx = 0;
+	int lineNumber = 0; /* For logging purposes            */
+	char *token;
+	char *saveptr;		/* For saving strtok_r's state, because host/guest analyses may also use strtok */
+	char hostSpec[50]; 	/* Host (PC) keys specification    */
+	char guestSpec[50];	/* Guest's (ST) keys specification */
+	const char invalidSpecificationMessage[] = "Keymap_LoadRemapFile: '%s' not a valid specification at line %d\n";
+
+	Log_Printf(LOG_DEBUG, "Keymap_LoadRemapFile: Loading '%s'\n", pszFileName);
 
 	/* Initialize table with default values */
-	memset(LoadedKeymap, 0, sizeof(LoadedKeymap));
+	memset(LoadedKeyMap, 0, sizeof(LoadedKeyMap));
 
-	if (!*pszFileName)
+	if (strlen(pszFileName) == 0)
 		return;
 
 	/* Attempt to load file */
@@ -635,66 +702,151 @@
 	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)
 	{
 		/* Read line from file */
-		if (fgets(szString, sizeof(szString), in) == NULL)
+		if (fgets(mapLine, sizeof(mapLine), in) == NULL)
 			break;
+
+		++lineNumber;
+
 		/* Remove white-space from start of line */
-		Str_Trim(szString);
-		if (strlen(szString)>0)
+		Str_Trim(mapLine);
+
+		/* Ignore empty line and comments */
+		if (strlen(mapLine) == 0 || mapLine[0] == ';' || mapLine[0] == '#')
+			continue;
+
+		/* Cut out the values between host and guest parts */
+		token = strtok_r(mapLine, ",", &saveptr);
+		if (token == NULL)
+			goto invalidSpecificationError;
+
+		/* Get the host's key specification */
+		strcpy(hostSpec,token);
+		Str_Trim(hostSpec);
+		if (strlen(hostSpec) == 0)
+			goto invalidSpecificationError;
+		hostSpecIsOk = HostSpecToSDLKeysym(hostSpec, &LoadedKeyMap[idx]);
+
+		/* Get the guest (ST) specification */
+		token = strtok_r(NULL, "\n", &saveptr);
+
+		if (token == NULL)
+			continue;
+		strcpy(guestSpec,token);
+		Str_Trim(guestSpec);
+		if (strlen(guestSpec) == 0)
+			goto invalidSpecificationError;
+		guestSpecIsOk = GuestSpecToSTScanCodes(guestSpec, &LoadedKeyMap[idx]);
+
+		/* Store into remap table, check both value within range */
+		if (guestSpecIsOk && hostSpecIsOk)
 		{
-			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;
-			}
+			LOG_TRACE(TRACE_KEYMAP,"keymap from file: host %s --> guest %s\n", hostSpec, guestSpec);
+			idx += 1;
+		}
+		else
+		{
+			Log_Printf(LOG_WARN, "Could not parse keymap specification: %s\n", mapLine);
+		}
+
+		continue;
+
+invalidSpecificationError:
+			Log_Printf(LOG_ERROR, invalidSpecificationMessage, pszFileName, lineNumber);
+	}
+
+	fclose(in);
+}
+
+static int HostSpecToSDLKeysym(const char *spec, struct KeyMapping* result)
+{
+	char buf[100];
+	char *token;
+	SDL_Scancode scancode;
+
+	/* Prepare buffer */
+	strcpy(buf, spec);
+
+	/* Prepare for early returns */
+	result->SdlKeysym.mod = 0;
+	result->modmask = 0;
+
+	/* Scancode part */
+	token = strtok(buf,"|");
+	if (token != NULL && (scancode = strtol(token, NULL, 16)))
+		result->SdlKeysym.scancode = scancode;
+	else
+		return 0;
+
+	/* Modifier part */
+	token = strtok(NULL,"|");
+	if (token != NULL)
+	{
+		/* We have a modifier specified */
+		result->SdlKeysym.mod = SDLKeymodFromName(token);
+
+		/* "modifier mask" part */
+		token = strtok(NULL,"|");
+		if (token != NULL)
+			result->modmask = SDLKeymodFromName(token);
+	}
+
+	return -1; /* Success */
+}
+
+
+static int GuestSpecToSTScanCodes(const char *spec, struct KeyMapping* mapping)
+{
+	/* Analyses the guest (Atari ST keyboard) specification from the table file */
+	char buf[100];
+	char *start;
+	char *separator;
+	uint8_t *scancodes = mapping->STScanCodes; /* Alias for readability */
+	uint8_t scancode;
+	int i = 0;
+
+	strcpy(buf, spec);
+	separator = buf-1;
+	do
+	{
+		start = separator+1;
+		separator = strchr(start, '|');
+		if (separator != NULL)
+			*separator = '\0';
+
+		if (strlen(start) <= 2)
+		{
+			scancode = strtol(start, NULL, 16);
+		}
+		else
+		{
+			/* Scancode may be expressed as LSHIFT,RSHIFT,ALTERNATE for user convenience */
+			if (strcmp(start, "LSHIFT") == 0)
+				scancode = 0x2a;
+			else if (strcmp(start, "RSHIFT") == 0)
+				scancode = 0x36;
+			else if (strcmp(start, "ALTERNATE") == 0)
+				scancode = 0x38;
 			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_ERROR, "GuestSpecToSTScanCodes: Cannot understand scancode '%s'\n", start);
+				i = 0; /* Error out */
+				break;
 			}
 		}
-	}
 
-	fclose(in);
+		scancodes[i++] = scancode;
+	} while (separator != NULL);
+
+	mapping->STScanCodesLength = i;
+
+	return i > 0;
 }
 
 
@@ -764,17 +916,48 @@
 }
 
 
+static int IsKeyTranslatable(SDL_Keycode symkey)
+{
+	/* Ignore modifier keys that are not passed to the ST */
+	switch (symkey)
+	{
+		case SDLK_RALT:
+	 	case SDLK_LMETA:
+	 	case SDLK_RMETA:
+	 	case SDLK_MODE:
+	 	case SDLK_NUMLOCK:
+			return 0;
+	}
+	return -1;
+}
+
+
+static bool IsSTModifier(uint8_t scancode)
+{
+	switch (scancode)
+	{
+		case 0x2a: /* left shift */
+		case 0x36: /* right shift */
+		case 0x38: /* alternate */
+			return true;
+	}
+	return false;
+}
+
 /*-----------------------------------------------------------------------*/
 /**
- * User press key down
+ * User pressed a key down
  */
 void Keymap_KeyDown(SDL_keysym *sdlkey)
 {
-	uint8_t STScanCode;
+	struct KeyMapping* mapping;
+	uint8_t* modifiers;
+	int i;
+	/* Convenience */
 	int symkey = sdlkey->sym;
 	int modkey = sdlkey->mod;
 
-	LOG_TRACE(TRACE_KEYMAP, "key down: sym=%i scan=%i mod=0x%x name='%s'\n",
+	LOG_TRACE(TRACE_KEYMAP, "Keymap_KeyDown: sym=%i scancode=0x%02x mod=0x%02x name='%s'\n",
 	          symkey, sdlkey->scancode, modkey, Keymap_GetKeyName(symkey));
 
 	if (ShortCut_CheckKeys(modkey, symkey, true))
@@ -785,40 +968,61 @@
 	if (Joy_KeyDown(symkey, modkey))
 		return;
 
-	/* Handle special keys */
-	if (symkey == SDLK_RALT || symkey == SDLK_LMETA || symkey == SDLK_RMETA
-	        || symkey == SDLK_MODE || symkey == SDLK_NUMLOCK)
-	{
-		/* Ignore modifier keys that aren't passed to the ST */
+	if (!IsKeyTranslatable(symkey))
 		return;
-	}
 
-	STScanCode = Keymap_RemapKeyToSTScanCode(sdlkey);
-	LOG_TRACE(TRACE_KEYMAP, "key map: sym=0x%x to ST-scan=0x%02x\n", symkey, STScanCode);
-	if (STScanCode != (uint8_t)-1)
+	mapping = Keymap_RemapKeyToSTScanCodes(sdlkey, true);
+	if (mapping == NULL)
+		return;
+
+	modifiers = mapping->PressedModifiers;
+	for (i = 0; i < mapping->STScanCodesLength ; i++)
 	{
-		if (!Keyboard.KeyStates[STScanCode])
+		uint8_t scancode = mapping->STScanCodes[i];
+
+		Keyboard.KeyStates[scancode]++;
+
+		/* If that key was not already pressed, press it */
+		if (Keyboard.KeyStates[scancode] == 1)
 		{
+			/* If it's a modifier that we're pressing, remember to release it later.
+			 * In case you're wondering why we don't release the modifiers immediately after
+             * pressing the key: the reason is that if the user keeps the key down to make it
+			 * repeat, the modifiers need to be there, otherwise it is the "unshifter/unalted"
+			 * character that will repeat instead.
+			 * TODO: Unfortunately this causes a bug as the modifiers will accumulate if you
+			 * press multiple modified keys at once. For example, if on a French keyboard you 
+			 * press ALT-5 (to get a [), the ALTERNATE modifier will be retained. Holding that
+			 * ALT down and pressing the key 6 at the same time (to get |), the | only requires
+			 * SHIFT to be pressed. But ALTERNATE also being emulated, you will get ~.
+			 * To maybe fix that we would have to manage a stack of modifiers, so we could 
+			 * release ALT while 7 is pressed (as it only requires SHIFT) then press it again
+			 * when 7 is released. Is it really worth the effort... */
+			if (IsSTModifier(scancode))
+				*modifiers++ = scancode;
+
 			/* Set down */
-			Keyboard.KeyStates[STScanCode] = true;
-			IKBD_PressSTKey(STScanCode, true);
+			IKBD_PressSTKey(scancode, true);
 		}
 	}
+	*modifiers = 0; /* End the list of modifers to release */
 }
 
 
 /*-----------------------------------------------------------------------*/
 /**
- * User released key
+ * User released a key
  */
 void Keymap_KeyUp(SDL_keysym *sdlkey)
 {
-	uint8_t STScanCode;
+	struct KeyMapping *mapping;
+	uint8_t *modifiers;
+	int i;
 	int symkey = sdlkey->sym;
 	int modkey = sdlkey->mod;
 
-	LOG_TRACE(TRACE_KEYMAP, "key up: sym=%i scan=%i mod=0x%x name='%s'\n",
-	          symkey, sdlkey->scancode, modkey, Keymap_GetKeyName(symkey));
+	/* LOG_TRACE(TRACE_KEYMAP, "Keymap_KeyUp: sym=%i scancode=0x%02x mod=0x%02x name='%s'\n",
+	          symkey, sdlkey->scancode, modkey, Keymap_GetKeyName(symkey));*/
 
 	/* Ignore short-cut keys here */
 	if (ShortCut_CheckKeys(modkey, symkey, false))
@@ -830,25 +1034,38 @@
 		return;
 
 	/* Handle special keys */
-	if (symkey == SDLK_RALT || symkey == SDLK_LMETA || symkey == SDLK_RMETA
-	        || symkey == SDLK_MODE || symkey == SDLK_NUMLOCK)
-	{
-		/* Ignore modifier keys that aren't passed to the ST */
+	if (!IsKeyTranslatable(symkey))
+		return;
+
+	mapping = Keymap_RemapKeyToSTScanCodes(sdlkey, false);
+	if (mapping == NULL)
 		return;
-	}
 
-	STScanCode = Keymap_RemapKeyToSTScanCode(sdlkey);
 	/* Release key (only if was pressed) */
-	if (STScanCode != (uint8_t)-1)
+	for (i = 0; i < mapping->STScanCodesLength ; i++)
 	{
-		if (Keyboard.KeyStates[STScanCode])
-		{
-			IKBD_PressSTKey(STScanCode, false);
-			Keyboard.KeyStates[STScanCode] = false;
-		}
+		uint8_t scancode = mapping->STScanCodes[i];
+
+		if (IsSTModifier(scancode) && mapping->STScanCodesLength > 1)
+			continue; /* We release emulated modifiers last */
+
+		/* Set up */
+		Keyboard.KeyStates[scancode]--;
+		if (Keyboard.KeyStates[scancode] == 0)
+			IKBD_PressSTKey(scancode, false);
+	}
+
+	/* Release modifiers that were pressed to emulate the key*/
+	modifiers = mapping->PressedModifiers;
+	while (*modifiers)
+	{
+		Keyboard.KeyStates[*modifiers]--;
+		if (Keyboard.KeyStates[*modifiers] == 0)
+			IKBD_PressSTKey(*modifiers++, false);
 	}
 }
 
+
 /*-----------------------------------------------------------------------*/
 /**
  * Simulate press or release of a key corresponding to given character
@@ -859,21 +1076,30 @@
 
 	sdlkey.mod = KMOD_NONE;
 	sdlkey.scancode = 0;
-	if (isupper((unsigned char)asckey)) {
-		if (press) {
+	if (isupper((unsigned char)asckey))
+	{
+		if (press)
+		{
 			sdlkey.sym = SDLK_LSHIFT;
 			Keymap_KeyDown(&sdlkey);
 		}
 		sdlkey.sym = tolower((unsigned char)asckey);
 		sdlkey.mod = KMOD_LSHIFT;
-	} else {
+	}
+	else
+	{
 		sdlkey.sym = asckey;
 	}
-	if (press) {
+
+	if (press)
+	{
 		Keymap_KeyDown(&sdlkey);
-	} else {
+	}
+	else
+	{
 		Keymap_KeyUp(&sdlkey);
-		if (isupper((unsigned char)asckey)) {
+		if (isupper((unsigned char)asckey))
+		{
 			sdlkey.sym = SDLK_LSHIFT;
 			Keymap_KeyUp(&sdlkey);
 		}
@@ -896,6 +1122,41 @@
 	return SDL_GetKeyName(keycode);
 }
 
+static SDL_Keymod SDLKeymodFromName(const char *name) {
+	struct {
+		SDL_Keymod mod;
+		const char *name;
+	} const keymodNames[] = {
+		{ KMOD_NONE, "KMOD_NONE" },
+		{ KMOD_LSHIFT, "KMOD_LSHIFT" },
+		{ KMOD_RSHIFT, "KMOD_RSHIFT" },
+		{ KMOD_LCTRL, "KMOD_LCTRL" },
+		{ KMOD_RCTRL, "KMOD_RCTRL" },
+		{ KMOD_LALT, "KMOD_LALT" },
+		{ KMOD_RALT, "KMOD_RALT" },
+		{ KMOD_LGUI, "KMOD_LGUI" },
+		{ KMOD_RGUI, "KMOD_RGUI" },
+		{ KMOD_NUM, "KMOD_NUM" },
+		{ KMOD_CAPS, "KMOD_CAPS" },
+		{ KMOD_MODE, "KMOD_MODE" },
+		{ KMOD_CTRL, "KMOD_CTRL" },
+		{ KMOD_SHIFT, "KMOD_SHIFT" },
+		{ KMOD_ALT, "KMOD_ALT" },
+		{ KMOD_GUI, "KMOD_GUI" },
+		{ 0/*whatever*/, NULL }};
+
+	int i;
+	for (i = 0; keymodNames[i].name != NULL; i++)
+	{
+		if (strcmp(name, keymodNames[i].name) == 0)
+			return keymodNames[i].mod;
+	};
+
+	LOG_TRACE(TRACE_KEYMAP, "SDLKeymodFromName: Didn't find SDL_Keymod \"%s\", defaulting to KMOD_NONE.\n", name);
+
+	return KMOD_NONE;
+}
+
 #else	/* !WITH_SDL2 */
 
 static struct {
@@ -1152,7 +1413,7 @@
 {
 	int i;
 
-	if (!keycode)
+	if (keycode == 0)
 		return "";
 
 	for (i = 0; sdl_keytab[i].name != NULL; i++)


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