Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid handling both the key-event and char-event

Tags:

glfw

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..

like image 925
yairchu Avatar asked Jan 19 '26 03:01

yairchu


1 Answers

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:

    1. KeyCallback - pressed
    1. CharCallback - typed
    1. KeyCallback - repeated
    1. CharCallback - typed
  • (3. and 4. repeat until key is released)
    1. 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*:

debug printout showing test results

*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.

like image 181
Logix Avatar answered Jan 22 '26 18:01

Logix



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!