International keyboard support

Okay, the problem is that the keyboard essentially has 128 keycodes,
and all input is done by translating combinations of keys into 8-bit
or 16-bit characters.

I can return raw scan codes, and possibly modifiers states, or I can
return translated (by the OS) characters, but I don’t know of anything
in between, at least that will work on all operating systems. Hey,
MacOS guru types, is it possible to get raw scancode changes under MacOS?
Is it possible to get translated keys?

SDL would still probably have to have translation tables because
some environments don’t have any translation at all (raw console)

What would be most useful? Use raw scancodes? OS translated characters?
SDL specific scancode translation tables? A combination?
Raw scancodes don’t necessarily have any inherent meaning…

A rambling e-mail…
-Sam Lantinga (slouken at devolution.com)

Lead Programmer, Loki Entertainment Software–
Author of Simple DirectMedia Layer -
http://www.devolution.com/~slouken/SDL/

As I mentioned in my other message, here’s a quick snippet for reading
the raw keymap and translating into cooked characters on the MacOS.

Matt
-------------- next part --------------
/* Function Prototypes */
extern short GetPressedKeys(void);
extern long
SimpleKeyTrans(short keycode, long modifiers);

/* Magic formula to test a
bit in the keymap */
#define IsKeyPressed(km,kc) ((km[kc/8] & (1L <<
kc%8)) ? true : false)

/* Typical charcode constants */
#define
kLeftArrowChar 0x1C,
#define kRightArrowChar
0x1D,
#define kUpArrowChar 0x1E,
#define
kDownArrowChar 0x1F,
#define kPageUpChar
0x0B,
#define kPageDownChar 0x0C,
#define kHomeChar
0x01,
#define kEndChar 0x04,
#define
kEnterChar 0x03,
#define kReturnChar
0x0D,
#define kForwardDeleteChar 0x7F,
#define kDeleteChar
0x08,
#define kHelpChar
0x05,
#define kTabChar 0x09,

/* These keycodes
have non-unique charcodes */
#define kEscapeKey
0x35,
#define kClearKey 0x47,
#define kF1Key
0x7A,
#define kF2Key
0x78,
#define kF3Key 0x63,
#define
kF4Key 0x76,
#define kF5Key
0x60,
#define kF6Key 0x61,
#define
kF7Key 0x62,
#define kF8Key
0x64,
#define kF9Key 0x65,
#define
kF10Key 0x6D,
#define kF11Key
0x67,
#define kF12Key 0x6F,
#define
kF13Key 0x69,
#define kF14Key
0x6B,
#define kF15Key 0x71,

/*
Standard modifier keycodes */
#define kCommandKey
0x37,
#define kShiftKey 0x38,
#define kCapsLockKey
0x39,
#define kOptionKey
0x3A,
#define kControlKey 0x3B,

short
GetPressedKeys() {
long modifiers;
short
keycode; /* Virtual (raw) key code /
short
keychar; /
ASCII (cooked) character /
short
deadchar; /
Accent character (optional) */
short
count = 0;
long result;

/* GetKeys() is more
compatible, but reading lowmem is faster */
#if 1
unsigned char
km[16];
GetKeys((unsigned long *) km);
#else
unsigned char *km =
(unsigned char *) 0x0174;
#endif

/* Event manager constants to

describe which modifier keys are pressed */
modifiers = 0;
if
(IsKeyPressed(km, kShiftKey)) modifiers |= shiftKey;
if
(IsKeyPressed(km, kOptionKey)) modifiers |= optionKey;
if
(IsKeyPressed(km, kCommandKey)) modifiers |= cmdKey;
if
(IsKeyPressed(km, kControlKey)) modifiers |= controlKey;
if
(IsKeyPressed(km, kCapsLockKey)) modifiers |= alphaLock;

for(keycode = 0; keycode < 128 /* 16 bytes */ ; keycode++) {

if (IsKeyPressed(km, keycode)) {
		/* Pass the

keycode and modifers, as described in IM. */
result =
SimpleKeyTrans(keycode, modifiers);

/* Dissect the result into one or two characters */

if (result & 0xFFFF0000) {
			keychar =

(ktResult & 0xFFFF0000) >> 16;
deadchar = ktResult
& 0x0000FFFF;
}
else {

			keychar = ktResult & 0x0000FFFF;

		deadchar = 0;
			}


	/* We found another pressed key -- record it */

	count++;
		}
	}


return(count);
}

/*

[From THINK Reference]

The
’KCHR’ resource specifies the mapping of virtual key codes to
character
codes (for example, ASCII). Each installed script system has one
or more
’KCHR’ resources; there may be one or more for each language or
region to
suit the preference of the user. The resource ID for each 'KCHR’
resource
is in the script’s resource number range. The default 'KCHR’
resource for a
script is specified by the script’s ‘itlb’ resource.

Also
see IM: Toolbox Essentials, 2-110 to 2-111 and IM: Text, C-22 to
C24.

*/

/* A simple wrapper for KeyTrans() that doesn’t need a ‘KCHR’
*/
long SimpleKeyTrans(short keycode, long modifiers) {
static
unsigned long kchrState = 0;
static Ptr kchrPtr = NULL;
Ptr
kchrNew = NULL;
long result =
0;

/* The script manager allows access to its internal 'KCHR'

data */
kchrNew = (Ptr) GetScriptManagerVariable(smKCHRCache);
if
(kchrPtr != kchrNew) {
kchrPtr = kchrNew;
kchrState =
0;
}

/* Pass the combined keycode and modifers,

as described in IM. */
return(KeyTranslate(kchrPtr, keycode | modifiers,
&kchrState));
}

-------------- next part --------------

/* Matt Slot, Bitwise Operator * One box, two box, yellow box, blue box. *

Hey, MacOS guru types, is it possible to get raw scancode changes under
MacOS? Is it possible to get translated keys?

There are a couple ways to get keypresses under the MacOS:

The Event Manager returns a keyDown or autoKey event via WaitNextEvent().
The event record contains the raw keycode, modifier flags, and the
"cooked" ASCII character.

You can read the raw keymap (a 16-byte bitmap) via GetKeys(), or directly
from lomem at 0x0174. Many games do this.

InputSprocket is a “high level” interface for mapping keys/mouse/joystick
to semantic events: axis movement, action keys, etc. It’s the “new” way
to do things, but probably isn’t suitable for a low-level lib like SDL.

Write a raw ADB or USB driver, but don’t go there.

I’m sending some sample code for reading the keymap to Sam in a seperate
message.

What would be most useful? Use raw scancodes? OS translated characters?
SDL specific scancode translation tables? A combination?
Raw scancodes don’t necessarily have any inherent meaning…

The nice thing about scan codes is that they address the same physical key
across different mappings. On the Mac, the user can switch between QWERTY,
DVORAK, non-US, or even non-Roman mappings at almost any time – so you are
much better off tracking the keycodes, and using translated characters for
display purposes only.

One last note, most Mac keyboards don’t distinguish between right and left
modifier keys.

Matt

/* Matt Slot, Bitwise Operator * One box, two box, yellow box, blue box. *

What would be most useful? Use raw scancodes? OS translated characters?
SDL specific scancode translation tables? A combination?
Raw scancodes don’t necessarily have any inherent meaning…

In my own programs, I use the following keyboard codes:

0x00000000 - 0x0000ffff ASCII codes, with shift keys processed
0x00010000 - 0x0001ffff codes for other keys (cursor, function, shift,
ctrl)

the other 15 bits are qualifier flags:

const sU32 sKEYQ_CAPS = 0x00020000; // caps lock
const sU32 sKEYQ_SHIFTL = 0x00040000; // left shift
const sU32 sKEYQ_SHIFTR = 0x00080000; // right shift
const sU32 sKEYQ_CTRLL = 0x00100000; // left ctrl key
const sU32 sKEYQ_CTRLR = 0x00200000; // right ctrl key
const sU32 sKEYQ_ALT = 0x00400000; // left alt key
const sU32 sKEYQ_ALTGR = 0x00800000; // right alt key
const sU32 sKEYQ_SHIFT = 0x01000000; // any shift key
const sU32 sKEYQ_CTRL = 0x02000000; // any ctrl key
const sU32 sKEYQ_NUM = 0x10000000; // numeric key pad
const sU32 sKEYQ_REPEAT = 0x20000000; // repeated key
const sU32 sKEYQ_NUMLOCK = 0x40000000; // num lock status
const sU32 sKEYQ_BREAK = 0x80000000; // this is a break code

keys on the numeric keypad send normal key-codes, but have sKEYQ_NUM set.
sKEYQ_BREAK is used to tell the difference between make and break codes.
When a key is pressed longer, repeated make-keycodes are generated, and to
tell the difference between pressing a key and repeating a key the
sKEYQ_REPEAT flag is set.

The good things are:

  • everything is in one 32 bit unsinged int
  • easily extendet to unicode
  • since shift keys are already processed, easy to use for text input
  • mask with 0x8001ffff when using for text input
  • mask with 0xb001ffff when using for game input (repeat keys ignored,
    numeric keys are seperate)
  • mask with 0x8341ffff when using for hotkeys (shift, ctrl, alt processing)

the bad things:

  • since shift keys are already processed, bad to use for gaming

Perhaps this could be refined by doing the shift-key processing as a
seperate function.

I have some code for dos and windows that you may rip, but it is no more
than a big conversion table, shift/alt/repeat processing and a function
that converts keycodes to ascii strings.

Dierk Ohlerich
@Dierk_Ohlerich

Matt, thanks for the info. :slight_smile:

How about this…

Keyboard events contain the following items:
struct keyevent {
Uint16 special; /* Virtual key, such as LEFT or SHIFT /
Uint16 unicode; /
Unicode translation of current key */
};

The special and unicode members would be mutually exclusive - i.e. if the
key was a special key, the character translation would be 0, and if the key
mapped to a UNICODE character, then it wouldn’t be a special modifier key.

The advantages of this:

  • People could input using purely non-latin keyboards, Kanji, for example.
    The arrow keys would work, and the keys pressed would be the native
    characters.
  • Programs designed to handle UNICODE output would be able to display it,
    and programs limited to the ASCII or Latin-1 character sets would be
    able to display those characters, since UNICODE is a superset of both.

The disadvantages of this:

  • There is no ‘q’ scancode. If you wrote a game that used Ctrl for shoot,
    and q for shield (for example), then when you hit Ctrl and Q and the same
    time, the unicode key would be Ctrl-Q.
    What is wierd is that the user may indeed have the keyboard set up so
    there is no ‘q’ key. The keyboard may be mapped so that what would be
    the ‘q’ key is actually a thai symbol, XK_Thai_dochada, but when capslock
    is on, the keys map into the normal US keyboard layout.
    Scancodes are keyboard dependent as well, so even if I returned the raw
    scancode, the ‘q’ scancode on an IBM keyboard may well be different from
    that of a Mac, or a Sparc, or a PC98 keyboard.

Ideas? Thoughts?

-Sam Lantinga				(slouken at devolution.com)

Lead Programmer, Loki Entertainment Software–
Author of Simple DirectMedia Layer -
http://www.devolution.com/~slouken/SDL/

  • There is no ‘q’ scancode. If you wrote a game that used Ctrl for shoot,
    and q for shield (for example), then when you hit Ctrl and Q and the same
    time, the unicode key would be Ctrl-Q.

During gameplay, the game probably shouldn’t worry about character input –
people tend to choose controls that are conveniently grouped. Set defaults
to metakeys escape, caps lock, arrow, and number keys, because using letters
like “q for quit” is language and key-mapping dependent.

For input, then, just use the processed Unicode characters – such as high
scores or displaying key preferences. In this case, you don’t need to watch
for scancodes, just they escape key.

Scancodes are keyboard dependent as well, so even if I returned the raw
scancode, the ‘q’ scancode on an IBM keyboard may well be different from
that of a Mac, or a Sparc, or a PC98 keyboard.

This looks like a case for abstraction. Create a lookup table for each
platform or keyboard, then examine the configuration and pick the right one.
Instead of storing raw scancodes, you’d use SDL #defined constants across
platform to reference specific keys (and look up names from resources).

Uint16 KeyMap_IBM = { 0x0000, 0x0001, 0x0002, … };
Uint16 KeyMap_Sparc = { 0x0003, 0x0002, 0x0003, … };
Uint16 * KeyMap = NULL; /* Assigned in initialize() */

#define kSDL_LeftArrow 0
#define kSDL_RightArrow 1
#define kSDL_InvalidKey -1

Uint16 leftArrowCode = KeyMap[kSDL_LeftArrow];

If you really wanted to map specific characters to controls, then you’d
have to write a function or table for the reverse lookup – assuming the
platform can do keycode translation without generating an event:

Uint16 CharMap[256]; /* ASCII char map */

for(myKey=0; myKey<numKeys; myKey++) {
myChar = TranslateCharacter(myKey, CAPSLOCK);
if (myChar < sizeof(CharMap)) CharMap[myChar] = myKey;
}

Fake the CapsLock modifier so all characters are in upper case but numbers
and puncuation aren’t shifted. Also keep in mind that on some keyboards, the
numbers are actually shifted-characters on the main keyboard. Finally, the
game would have to accept that a given character may not be there.

Matt

/* Matt Slot, Bitwise Operator * One box, two box, yellow box, blue box. *

Just a thought…

Why not store it in a signed int of some sort. Then, set the negative bit if
it’s a special character.

That way, processing of the information would be faster, and won’t cause much
slowdown in those apps that like to process these things fast.

Even if that meant pusing it over to a 32bit Integer to have it big enough, it
would still be the same size in RAM.

As I said, just a thought.On Thu, May 06, 1999 at 10:59:44AM -0700, Sam Lantinga wrote:

Matt, thanks for the info. :slight_smile:

How about this…

Keyboard events contain the following items:
struct keyevent {
Uint16 special; /* Virtual key, such as LEFT or SHIFT /
Uint16 unicode; /
Unicode translation of current key */
};


– Michael Samuel

Okay, I have a solution which seems to work for all Latin-1 keyboards,
and can possibly be extended to other languages as well.

Note that this is subject to change - particularly that there may be
an API to enable/disable UNICODE key translation.

First, the list of SDL keysyms has been extended to include 95 general keys
which can be found on international keyboards.

Second, the character translation has been moved into the OS-specific
routines, and SDL_SymToASCII() has been replaced with SDL_GetKeyName().
This means that you can no longer translate a key sym into an ASCII value.
The key event is translated into UNICODE at event-time by the OS, if
UNICODE translation is enabled.

/* Keysym structure

  • The scancode is hardware dependent, and should not be used by general
    applications. If no hardware scancode is available, it will be 0.

  • The ‘unicode’ translated character is only available when character
    translation is enabled by the SDL_EnableUNICODE() API. If non-zero,
    this is a UNICODE character corresponding to the keypress. If the
    high 9 bits of the character are 0, then this maps to the equivalent
    ASCII character:
    char ch;
    if ( (keysym.unicode & 0xFF80) == 0 ) {
    ch = keysym.unicode & 0x7F;
    } else {
    An international character…
    }
    /
    typedef struct {
    Uint8 scancode; /
    hardware specific scancode /
    SDLKey sym; /
    SDL virtual keysym /
    SDLMod mod; /
    current key modifiers /
    Uint16 unicode; /
    translated character */
    } SDL_keysym;

The rest of the SDL keysyms and modifiers are unchanged, so most code
will work unchanged with the new API.

TODO:
Implement real UNICODE translation under X11 (not Latin-1)
Add API to enable/disable UNICODE translation (default to off?)
Add Win32 and BeOS (and MacOS?) implementations of the API.

The current code is available in the latest CVS snapshot.

Note: Since the set of SDL virtual scancodes is fairly complete, I may
drop the hardware scancode from the SDL_keysym structure.

-Sam Lantinga				(slouken at devolution.com)

Lead Programmer, Loki Entertainment Software–
Author of Simple DirectMedia Layer -
http://www.devolution.com/~slouken/SDL/

“Sam” == Sam Lantinga writes:

Sam> Note: Since the set of SDL virtual scancodes is fairly
Sam> complete, I may drop the hardware scancode from the
Sam> SDL_keysym structure.

NOTE: this is an incredibly bad way to think. The extra overhead of
passing the hardware scancode is incredibly tiny. If you make it easy
for people to use the SDL virtual scancodes, then they’ll have no need
to use the hardware scancode, but unless you can guarantee that,
even when things change in ways you can’t anticipate, the virtual
scancodes will encompass everything that the hardware scancode does,
then getting rid of the hardware scancode is an incredibly bad idea.

Sam> 	-Sam Lantinga (slouken at devolution.com)

Sam> Lead Programmer, Loki Entertainment Software -- Author of
Sam> Simple DirectMedia Layer -
Sam> http://www.devolution.com/~slouken/SDL/ --

–Cliff
@Clifford_T_Matthews

“Sam” == Sam Lantinga writes:

Sam> Note: Since the set of SDL virtual scancodes is fairly
Sam> complete, I may drop the hardware scancode from the
Sam> SDL_keysym structure.

NOTE: this is an incredibly bad way to think. The extra overhead of
passing the hardware scancode is incredibly tiny. If you make it easy
for people to use the SDL virtual scancodes, then they’ll have no need
to use the hardware scancode, but unless you can guarantee that,
even when things change in ways you can’t anticipate, the virtual
scancodes will encompass everything that the hardware scancode does,
then getting rid of the hardware scancode is an incredibly bad idea.

Okay, well it’s there. :slight_smile:
It’s probably a good idea for keys that aren’t mapped by the virtual keys,
like some dead diacriticals.

-Sam Lantinga				(slouken at devolution.com)

Lead Programmer, Loki Entertainment Software–
Author of Simple DirectMedia Layer -
http://www.devolution.com/~slouken/SDL/