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.
Related
I am using C++ and ncurses to get key input for a game. I have a moveable object that the arrow keys control. The behavior that I want is to be able to constantly move right with an arrow key and jump with the up keyy without interrupting the moving right. However, currently with ncurses, when RIGHT is being held and UP is pressed, it stops registering that RIGHT is being held, and you have to release and press RIGHT again.
How can I fix this?
I have seen this post, which is how I'm getting the multiple key input (see below), however I don't think this is the problem.
while (gameRunning) {
int ch;
vector<int> keys;
while ((ch == getch()) != ERR) {
keys.push_back(ch);
// keys vector now has all pressed keys,
// however if you are holding right and up is pressed,
// holding right is not recognized until you release and
// right is pressed again
}
// loop through keys vector here, do things with pressed keys
}
I am using Qt creator to make a simple drawing program in c++. I have a mouseevent, that should get the coordinates to the nearest point (dot) allready existing from the point where mouse was clicked, and put them to line starting point coordinates. Thats done. But now the event should wait for a second mouse click to get the nearest dot coordninates again and put them to end point of line. But instead it does not wait for the second input and puts the same first point for the line endpoint also.
How can I make mouseevent take two click inputs, not do everything right away? Is it even possible? Thank you in advance.
You cannot make one event require two clicks (excluding, of course, the double click event). You can, however, establish a state within your application, where the first click starts the state and the second completes it. Pseudo-code to manage this:
// in your constructor:
StartingClickPoint = INVALID; // indicate that we have not entered our special state
// in your mouse event handler:
if (StartingClickPoint == INVALID) {
// we're only now starting this state; we don't have enough information to complete it
StartingClickPoint = CurrentClickPoint;
return;
}
else {
// complete the state handling and arm for the next pair of clicks
EndingClickPoint = CurrentClickPoint;
DoSomethingWithTheClickPoints();
StartingClickPoint = INVALID; // reset for the next pair of clicks
}
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.
I'm in a situation whereby I am trying to read in a JSON config file which dictates what key commands map to given actions. For example:
...
{
"Action": "Quit",
"Combo" : "CTRL+Q"
},
...
Constructing a QKeySequence from the combo tag is trivial but I need to monitor QKeyEvents in order to trigger actions. Please note I have to monitor QKeyEvents because they are used for other purposes in the application as well. i.e. it would not be acceptable to only monitor key commands for QKeySequences (if that is even possible).
Short of writing a custom parser to construct a QKeyEvent object for each "Combo" tag, is there anyway of comparing a QkeyEvent to a QKeySequence? For example:
QKeyEvent KeyCommandsHandler::toKeyEvent(QKeySequence sequence) {
//somehow convert to QKeyEvent
}
In general, you cannot compare QKeyEvent and QKeySequence objects. QKeyEvent represents the event of a single key press or release, whereas a QKeySequence can contain a sequence of up to four keys, each with optional modifier information.
You can, however, compare the objects if you are sure that your key sequences will always contain just one key:
bool isEquiv(const QKeyEvent& event, const QKeySequence& seq)
{
if (seq.count() != 1)
return false;
return seq[0] == (event.key() | event.modifiers());
}
You can even write a conversion function for QKeyEvent to QKeySequence:
QKeySequence toKeySequence(const QKeyEvent& event)
{
return QKeySequence(event.key() | event.modifiers());
}
Note that it does not make sense to convert a QKeySequence to a QKeyEvent, though, since you have to choose a specific event type such as QEvent::KeyPress or QEvent::KeyRelease.
A simple solution (written in python):
key = QKeySequence(event.modifiers()|event.key()).toString()
Will give you the entire sequence in string form, such as "Ctrl+Q".
The benefits are (at least in python) that you can find in a dict of shortcuts, while a QKeySequence would not have been hashable.
Beware that this expects you use the correct typecase and spacing. "ctrl +Q" will not match. To avoid all issues, you can do the following when first reading the shortcuts:
shortcut = shortcut.lower().remove(' ')
and match/find using
key = QKeySequence(event.modifiers()|event.key()).toString().lower()
or better yet:
shortcut = QKeySequence(shortcut).toString()
and match directly.
A Qt4.7 note with code for converting KeyEvent to KeySequence. (But the code is flawed because it casts an int for the keycode from QKeyEvent.key() to a string. Better to use QKeyEvent.text() ?)
Also, the code in Ferdinand's answer:
QKeySequence(event.key() | event.modifiers())
is not type safe (mixes int and QKeyboardModifiers) and if converted to Python fails in PyQt, but not in PySide?
Also, "QKeyEvent represents the event of a single key press or release" doesn't really explain it. A QKeyEvent can tell you what combination of keys were down, just not the order in which they were pressed. When the user presses keys in sequence, your app might get a sequence of QKeyEvents (depending on whether your app is using default versus overridden handlers for QKeyEvent.) The later QKeyEvents will show you all the keys that were down at the time of the event. They might no longer be down. It is rather complicated.
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.