SDL2 get keymapped / "cooked" code for a keypress event? - sdl

I'm trying to "cook" a modifier + keycode as received from SDL_KEYDOWN events, into what the combination represents in the current OS keymap.
In SDL1, this was available in the keysym.unicode field, but this is gone in SDL2.
Instead, you're supposed to listen to SDL_TEXTINPUT events for this, which does work, but the problem is they come after the corresponding SDL_KEYDOWN events, so if you have for example a hotkey that you use and want to react to, which could need to be a part of a composite key, you get into trouble.
More specifically, I'm trying to react to the '/' press, in the current keymap, to open an admin command box in a game. But on my keymap, / is reached with Shift+7, and I'm already using 7 as a hotkey for other stuff and don't want to trig that other stuff in the Shift+7 case (but I can't know from the game that it's the particular Shift+7 which represents '/'!)
In SDL2, I get these events in this order:
SDL_KEYDOWN for L_SHIFT
SDL_KEYDOWN for 7
SDL_TEXTINPUT for '/'
One workaround I was going to ask about is if there was a way to "cook" the "L_SHIFT + '7'" combination into '/' myself, this way I could check this immediately on the SDL_KEYDOWN for 7, before waiting for an SDL_TEXTINPUT event.
In SDL1 on the other hand, I would get:
SDL_KEYDOWN for L_SHIFT
SDL_KEYDOWN for '7' with keysym.unicode field set to '/'
which worked fine (and also worked then for keymaps that had '/' on a dedicated key).

Related

Chromium Embedded Framework bind key press

I saw this thread on the official forum of Chromium Embedded Framework but it seems that there were no solutions given. To be honest, I'm not comfortable with C++ platform. Could you help me provide a snippet that binds CEF to the webapp.
I wanted to control the application using the default controls:
ALT+F4 - close
F5 - refresh browser
Short Version: Implement CefKeyboardHandler, specifically OnPreKeyEvent()
ClientHandler::OnPreKeyEvent(CefRefPtr<CefBrowser> browser,
const CefKeyEvent& event,
CefEventHandle os_event,
bool* is_keyboard_shortcut) {
if (os_event && os_event->message == WM_SYSKEYDOWN) {
case VK_F10: HandleF10(); break;
case VK_F4: HandlerF4(); break; //Use GetKeyState(VK_MENU) to check if ALT is down...
}
}
This follows the CefClient project, where ClientHandler implements CefKeyboardHandler. Check client_handler_win.cpp
Longer version follows...
Looking at this thread - Keyboard events "eaten" by browser - this stands out:
When the browser control has the focus, any keys being pressed seem to
be eaten by the browser control, no matter whether they also can be
handled by the browser control or not.
There are now two options:
Intercept the key-press before it is sent to the CEF engine, which would require quite a little bit of digging into CEF and be platform specific.
Capture the key-press using a normal Javascript event handler, and callback to C++.
Intercept the key-press before the CEF engine processes it if CEF has such interfaces - this would ideally be platform independant.
Capture Keypress at Native-App level
On a windows machine, I tried searching for WM_KEYDOWN which is the usual practice in capturing key events (See Here). I was unable to get any hits on the CefClient project I was running, so this was a dead end.
Anybody with further info on this, please edit and add to this.
Capture Keypress in JS and callback to C++
Once the keypress goes into CefBrowser, we could always use Javascript to capture the keypress we want and then call an app handler, like so:
$(document).keypress(function (e) {
...
NativeAppFunction();
//Or NativeAppExtension.Function();
}
Communicating between JS and C++ is done through either V8Extensions, or by binding a Function to the CefContext. Further info at Javascript Integration
This comes with some pitfalls - your event-capturer is 'just another Javascript event handler', and with that comes all the uncertainties of when it is called (before or after other event handlers) and so on. Thankfully, CEF has a nifty little CefKeyboardHandler just to do what you want!
Intercept Keypress using CefKeyboardHandler
See cef_keyboard_handler.h - the doc for OnPreKeyEvent() says:
// Called before a keyboard event is sent to the renderer. |event| contains
// information about the keyboard event. |os_event| is the operating system
// event message, if any. Return true if the event was handled or false
// otherwise. If the event will be handled in OnKeyEvent() as a keyboard
// shortcut set |is_keyboard_shortcut| to true and return false.
From here, it's pretty straightforward. The CefEventHandle resolves to a platform specific (sadly - oh well!) standard Windows MSG. Note that Alt+F4 is a special system command:
When you press a key, the window that has keyboard focus receives one
of the following messages.
WM_SYSKEYDOWN (or) WM_KEYDOWN
The WM_SYSKEYDOWN message indicates a system key, which is a key stroke that invokes a
system command. There are two types of system key: ALT + any key and F10
Full text at MSDN

Wait for a specific key in a Win32 application

At some point in my program, I want to wait for the user to either press [return] or [escape].
This is what I did:
while(1)
{
Sleep(100);
if( GetAsyncKeyState( VK_RETURN ) )
{
//do something
}
if( GetAsyncKeyState( VK_ESCAPE ) )
{
//do something else
}
}
But (only in the release build) after waiting for about 2 seconds, Windows says it's not responding, and it crashes.
What should I do?
Your application is a GUI subsystem application and its main thread must regularly pump its message queue. You are not doing that because you enter a tight loop looking for specific key state. Because you don't service your queue, the system concludes that your application is broken and ghosts your window.
Before we go on to how to do it right, your existing approach is broken in other ways. Suppose that the key is pressed and released during the Sleep(100). Then you miss that event. Or suppose your app is not in the foreground. Then it responds to key presses meant for other applications.
To solve the problem you simply need to let your normal message loop process and dispatch messages. When you get a WM_KEYDOWN message for the appropriate key you can react accordingly.
Using the message loop in the intended way not only fixes the behaviour you observe in the question, but also the issues I describe above.

Keyboard Event SFML 2.0

Im trying to get the key input in SFML 2, in the SFML 1.6 im using
while (App.GetEvent(Event))
{
if (App.GetInput().IsKeyDown(sf::Key::Down)) { dir='d'; }
}
But i have no idea how to do this in SFML 2.
When you don't need to worry about real-time keyboard input, you can use an approach very similar to the SFML 1.6 code you provided. In your application's event processing loop, you can do something like this:
sf::Event event;
while (mWindow.pollEvent(event))
{
if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::Escape)
{
// Do something when Escape is pressed...
}
if (event.key.code == sf::Keyboard::W)
{
// Do something when W is pressed...
}
// And so on.
}
}
This type of input handling is nice when you must guarantee your application has focus when the user presses the key, as key events aren't generated otherwise. It is also great for when the key in question is pressed infrequently. You can check out an example of this from the SFML 2.0 tutorials here, under the section titled "The KeyPressed and KeyReleased events": http://sfml-dev.org/tutorials/2.0/window-events.php
On the other hand, you may actually need access to real-time keyboard input. To do this, use SFML 2.0's Keyboard class like this:
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
{
// 'W' is currently pressed, do something...
}
With real-time input, you are accessing the state of the input device at that particular point in time. This is handy because you do not have to lump all your key checks in your event processing loop. A disadvantage of this approach is since SFML is just reading the state of the keyboard, your event handling code can still execute if the application doesn't have focus, is minimized, etc. You can find a tutorial on all the real-time inputs here: http://sfml-dev.org/tutorials/2.0/window-inputs.php
Be careful in choosing the event processing vs. real-time approach. For a game example, consider a situation where a character fires a machine gun when the user holds down the space bar. If you handle the space bar in the event processing loop, the machine gun will incorrectly fire like a semi-automatic since there is a delay between sf::Event::KeyPressed events for the same key, even if the user is holding the key down. If you handle the space bar by checking with real-time keyboard input, the machine gun will fire repeatedly, as expected.

What is the fastest way to determine a key press and key holding in Win32?

What is the fastest way to determine a key press and also how to determine if a key is being held? It appears that window messaging is slow. Please provide an example of how to do so, and why it is faster than an alternative.
To be clear, this for a real time loop (a simulation) so I am looking for the fastest way to determine if a key has been pressed and also to check to see if it is being held.
GetAsyncKeyState() is what you're looking for. It reads the physical state of the keyboard, regardless of the input queue state. If the high-bit is set, then the key was down at the time of the call.
// Fetch tab key state.
SHORT tabKeyState = GetAsyncKeyState( VK_TAB );
// Test high bit - if set, key was down when GetAsyncKeyState was called.
if( ( 1 << 15 ) & tabKeyState )
{
// TAB key down...
}
Also, for the record, Windows is not a real-time operating system. If your application requires real-time precision, you may want to select another platform.
If you just want to poll the keyboard state so as to discover which keys are up/down as well as the shift/alt/ctrl state, just call GetKeyboardState (MSDN reference).
When I worked in a game studio, this is exactly how we got keyboard state for each frame. Should be applicable to your simulation code.
TL;DR: you can use GetAsyncKeyState for checking if a key is currently down, but for best application responsiveness to key presses and releases, you want to use the Win32 pipeline code near the bottom of my post.
GetAsyncKeyState works perfectly fine for determining if a key is currently down, but in terms of determining whether a key was first pressed or released and how many times this was done, GetAsyncKeyState misses keystrokes in a CPU-intensive application, even after storing the previous key state.
This was what I tried:
static const unsigned int NumberOfKeys = 256U;
bool previousKeyboardState[NumberOfKeys];
//Get the current state of each key as the application starts to ensure that keys held down beforehand are not processed as pressed keys.
for (unsigned int keyNum = 0U; keyNum < NumberOfKeys; ++keyNum)
{
previousKeyboardState[keyNum] = isKeyDown(keyNum);
}
//Works fine.
bool isKeyDown(int key)
{
return (GetAsyncKeyState(key) & (1 << 16));
}
//Misses key presses when application is bogged down.
bool isKeyFirstPressed(int key)
{
bool previousState = previousKeyboardState[key];
previousKeyboardState[key] = isKeyDown(key);
return (previousKeyboardState[key] && !previousState);
}
//Misses key releases when application is bogged down.
bool isKeyFirstReleased(int key)
{
bool previousState = previousKeyboardState[key];
previousKeyboardState[key] = isKeyDown(key);
return (!previousKeyboardState[key] && previousState);
}
//Example usage:
if (isKeyDown(VK_W))
{
//W key.
}
if (isKeyFirstReleased(VK_SNAPSHOT))
{
//Print screen.
}
GetKeyboardState is no good either, as it does not keep track of the number of key presses or releases. As Erik Philips said in his answer, these are unbuffered solutions, which are no good if you are e.g. writing a game. You would have to process all keystrokes faster than they are received.
Now, my code above works decently well, and may be suitable for many people, but I much prefer not to miss a single keystroke. I hate using applications that are unresponsive. I think the best solution for Win32 applications is to catch WM_KEYDOWN and WM_KEYUP messages in the pipeline and process them. What's nice is that WM_KEYDOWN also provides an auto-repeat count, which could be useful for applications that support entering text (e.g. chat, IDE's, etc.). This also adds a slight complication, which is mentioned in the WM_KEYDOWN documentation:
Because of the autorepeat feature, more than one WM_KEYDOWN message
may be posted before a WM_KEYUP message is posted. The previous key
state (bit 30) can be used to determine whether the WM_KEYDOWN message
indicates the first down transition or a repeated down transition.
There are also Windows keyboard hooks you could look into, but those are more difficult to use. They're good for receiving global key presses though.
Considering that all inter-windows communications are through windows messaging (keyboard events, mouse events, pretty much all events you can imagine), there isn't a lower level way to access the keyboard events (unless you write your own keyboard driver) that I know of.
DirectX still uses the windows keyboard messaging to provide DirectX programmers easier access to keyboard events.
Updated
My note about DirectX was not to use it, but that when Microsoft wanted to make an interface for programmers to use for real time games, they still wrote DirectX on top of the Windows Message Queue.
I would suggest taking a look at how to write a program that can read directly from the message queue. I believe there is a good example Code Project Windows Message Handling - Part 1.
Your two options are to either read from the message queue (buffered) or read directly from the keyboard state (as Bukes states) which means your own loop could techinically miss a keyboard event for any number of reasons.

Using keypress to switch a bool

I am writing a Windows C++ app which I would like to have detect a keypress (for this example, using the letter 'S'). When the key is pressed, the program should switch a bool value either on or off (depending on its current state).
I know that in console apps you can use cin.get, but I'm unfamiliar with the Win32 API. I also would like to be sure that when the key is pressed, the event is only registered once, i.e. if the user presses 'S' but holds the key down for a while, the program should detect only 'S'; not 'SSSSSSS'.
So you have a windows message loop going, right? Capture WM_KEYDOWN and check whether it's on autorepeat.
But I'd guess you are actually using a framework, be it MFC, QT or something else. The framework will wrap your windows message loop and allow you to capture key events, but if you want us to tell you how, you'll have to say what framework you're using.