Managing user inputs with SDL

Important note: this file that gathers some topic-specific knowledge from the SDL mailing list has not been integrated in our SDL documentation corner yet, as OpenGL or audio topics already are. If you want to help us doing so, feel free to tell us. Thanks!

All the pieces of information described here come from the SDL mailing list. Numerous SDL experts gave useful pieces of advice that we tried to collect thematically through the years. We deem these hints useful, and we hope not too many errors survived.
Many thanks to all the members of the SDL list for this shared knowledge!

Touchscreen

Elo Touch Screen

The Elo Touch Screen is supported by SDL from version 1.1.8. All you need to do is to indicate to the SDL engine that you want to use the Elo touch system with frame buffer console (not X). To do that we have the SDL_putenv method:

    SDL_putenv("SDL_VIDEODRIVER=fbcon"); //We use Frame Buffer Console which support elotouch and don't use X
    SDL_putenv("SDL_MOUSEDRV=ELO");//We use elo touch screen as mouse
    SDL_putenv("SDL_MOUSEDEV=/dev/ttyS0");//elo touch screen is plugged on COM1

Troubleshooting

Touchscreen moving randomly in fullscreen mode
The issue might be that the screen resolution set in the xorg.conf file does not match what was set to the fullscreen parameters. Adjust it and restart X to try it.
Touchscreen cursor going crazy
MS Windows on tablet PCs had this problem. It can happen when using the DirectX driver. Fall back to WinDIB driver to get it working properly. Decreasing the acceleration of the video driver to disable DirectDraw acceleration also fixes the problem.



The touch screen on most winCE devices maps as a mouse.  beware that the mouse postition is valid only while the 'button is pushed'.  The direction pad maps as arrow keys.  

The directional pad on a PocketPC device is exposed in SDL as keyboard 
keys, even though it's not really a full keyboard, and never as a joystick.


You cannot catch the mousewheel with GetMouseState, since they do not stay 
pushed down. You have to use the event queue, and look for SDL_MOUSEDOWN 
events for these buttons.


I was wondering what the difference is between:

+ using SDL_GetKeyState to read the current keyb state and
+ using the SDL_KEYDOWN/UP events to handle key presses


They have different purposes - use whichever fits your need.

If the question is, "Has the user pressed key x since I last checked?", 
use events. (Don't use SDL_GetKeyState because you might miss short key 
presses.)

If the question is, "Is the x key pressed right now?", use 
SDL_GetKeyState. (You can keep your own array of key states and update 
it using events, as you seem to be doing now, but why reinvent the wheel?)

If I'm reading the source right, you only need to call SDL_GetKeyState 
once, and the pointer you get from it will stay valid forever (it's a 
static variable in SDL_keyboard.c). (The documentation was not clear 
about this - I just corrected it.) SDL is updating this array all the 
time anyway, you don't lose any performance by reading it.




This article may be helpful in clearing up your confusion about utf-6
and utf-16:
http://www.joelonsoftware.com/articles/Unicode.html

But two questions about UTF-8:

>>
>> 1. How do i know if the next character uses 1 Byte, 2 Bytes
>> or more?



Here's a snippet from Tux Paint that might help:

/* For each UTF8 character: */

for (j = 0; j < utf8_str_len; j++)
  {
    /* How many bytes does this character need? */

    if (utf8_str[j] < 128)  /* 0xxx xxxx - 1 byte */
      {
        utf8_char[0] = utf8_str[j];
        utf8_char[1] = '\0';
      }
    else if ((utf8_str[j] & 0xE0) == 0xC0)  /* 110x xxxx - 2 bytes */
      {
        utf8_char[0] = utf8_str[j];
        utf8_char[1] = utf8_str[j + 1];
        utf8_char[2] = '\0';
        j = j + 1;
      }
    else if ((utf8_str[j] & 0xF0) == 0xE0)  /* 1110 xxxx - 3 bytes */
      {
        utf8_char[0] = utf8_str[j];
        utf8_char[1] = utf8_str[j + 1];
        utf8_char[2] = utf8_str[j + 2];
        utf8_char[3] = '\0';
        j = j + 2;
      }
    else  /* 1111 0xxx - 4 bytes */
      {
        utf8_char[0] = utf8_str[j];
        utf8_char[1] = utf8_str[j + 1];
        utf8_char[2] = utf8_str[j + 2];
        utf8_char[3] = utf8_str[j + 3];
        utf8_char[4] = '\0';
        j = j + 3;
      }
...

This is probably good reading:  http://en.wikipedia.org/wiki/Utf-8

What marks the end of my textline?

Utf-8 is compatible with null-terminated strings.  (The byte 0x00 refers
  to codepoint 0, which is equivalent to the ASCII character '\0'.)
When there are 2 Bytes do i still have to swap

>> for different endianess?


No.


The documentation of SDL_CreateRGBSurface states,

that SDLSetVideoMode has to be called before.

What if i want to only manipulate a surface without displaying it?
If i call SDL_SetVideoMode a display surface will always be shown.

You can use the Dummy Video Driver.


how do I get joystick input without creating a window? Try the dummy video
driver


 I was wondering if there is a way to get the ASCII code of an 
aggregate key event, for example shift+'a' to get 'A'.

SDL_EnableUNICODE (1);   in your init stuff
Then use the `unicode' field in the keysym. This does not solve the numeric keypad issue, as it doesn't produce unicode.
Another option is to do the conversions yourself:

--

int get_char(SDL_keysym sym)
{
       int c;

       // Do not want the modifier keys by themselves
       if (sym.sym >= SDLK_NUMLOCK && sym.sym <= SDLK_COMPOSE) {
               return (0);
       }

       c = sym.sym;

       if ((sym.mod & KMOD_LSHIFT) || (sym.mod & KMOD_RSHIFT)
           || (sym.mod & KMOD_CAPS)) {

               // Get upper case
               if (c >= SDLK_a && c <= SDLK_z) {
                       c = c - 32;
               } else if (c == SDLK_BACKQUOTE) {
                       c = '~';
               } else if (c == SDLK_1) {
                       c = SDLK_EXCLAIM;
               } else if (c == SDLK_2) {
                       c = SDLK_AT;
               } else if (c == SDLK_3) {
                       c = SDLK_HASH;
               } else if (c == SDLK_4) {
                       c = SDLK_DOLLAR;
               } else if (c == SDLK_5) {
                       c = '%';
               } else if (c == SDLK_6) {
                       c = SDLK_CARET;
               } else if (c == SDLK_7) {
                       c = SDLK_AMPERSAND;
               } else if (c == SDLK_8) {
                       c = SDLK_ASTERISK;
               } else if (c == SDLK_9) {
                       c = SDLK_LEFTPAREN;
               } else if (c == SDLK_0) {
                       c = SDLK_RIGHTPAREN;
               } else if (c == SDLK_MINUS) {
                       c = SDLK_UNDERSCORE;
               } else if (c == SDLK_EQUALS) {
                       c = SDLK_PLUS;
               } else if (c == SDLK_LEFTBRACKET) {
                       c = '{';
               } else if (c == SDLK_RIGHTBRACKET) {
                       c = '}';
               } else if (c == SDLK_SEMICOLON) {
                       c = SDLK_COLON;
               } else if (c == SDLK_QUOTE) {
                       c = SDLK_QUOTEDBL;
               } else if (c == SDLK_COMMA) {
                       c = SDLK_LESS;
               } else if (c == SDLK_PERIOD) {
                       c = SDLK_GREATER;
               } else if (c == SDLK_SLASH) {
                       c = SDLK_QUESTION;
               } else if (c == SDLK_BACKSLASH) {
                       c = '|';
               }
       }
       // control keys
       if ((sym.mod & KMOD_LCTRL) || (sym.mod & KMOD_RCTRL)) {
               if (c >= SDLK_a && c <= SDLK_z) {
                       c = c - 96;
               }
       }
       // numeric key pad
       if (c == SDLK_KP_DIVIDE) {
               c = SDLK_SLASH;
       } else if (c == SDLK_KP_MULTIPLY) {
               c = SDLK_ASTERISK;
       } else if (c == SDLK_KP_MINUS) {
               c = SDLK_MINUS;
       } else if (c == SDLK_KP_PLUS) {
               c = SDLK_PLUS;
       } else if (c == SDLK_KP_ENTER) {
               c = SDLK_RETURN;
       }

       if (sym.mod & KMOD_NUM) {
               if (c == SDLK_KP0) {
                       c = SDLK_0;
               } else if (c == SDLK_KP1) {
                       c = SDLK_1;
               } else if (c == SDLK_KP2) {
                       c = SDLK_2;
               } else if (c == SDLK_KP3) {
                       c = SDLK_3;
               } else if (c == SDLK_KP4) {
                       c = SDLK_4;
               } else if (c == SDLK_KP5) {
                       c = SDLK_5;
               } else if (c == SDLK_KP6) {
                       c = SDLK_6;
               } else if (c == SDLK_KP7) {
                       c = SDLK_7;
               } else if (c == SDLK_KP8) {
                       c = SDLK_8;
               } else if (c == SDLK_KP9) {
                       c = SDLK_9;
               } else if (c == SDLK_KP_PERIOD) {
                       c = SDLK_PERIOD;
               }
       } else {
               if (c == SDLK_KP0) {
                       c = SDLK_INSERT;
               } else if (c == SDLK_KP1) {
                       c = SDLK_END;
               } else if (c == SDLK_KP2) {
                       c = SDLK_DOWN;
               } else if (c == SDLK_KP3) {
                       c = SDLK_PAGEDOWN;
               } else if (c == SDLK_KP4) {
                       c = SDLK_LEFT;
               } else if (c == SDLK_KP5) {
                       c = 0;
               } else if (c == SDLK_KP6) {
                       c = SDLK_RIGHT;
               } else if (c == SDLK_KP7) {
                       c = SDLK_HOME;
               } else if (c == SDLK_KP8) {
                       c = SDLK_UP;
               } else if (c == SDLK_KP9) {
                       c = SDLK_PAGEUP;
               } else if (c == SDLK_KP_PERIOD) {
                       c = SDLK_DELETE;
               }
       }

       return (c);
}




How to collect all the pending keypresses and avoiding to 
have to discard all the mouse events? This is needed 
in any FPS game (DooM, Quake etc..) where the user uses 
both the keyboard and the mouse at the same time.

EG:

    while (SDL_PollEvent(&event))
        root_sdl_event_filter(&event);

with

void root_sdl_event_filter(const SDL_Event *event)
{
    switch (event->type)
    {
        case SDL_KEYUP:
        case SDL_KEYDOWN:
            return(sdl_key_filter(event));
        case SDL_MOUSEMOTION:
            break;
      ...
    }

    return;
} 

The above code makes the while() stuck as long as the user 
keeps moving the mouse thus introducing a jerky movement in 
the game - That's one of the reasons why Icculus' port of Duke 
Nukem 3d has a Jerky mouse behavior.

So I tryed to use a 

SDL_PumpEvents(); 
SDL_GetMouseState(...);

before doing a 

while (SDL_PollEvent(&event)) 
   root_sdl_event_filter(&event); 

because only the mouse position at the time we probe 
is actually needed for FPS games. But SDL_GetMouseState 
doesn't seem to remove the move mouse events at all, so 
it's pointless and the while(...) will remain very laggy.

I also had this issue. I solved it by telling SDL to ignore mouse
movement events:

SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);

Then I polled for mouse movement per frame with SDL_GetMouseState()

For an FPS you might be best reading key input this way too, since as
multiple keypresses will no doubt be needed. It would be far more
efficient to dump keystate into an array and check buttons in there
than process messages.


Apparently SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE) doesn't discard the
events for "SDL_PumpEvents(); SDL_GetMouseState();" but hide them from
"SDL_PollEvent(&event)"


  
What is the supposed meaning of the SDL_Event.key.keysym.sym field and the corresponding SDLK_* constants? "the key that's labeled 'Z'", i.e. a reference to a key that generates a specific character

The best solution is to map to the unshifted code for the current keyboard. Unfortunately, this leaves SDL's keysym set lacking, and this can't be addressed until a major API change (1.3).




I'm trying to run a SDL application in an embedded enviroment,
where the X-server has no window manager. The mouse and video are working properly but I have a problem: I don't get the keyboard events in my app

when using X, the keyboard
handler is built into the X-server.  It's possible that your X isn't
loading the required server to handle the input.
  
  
Now I verify the event 's type
 if (event.type==SDL_MOUSEMOTION)
before SDL_GetMouseState and it works fine.


Anyway, X11 may support rgba cursors. Take a look at
http://www.die.net/doc/linux/man/man3/xcursor.3.html or google for "man
page xcursor" for more info

it's the hardware that draws the cursor as an overlay, so the cursor rendering is free. But the main advantage is that it makes the cursor a lot more responsive since it is refreshed more often, and thus gameplay is way smoother.

I have just a little problem with the mouse in Fullscreen, the cursor stays at the left top corner. If I move the mouse the 
SDL_MOUSEMOTION event is generated, but the cursor doesn't move.
In fullscreen normally the mouse doesn't show (at least that is what happens here)

Also I use:

SDL_ShowCursor(SDL_DISABLE);
SDL_WM_GrabInput(SDL_GRAB_ON);

to "grab" the mouse-input (even in non-fullscreen the visual cursor is 
turned off and it's input is always mine, as
long as my application has focus.

And to release it:

SDL_ShowCursor(SDL_ENABLE);
SDL_WM_GrabInput(SDL_GRAB_OFF);

Using the cursor as normal in fullscreen mode would sort of conflicts with the full-screen concept  because it's cursor is controlled and drawn by the window-manager..

 
force feedback: for linux there is libff

  
The SDLK_* symbols are to be considered portable "scancodes", and have nothing much to do with ASCII. Each symbol is supposed to represent a physical key on the keyboard, pretty much disregarding whatever might be printed on the keycap. Use these when you want a "mega gamepad" sort of input device.

Use the UNICODE stuff if you want the ASCII (or rather, UNICODE) representations of keystrokes when available. (In a normal layout, only key down events generate UNICODE, and some keys are primarilly 
used to alter the keyboard state, affecting other keys; ie "dead keys".)


When about 3 keyboard keys are keeped pressed, it is impossible to press more keys (the presses are ignored).

It can be used to cheat in games in which several people play on the same keyboard .

It is mainly a hardware problem. Keyboards are wired like a big array,and pressing a key gives a row and a column (approximately). So if you press more than 2-3 keys, you have many more connections done, and the hardware don't send all the keys pressed.

http://www.sjbaker.org/steve/omniv/keyboards_are_evil.html

To make things even worse, keyboards deal with ghost keys differently. Some very cheap and simple ones just ignore the problem and throw any "detected" keys, including ghosts, at the PC. Others try to hide the problem by filtering out some or all potential ghosts in combinations that are known to produce ghosts.

Solution: Use a joystick! This is what they are for. And SDL support both analog and digital joysticks. Only force feedback (and maybe rumble?) is missing.

I have two USB mouses connected to my linux box. Is there a way to check inside a SDL application which mouse was pressed? For me it looks like even in the X11 core routines there is no way to figure out which mouse was pressed?


Chances are, you're using /dev/input/mice, which blends all USB mouse 
input into one logical device (partially so that mouse input in the X 
server continues to work as you plug and unplug mice).




Input handling in general

To fully support 4 people playing on the same system, one will probably have to support joystick input.

Why using SDL_PollEvent() to deal with events, instead of using SDL_GetKeyState() and SDL_GetMouseState()?

Basically, SDL_Get*State() gives you the current state after all events have been processed, so if a key or button has been pressed and released before you process events, then the pressed state will never show up in the SDL_Get*State calls.

Since most operating systems handle keyboard and mouse input in an input queue, and you only have a snapshot of the keyboard state at any given time, the only way to catch keyboard state changes as they occur is to process events and handle them appropriately.

[Back to the table of contents]



Keyboard handling

Raw and text input

Handling keyboard for a game control (raw input) and a GUI (text input) are two different things.

In a game context, whenever a key is pressed, you store in a array the fact the key is down, so you will react accordingly in your input handling code as long as it is not released. For example, if left is down, you will move player some pixels to the left. When the key is released, you clear its state. That's what you are doing in your code.

In a GUI context, whenever a key is pressed, you react to that event. If left is pressed, you move cursor one char to the left. That's it. You do not need to store anything or even handle SDL_KEYUP events. Of course, you need to use SDL_EnableUNICODE() to get usuable infos. You will also use SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL), to properly handle key repeat. Only key presses and repeats will be handled, not key releases in this context.

So, you need to switch from one mode to the other depending on the context. For example when you see in your main game loop that user pressed, say, F12, then you clear all key pressed, set a flag saying you are in GUI mode, and draw your dialog box. Depending on the type of game (realtime or not), you will go to another loop (in which case the games is paused) or continue in the main one, but handling keyboard differently since you are in GUI mode.

The way SDL handles its key state is when you pump the event loop, as it queues each key event, it will update its internal keyboard state array.

How do I create a code that lets users of different keyboard mappings enter those characters their way?
Enable UNICODE input and use the unicode field instead. Use SDLK_* only when you want to use the keyboard as an oversized gamepad.

Simultaneous key presses

Many keyboards cannot handle even 3 keys pressed simultaneously, depending on where they are on the keyboard. This is a hardware limitation, due to the circuitry on the keyboard.

You can find a normal PS2 keyboard that passes well multiple keypress events. For example, old DELL keyboards (model AT102W heavy, indestructible) do it well, but newer multimedia Logitech (Y-SQ33) and many others cannot do.

Converting a keypress into the actual character displayed on it

Or: why neither of the two classical ways of handling keypresses are satisfactory for games

SDL provides Unicode translation for key-down events. Look at the 'unicode' field of the keysym structure. In order for it to be valid, though, you must first enable Unicode translation with an SDL_EnableUNICODE(1) call. The release events do not get translated (more infos: SDL Unicode FAQ).

Let's suppose I want to program a game where the keys that are covered with the '[' and ']' characters on an american keyboard should have a certain meaning. Now, I can interpret the scancode field of SDL_keysym struct, but this would make my program platform dependent (I would have to "#ifdef __WIN32__" etc.).

Or I can use the sym field, which provide platform independent keys, but this makes my program dependent on the selected "locale" of the user, i.e. a german user would have to press AltGr+'8' or AltGr+'9' for the '[' and ']' keys respectively. Of course the later option is completely useless if the AltGr key had also a certain meaning.

The situation gets even worse when I want to assign meta-keys or dead keys (ex: on some keyboard layouts, the spacebar is a modifier key). Here I can only fallback to the scancode solution.

It is not that the keyssyms are really bad, they are perfectly suitable for text input-lines etc, where the user expects his current locale, but useless for "raw" keyboard accesses.

The solution: customization by the user

You could allow for keys to be customized, or use different keymaps depending on the locale. But this problem has little or nothing to do with the language / country / locale of the user, on a laptop plenty of keys that exist on a full keyboard are missing, or require holding the dreaded Function key. This is generally not suitable for games, but most tend to let me customize my controls, so it is not a problem any longer, you should probably do what they are doing.

While customization is in general a good idea, it does not really help me in this topic. I woud still be forced to use scancodes instead of the keysyms, since not all keys are reachable via the keysyms. For example, on my current locale, both the ESC and the '[' key map to escape. While this is good for programming / writing text, I would prefer a raw keyboard when playing games.

[Back to the table of contents]



Mouse handling

Event frequency

Your average CPU executes some 50 million instructions between each event from a good, properly configured mouse. That's 600 000 printed pages of assembler code, or somewhere around 100 000 printed pages of some high level language.

Handling double click

When you get a single click, just:


now = SDL_GetTicks() ;

if last_time - now < some number
  the user did a double click
else
  last_time = now

To act nicely, also check if the mouse has moved substantially, or a too low interval between clicks, to avoid accidental double clicks.

You should ideally make the interval user-configurable, and possibly include some conditionally compiled OS-specific code to get system-wide settings.

Cursor

In win32, if I run fullscreen, the windows cursor disappears, while windowed applications keep the cursor. Does this act the same in other OS's? Or should I be using SDL_ShowCursor() anyway?

This happens if you are writing directly to video memory (see SDL FAQ). You can disable system cursor, create a sprite and blit it where the cursor is. It allows both color and total control

You can have SDL use the X11 cursor, which is not bound to your game's framerate, but this is specific to the application and not SDL. Many games hide the system (X11) cursor and render a textured quad in-game for the mouse cursor.

[Back to the table of contents]



Joystick handling

The SDL joystick code is factored out of the rest of the event code. It is built in such a way that it is possible to get joystick events even when you cannot get any other type of events.

As joysticks are often special cases, they are treated as such. For example, if the X server is properly configured using the X input extension, all input devices are handled by the server and they all come to your application over the connection to the server and are all tied tightly to the video systems. You cannot get them unless you have a window. But X servers are rarely configured to handle joysticks correctly, so they have to be handled by a polling loop.

Windows does things differently. But, generally the joysticks are either tied closely to the video system, or they are not at all.

So, what do you do if you want to have a portable consistent way of handling joystick input? You make it all look like events, even though it may be polled. It is easy to make a polling loop push events on an event queue.

[Back to the table of contents]





Please react!

If you have information more detailed or more recent than those presented in this document, if you noticed errors, neglects or points insufficiently discussed, drop us a line!




[Top]

Last update: Monday, March 9, 2009