RFC: prototype implementation of physical key codes

So, at http://bugzilla.libsdl.org/show_bug.cgi?id=319 is the thing
I’ve been talking about for a while: a prototype implementation (for Mac
OS X (Quartz) and X11) of a keyboard handling feature that in my opinion
could replace both the “sym” field of the SDL_keysym structure
(SDL_Event.key.keysym) with the associated SDLK_* constants and its
"scancode" field. However, since I’ve gotten the impression that some
people are of different opinion, I’m proposing it as an addition rather
than a replacement.

It is implemented as a patch against SDL 1.2 (rev. 2741 from today, but
none of the affected files has changed after rev. 2549) because at the
time I started it, SDL 1.3 wasn’t functional enough for it, but the
intention is to get it into SDL 1.3 if people agree.

It adds three things to the SDL API:

  • A new field in the SDL_keysym (SDL_Event.key.keysym) structure:
    SDLPKey pkey.

  • An enumerated type SDLPKey in SDL_keysym.h with a set of constants for
    this field: e.g. SDL_PK_A, SDL_PK_RETURN, SDL_PK_LSHIFT.

  • A new function: const char * SDL_PKeyName(SDLPKey pkey), explained below.

Unlike the SDL_Event.key.keysym.sym field with its SDLK_* constants,
which refers to a key with a particular label, the
SDL_Event.key.keysym.pkey field with its SDL_PK_* constants refers to a
key at a specific physical position on the keyboard (hence the name
"physical key code"). E.g. SDL_PK_A means “the leftmost letter key in
the middle alphabetic row” (which is labeled “A” on a US keyboard).

The SDL_PKeyName function takes a physical key code and returns a
human-readable description for that key that ideally matches what’s
printed on the key cap on the user’s keyboard, i.e. it takes into
account the OS’ current key mapping settings.

One case where the usefulness of such a system should be obvious is
emulators (of devices with PC-like keyboards, e.g. Basilisk or Qemu or
PearPC) - what they usually did up to now was to work with the
platform-dependent “scancode” field and translate it to the emulated
scancodes they need using platform-specific lookup tables. Now SDL takes
the work of compiling these tables off them.

However, even in the case of games I think this system has its
advantages. First, when the keyboard is used as some sort of gamepad, it
seems much more natural to map the keys by their position than by their
label. E.g. SDL_PK_Z will always be below SDL_PK_A, while SDLK_z is only
below SDLK_a on a US keyboard, not on a French or German one. Second, I
find that SDL_PKeyName() works much better for presenting key names to
the user than SDL_KeyName() - e.g. when I press the “?” key on my Swiss
German keyboard, the latter will say something like “world 86”, while
the former says “?”.

(And, just for completeness, I should mention that the pkey field is
just as useless as the sym field for text input. To do text input, use
the unicode field or SDL 1.3’s text input events.)

So, I’d like to hear some feedback about this. In particular, three
questions:

  • Do you think such a feature would be useful to have in SDL 1.3?

  • Does it work for you? I’m particularly interested in keyboards other
    than the standard 104/105-key ISO/ANSI Mac and PC keyboards I have here.
    You can test it using the SDL/test/checkkeys.c which I’ve updated to
    also show the physical key codes and the names determined by
    SDL_PKeyName(). In addition, the pkeydemo.c attached to the bugzilla
    entry is a small example of how I expect this to be used in a game. Try
    it with different keyboard mapping settings and observe how (hopefully)

  1. the default keys stay in sensible positions (e.g. the “left” key will
    always be to the left of the “right” key) and 2. the keys are presented
    using their correct labels.
  • What do you think about the naming of functions, constants and
    everything? The names I picked are pretty arbitrary and I’m open to
    suggestions for better names.

What’s still missing:

  • Implementations for other backends, most importantly on Windows. I
    could try to do this, but we would probably have a result of better
    quality in less time if someone with more experience (and interest) in
    Windows programming than I would do it.

  • The X11 implementation needs X-server-specific lookup tables to work
    properly and falls back to mapping-by-keysym if none of the included
    ones fits. At the moment, only tables for Linux and Mac OS X are
    included (and the former is incomplete, too) - more will probably be needed.

And a few more notes:

  • When an unknown key is pressed or when the X server’s key codes are
    unknown, a message is printed to stderr. These pieces of code are
    bracketed by #if 1 … #endif and I’m unsure if they should be enabled
    in a release binary. Any opinions?

  • The names returned by SDL_PKeyName() (for keys like “return”, “space”,
    “command”) are English, not localized. I’m not sure if there’s a
    sensible way around this. For now, the burden of localizing them (at
    least a few known ones, since SDL_PKeyName() can return anything) is on
    the application developer.

  • In the Cocoa implementation, mostly out of laziness, I’m
    short-circuiting the complicated handling of modifier keys and instead
    handle them like the other keys, just in response to NSFlagsChanged
    events. It seems that the complicated system was partly there from the
    beginning, partly introduced in rev. 1241 to fix the problem of
    incorrect modifier states when switching to or away from the application
    with modifier keys pressed, and later expanded in rev. 1394. However,
    that problem doesn’t seem to resurface now with my simplified solution.
    This needs some more careful examination, but it seems possible to me
    that the complicated system could be removed entirely.

    -Christian

  • Do you think such a feature would be useful to have in SDL 1.3?

Yes! Definitely. Essential, even.

  • Does it work for you? I’m particularly interested in keyboards other
    than the standard 104/105-key ISO/ANSI Mac and PC keyboards I have here.

Linux (64-bit gentoo), X.org 7.0, norwegian keyboard w/some extra multi-
media and web buttons: Seems to work fine! It even picked up the correct
names of the extra keys.

The only “problem” I had was that it printed the names of the keys in
utf-8, while the term i ran it was in iso mode, but that’s not really
your fault =). Having the names in utf-8 makes sense.

Oh, and the caps lock and num lock keys only registered when enabling, not
disabling, but scroll lock worked like a regular key. I know SDL always
worked like this, but while we’re on the subject of keys anyway … First,
why doesn’t scroll lock work like the other lock keys (no light, even) ?
And second, it’d be very nice if these lock keys could (perhaps optionally)
be treated like regular keys. We’ve got the modifier information handy
anyway, so there’s no reason for them not to, is there ?

  • What do you think about the naming of functions, constants and
    everything? The names I picked are pretty arbitrary and I’m open to
    suggestions for better names.

If 1.3 will replace the current key functions with these, there’s no need
for the P, but otherwise I think the names you picked are fine.

  • GerryOn Sun, 03 Sep 2006 13:03:13 +0200, Christian Walther wrote:

Gerry JJ wrote:

  • Do you think such a feature would be useful to have in SDL 1.3?

Yes! Definitely. Essential, even.

Nice to hear! :slight_smile:

  • Does it work for you? I’m particularly interested in keyboards other
    than the standard 104/105-key ISO/ANSI Mac and PC keyboards I have here.

Linux (64-bit gentoo), X.org 7.0, norwegian keyboard w/some extra multi-
media and web buttons: Seems to work fine! It even picked up the correct
names of the extra keys.

Cool. I couldn’t test these as I don’t have such a keyboard, and their X
keycodes don’t seem to be very standardized - I gathered that info from
various places on the web, and only included those that most sources
agreed on.

Oh, and the caps lock and num lock keys only registered when enabling, not
disabling, but scroll lock worked like a regular key. I know SDL always
worked like this, but while we’re on the subject of keys anyway … First,
why doesn’t scroll lock work like the other lock keys (no light, even) ?
And second, it’d be very nice if these lock keys could (perhaps optionally)
be treated like regular keys. We’ve got the modifier information handy
anyway, so there’s no reason for them not to, is there ?

I don’t have any strong opinion on this. Making SDL’s locking optional
does sound sensible (e.g. for emulators, where the emulated OS already
does this), but I wouldn’t remove it entirely. One other thing to keep
in mind is that on Mac OS, the num lock key doesn’t work as “num lock"
but as “clear”, for which locking makes no sense (it’s also labeled
"clear” on Apple keyboards). What the purpose of the scroll lock key is
I don’t even know.

-Christian

I wrote:

Gerry JJ wrote:

Oh, and the caps lock and num lock keys only registered when enabling, not
disabling, but scroll lock worked like a regular key. I know SDL always
worked like this, but while we’re on the subject of keys anyway … First,
why doesn’t scroll lock work like the other lock keys (no light, even) ?
And second, it’d be very nice if these lock keys could (perhaps optionally)
be treated like regular keys. We’ve got the modifier information handy
anyway, so there’s no reason for them not to, is there ?

I don’t have any strong opinion on this. Making SDL’s locking optional
does sound sensible (e.g. for emulators, where the emulated OS already
does this), but I wouldn’t remove it entirely.

Wait, I just had an idea: This could be a use for the “locking caps
lock” etc. usage codes that are in the USB spec, but that I commented
out in my SDLPKey list. Like this:

  1. Press caps lock key (caps lock light goes on) - SDL sends a key down
    event with SDL_PK_LOCKINGCAPSLOCK and a key down event with SDL_PK_CAPSLOCK.
  2. Release caps lock key - SDL sends a key up event with SDL_PK_CAPSLOCK.
  3. Press caps lock key - SDL sends a key down event with SDL_PK_CAPSLOCK.
  4. Release caps lock key (light goes off) - SDL sends a key up event
    with SDL_PK_CAPSLOCK and a key up event with SDL_PK_LOCKINGCAPSLOCK.

(Or should the SDL_PK_LOCKINGCAPSLOCK up event come already at 3? That’s
how the actual caps lock state behaves here on Mac OS X, though the
light behaves as above. Weird.)

On the other hand, it might not even be possible to detect 2 and 3 (or 2
and 4) on some platforms. In fact, I think it isn’t in Cocoa (though it
may be in some lower level API).

-Christian

  1. Press caps lock key (caps lock light goes on) - SDL sends a key down
    event with SDL_PK_LOCKINGCAPSLOCK and a key down event with
    SDL_PK_CAPSLOCK.
  2. Release caps lock key - SDL sends a key up event with SDL_PK_CAPSLOCK.
  3. Press caps lock key - SDL sends a key down event with SDL_PK_CAPSLOCK.
  4. Release caps lock key (light goes off) - SDL sends a key up event
    with SDL_PK_CAPSLOCK and a key up event with SDL_PK_LOCKINGCAPSLOCK.

The problem is, such locking events are unreliable at best. Think of what
happens when you press Caps Lock (-> enabled) in your SDL app, then switch
focus to a different app and press it again (-> disabled). If you then
switch back to the SDL app and press Caps Lock again, you’ll get another
enable event, but you never got the disable! In this way, press and
release events can occur on their own – switching back and forth, you can
generate as many events as you wish in a row without any complementing
events in between. With the current behavior of the lock keys, this sort
of thing can easily happen accidentally.

I just tried this, and it is actually possible to do this with regular
keys as well. Holding a regular key and switching away from the SDL app
before releaseing it does generate a release event, but holding a key,
then switching to the SDL app and releasing it generates the release
event without any press event first. This is obviously less likely to
happen accidentally, but it could still lead to problems. Should it be
considered a bug in SDL, though ? The key did get released, it just
didn’t get pressed while the SDL app had focus. However, since SDL
generated a release event on switch-away, I think this isn’t intended
behavior. (In fact, pressing a key, switching away from the SDL app
and back, and then releasing it, generates two release events …)

Anyway, what I’d like to have is a way for the lock keys to generate
press/release events as if they were regular keys (thus making them more
reliable and available for non-locking (ab)uses; handy for emulators),
and also, optional led control! That is, making it possible to press
eg the Caps Lock key in the SDL app without affecting the state of the
Caps Lock led. This would obviously be optional.

While I’m at it, a way to directly control the leds on the keyboard would
be great (think disk drive lights in an emulator, etc) =)

Maybe something like this ?

SDL_SetLed(led, state);

led = SDL_KLED_CAPS_LOCK|SDL_KLED_NUM_LOCK|SDL_KLED_SCROLL_LOCK|…
state = SDL_ENABLE|SDL_DISABLE|SDL_AUTOMATIC|SDL_QUERY
(AUTOMATIC = state controlled by keyboard, default)

(Or should the SDL_PK_LOCKINGCAPSLOCK up event come already at 3? That’s
how the actual caps lock state behaves here on Mac OS X, though the
light behaves as above. Weird.)

On my keyboard, the leds go on at 1 and off at 3. Exactly what happens
probably depends on the keyboard.

On the other hand, it might not even be possible to detect 2 and 3 (or 2
and 4) on some platforms. In fact, I think it isn’t in Cocoa (though it
may be in some lower level API).

That would be a problem, yeah =(. Still, at least supporting this where
possible would be nice, I think.

  • GerryOn Tue, 05 Sep 2006 22:26:31 +0200, Christian Walther wrote:

Two weeks ago, I wrote:

So, I’d like to hear some feedback about this. In particular, three
questions:

  • Do you think such a feature would be useful to have in SDL 1.3?

OK, so far we have

1 vote for replacing the sym/SDLK_* system with physical key codes
(not counting mine)
0 votes for implementing it as an addition
0 votes for not adding it

Any more opinions?

(The issue seemed to have generated more interest in earlier threads:
http://thread.gmane.org/gmane.comp.lib.sdl/20846/focus=20860,
http://thread.gmane.org/gmane.comp.lib.sdl/24697/focus=24697,
http://thread.gmane.org/gmane.comp.lib.sdl/25706/focus=25747,
http://thread.gmane.org/gmane.comp.lib.sdl/26516/focus=26607,
http://thread.gmane.org/gmane.comp.lib.sdl/27300/focus=27327…)

-Christian

2006/9/17, Christian Walther :

Two weeks ago, I wrote:

So, I’d like to hear some feedback about this. In particular, three
questions:

  • Do you think such a feature would be useful to have in SDL 1.3?

OK, so far we have

1 vote for replacing the sym/SDLK_* system with physical key codes
(not counting mine)
0 votes for implementing it as an addition
0 votes for not adding it

Any more opinions?

(The issue seemed to have generated more interest in earlier threads:
http://thread.gmane.org/gmane.comp.lib.sdl/20846/focus=20860,
http://thread.gmane.org/gmane.comp.lib.sdl/24697/focus=24697,
http://thread.gmane.org/gmane.comp.lib.sdl/25706/focus=25747,
http://thread.gmane.org/gmane.comp.lib.sdl/26516/focus=26607,
http://thread.gmane.org/gmane.comp.lib.sdl/27300/focus=27327…)

  • 1 vote for implementing it as an addition–
    Roman Kyrylych (??? ???)