I want to implement a Keyboard class like below;
class Keyboard
{
public:
Keyboard();
~Keyboard();
bool IsKeyDown(Key);
bool IsKeyUp(Key);
void SetPressedKey(int);
void SetReleasedKey(int);
private:
Key pressedKey;
Key releasedKey;
};
Key is an enum like below;
enum Key
{
A,
Enter,
Up,
Down
};
Here is the window callback function;
case WM_KEYDOWN:
kb.SetPressedKey(wParam);
break;
case WM_KEYUP:
kb.SetReleasedKey(wParam);
break;
First of all, my design can be completely wrong. If it's an acceptable design, there are some questions that I couldn't answer. Can value of the pressedKey be overwritten if user presses two buttons at the same time and how can I determine if user uses combinations like CTRL+C. The other question is I couldn't find a way to make a relationship between wParam's value and enum keys' indexes.
Most of the implementation of this class is unnecessary and should be removed. You are re-implementing functionality that the operating system already provides. If you want to know the state of a key, simply call GetKeyState.
You may choose to wrap up the low-level Win32 API GetKeyState with your class. But do not store the information as state in your class. When you need to know the state of a key, i.e. when implementing IsKeyDown() and IsKeyUp(), call GetKeyState.
Another good reason for this is that you cannot guarantee that all keyboard messages will arrive in your window. Somebody may press a key whilst a different window is active, and then switch to your program. At that point, your attempt to track state using keyboard messages will break down because you never got the key down message. However, GetKeyState will work.
That said, if you need to know the instantaneous state, outside of a window procedure, then you would use GetAsyncKeyState rather than GetKeyState. Only you fully understand the purpose of this class and so are in a position to make that decision.
Windows already keeps track of all this information. If you want to know if a key is currently pressed, GetKeyState. This queries the keyboard state tied to the current message. So for example it's accurate to use GetKeyState to know if CTRL was pressed inside a WM_KEYDOWN message.
There is also GetAsyncKeyState, if you want to query the keyboard state "right now", rather than the state when the current message was generated.
In terms of your design I personally would try to make it a bit better by having it as a set of Key object and have the keys maintain their own state. That way you can model multiple key presses. The example (incomplete and not tested!) shows the idea.
class Keyboard
{
public:
Keyboard();
~Keyboard();
bool isPressed(Key);
void pressKey(int); // or Key
void releaseKey(int);
private:
std::set<Key> keys;
};
class Keyboard
{
public:
// define your comparison operators to use std::set or else write a comparator.
Key(uint32 id);
~Key();
bool
void setPressed();
void setReleased();
bool isPressed() const;
private:
uint32 keyId;
bool pressed;
};
In order to relate the windows key values to Key objects you could build a factory to return the keyIds that you need. Your KeyIds could be an enum or just re-use the windows values. As long as they are unique and your application understands then then there is no point in being restrictive.
Related
So inside the player controller there is a nice function called WasInputKeyJustPressed, you give it a key and it outputs a bool. I was curious if there was a similar function for Action Keys. For example, if I had an action mapping called MyActionKey which was tied to the Space bar. Is there a function like WasActionKeyJustPressed that essentially does the same thing as the original? If not how can I detect that with my player controller in a similar manner to WasInputKeyJustPressed?
I am not interested with the InputAction MyActionKey for this case it does not work for me. I am really looking for a function that will just output whether or not that particular Action Key is pressed in a bool form.
If this is a custom function that needs to be written then it would be nice if say I could pass in the name of the Action Key. I'd prefer if the solution was in C++.
I was able to find a sort of work around. Essentially, you can grab the input settings and then from there grab your axis mappings or your action mappings. They are both very similar. From there I looped through each of the mappings and grabbed the key it was referencing and called the WasInputKeyJustPressed function. An example below:
TArray<FInputAxisKeyMapping> axisMapping;
UInputSettings::GetInputSettings()->GetAxisMappingByName(axisName, axisMapping);
for(auto axis : axisMapping)
{
if (playerController->WasInputKeyJustPressed(axis.Key))
{
return true;
}
}
return false;
The RichEdit control has this very annoying feature. It beeps every time the user tries to move the cursor past its "end point". For instance, you can test it with the WordPad that also implements RICHEDIT. Open it up, type in some text, then hit the Home key. If the cursor was not in the beginning of the line:
hitting Home key will move it there, but then hitting the Home key again will produce this beep.
At first glance it seemed like overriding WM_KEYDOWN and WM_KEYUP messages and blocking the situations when RICHEDIT can produce that beep was a solution ... until I actually started implementing it. Unfortunately though, it's not as simple as it sounds, as that control beeps in a LOT OF CASES! So my keystroke blocking code literally ballooned to over 300+ lines, and I still see that there are some key-presses that I either didn't account for, or worse, I might have overridden some useful behavior with. (Read below for more details.)
Then I decided to look inside the implementation of the RICHEDIT control itself. And sure enough, for instance if we look at the implementation of the Home key press, the C:\WINDOWS\SysWOW64\msftedit.dll on my Windows 10 OS, has the function called ?Home#CTxtSelection##QAEHHH#Z (or public: int __thiscall CTxtSelection::Home(int,int) demangled) at the mapped offset 0x3FC00, that is hard-coded to call the MessageBeep(MB_OK), or exactly what I'm trying to eliminate:
And if you look at the address 0x6B64FD38 in the screenshot above, there's a built-in way to bypass it, with what looks to be flag 0x800.
So having dug into msftedit.dll a little bit more, there appears to be a function called ?OnAllowBeep#CTxtEdit##QAEJH#Z (or public: long __thiscall CTxtEdit::OnAllowBeep(int) demangled) that can modify this flags:
After a bit more research I found out that there are COM interfaces built into RICHEDIT control, such as ITextServices and ITextHost that reference that flag as TXTBIT_ALLOWBEEP in ITextServices::OnTxPropertyBitsChange method.
Unfortunately though, I can't seem to find the way how I can directly change that TXTBIT_ALLOWBEEP flag (COM is not my forte.) I tried looking into implementing ITextHost, but it has a lot of virtual methods that have nothing to do with what I'm trying to achieve that I don't know how to implement.
Does anyone have any idea how to clear that TXTBIT_ALLOWBEEP flag?
PS. Here's why I didn't go the route of overriding key-presses:
Just to give you an example. Say, if I override the VK_HOME key press. I need to make sure that the cursor is not at the beginning of the line, but also that there's no selection. Yet, I need to make sure that Ctrl key is not down in a situation when the cursor is at the very top of the window. Then the same with the Shift key, and I'm not even sure what Alt does with it ... and so forth. Oh, and this is just the Home key. There's also Up, Down, Left, Right, PageUp, PageDown, End, Delete, Backspace. (And that's what I was aware of. There may be more, plus I'm not even talking about IME or other keyboard layouts, etc.) In other words, it becomes a mess!
So, eventually I realized that anticipating a keystroke is not the way to go.
first we need send EM_GETOLEINTERFACE message to rich edit window - this is Retrieves an IRichEditOle object that a client can use to access a rich edit control's Component Object Model (COM) functionality.
then for retrieve an ITextServices pointer, call QueryInterface on the private IUnknown pointer returned by EM_GETOLEINTERFACE.
here exist interesting point - the IID_ITextServices not well known but need get in runtime from Msftedit.dll
from About Windowless Rich Edit Controls
Msftedit.dll exports an interface identifier (IID) called IID_ITextServices that you can use to query the IUnknown pointer for the ITextServices interface.
after we got ITextServices pointer - we simply can call OnTxPropertyBitsChange with TXTBIT_ALLOWBEEP mask
code example:
#include <textserv.h>
if (HMODULE hmodRichEdit = LoadLibrary(L"Msftedit.dll"))
{
// create richedit window
if (HWND hwndRich = CreateWindowExW(0, MSFTEDIT_CLASS, ...))
{
if (IID* pIID_ITS = (IID*) GetProcAddress(hmodRichEdit, "IID_ITextServices"))
{
IUnknown* pUnk;
if (SendMessageW(hwndRich, EM_GETOLEINTERFACE, 0, (LPARAM)&pUnk))
{
ITextServices* pTxtSrv;
HRESULT hr = pUnk->QueryInterface(*pIID_ITS, (void**)&pTxtSrv);
pUnk->Release();
if (0 <= hr)
{
pTxtSrv->OnTxPropertyBitsChange(TXTBIT_ALLOWBEEP, 0);
pTxtSrv->Release();
}
}
}
}
}
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 am playing around with some graphics, and I have implemented simple camera movement with the arrow keys. My first approach was to override keyPressEvent to do something like this:
switch(key)
{
case up: MoveCameraForward(step); break;
case left: MoveCameraLeft(step); break;
...
}
This doesn't work as I wish it would. When I press and hold, for example, the forward key, the camera moves forward "step" units, then halts for a while and then continues moving. I am guessing that this is how the event is generated, in order to avoid multiple events in case of a little bit long keypress.
So, I need to poll the keyboard in my Paint() routine. I haven't found how to do it with Qt. I thought of having a map<Key, bool> which would be updated in keyPressEvent and keyReleaseEvent and poll that map in Paint(). Any better ideas? Thanks for any insights.
This doesn't solve the general problem of detecting which keys are pressed, but if you are only looking for keyboard modifiers (shift, ctrl, alt, etc.), you can retrieve that through the static QApplication::keyboardModifiers() and QApplication::queryKeyboardModifiers() methods.
So, I need to poll the keyboard in my Paint() routine. I haven't found
how to do it with Qt. I thought of having a map which would
be updated in keyPressEvent and keyReleaseEvent and poll that map in
Paint().
Your second method is what I would have done, except that I would use a continuous, periodic QTimer event to poll the keyboard-pressed map and call QWidget::Update() function when necessary to invalidate the display widget instead. Performing non-painting operations inside Paint() is strongly discouraged for many reasons but I do not know how to explain that well.
There is no Qt API for checking whether a key is pressed or not.
You may have to write separate code for different platforms and add a bit of #ifdef logic.
On Windows you can use GetKeyState() and GetKeyboardState(), both declared in windows.h.
This is not straight forward when using Qt, but the Gluon team has been working on exactly that problem (along with a bunch of others). GluonInput solves the issue, and is available as part of Gluon: http://gluon.gamingfreedom.org/ It is also a nice, Qt-like API, so while it's an extra dependency, it should be possible for you to use it.
This is caused by auto repeation of keys:
When I press and hold, for example, the forward key, the camera moves
forward "step" units, then halts for a while and then continues
moving.
In QT5 you can detect key held down by function isAutoRepeat() of QKeyEvent object. If the key is held down then isAutoRepeat() will return true.
for example:
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if (event->isAutoRepeat())
{
return;
}
if (!event->isAutoRepeat())
{
qDebug() << "[MainWindow::keyPressEvent()] " << event->key() << "; " << event->isAutoRepeat();
}
}
void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
if (event->isAutoRepeat())
{
return;
}
qDebug() << "[MainWindow::keyReleaseEvent()] " << event->key() << "; " << event->isAutoRepeat();
}
Use QGuiApplication::keyboardModifiers() and QGuiApplication::queryKeyboardModifiers() for keyboard modifiers in Qt5
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.