diff --git a/examples/text/text_input_box.c b/examples/text/text_input_box.c index 4d2eee442..8234f05ce 100644 --- a/examples/text/text_input_box.c +++ b/examples/text/text_input_box.c @@ -2,7 +2,7 @@ * * raylib [text] example - Input Box * -* Example originally created with raylib 1.7, last time updated with raylib 3.5 +* Example originally created with raylib 1.7, last time updated with raylib 4.0 * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software @@ -35,7 +35,7 @@ int main(void) int framesCounter = 0; - SetTargetFPS(10); // Set our game to run at 10 frames-per-second + SetTargetFPS(30); // Set our game to run at 30 frames-per-second //-------------------------------------------------------------------------------------- // Main game loop @@ -68,7 +68,7 @@ int main(void) key = GetCharPressed(); // Check next character in the queue } - if (IsKeyPressed(KEY_BACKSPACE)) + if (IsKeyRepeated(KEY_BACKSPACE)) { letterCount--; if (letterCount < 0) letterCount = 0; @@ -118,15 +118,3 @@ int main(void) return 0; } - -// Check if any key is pressed -// NOTE: We limit keys check to keys between 32 (KEY_SPACE) and 126 -bool IsAnyKeyPressed() -{ - bool keyPressed = false; - int key = GetKeyPressed(); - - if ((key >= 32) && (key <= 126)) keyPressed = true; - - return keyPressed; -} diff --git a/src/config.h b/src/config.h index 6856ce4fc..25de33469 100644 --- a/src/config.h +++ b/src/config.h @@ -83,6 +83,7 @@ #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported #define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue #define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue +#define MAX_KEY_REPEATED_QUEUE 16 // Maximum number of keys in the key repeat queue #define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB diff --git a/src/raylib.h b/src/raylib.h index a3ce8c1ff..e8158fa69 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1096,9 +1096,11 @@ RLAPI bool IsKeyPressed(int key); // Check if a key RLAPI bool IsKeyDown(int key); // Check if a key is being pressed RLAPI bool IsKeyReleased(int key); // Check if a key has been released once RLAPI bool IsKeyUp(int key); // Check if a key is NOT being pressed +RLAPI bool IsKeyRepeated(int key); // Check if a key has been repeated RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC) RLAPI int GetKeyPressed(void); // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty RLAPI int GetCharPressed(void); // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty +RLAPI int GetKeyRepeated(void); // Get key pressed (keycode) with repeat rate, call it multiple times for keys queued, returns 0 when the queue is empty // Input-related functions: gamepads RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available diff --git a/src/rcore.c b/src/rcore.c index 5f9633fa6..19d28139b 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -424,6 +424,7 @@ typedef struct CoreData { int exitKey; // Default exit key char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state + char repeatedKeyState[MAX_KEYBOARD_KEYS]; // Registers repeated key state int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue int keyPressedQueueCount; // Input keys queue count @@ -431,6 +432,9 @@ typedef struct CoreData { int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode) int charPressedQueueCount; // Input characters queue count + int keyRepeatedQueue[MAX_KEY_REPEATED_QUEUE]; // Input keys repeated queue + int keyRepeatedQueueCount; // Input keys repeated queue count + #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) int defaultMode; // Default keyboard mode #if defined(SUPPORT_SSH_KEYBOARD_RPI) @@ -1414,7 +1418,7 @@ void SetWindowState(unsigned int flags) { TRACELOG(LOG_WARNING, "WINDOW: High DPI can only by configured before window initialization"); } - + // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) != (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)) { @@ -1522,7 +1526,7 @@ void ClearWindowState(unsigned int flags) { TRACELOG(LOG_WARNING, "WINDOW: High DPI can only by configured before window initialization"); } - + // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)) { @@ -1762,7 +1766,7 @@ int GetMonitorWidth(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) { const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - + if (mode) return mode->width; else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); } @@ -1781,7 +1785,7 @@ int GetMonitorHeight(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) { const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - + if (mode) return mode->height; else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); } @@ -2844,7 +2848,7 @@ bool FileExists(const char *fileName) bool IsFileExtension(const char *fileName, const char *ext) { #define MAX_FILE_EXTENSION_SIZE 16 - + bool result = false; const char *fileExt = GetFileExtension(fileName); @@ -3125,14 +3129,14 @@ FilePathList LoadDirectoryFiles(const char *dirPath) { FilePathList files = { 0 }; unsigned int fileCounter = 0; - + struct dirent *entity; DIR *dir = opendir(dirPath); if (dir != NULL) // It's a directory { // SCAN 1: Count files - while ((entity = readdir(dir)) != NULL) + while ((entity = readdir(dir)) != NULL) { // NOTE: We skip '.' (current dir) and '..' (parent dir) filepaths if ((strcmp(entity->d_name, ".") != 0) && (strcmp(entity->d_name, "..") != 0)) fileCounter++; @@ -3144,16 +3148,16 @@ FilePathList LoadDirectoryFiles(const char *dirPath) for (unsigned int i = 0; i < files.capacity; i++) files.paths[i] = (char *)RL_MALLOC(MAX_FILEPATH_LENGTH*sizeof(char)); closedir(dir); - + // SCAN 2: Read filepaths // NOTE: Directory paths are also registered ScanDirectoryFiles(dirPath, &files, NULL); - + // Security check: read files.count should match fileCounter if (files.count != files.capacity) TRACELOG(LOG_WARNING, "FILEIO: Read files count do not match capacity allocated"); } else TRACELOG(LOG_WARNING, "FILEIO: Failed to open requested directory"); // Maybe it's a file... - + return files; } @@ -3226,13 +3230,13 @@ FilePathList LoadDroppedFiles(void) void UnloadDroppedFiles(FilePathList files) { // WARNING: files pointers are the same as internal ones - + if (files.count > 0) { for (unsigned int i = 0; i < files.count; i++) RL_FREE(files.paths[i]); RL_FREE(files.paths); - + CORE.Window.dropFileCount = 0; CORE.Window.dropFilepaths = NULL; } @@ -3485,6 +3489,16 @@ bool IsKeyUp(int key) else return false; } +// Check if a key has been repeated +bool IsKeyRepeated(int key) +{ + bool repeated = false; + + if (CORE.Input.Keyboard.repeatedKeyState[key] == 1) repeated = true; + + return repeated; +} + // Get the last key pressed int GetKeyPressed(void) { @@ -3529,6 +3543,28 @@ int GetCharPressed(void) return value; } +// Get the last key pressed with repeat rate +int GetKeyRepeated(void) +{ + int value = 0; + + if (CORE.Input.Keyboard.keyRepeatedQueueCount > 0) + { + // Get character from the queue head + value = CORE.Input.Keyboard.keyRepeatedQueue[0]; + + // Shift elements 1 step toward the head. + for (int i = 0; i < (CORE.Input.Keyboard.keyRepeatedQueueCount - 1); i++) + CORE.Input.Keyboard.keyRepeatedQueue[i] = CORE.Input.Keyboard.keyRepeatedQueue[i + 1]; + + // Reset last character in the queue + CORE.Input.Keyboard.keyRepeatedQueue[CORE.Input.Keyboard.keyRepeatedQueueCount] = 0; + CORE.Input.Keyboard.keyRepeatedQueueCount--; + } + + return value; +} + // Set a custom key to exit program // NOTE: default exitKey is ESCAPE void SetExitKey(int key) @@ -3771,7 +3807,7 @@ void SetMouseScale(float scaleX, float scaleY) float GetMouseWheelMove(void) { float result = 0.0f; - + #if !defined(PLATFORM_ANDROID) if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x; else result = (float)CORE.Input.Mouse.currentWheelMove.y; @@ -3999,7 +4035,7 @@ static bool InitGraphicsDevice(int width, int height) #endif } else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE); - + // Mouse passthrough if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); else glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); @@ -4920,6 +4956,10 @@ void PollInputEvents(void) // Register previous mouse states for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; + // Clear previous repeated keys + // for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.repeatedKeyState[i] = 0; + memset(CORE.Input.Keyboard.repeatedKeyState, 0, MAX_KEYBOARD_KEYS * (sizeof(char))); + // Register previous mouse wheel state CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f }; @@ -5132,13 +5172,13 @@ void PollInputEvents(void) } // Scan all files and directories in a base path -// WARNING: files.paths[] must be previously allocated and +// WARNING: files.paths[] must be previously allocated and // contain enough space to store all required paths static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const char *filter) { static char path[MAX_FILEPATH_LENGTH] = { 0 }; memset(path, 0, MAX_FILEPATH_LENGTH); - + struct dirent *dp = NULL; DIR *dir = opendir(basePath); @@ -5150,7 +5190,7 @@ static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const (strcmp(dp->d_name, "..") != 0)) { sprintf(path, "%s/%s", basePath, dp->d_name); - + if (filter != NULL) { if (IsFileExtension(path, filter)) @@ -5177,7 +5217,7 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *fi { static char path[MAX_FILEPATH_LENGTH] = { 0 }; memset(path, 0, MAX_FILEPATH_LENGTH); - + struct dirent *dp = NULL; DIR *dir = opendir(basePath); @@ -5294,6 +5334,10 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0; else CORE.Input.Keyboard.currentKeyState[key] = 1; + // Same thing here: GLFW could return GLFW_PRESS, which is the initial + // key press that starts the repeat + if (action != GLFW_RELEASE) CORE.Input.Keyboard.repeatedKeyState[key] = 1; + // Check if there is space available in the key queue if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS)) { @@ -5302,6 +5346,14 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i CORE.Input.Keyboard.keyPressedQueueCount++; } + // Check if there is space available in the repeated key queue + if ((CORE.Input.Keyboard.keyRepeatedQueueCount < MAX_KEY_REPEATED_QUEUE) && (action == GLFW_REPEAT || action == GLFW_PRESS)) + { + // Add character to the queue + CORE.Input.Keyboard.keyRepeatedQueue[CORE.Input.Keyboard.keyRepeatedQueueCount] = key; + CORE.Input.Keyboard.keyRepeatedQueueCount++; + } + // Check the exit key to set close window if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE); @@ -5474,11 +5526,11 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]); RL_FREE(CORE.Window.dropFilepaths); - + CORE.Window.dropFileCount = 0; CORE.Window.dropFilepaths = NULL; } - + // WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy CORE.Window.dropFileCount = count; CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *));