[rcore] Caps Lock release is not registered on Desktop platforms
DenizBasgoren opened this issue · comments
- [ X] I tested it on latest raylib version from master branch
- [ X] I checked there is no similar issue already reported
- [ X] I checked the documentation on the wiki
- [ X] My code has no errors or misuse of raylib
Issue description
KEY_CAPS_LOCK is up at the program startup. When it's pressed, it goes down (IsKeyDown and IsKeyPressed work correctly), but then when I release it it gets stuck at down forever (IsKeyUp and IsKeyReleased don't work, and IsKeyDown, IsKeyPressed don't work correctly anymore).
Environment
OS: Linux (kernel version 6.9.3-arch1-1).
Code Example
#include <stdio.h>
#include <raylib.h>
#include <unistd.h>
#include <time.h>
void clearScreen(void) {
printf("\x1b[2J\x1b[H");
}
void printTime(void) {
time_t current_time = time(NULL);
char* time_string = ctime(¤t_time);
printf("%s\n", time_string);
}
void logCapsLock(void) {
if (IsKeyPressed(KEY_CAPS_LOCK)) {
puts("Capslock is pressed!");
}
if (IsKeyReleased(KEY_CAPS_LOCK)) {
puts("Capslock is released!");
}
if (IsKeyDown(KEY_CAPS_LOCK)) {
puts("Capslock is down!");
}
if (IsKeyUp(KEY_CAPS_LOCK)) {
puts("Capslock is up!");
}
}
int main(void) {
InitWindow(100, 100, "title");
SetTargetFPS(1);
while(true) {
clearScreen();
BeginDrawing();
if (IsKeyDown(KEY_Q)) {
break;
}
printTime();
logCapsLock();
EndDrawing();
}
puts("Done");
return 0;
}
Notes
I suspect that https://github.com/raysan5/raylib/blob/master/src/platforms/rcore_desktop.c is the file to be fixed. Particularly, these lines:
// GLFW3 Keyboard Callback, runs on key pressed
static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
{
// ...
// WARNING: Check if CAPS/NUM key modifiers are enabled and force down state for those keys
if (((key == KEY_CAPS_LOCK) && ((mods & GLFW_MOD_CAPS_LOCK) > 0)) ||
((key == KEY_NUM_LOCK) && ((mods & GLFW_MOD_NUM_LOCK) > 0))) CORE.Input.Keyboard.currentKeyState[key] = 1;
)
I just spent the better part of ~2 hours staring at this and trying stuff. it might be a bit ranty but hopefully this helps someone else
mods
bitwise flags do not seem to be updated until the next frame. that is on GLFW, but this is what raylib is using to identify the caps/num lock states. this is VERY important- the callback triggers (at least) twice. once for press and once for release (and hold for a 3rd trigger). this is important to note because the if statement to set the
.state = 1
does NOT depend on the action - GLFW "mods" and raylibs internal keyboard states are entirely separate so the logic to "fix" this is a bit awkward imo
these steps may make it easier to understand (assuming 1 FPS but you get the point):
- start the app with caps lock disabled
- enable caps lock.
- frame 1 (press): the output will be mods = 32 (disabled)
- frame 1 (release): the output will be mods = 48 (enabled)
- frame 2:
IsKeyPressed(KEY_CAPS_LOCK) = true
- disable caps lock.
- frame 3 (press): the output will be mods = 48 (enabled)
- frame 3 (release): the output will be mods = 48 (enabled)
- note: this should disable caps lock but it can not due to the 1 frame delay on
mods
and raylib forcingstate = 1
- press any other key, such as "a"
- frame 4 (press): the output will be mods = 32 (disabled)
- frame 4 (release): the output will be mods = 32 (disabled)
- frame 5:
IsKeyReleased(KEY_CAPS_LOCK) = true
notice the GLFW mods
updates on the NEXT press. however, raylib never disables it unless you press another key and wait another frame or two
also notice the IsKeyReleased
only happens when raylib internally sets CORE.Input.Keyboard.currentKeyState = 0
, which only happens after mods
updates
i dont know an easy solution for this. running it locally and playing with static void KeyCallback
the logic gets really messy due to GLFW updating 1 frame later
it would be nice to do something like this, but we cant since mods
is not updated until 1 frame later and the callback only happens on key press
if ((mods & GLFW_MOD_CAPS_LOCK) > 0) {
CORE.Input.Keyboard.currentKeyState[KEY_CAPS_LOCK] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_CAPS_LOCK] = 0;
}
modified sample from OP:
#include <stdio.h>
#include <raylib.h>
void logCapsLock() {
if (IsKeyPressed(KEY_CAPS_LOCK)) {
printf("Capslock is pressed!\n");
}
if (IsKeyReleased(KEY_CAPS_LOCK)) {
printf("Capslock is released!\n");
}
if (IsKeyDown(KEY_CAPS_LOCK)) {
printf("Capslock is enabled!\n");
}
if (IsKeyUp(KEY_CAPS_LOCK)) {
printf("Capslock is NOT enabled!\n");
}
}
int main() {
InitWindow(100, 100, "");
SetTargetFPS(1);
int curFrame = 1;
while(!WindowShouldClose()) {
printf("starting frame %d\n", curFrame);
BeginDrawing();
ClearBackground(RAYWHITE);
logCapsLock();
EndDrawing();
curFrame++;
printf("\n\n");
}
return 0;
}
modified raylib for logging:
// GLFW3 Keyboard Callback, runs on key pressed
static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
{
if (key < 0) return; // Security check, macOS fn key generates -1
printf("new key: %d, action: %d, mods: %d\n", key, action, mods);
// ...
OS: Linux (kernel version 6.9.4-zen1-1-zen).
@DenizBasgoren @CrackedPixel This could be a very sensitive change and can break some codebases, not sure about supporting it. It's also dependant on GLFW behaviour...
@DenizBasgoren @CrackedPixel This could be a very sensitive change and can break some codebases, not sure about supporting it. It's also dependant on GLFW behaviour...
Agreed. I think it should be fixed in GLFW because that's the part that's not working in the desired way
@CrackedPixel Agree, this behaviour comes from a lower-level API...