How to avoid handling both the key-event and char-event
Asked Answered
E

1

16

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

Excuse answered 27/10, 2018 at 8:46 Comment(4)
You could add extra logic to each callback to make sure they only do what they're supposed to when they're supposed to. You could also only handle 'a' in one of the callbacks instead of both.Lawrencelawrencium
@Romen: What if you make a game or app that has configurable key bindings? Then you may need the general knowledge of knowing which keys map to characters which can also vary across input languagesExcuse
You can handle everything from the Key input handlers but language support is just a lot harder. The text input handler has its uses too but that is almost exclusively for typing. So that handler should do nothing when a text input is not focused. If a text field is focused, then the key input handler should do nothing. You need to implement logic that makes them mutually exclusive.Lawrencelawrencium
"If a text field is focused, then the key input handler should do nothing. You need to implement logic that makes them mutually exclusive." - Then you can't process keys like Enter, Backspace, Escape, Arrow keys when a text field is focused. Doesn't sound great.Selfwinding
G
1

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.

Gimel answered 1/2, 2023 at 6:44 Comment(2)
What platform are you on? This looks like what we do in github.com/lamdu/lamdu/commit/… but as I mention in the question we've got reports that this workaround didn't work well on some Ubuntu Linux setups. From what I've figured the real solution is that GLFW would needs to explicitly add support to this in the key event callback, see github.com/glfw/glfw/issues/1140#issuecomment-434104005Excuse
I'm testing this on Windows 11. I do agree that this seems like something the library could/should implement, since having two separate event handlers for keyboard input convolutes matters.Gimel

© 2022 - 2024 — McMap. All rights reserved.