I am currently trying to get KeyBoard Input from the WM_KEYDOWN and WM_CHAR message for my own InputBox.
This is the code that I am using for basic input, which works fine for characters:
if(msg.message == WM_KEYDOWN)
{
keyHandled = false;
//handle other keys here, e.g. VK_LEFT
}
else if(msg.message == WM_CHAR && !keyHandled)
{
keyHandled = true;
gui->UpdateInput(msg.wParam);
}
If the key that is being pressed is also a key that triggers the WM_CHAR message, the interval is as in usual input boxes.
However, if it is a key like VK_LEFT, it keeps receiving the WM_KEYDOWN message without any delay.
Is there any way that I can receive all keys with the same interval or do I have to implement a timer that handles the delay between the messages? I have also had a look at the WM_KEYDOWN message on msdn, but I could not find anything related to the intervals.
Windows has its own delays for sending Keyboard events from Keyboard input and this isn't something you can simply change. As you know, holding down a key will result in a first message, a delay, and then a series of more messages with a quicker interval. You can get around this by requesting states rather than relying on the messages directly. This is called Unbuffered input and is state oriented. To store your states, you can do the following:
bool keys[256];
When you are checking windows events, you can update the key states accordingly like this:
//In your WinProc function most likely
if (msg.message == WM_KEYDOWN)
{
keys[msg.wParam] = true;
}
if (msg.message == WM_KEYUP)
{
keys[msg.wParam] = false;
}
Then whenever you'd like, you can request the state of a specific key through the following function:
bool getKeyPressed(char keycode)
{
return keys[keycode];
}
For example:
//This could be in some random update function and called
//whenever you need the information.
if (getKeyPressed(VK_UP))
{
//Do something here...
}
The above function can be used wherever you'd like, therefore the frequency at which you update is completely up to you at that point. As mentioned before, this is Unbuffered input and is State oriented, whereas Buffered Input is Event oriented.
I think that this internal delay can not be modified easily.
One approach could be writing your own general key handler that keeps track of the states of all keys. For example, a list containg the keycodes of all pressed keys. On WM_KEYDOWN you add the keycode to the list and on WM_KEYUP you remove it. Then create something like a timer that simply notifies you in the desired delay times and calls your key handling function for each element of the list.
Related
I need to intercept key pressings and other user actions in my RichEdit box. I found it too complex to intercept user input in WM_KEYDOWN or WM_CHAR since some of the key pressings fire WM_CHAR, some of them not and also it has some other problems.
So I decided to listen EN_UPDATE messages, cuz it is said that this event fires on every change and just before RichEdit control starts to redraw itself ( https://learn.microsoft.com/en-us/windows/desktop/controls/en-update ). Well it sounds like trustworthy mechanism which allows to intercept all the changes.
But I found that not every WM_KEYDOWN causes firing of EN_UPDATE. I rapidly pressed many buttons (usual "char" buttons like "d", "f" and so on, no special keys), and found that when I inputed 100 chars, and WM_KEYDOWN also fired 100 times, but EN_UPDATE fired only 96 times (the number of activations of EN_UPDATE varies, sometimes it equals to the number of keypressings, don't know what it depends on). The number of WM_KEYDOWN and the number of characters entered are always equal of course.
Here's the code:
BOOL CEditorView::OnCommand( WPARAM wParam, LPARAM lParam )
{
static long count = 0;
if( HIWORD( wParam) == EN_UPDATE )
{
if( (HWND)lParam == g_hwnd_RE )
{
if( g_allowProcessing )
{
count++;
}
}
}
return CDockablePane::OnCommand( wParam, lParam );
}
///// and WM_KEYDOWN
case WM_KEYDOWN:
{
g_testCount++;
return DefSubclassProc( hwnd, msg, wp, lp );
break;
}
Am I doing something wrong or is it just specific working style of EN_UPDATE? Like may be it accumulates changes when user input is too fast.
EN_UPDATE is sent when the control is about to redraw itself. This does not imply that the control has to update after each WM_KEYDOWN, the control might have some caching mechanism to update delaying when keys are down within a little margin, for example.
This is not standard per control, you may have a control that updates on each keypress, even if there is 1 ms delay between keys, and you may have a control that updates each second, no matter how many keys it gets. It depends on the control.
Therefore, if you really want to get notified of every key, you have to use WM_KEYDOWN. EN_UPDATE is usually used to take the current state of the control and update another control.
To handle text input I've set up a char-event callback with glfwSetCharCallback, and to handle non-text keypresses (arrow keys & hotkeys) I've set up a key-event callback with glfwSetKeyCallback.
What happens in this situation is that for a key press of a character key, I get two calls, one in the key-event callback, and then one in the char-event callback. This can cause unwanted effects - for example let's suppose the user configured the key "a" to enter "Append Mode" of a text editor - after it enters the mode it will also enter the character "a".. Is there a good way to handle this?
So far I've relied on both events arriving together before glfwPollEvents returns, and have merged them. But I get reports that this scheme doesn't work well on some Ubuntu systems..
I've been having trouble with this one as well. After some rudimentary debugging I found that if you press, hold then release a 'typable' key (meaning a key which may fire both the glfwKeyCallback and glfwCharCallback), the output is as follows:
KeyCallback - pressed
CharCallback - typed
KeyCallback - repeated
CharCallback - typed
(3. and 4. repeat until key is released)
KeyCallback - released
With this, and judging from the fact that there is a 0ms delay between the two events firing, they're probably fired sequentially. The solution I came up with (is rather janky), and involves creating some sort of KeyEvent structure:
(examples are in C++)
enum KeyEventType
{
Pressed,
Repeated,
Released
}
struct KeyEvent
{
KeyEventType Type;
int Key;
unsigned char Codepoint;
bool IsTyped;
}
and store it along with an index variable, such as
[main/input class]
std::vector<KeyEvent> m_KeyEvents;
size_t m_LastKeyEventIndex;
in the main file.
Then, when the glfwKeyCallback fires, push a new KeyEvent into the vector:
[glfwKeyCallback]
KeyEventType type = (action == GLFW_PRESS ? KeyEventType::Pressed : (action == GLFW_REPEAT ? KeyEventType::Repeated : KeyEventType::Released));
KeyEvent event = KeyEvent(type, key);
m_KeyEvents.push_back(event);
m_LastKeyEventIndex = m_KeyEvents.size() - 1;
and if the glfwCharCallback fires, we know from the debugging that it should be (immediately) after the corresponding keyCallback event, so you can modify the last added entry in the vector to add the codepoint and mark it as a 'typed' event, after-the-fact. This also gives the added benefit of tying the actual key that was pressed to the generated codepoint, which could come in useful.
[glfwCharCallback]
m_KeyEvents.at(m_LastKeyEventIndex).Codepoint = codepoint;
m_KeyEvents.at(m_LastKeyEventIndex).IsTyped = true;
Finally, in the main loop when you go to call glfwPollEvents(), process all those pending KeyEvents and then clear the vector and reset the index.
I haven't fully tested this yet, but some very rudimentary debugging shows this as a promising solution, resulting in the following*:
*I'm using a custom Key enum in place of the int Key. You could probably use glfwGetKeyName() to get the printable key name, however this resulted in exceptions for me when pressing some keys.
I'm handling the LVN_ITEMCHANGING message, but it gets signaled every time the check state is changed.
I need a way to distinguish between the user changing the check state, and me calling ListView_SetCheckState
Is there any easy way to do this? A different message I don't know about maybe? Or does anyone have any suggestions?
ListView_SetCheckState sends the LVM_SETITEMSTATE message. LVN_ITEMCHANGING message is also sent to the control's parent window. This means, the function is synchronous, and LVN_ITEMCHANGING handler is executed before ListView_SetCheckState call returns. This allows to use simple boolean flag, like:
bChangedByProgram = TRUE;
ListView_SetCheckState(...);
bChangedByProgram = FALSE;
In LVN_ITEMCHANGING handler:
if ( ! bChangedByProgram )
{
// item state is changed by user
}
I'm currently using GetAsyncKeyState() to detect Keydown events, but then the events will be repeated while you are holding down the key.
What would be an easy way to stop the event from repeating?
Example
If I hold down the key i on my keyboard for a while, I will get an output like this:
iiiiiiiiiiiiiiiiiiiiiiiii
Instead of this:
i
I want to force the user to press the key again to trigger the event.
Avoid using a keyboard related message like WM_KEYDOWN or WM_CHAR to detect a key, Windows repeats WM_KEYDOWN when the user holds it down. You simply need a your own bool flag that keeps track of the state of the key. Only change your game object state when you see a difference between the state as reported by GetAsyncKeyState() and this flag. Roughly:
bool movingLeft = false;
...
if ((GetAsyncKeyState(VK_LEFT) < 0) != movingLeft) {
movingLeft = !movingLeft;
gameObject->setVelocity(movingLeft ? -10 : 0);
}
Use KeyPress events (or KeyUp).
Btw according to MSDN SHORT WINAPI GetAsyncKeyState(__in int vKey);
Determines whether a key is up or down
at the time the function is called,
and whether the key was pressed after
a previous call to GetAsyncKeyState.
It doesn't say anything about detecting a keydown event.
The main form opens a child form that has a handful of button CONTROLs on it. I need to trap keyboard events so I subclassed one of the controls. All is good until the control loses focus of course.
Ideally, as long as this child form is open I would like to assign the focus to this control and thus trap all the keystrokes, no matter where the user clicks.
I suspect superclassing might be a better way to go but I am not as familiar with it.
Perhaps what I should do is use accelerators on the main form?
ADDED:
I should mention that the main form has a large listview control that is subclassed to recover up/down arrows and mousewheel etc.
The traditional way is to install a keyboard hook (SetWindowsHookEx), but you need to inject it into every application, and it doesn't work across 32/64 bit boundaries.
What you can do however, and quite easily at that, is to poll the keyboard with GetKeyboardState on a timer and check whether your f1-f12 keys are activated. The timer can be as slow ticking as 100ms and it will catch almost everything while using virtually no resources.
Assuming that this is within Windows and the Win32 API, one option is to look for messages in your main GetMessage, TranslateMessage, DispatchMessage loop. You can special-case any message within this loop, irrespective of which window it's aimed at.
You should probably use IsChild to check that the message is intended for a control on your main window (as opposed to some dialog box or message box that might be displayed separately). Getting the logic right can be fiddly, too. It would be best to only intercept messages when you know your control has lost the focus, and only intercept the exact messages you need to.
Years ago, I wrote a library message loop with a lot of this built in. I had a simple manager class that held pointers to instances of my own little window class. The loop knew the difference between dialogs and normal windows, gave each window class a chance to spy on its childrens messages, and so on. You won't be able to run this directly and the conventions are a bit strange, but you might find this useful...
int c_Window_List::Message_Loop (void)
{
MSG msg;
bool l_Handled;
while (GetMessage (&msg, NULL, 0, 0))
{
l_Handled = false;
c_Windows::c_Cursor l_Cursor;
bool ok;
for (ok = l_Cursor.Find_First (g_Windows); ok; ok = l_Cursor.Step_Next ())
{
if (IsChild (l_Cursor.Key (), msg.hwnd))
{
if (l_Cursor.Data ().f_Accelerators != NULL)
{
l_Handled = TranslateAccelerator (l_Cursor.Key (), l_Cursor.Data ().f_Accelerators, &msg);
if (l_Handled) break;
}
if (l_Cursor.Data ().f_Manager != 0)
{
l_Handled = l_Cursor.Data ().f_Manager->Spy_Msg (l_Cursor.Key (), msg);
}
if (l_Handled) break;
if (l_Cursor.Data ().f_Is_Dialog)
{
l_Handled = IsDialogMessage (l_Cursor.Key (), &msg);
if (l_Handled) break;
}
}
}
if (!l_Handled)
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
if (g_Windows.Size () == 0)
{
// When all windows have closed, exit
PostQuitMessage (0);
}
}
return msg.wParam;
}
The f_ prefixes mean field - I picked up the m_ convention later, but this code hasn't been revisited in a very long time. f_Manager in particular points to an instance of my c_Window_Base class. The c_Cursor class is a kind of iterator, used to step through all the windows stored in the g_Windows variable (actually a static class member rather than a global).